C# 版 flvmerge:快速合并多个flv文件

简介:

网上的视频很多都是分片的flv文件,怎么把他们合为一体呢?GUI工具就不考虑了,不适合批量执行,不适合在后台运行。有没有命令行工具或库可以实现呢?

ffmpeg 提供了一个方法:

(1)先把flv文件转换成mpeg;

(2)将多个mpeg文件合并成1个独立的mpeg文件(二进制合并即可)

(3)将独立的mpeg文件转换成独立的flv文件。

网上搜到的最多的也是这种解决办法。这种方法有两个缺点:

(1)需要两遍转码,非常耗时;

(2)转换后的独立的mpeg文件比原视频要短一点点。

木有办法了,只好另寻他路。有人说有一个flvmerge.exe 程序可以将多个flv合并成一个,可惜的是俺搜了很久,都没找到这个程序,最后还是在一款免费软件里把这个“flvmerge.exe”文件给揪出来了,不幸的是,这个“flvmerge.exe”得不到正确的结果。

润之同学说过,自己动手,丰衣足食。上 github 上搜“flvmerge”,发现两个项目,“flvmerge”和“flvmerger”,都是C写的。前者不依赖于第三方库,后者依赖于第三方库,那么就从第一个开始吧。

看了看它的代码,知道了flv文件合并的原理:

(1) flv 文件由1个header和若干个tag组成;

(2) header记录了视频的元数据;

(3) tag 是有时间戳的数据;

(4) flv合并的原理就是把多个文件里的tag组装起来,调整各tag的时间戳,再在文件起始处按个头部。

下面是我参照 flvmerge 项目,用linqpad写的一个C#版本的 flvmerge 代码:

