c#實(shí)現(xiàn)flv解析詳解示例
先上效果圖:
![]() |
在解析的過(guò)程中,我們會(huì)和byte做各種運(yùn)算,所以我定義了一個(gè)byte工具類ByteUtils:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace FLVParer.Utils
{
class ByteUtils
{
public static uint ByteToUInt(byte[] bs, int length)
{
if (bs == null || bs.Length < length)
return 0;
uint rtn = 0;
for (int i = 0; i < length; i++)
{
rtn <<= 8;
rtn |= bs[i];
}
return rtn;
}
public static double ByteToDouble(byte[] bs)
{
if (bs == null || bs.Length < 8)
return 0;
byte[] b2 = new byte[8];
for (int i = 0; i < 8; i++)
{
b2[i] = bs[7 - i];
}
return BitConverter.ToDouble(b2, 0);
}
public static short ReadUI16(Stream src)
{
byte[] bs = new byte[2];
if (src.Read(bs, 0, 2) <= 0)
return 0;
return (short)((bs[0] << 8) | bs[1]);
}
public static uint ReadUI24(Stream src)
{
byte[] bs = new byte[3];
if (src.Read(bs, 0, 3) <= 0)
throw new IOException("Stream end.");
return ByteToUInt(bs, 3);
}
public static uint ReadUI32(Stream src)
{
byte[] bs = new byte[4];
if (src.Read(bs, 0, 4) <= 0)
throw new IOException("Stream end.");
return ByteToUInt(bs, 4);
}
public static string GetTime(uint time)
{
return (time / 60000).ToString() + ":"
+ (time / 1000 % 60).ToString("D2") + "."
+ (time % 1000).ToString("D3");
}
}
}
FLV類
FLV類,主要的類,里面包括一個(gè)header和許多的tag,也就是一個(gè)FLV文件的結(jié)構(gòu):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using FLVParer.Utils;
namespace FLVParer.Model
{
class FLV
{
public Header header { get; private set; }
List<Tag> tags;
public FLV(Stream stream)
{
header = new Header();
header.readHeader(stream);
stream.Seek(header.size, SeekOrigin.Begin);
tags = new List<Tag>();
while (stream.Position < stream.Length-4)
{
tags.Add(readTag(stream));
}
}
private Tag readTag(Stream stream)
{
Tag tag = null;
byte[] buf = new byte[4];
stream.Read(buf, 0, 4);
int type = stream.ReadByte();
switch (type)
{
case 8:
tag = new AudioTag();
break;
case 9:
tag = new VideoTag();
break;
case 18:
tag = new ScriptTag();
break;
}
tag.presize = ByteUtils.ByteToUInt(buf, 4);
tag.datasize = ByteUtils.ReadUI24(stream);
tag.timestamp = ByteUtils.ReadUI24(stream);
tag.timestamp_ex = stream.ReadByte();
tag.streamid = ByteUtils.ReadUI24(stream);
tag.readData(stream);
return tag;
}
}
}
Header類
Header類,保存FLV文件的頭信息:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using FLVParer.Utils;
namespace FLVParer.Model
{
class Header
{
public String type { get; private set; }
public int version { get; private set; }
public bool hasVideo { get; private set; }
public bool hasAudio { get; private set; }
public uint size { get; private set; }
public void readHeader(Stream stream)
{
byte[] buf = new byte[4];
stream.Read(buf, 0, 3);
type = Encoding.Default.GetString(buf);
stream.Read(buf, 0, 1);
version = buf[0];
stream.Read(buf, 0, 1);
buf[0] &= 0x0f;
if ((buf[0] & 0x01) == 1)
{
hasVideo = true;
}
if ((buf[0] & 0x04) == 4)
{
hasAudio = true;
}
stream.Read(buf, 0, 4);
size = ByteUtils.ByteToUInt(buf, 4);
}
}
}
Tag類
Tag類是一個(gè)抽象類,因?yàn)閠ag的種類有三種,為了統(tǒng)一管理,抽象出Tag類:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace FLVParer.Model
{
enum TagType
{
video,
audio,
Script
}
abstract class Tag
{
public TagType tagType;//tag類型
public uint presize;//前一tag大小
public uint datasize;//數(shù)據(jù)區(qū)大小
public uint timestamp; //時(shí)間戳 單位ms
public int timestamp_ex;//時(shí)間戳擴(kuò)展
public uint streamid;//ID
public long offset;//偏移量
public byte[] data;//數(shù)據(jù)
//對(duì)tag進(jìn)行讀取
public abstract void readData(Stream stream);
}
}
ScriptTag類
腳本tag類,繼承自Tag類,并添加成員變量Values,用于保存腳本信息:
using System.Collections.Generic;
using System.Text;
using System.IO;
using FLVParer.Utils;
namespace FLVParer.Model
{
class ScriptTag : Tag
{
public List<KeyValuePair<string, object>> Values { get; private set; }
public ScriptTag()
{
tagType = TagType.Script;
Values = new List<KeyValuePair<string, object>>();
}
public override void readData(Stream stream)
{
offset = 0;
Values.Clear();
byte[] bs = new byte[3];
while (offset < this.datasize)
{
stream.Read(bs, 0, 3);
if (bs[0] == 0 && bs[1] == 0 && bs[2] == 9)
{
offset += 3;
break;
}
stream.Seek(-3, SeekOrigin.Current);
AddElement("#" + offset, ReadElement(stream));
}
}
private void AddElement(string key, object o)
{
Values.Add(new KeyValuePair<string, object>(key, o));
}
private object ReadElement(Stream src)
{
int type = src.ReadByte();
offset++;
switch (type)
{
case 0: // Number - 8
return ReadDouble(src);
case 1: // Boolean - 1
return ReadByte(src);
case 2: // String - 2+n
return ReadString(src);
case 3: // Object
return ReadObject(src);
case 4: // MovieClip
return ReadString(src);
case 5: // Null
break;
case 6: // Undefined
break;
case 7: // Reference - 2
return ReadUShort(src);
case 8: // ECMA array
return ReadArray(src);
case 10: // Strict array
return ReadStrictArray(src);
case 11: // Date - 8+2
return ReadDate(src);
case 12: // Long string - 4+n
return ReadLongString(src);
}
return null;
}
private object ReadObject(Stream src)
{
byte[] bs = new byte[3];
ScriptObject obj = new ScriptObject();
while (offset < this.datasize)
{
src.Read(bs, 0, 3);
if (bs[0] == 0 && bs[1] == 0 && bs[2] == 9)
{
offset += 3;
break;
}
src.Seek(-3, SeekOrigin.Current);
string key = ReadString(src);
if (key[0] == 0)
break;
obj[key] = ReadElement(src);
}
return obj;
}
private double ReadDate(Stream src)
{
double d = ReadDouble(src);
src.Seek(2, SeekOrigin.Current);
offset += 2;
return d;
}
private ScriptObject ReadArray(Stream src)
{
byte[] buffer = new byte[4];
src.Read(buffer, 0, 4);
offset += 4;
uint count = ByteUtils.ByteToUInt(buffer, 4);
ScriptObject array = new ScriptObject();
for (uint i = 0; i < count; i++)
{
string key = ReadString(src);
array[key] = ReadElement(src);
}
src.Seek(3, SeekOrigin.Current); // 00 00 09
offset += 3;
return array;
}
private ScriptArray ReadStrictArray(Stream src)
{
byte[] bs = new byte[4];
src.Read(bs, 0, 4);
offset += 4;
ScriptArray array = new ScriptArray();
uint count = ByteUtils.ByteToUInt(bs, 4);
for (uint i = 0; i < count; i++)
{
array.Add(ReadElement(src));
}
return array;
}
private double ReadDouble(Stream src)
{
byte[] buffer = new byte[8];
src.Read(buffer, 0, 8);
offset += 8;
return ByteUtils.ByteToDouble(buffer);
}
private byte ReadByte(Stream src)
{
offset++;
return (byte)src.ReadByte();
}
private string ReadString(Stream src)
{
byte[] bs = new byte[2];
src.Read(bs, 0, 2);
offset += 2;
int n = (int)ByteUtils.ByteToUInt(bs, 2);
bs = new byte[n];
src.Read(bs, 0, n);
offset += n;
return Encoding.ASCII.GetString(bs);
}
private string ReadLongString(Stream src)
{
byte[] bs = new byte[4];
src.Read(bs, 0, 4);
offset += 4;
int n = (int)ByteUtils.ByteToUInt(bs, 4);
bs = new byte[n];
src.Read(bs, 0, n);
offset += n;
return Encoding.ASCII.GetString(bs);
}
private ushort ReadUShort(Stream src)
{
byte[] buffer = new byte[2];
src.Read(buffer, 0, 2);
offset += 2;
return (ushort)ByteUtils.ByteToUInt(buffer, 2);
}
}
public class ScriptObject
{
public static int indent = 0;
private Dictionary<string, object> values = new Dictionary<string, object>();
public object this[string key]
{
get
{
object o;
values.TryGetValue(key, out o);
return o;
}
set
{
if (!values.ContainsKey(key))
{
values.Add(key, value);
}
}
}
public override string ToString()
{
string str = "{\r\n";
ScriptObject.indent += 2;
foreach (KeyValuePair<string, object> kv in values)
{
str += new string(' ', ScriptObject.indent)
+ kv.Key + ": " + kv.Value + "\r\n";
}
ScriptObject.indent -= 2;
//if (str.Length > 1)
// str = str.Substring(0, str.Length - 1);
str += "}";
return str;
}
}
public class ScriptArray
{
private List<object> values = new List<object>();
public object this[int index]
{
get
{
if (index >= 0 && index < values.Count)
return values[index];
return null;
}
}
public void Add(object o)
{
values.Add(o);
}
public override string ToString()
{
string str = "[";
int n = 0;
foreach (object o in values)
{
if (n % 10 == 0)
str += "\r\n";
n++;
str += o + ",";
}
if (str.Length > 1)
str = str.Substring(0, str.Length - 1);
str += "\r\n]";
return str;
}
}
}
VideoTag類
視頻tag類:
using System.IO;
namespace FLVParer.Model
{
class VideoTag : Tag
{
public int frameType;//幀類型
public int encodeID;//編碼ID
public VideoTag()
{
tagType = TagType.video;
}
public override void readData(Stream stream)
{
int info = stream.ReadByte();
frameType = info >> 4;
encodeID = info & 0x0f;
data = new byte[datasize - 1];
stream.Read(data, 0, (int)datasize - 1);
}
}
}
AudioTag 類
音頻tag類:
using System.IO;
namespace FLVParer.Model
{
class AudioTag : Tag
{
public int formate;//音頻格式
public int rate;//采樣率
public int size;//采樣的長(zhǎng)度
public int type;//音頻類型
public AudioTag()
{
tagType = TagType.audio;
}
public override void readData(Stream stream)
{
int info = stream.ReadByte();
formate = info >> 4;
rate = (info & 0x0c) >> 2;
size = (info & 0x02) >> 1;
type = info & 0x01;
data = new byte[datasize - 1];
stream.Read(data, 0, (int)datasize - 1);
}
}
}
使用方法
用法很簡(jiǎn)單,new出來(lái)的時(shí)候把FLV文件的stream對(duì)象傳進(jìn)去就行了,比如我這樣的:
FLV flv = null;
using (FileStream fs = new FileStream("t31_stract.flv", FileMode.Open, FileAccess.Read))
{
flv = new FLV(fs);
}
之后就可以使用flv對(duì)象來(lái)分析當(dāng)前flv的信息了。
相關(guān)文章
C# 使用動(dòng)態(tài)庫(kù)DllImport("kernel32")讀寫ini文件的步驟
kernel32.dll是Windows中非常重要的32位動(dòng)態(tài)鏈接庫(kù)文件,屬于內(nèi)核級(jí)文件,這篇文章主要介紹了C# 利用動(dòng)態(tài)庫(kù)DllImport("kernel32")讀寫ini文件,需要的朋友可以參考下2023-05-05Windows系統(tǒng)中C#讀寫ini配置文件的程序代碼示例分享
這篇文章主要介紹了C#讀寫ini配置文件的程序代碼示例分享,在Windows下可以利用Win32的API函數(shù)輕松實(shí)現(xiàn),需要的朋友可以參考下2016-04-04C# 遞歸查找樹(shù)狀目錄實(shí)現(xiàn)方法
這篇文章主要介紹了C# 遞歸查找樹(shù)狀目錄實(shí)現(xiàn)方法,需要的朋友可以參考下2014-02-02c#動(dòng)態(tài)調(diào)用Webservice的兩種方法實(shí)例
這篇文章介紹了c#動(dòng)態(tài)調(diào)用Webservice的兩種方法實(shí)例,有需要的朋友可以參考一下2013-08-08C#開(kāi)發(fā)微信公眾號(hào)接口開(kāi)發(fā)
這篇文章主要介紹了C#微信公眾號(hào)接口開(kāi)發(fā),靈活利用網(wǎng)頁(yè)授權(quán)、帶參數(shù)二維碼、模板消息,提升用戶體驗(yàn)之完成用戶綁定個(gè)人微信及驗(yàn)證碼獲取,需要的朋友可以參考下2015-07-07