网上的视频很多都是分片的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 代码:

 void Main()
{
String path1 = "D:\\Videos\\Subtitle\\OutputCache\\1.flv";
String path2 = "D:\\Videos\\Subtitle\\OutputCache\\2.flv";
String path3 = "D:\\Videos\\Subtitle\\OutputCache\\3.flv";
String output = "D:\\Videos\\Subtitle\\OutputCache\\output.flv"; using(FileStream fs1 = new FileStream(path1, FileMode.Open))
using(FileStream fs2 = new FileStream(path2, FileMode.Open))
using(FileStream fs3 = new FileStream(path3, FileMode.Open))
using(FileStream fsMerge = new FileStream(output, FileMode.Create))
{
Console.WriteLine(IsFLVFile(fs1));
Console.WriteLine(IsFLVFile(fs2));
Console.WriteLine(IsFLVFile(fs3)); if(IsSuitableToMerge(GetFLVFileInfo(fs1),GetFLVFileInfo(fs2)) == false
|| IsSuitableToMerge(GetFLVFileInfo(fs1),GetFLVFileInfo(fs3)) == false)
{
Console.WriteLine("Video files not suitable to merge");
} int time = Merge(fs1,fsMerge,true,);
time = Merge(fs2,fsMerge,false,time);
time = Merge(fs3,fsMerge,false,time);
Console.WriteLine("Merge finished");
}
} const int FLV_HEADER_SIZE = ;
const int FLV_TAG_HEADER_SIZE = ;
const int MAX_DATA_SIZE = ; class FLVContext
{
public byte soundFormat;
public byte soundRate;
public byte soundSize;
public byte soundType;
public byte videoCodecID;
} bool IsSuitableToMerge(FLVContext flvCtx1, FLVContext flvCtx2)
{
return (flvCtx1.soundFormat == flvCtx2.soundFormat) &&
(flvCtx1.soundRate == flvCtx2.soundRate) &&
(flvCtx1.soundSize == flvCtx2.soundSize) &&
(flvCtx1.soundType == flvCtx2.soundType) &&
(flvCtx1.videoCodecID == flvCtx2.videoCodecID);
} bool IsFLVFile(FileStream fs)
{
int len;
byte[] buf = new byte[FLV_HEADER_SIZE];
fs.Position = ;
if( FLV_HEADER_SIZE != fs.Read(buf,,buf.Length))
return false; if (buf[] != 'F' || buf[] != 'L' || buf[] != 'V' || buf[] != 0x01)
return false;
else
return true;
} FLVContext GetFLVFileInfo(FileStream fs)
{
bool hasAudioParams, hasVideoParams;
int skipSize, readLen;
int dataSize;
byte tagType;
byte[] tmp = new byte[FLV_TAG_HEADER_SIZE+];
if (fs == null) return null; FLVContext flvCtx = new FLVContext();
fs.Position = ;
skipSize = ;
fs.Position += skipSize;
hasVideoParams = hasAudioParams = false;
skipSize = ;
while (!hasVideoParams || !hasAudioParams)
{
fs.Position += skipSize; if (FLV_TAG_HEADER_SIZE+ != fs.Read(tmp,,tmp.Length))
return null; tagType = (byte)(tmp[] & 0x1f);
switch (tagType)
{
case :
flvCtx.soundFormat = (byte)((tmp[FLV_TAG_HEADER_SIZE] & 0xf0) >> ) ;
flvCtx.soundRate = (byte)((tmp[FLV_TAG_HEADER_SIZE] & 0x0c) >> ) ;
flvCtx.soundSize = (byte)((tmp[FLV_TAG_HEADER_SIZE] & 0x02) >> ) ;
flvCtx.soundType = (byte)((tmp[FLV_TAG_HEADER_SIZE] & 0x01) >> ) ;
hasAudioParams = true;
break;
case :
flvCtx.videoCodecID = (byte)((tmp[FLV_TAG_HEADER_SIZE] & 0x0f));
hasVideoParams = true;
break;
default :
break;
} dataSize = FromInt24StringBe(tmp[],tmp[],tmp[]);
skipSize = dataSize - + ;
} return flvCtx;
} int FromInt24StringBe(byte b0, byte b1, byte b2)
{
return (int)((b0<<) | (b1<<) | (b2));
} int GetTimestamp(byte b0, byte b1, byte b2, byte b3)
{
return ((b3<<) | (b0<<) | (b1<<) | (b2));
} void SetTimestamp(byte[] data, int idx, int newTimestamp)
{
data[idx + ] = (byte)(newTimestamp>>);
data[idx + ] = (byte)(newTimestamp>>);
data[idx + ] = (byte)(newTimestamp>>);
data[idx + ] = (byte)(newTimestamp);
} int Merge(FileStream fsInput, FileStream fsMerge, bool isFirstFile, int lastTimestamp = )
{
int readLen;
int curTimestamp = ;
int newTimestamp = ;
int dataSize;
byte[] tmp = new byte[];
byte[] buf = new byte[MAX_DATA_SIZE]; fsInput.Position = ;
if (isFirstFile)
{
if(FLV_HEADER_SIZE+ == (fsInput.Read(tmp,,FLV_HEADER_SIZE+)))
{
fsMerge.Position = ;
fsMerge.Write(tmp,,FLV_HEADER_SIZE+);
}
}
else
{
fsInput.Position = FLV_HEADER_SIZE + ;
} while(fsInput.Read(tmp, , FLV_TAG_HEADER_SIZE) > )
{
dataSize = FromInt24StringBe(tmp[],tmp[],tmp[]);
curTimestamp = GetTimestamp(tmp[],tmp[],tmp[],tmp[]);
newTimestamp = curTimestamp + lastTimestamp;
SetTimestamp(tmp,, newTimestamp);
fsMerge.Write(tmp,,FLV_TAG_HEADER_SIZE); readLen = dataSize+;
if (fsInput.Read(buf,,readLen) > ) {
fsMerge.Write(buf, , readLen);
} else {
goto failed;
}
} return newTimestamp; failed:
throw new Exception("Merge Failed");
}

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

