http://noahgenius.javaeye.com/blog/164782
FLV是一个二进制文件,由文件头(FLV header)和很多tag组成。tag又可以分成三类:audio,video,script,分别代表音频流,视频流,脚本流(关键字或者文件信息之类)。
FLV Header
一般比较简单,包括文件类型之类的全局信息
文件类型 | 3bytes | 总是FLV(0x46 0x4C 0x56),否则... |
版本
| 1byte | 一般是0x01,表示FLV version 1
|
流信息 | 1byte
| 倒数第一bit是1表示有视频,倒数第三bit是1表示有音频,其他都应该是0(有些软件如flvtool2可能造成倒数第四bit是1,不过也没发现有什么不对) |
header长度
| 4bytes | 整个文件头的长度,一般是9(3+1+1+4),有时候后面还有些别的信息,就不是9了 |
| | |
FLV Body
FLV body就是由很多tag组成的,一个tag包括下列信息:
previoustagsize | 4bytes | 前一个tag的长度,第一个tag就是0 |
tag类型 | 1byte | 三类: - 8 -- 音频tag
- 9 -- 视频tag
- 18 -- 脚本tag
|
数据区长度 | 3bytes | |
时间戳 | 3bytes | 单位毫秒,如果是脚本tag就是0 |
扩展时间戳
| 1byte | 作为时间戳的高位 |
streamsID | 3bytes | 总是0(不知道干啥用) |
数据区 | | |
根据不同的tag类型就有不同的数据区
Audio tag 数据区
audio信息 | 1byte | 前四位bits表示音频格式: - 0 -- 未压缩
- 1 -- ADPCM
- 2 -- MP3
- 5 -- Nellymoser 8kHz momo
- 6 -- Nellymoser
下面两位bits表示samplerate: - 0 -- 5.5kHz
- 1 -- 11kHz
- 2 -- 22kHz
- 3 -- 44kHz
下面一位bit表示每个采样的长度: - 0 -- snd8Bit
- 1 -- snd16Bit
下面一位bit表示类型: - 0 -- sndMomo
- 1 -- sndStereo
|
audio数据区
| 不定 | |
video tag 数据区
video信息 | 1byte | 前四位bits表示类型: - 1 -- keyframe
- 2 -- inner frame
- 3 -- disposable inner frame (H.263 only)
后四位bits表示编码器id: - 2 -- Seronson H.263
- 3 -- Screen video
- 4 -- On2 VP6
- 5 -- On2 VP6 without channel
- 6 -- Screen video version 2
|
video数据区
| 不定 | |
script tag 数据区
略n字...
代码
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
目前只用到读取视频时长的部分,有待完善
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
namespace _30edu.Common
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
public class FlvInfo
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
public FlvInfo()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Header = new FLVHeader();
TagList = new List<FLVTag>();
}
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public FLVHeader Header
{ get;private set; }
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public List<FLVTag> TagList
{ get; private set; }
public TimeSpan Time
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
get
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
int time=0;
//foreach (FLVTag tag in this.TagList.Where(p=>p.Type))
//{
// byte[] tmp = new byte[4];
// tmp[3] = tag.TimeEx;
// tmp[0] = tag.Time[2];
// tmp[1] = tag.Time[1];
// tmp[2] = tag.Time[0];
// time += BitConverter.ToInt32(tmp,0);
//}
FLVTag tag = this.TagList.Where(p => p.Type == 9).Last();
byte[] tmp = new byte[4];
tmp[3] = tag.TimeEx;
tmp[0] = tag.Time[2];
tmp[1] = tag.Time[1];
tmp[2] = tag.Time[0];
return new TimeSpan(0, 0, 0, 0, BitConverter.ToInt32(tmp, 0));
}
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 文件头
/// </summary>
public class FLVHeader
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
public FLVHeader()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
this.Type = new byte[3];
this.Length = new byte[4];
}
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 3byte 总是FLV(0x46 0x4C 0x56)
/// </summary>
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public byte[] Type
{ get; set; }
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 版本 一般是0x01,表示FLV version 1
/// </summary>
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public byte Version
{ get; set; }
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 流信息 倒数第一bit是1表示有视频,倒数第三bit是1表示有音频
/// </summary>
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public byte Stream
{ get; set; }
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 长度 4byte
/// </summary>
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public byte[] Length
{ get; set; }
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public class FLVTag
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
public FLVTag()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
this.PreviousTagSize = new byte[4];
this.DataLength = new byte[3];
this.Time = new byte[3];
this.streamsID = new byte[3];
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
}
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 前一个Tag长度 4byte
/// </summary>
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public byte[] PreviousTagSize
{ get; set; }
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 8 -- 音频tag 9 -- 视频tag 18 -- 脚本tag
/// </summary>
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public byte Type
{ get; set; }
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 数据区长度 3byte
/// </summary>
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public byte[] DataLength
{ get; set; }
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 时间戳 3byte 毫秒
/// </summary>
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public byte[] Time
{ get; set; }
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 扩展时间戳 3byte 毫秒 作为时间戳的高位
/// </summary>
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public byte TimeEx
{ get; set; }
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 一般为0 3byte
/// </summary>
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public byte[] streamsID
{ get; set; }
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
///
/// </summary>
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public byte[] Data
{ get; set; }
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public static class FlvInfoHelper
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
public static FlvInfo Read(string Path)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
using (FileStream fs = File.OpenRead(Path))
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
FlvInfo aFlvInfo = new FlvInfo();
fs.Read(aFlvInfo.Header.Type, 0, aFlvInfo.Header.Type.Length);
aFlvInfo.Header.Version=(byte)fs.ReadByte();
aFlvInfo.Header.Stream = (byte)fs.ReadByte();
fs.Read(aFlvInfo.Header.Length, 0, aFlvInfo.Header.Length.Length);
byte[] previoustagsize=new byte[4];
while (fs.Read(previoustagsize, 0, previoustagsize.Length) != 0)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
FLVTag Tag = new FLVTag();
Tag.PreviousTagSize = previoustagsize;
Tag.Type = (byte)fs.ReadByte();
fs.Read(Tag.DataLength, 0, Tag.DataLength.Length);
fs.Read(Tag.Time, 0, Tag.Time.Length);
Tag.TimeEx = (byte)fs.ReadByte();
fs.Read(Tag.streamsID, 0, Tag.streamsID.Length);
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
byte[] tmp = new byte[4];
tmp[3] = 0;
tmp[2] = Tag.DataLength[0];
tmp[1] = Tag.DataLength[1];
tmp[0] = Tag.DataLength[2];
int n=System.BitConverter.ToInt32(tmp, 0);
fs.Seek(n, SeekOrigin.Current);
aFlvInfo.TagList.Add(Tag);
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
}
return aFlvInfo;
}
return null;
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
}