复制代码
1 void Main()
2 {
3 String path1 = "D:\Videos\Subtitle\OutputCache\1.flv";
4 String path2 = "D:\Videos\Subtitle\OutputCache\2.flv";
5 String path3 = "D:\Videos\Subtitle\OutputCache\3.flv";
6 String output = "D:\Videos\Subtitle\OutputCache\output.flv";
7
8 using(FileStream fs1 = new FileStream(path1, FileMode.Open))
9 using(FileStream fs2 = new FileStream(path2, FileMode.Open))
10 using(FileStream fs3 = new FileStream(path3, FileMode.Open))
11 using(FileStream fsMerge = new FileStream(output, FileMode.Create))
12 {
13 Console.WriteLine(IsFLVFile(fs1));
14 Console.WriteLine(IsFLVFile(fs2));
15 Console.WriteLine(IsFLVFile(fs3));
16
17 if(IsSuitableToMerge(GetFLVFileInfo(fs1),GetFLVFileInfo(fs2)) == false
18 || IsSuitableToMerge(GetFLVFileInfo(fs1),GetFLVFileInfo(fs3)) == false)
19 {
20 Console.WriteLine("Video files not suitable to merge");
21 }
22
23 int time = Merge(fs1,fsMerge,true,0);
24 time = Merge(fs2,fsMerge,false,time);
25 time = Merge(fs3,fsMerge,false,time);
26 Console.WriteLine("Merge finished");
27 }
28 }
29
30 const int FLV_HEADER_SIZE = 9;
31 const int FLV_TAG_HEADER_SIZE = 11;
32 const int MAX_DATA_SIZE = 16777220;
33
34 class FLVContext
35 {
36 public byte soundFormat;
37 public byte soundRate;
38 public byte soundSize;
39 public byte soundType;
40 public byte videoCodecID;
41 }
42
43 bool IsSuitableToMerge(FLVContext flvCtx1, FLVContext flvCtx2)
44 {
45 return (flvCtx1.soundFormat == flvCtx2.soundFormat) &&
46 (flvCtx1.soundRate == flvCtx2.soundRate) &&
47 (flvCtx1.soundSize == flvCtx2.soundSize) &&
48 (flvCtx1.soundType == flvCtx2.soundType) &&
49 (flvCtx1.videoCodecID == flvCtx2.videoCodecID);
50 }
51
52 bool IsFLVFile(FileStream fs)
53 {
54 int len;
55 byte[] buf = new byte[FLV_HEADER_SIZE];
56 fs.Position = 0;
57 if( FLV_HEADER_SIZE != fs.Read(buf,0,buf.Length))
58 return false;
59
60 if (buf[0] != 'F' || buf[1] != 'L' || buf[2] != 'V' || buf[3] != 0x01)
61 return false;
62 else
63 return true;
64 }
65
66 FLVContext GetFLVFileInfo(FileStream fs)
67 {
68 bool hasAudioParams, hasVideoParams;
69 int skipSize, readLen;
70 int dataSize;
71 byte tagType;
72 byte[] tmp = new byte[FLV_TAG_HEADER_SIZE+1];
73 if (fs == null) return null;
74
75 FLVContext flvCtx = new FLVContext();
76 fs.Position = 0;
77 skipSize = 9;
78 fs.Position += skipSize;
79 hasVideoParams = hasAudioParams = false;
80 skipSize = 4;
81 while (!hasVideoParams || !hasAudioParams)
82 {
83 fs.Position += skipSize;
84
85 if (FLV_TAG_HEADER_SIZE+1 != fs.Read(tmp,0,tmp.Length))
86 return null;
87
88 tagType = (byte)(tmp[0] & 0x1f);
89 switch (tagType)
90 {
91 case 8 :
92 flvCtx.soundFormat = (byte)((tmp[FLV_TAG_HEADER_SIZE] & 0xf0) >> 4) ;
93 flvCtx.soundRate = (byte)((tmp[FLV_TAG_HEADER_SIZE] & 0x0c) >> 2) ;
94 flvCtx.soundSize = (byte)((tmp[FLV_TAG_HEADER_SIZE] & 0x02) >> 1) ;
95 flvCtx.soundType = (byte)((tmp[FLV_TAG_HEADER_SIZE] & 0x01) >> 0) ;
96 hasAudioParams = true;
97 break;
98 case 9 :
99 flvCtx.videoCodecID = (byte)((tmp[FLV_TAG_HEADER_SIZE] & 0x0f));
100 hasVideoParams = true;
101 break;
102 default :
103 break;
104 }
105
106 dataSize = FromInt24StringBe(tmp[1],tmp[2],tmp[3]);
107 skipSize = dataSize - 1 + 4;
108 }
109
110 return flvCtx;
111 }
112
113 int FromInt24StringBe(byte b0, byte b1, byte b2)
114 {
115 return (int)((b0<<16) | (b1<<8) | (b2));
116 }
117
118 int GetTimestamp(byte b0, byte b1, byte b2, byte b3)
119 {
120 return ((b3<<24) | (b0<<16) | (b1<<8) | (b2));
121 }
122
123 void SetTimestamp(byte[] data, int idx, int newTimestamp)
124 {
125 data[idx + 3] = (byte)(newTimestamp>>24);
126 data[idx + 0] = (byte)(newTimestamp>>16);
127 data[idx + 1] = (byte)(newTimestamp>>8);
128 data[idx + 2] = (byte)(newTimestamp);
129 }
130
131 int Merge(FileStream fsInput, FileStream fsMerge, bool isFirstFile, int lastTimestamp = 0)
132 {
133 int readLen;
134 int curTimestamp = 0;
135 int newTimestamp = 0;
136 int dataSize;
137 byte[] tmp = new byte[20];
138 byte[] buf = new byte[MAX_DATA_SIZE];
139
140 fsInput.Position = 0;
141 if (isFirstFile)
142 {
143 if(FLV_HEADER_SIZE+4 == (fsInput.Read(tmp,0,FLV_HEADER_SIZE+4)))
144 {
145 fsMerge.Position = 0;
146 fsMerge.Write(tmp,0,FLV_HEADER_SIZE+4);
147 }
148 }
149 else
150 {
151 fsInput.Position = FLV_HEADER_SIZE + 4;
152 }
153
154 while(fsInput.Read(tmp, 0, FLV_TAG_HEADER_SIZE) > 0)
155 {
156 dataSize = FromInt24StringBe(tmp[1],tmp[2],tmp[3]);
157 curTimestamp = GetTimestamp(tmp[4],tmp[5],tmp[6],tmp[7]);
158 newTimestamp = curTimestamp + lastTimestamp;
159 SetTimestamp(tmp,4, newTimestamp);
160 fsMerge.Write(tmp,0,FLV_TAG_HEADER_SIZE);
161
162 readLen = dataSize+4;
163 if (fsInput.Read(buf,0,readLen) > 0) {
164 fsMerge.Write(buf, 0, readLen);
165 } else {
166 goto failed;
167 }
168 }
169
170 return newTimestamp;
171
172 failed:
173 throw new Exception("Merge Failed");
174 }
复制代码