不过,这个方法有一个缺点:没有将各个文件里的关键帧信息合并,这个关键帧信息,切分flv文件时很重要,合并时就没那么重要了。如果确实需要的话,可以用 yamdi 来处理。

C# 版 flvmerge:快速合并多个flv文件的更多相关文章

  1. 批处理快速合并多分Excel文件并将指定列的数据去重复

    1.批处理快速合并多个excel文件方法: 新建一个.txt文本文件,就命名为合并.txt吧. 而后开启文件,复制以下代码到文件中: @echo off E: cd xls dir copy *.cs ...

  2. C# 版dll 程序集合并工具

    C# 版dll 程序集合并工具 最近要开发一个控件给同事用,开发中会引用一些第三方DLL,这样交给用户很不方便,希望的效果是直接交付一个DLL文件.网上找了一些资料. 1.       使用 Cost ...

  3. 【前端】一句命令快速合并压缩 JS、CSS

    引用自:一句命令快速合并 JS.CSS 在项目开发环境下,我们会把 JS 代码尽可能模块化,方便管理和修改,这就避免不了会出现一个项目自身 JS 文件数量达到10个或者更多. 而项目上线后,会要求将所 ...

  4. Excel 批量快速合并相同的单元格:数据透视表、宏代码、分类汇总

    Excel 批量快速合并相同的单元格   在制作Excel表格的时候,为了使得自己制作的报表更加简洁明了,方便查阅,经常需要合并很多相同的单元格,如果有几千几万条记录需要合并的话,真的会让人发疯.怎样 ...

  5. 使用ffmpeg批量合并flv文件

    title: 使用ffmpeg批量合并flv文件 toc: false date: 2018-10-14 16:08:19 categories: methods tags: ffmpeg flv 使 ...

  6. 152-技巧-Power Query 快速合并文件夹中表格之自定义函数 TableXlsxCsv

    152-技巧-Power Query 快速合并文件夹中表格之自定义函数 TableXlsxCsv 附件下载地址:https://jiaopengzi.com/2602.html 一.背景 在我们使用 ...

  7. 如何快速合并多个TXT文本内容

    工作中有时候需要合并很多文本内容,例如一些推送清单之类,一个一个打开去复制粘贴的话,少量还行,如果txt文本数据量大(10+M以上)且文件数量多(成百上千),这种方式就显得很低效了.具体要求如下:   ...

  8. 【转】打包AAC码流到FLV文件

    AAC编码后数据打包到FLV很简单.1. FLV音频Tag格式                              字节位置    意义0x08,                         ...

  9. C# 合并和拆分PDF文件

    一.合并和拆分PDF文件的方式 PDF文件使用了工业标准的压缩算法,易于传输与储存.它还是页独立的,一个PDF文件包含一个或多个"页",可以单独处理各页,特别适合多处理器系统的工作 ...

随机推荐

  1. webrtc进阶-信令篇-之三:信令、stun、turn、ice

    webRTC支持点对点通讯,但是webRTC仍然需要服务端:  . 协调通讯过程中客户端之间需要交换元数据,    如一个客户端找到另一个客户端以及通知另一个客户端开始通讯.  . 需要处理NAT(网 ...

  2. DIV+CSS:页脚永远保持在页面底部

    页脚永远保持在页面底部 有时候,我们用CSS创建一个高度自适应布局,如何保证页脚(footer)在内容不超过一屏的情况下始终保持在布局最下方是一个比较头疼的事.我看过一些利用绝对定位的例子,但总感觉不 ...

  3. python代码优化---就喜欢细节

    地址:http://www.codeproject.com/Tips/829060/Python-Code-Optimizations-Part-One 转发过来保存一下.喜欢精雕细琢,编程才有乐趣. ...

  4. 使用my exclipse对数据库进行操作(3)

    public class class3 { public static void main(String[] args) { // TODO Auto-generated method stub tr ...

  5. OD使用教程10

       首先载入程序,然后Ctrl+N打开输入输出表 然后直接输入要找到函数,找到之后下断点,右键-在每个参考上设置断点 然后运行程序来到第一个断点处 然后f8走,开始找注册码没找到就换一个函数,然后看 ...

  6. ElasticSearch学习-centos下安装

    1.安装java运行环境(jre) //这里我安装了jdk 其实只需要安装jre就可以了 0)cd /usr;mkdir /usr/java; cd java 1)wget http://downlo ...

  7. MyEclipse快捷键敏感设置

    对于一个程序员来说,敲代码没有快捷键是很难受的.自从我装了MyEclipse之后发现快捷键敏感性太差了比如说我打输出语句System.out.println();一般打syso就会有提示,但是我的My ...

  8. MATLAB 画出三个通信小区cell边界示意图

    d=1000; %两个小区中心间距离的一半 rcell=2*d/sqrt(3); %小区半径 ncell=3; %小区个数 cellposition=zeros(ncell,2); %初始化小区中心位 ...

  9. ViewPager及PagerTabStrip 的使用详解

    ViewPager 就是一个滑屏效果的一个控件,使用比较简单.使用过程思路流程基本如下: 在需要添加的ViewPager的布局文件中添加ViewPager控件--->准备好滑屏所有的View-- ...

  10. 近期C#项目中总结

    1. 读写文件操作 using (file = new System.IO.StreamReader(inputfile)) { using (outfile = new System.IO.Stre ...