网上的视频很多都是分片的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 来处理。

转载于:https://www.cnblogs.com/xiaotie/p/3441030.html

C# 版 flvmerge:快速合并多个flv文件相关推荐

  1. html多个图片转换为pdf文件,把多张图片快速合并成一个pdf文件

    08 September 2020 把多张图片快速合并成一个pdf文件 img2pdf方案 安装img2pdf,参考:https://gitlab.mister-muffin.de/josch/img ...

  2. wireshark合并多个文件_小技巧:快速合并多个excel文件(收藏版)

    我们在日常工作或科研中,总会遇到需要汇总多个excel的情况,若仅仅是几个表格,大多人会直接复制,若有上百个表格呢?     那么小编就告诉大家一个稍微简洁一点的方法,将多个单独的excel表格快速合 ...

  3. 利用Python快速合并多个excel文件

    # -*- coding: utf-8 -*- """ Created on Mon Jan 7 12:07:56 2019@author: BIG DATA " ...

  4. tsd3dmapper软件使用方法_TS文件连接器:如何快速合并TS文件?

    吴川 华南区技术负责人 概要 TS是"Transport Stream"的缩写,它是一种音视频封装格式,格式全称为MPEG2-TS.当我们想合并多个TS文件,或者想将TS文件与其他 ...

  5. Excel快速合并多行数据

    Excel快速合并多行数据 目录 Excel快速合并多行数据 1.将内容所在列拉宽. 2.选中数据,找到"开始"选项卡中"填充"点击"两端对齐&quo ...

  6. Excel表格中多个文本内容快速合并到一个单元格内

    Excel表格中多个文本内容快速合并到一个单元格内 目录 Excel表格中多个文本内容快速合并到一个单元格内 1.在合并单元格内输入"=PHONETIC()"函数 2.框选需要合并 ...

  7. 【ffmpeg教程】【无损快速转换】两行代码 快速无损转换mkv flv视频文件 第一期

    [ffmpeg教程][无损快速转换]两行代码 快速无损转换mkv flv视频文件 第一期 前言 环境准备 脚本编写 运行脚本 前言 视频版教程:[无损快速转换]两行代码 快速无损转换mkv flv视频 ...

  8. 快速分割任何视频 Flv 音频aac wma等等文件,无需重新编码

    快速分割任何视频 Flv 音频aac wma等等文件,无需重新编码 http://www.rin9.com/read.php?tid=761469 图片:  图片:  软件大小:4.2MB 软件语言: ...

  9. 一款绿色免费的FLV文件合并裁剪器

    软件名称:花园FLV文件合并裁剪器 软件版本:1.0版 文件大小:452KB 授权形式:免费软件 应用平台:Windows 98/Windows 2000/Windows XP/VISTA/Windo ...

  10. 视频合成软件哪个好,怎么把多个视频快速合并成一个视频

    在观看抖音短视频时,进场会碰到视频中用类似的视频作比较,其实就是把单个视频快速合并成一个视频,这种方法在很多时候都需要用的,也可以将一些类似或者有趣的短视频拼凑在一起观看效果更佳.以下就是使用视频合成 ...

最新文章

  1. linux互斥锁和条件变量,如何理解互斥锁和条件变量?
  2. 腾讯:我就是那只吃了假辣椒酱的憨憨。老干妈:企鹅你可长点心吧!
  3. [译] NSCollectionView 入门教程
  4. max格式转obj小工具_Python写图片格式批量处理工具!你还一张一张转格式吗?
  5. 2019ICPC(南京) - Holy Grail(最短路)
  6. 由strcat函数引发的对char *a和char a[]以及sizeof和strlen
  7. Oracle查询所有序列
  8. mobile web页面调试方法
  9. db2表名大小写问题
  10. 【优化算法】自治群体粒子群优化算法(AGPSO)【含Matlab源码 1450期】
  11. visual studio 2015 启动停止工作, 问题事件名称:APPCRASH 应用程序名:devenv.exe 故障模块名称:
  12. java网课|File类递归
  13. 【冬察冬见】FFmpeg系列学习笔记
  14. 微机原理与接口技术课内实验-NUAA-Masm for Windows
  15. NSFC 国家自然科学基金查询
  16. 暑期实习Day7---SpringMVC
  17. 感悟·随笔·自弹自唱
  18. chrome DevTools之黑箱大法(Blackbox )
  19. iOS 获取连接的WiFi和Mac地址
  20. Gradle 的Dependencies

热门文章

  1. uva437 巴比伦塔
  2. 8-08双重循环--九九乘法表
  3. git an error occurred
  4. linux下多点电容触摸屏实验
  5. BZOJ[2827]千山鸟飞绝 线段树
  6. 互联网老辛2022年3月上旬社群精华
  7. 高等代数--双线性空间与辛空间
  8. 关于反转的总结(C/C++)
  9. 智能仓储系统作业流程及价值
  10. python发outlook邮件_通过Python发送Outlook电子邮件?