测试通过,合并速度很快!

不过,这个方法有一个缺点:没有将各个文件里的关键帧信息合并,这个关键帧信息,切分flv文件时很重要,合并时就没那么重要了。如果确实需要的话,可以用 yamdi 来处理。
本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/p/3441030.html如需转载请自行联系原作者

xiaotie 集异璧实验室(GEBLAB)

相关文章
|
9月前
|
存储 编解码 算法
MP4文件格式的解析,以及MP4文件的分割算法
MP4文件格式的解析,以及MP4文件的分割算法
123 0
|
12月前
|
存储 编解码 API
FLV格式解析
FLV(Flash Video)是现在非常流行的流媒体格式,由于其视频文件体积轻巧、封装播放简单等特点,使其很适合在网络上进行应用,目前主流的视频网站无一例外地使用了FLV格式。另外由于当前浏览器与Flash Player紧密的结合,使得网页播放FLV视频轻而易举,也是FLV流行的原因之一。
190 0
|
编解码 前端开发 安全
详细讲解ffmpeg命令的使用(视频合并&avi转MP4&补空白音频【收藏下来一定用的到】)
您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦。 本文从实战的角度出发详细讲解ffmpeg命令的使用。 干货满满,建议收藏,需要用到时常看看。 小伙伴们如有问题及需要,欢迎踊跃留言哦~ ~ ~。
1908 0
详细讲解ffmpeg命令的使用(视频合并&avi转MP4&补空白音频【收藏下来一定用的到】)
使用ffmpeg合并两个视频文件
使用ffmpeg合并两个视频文件
222 0
|
JavaScript 前端开发
requireJs压缩合并路径问题
随着前端开发的重要性,以及业务的复杂性,前端的模块化开发也被大众所接收,最常见的js框架requireJs,一个js文件对应一个模块,方便开发人员调试与维护,但是一个文件对应一个模块增加了http请求,降低了网站的性能。幸运的是requireJs提供了压缩工具r.js(点击下载),r.js需要node(Node 0.4.0 或更高版本,点击下载)环境支持,安装完node就可以在命令行里对前端代码进行优化了。
requireJs压缩合并路径问题
|
编解码 Ubuntu
FFMPEG音频视频开发: 视频转码、合并、修改分辨率、比特率
FFMPEG音频视频开发: 视频转码、合并、修改分辨率、比特率
1148 0
|
存储 编解码 数据安全/隐私保护
FLV 封装格式解析
FLV (Flash Video) 是由 Adobe 公司推出的一种封装格式,主要用于流媒体系统。FLV 封装的媒体文件具有体积轻巧、封装播放简单等特点,很适合网络应用。目前各浏览器普遍使用 Flash Player 作为网页播放器,使得安装有浏览器的计算机终端不需要另外安装播放器,这也是 FLV 格式广为流行的原因之一。
358 0
FLV 封装格式解析
|
计算机视觉
ffmpeg用mp4分段将hls保存到m3u8
ffmpeg用mp4分段将hls保存到m3u8 我正在寻找一个命令ffmpeg,用mp4分段文件保存实时输入(rtmp或hls)以使m3u8成为可能。我知道这是可能的,即有infohttps://bitmovin.com/hls-news-wwdc-2016/但我尝试的每个命令都会生成ts文件。
3963 0
|
前端开发 JavaScript 数据格式
gulp之自动化静态资源压缩合并加版本号
gulp之自动化压缩合并加版本号 这个方案主要是为了实现js/css/image的压缩合并、自动添加版本号、自动加浏览器前缀和压缩html。 先把下面这里插件 npm i (插件名) -D 安装到项目环境内 gulp-sequence //顺序执行任务 gulp-csso //css压缩 gulp...
1813 0
媒体格式分析之flv -- 基于FFMPEG
本来是应该先写一个媒体文件格式的简单讲解的,还没来得及写,以后再写。今天就先根据ffmpeg的flv.c的flv_demux这个结构体来讲解一下当前比较流行的媒体格式flv. FLV 是FLASH VIDEO的简称,FLV流媒体格式是随着Flash MX的推出发展而来的视频格式。
1090 0

热门文章

最新文章