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 代码:
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文件的更多相关文章
- 批处理快速合并多分Excel文件并将指定列的数据去重复
1.批处理快速合并多个excel文件方法: 新建一个.txt文本文件,就命名为合并.txt吧. 而后开启文件,复制以下代码到文件中: @echo off E: cd xls dir copy *.cs ...
- C# 版dll 程序集合并工具
C# 版dll 程序集合并工具 最近要开发一个控件给同事用,开发中会引用一些第三方DLL,这样交给用户很不方便,希望的效果是直接交付一个DLL文件.网上找了一些资料. 1. 使用 Cost ...
- 【前端】一句命令快速合并压缩 JS、CSS
引用自:一句命令快速合并 JS.CSS 在项目开发环境下,我们会把 JS 代码尽可能模块化,方便管理和修改,这就避免不了会出现一个项目自身 JS 文件数量达到10个或者更多. 而项目上线后,会要求将所 ...
- Excel 批量快速合并相同的单元格:数据透视表、宏代码、分类汇总
Excel 批量快速合并相同的单元格 在制作Excel表格的时候,为了使得自己制作的报表更加简洁明了,方便查阅,经常需要合并很多相同的单元格,如果有几千几万条记录需要合并的话,真的会让人发疯.怎样 ...
- 使用ffmpeg批量合并flv文件
title: 使用ffmpeg批量合并flv文件 toc: false date: 2018-10-14 16:08:19 categories: methods tags: ffmpeg flv 使 ...
- 152-技巧-Power Query 快速合并文件夹中表格之自定义函数 TableXlsxCsv
152-技巧-Power Query 快速合并文件夹中表格之自定义函数 TableXlsxCsv 附件下载地址:https://jiaopengzi.com/2602.html 一.背景 在我们使用 ...
- 如何快速合并多个TXT文本内容
工作中有时候需要合并很多文本内容,例如一些推送清单之类,一个一个打开去复制粘贴的话,少量还行,如果txt文本数据量大(10+M以上)且文件数量多(成百上千),这种方式就显得很低效了.具体要求如下: ...
- 【转】打包AAC码流到FLV文件
AAC编码后数据打包到FLV很简单.1. FLV音频Tag格式 字节位置 意义0x08, ...
- C# 合并和拆分PDF文件
一.合并和拆分PDF文件的方式 PDF文件使用了工业标准的压缩算法,易于传输与储存.它还是页独立的,一个PDF文件包含一个或多个"页",可以单独处理各页,特别适合多处理器系统的工作 ...
随机推荐
- nopcommerce之一(结构分析)
公司的项目,基于nopcommerce开发.接触项目至今已经快一个月了,对nopcommerce这个开源框架整个结构比较熟悉了.这个框架主要要知道三个文件夹,分别是Libraries.Plugins和 ...
- WPF Combobox样式
<ControlTemplate x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}&qu ...
- android json解析详细介绍之gson
废话不多说,什么json是轻量级数据交换标准:自己百度去深入了解:这里有三种json解析工具.本人只用过其中两种: 1.Google Json利器之Gson 评价:简单,方便. 2.阿里巴巴 ...
- extjs6环境
安装JDK http://www.oracle.com/technetwork/java/javase/downloads/ 安装到指定路径,例如D:\Java配置环境变量 此电脑—属性—高级系统设置 ...
- 安安视频网anan.video为您提供免费高清视频
安安视频网anan.video为您提供免费高清视频,最新电影,电视剧,动漫,微电影,纪录片,音乐MV在线观看(高清):安安视频网,一个干净的视频在线播放网站,百万高清影视,视频在线观看. 安安视频网整 ...
- MyBatis使用动态SQL标签的小陷阱
现在MyBatis越来越受大家的喜爱了,它的优势大家都知道,我就不多说了,直接说重点. MyBatis中提供动态SQL功能,我们可以使用<if><when><where& ...
- Dojo框架学习笔记<二>
一.dojo/dom 该模块定义了Dojo Dom API,主要有以下几种用法: 1.dom.byId();(相当于document.getElementById()) ①最直接的用 ...
- IIS出现问题时修改配置文件的几项说明
近期系统在线运行经常出现object moved错误 通过查询资料,做了几项web.config文件的调整 1,调整应用程序池使用集成模式 <system.webServer> ...
- 在Web应用中接入微信支付的流程之极简清晰版
在Web应用中接入微信支付的流程之极简清晰版 背景: 在Web应用中接入微信支付,我以为只是调用几个API稍作调试即可. 没想到微信的API和官方文档里隐坑无数,致我抱着怀疑人生的心情悲愤踩遍了丫们布 ...
- python socket server源码学习
原文请见:http://www.cnblogs.com/wupeiqi/articles/5040823.html 这里就是自己简单整理一下: #!/usr/bin/env python # -*- ...