打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

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

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 文件由1header和若干个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     else150     {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 来处理。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
串联波文件使用CSharp 2005
.NET实现word在线对比
pictureEdit 实现图片的自适应大小
C#中文件和byte[]互换问题
将照片转化为byte[]
FileStream 类 (System.IO)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服