c#实现flv解析详解示例
程序员文章站
2024-02-16 23:09:22
先上效果图:
工具类
在解析的过程中,我们会和byte做各种运算,所以我定义了一个byte工具类byteutils:
复制代码...
先上效果图:
在解析的过程中,我们会和byte做各种运算,所以我定义了一个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类,主要的类,里面包括一个header和许多的tag,也就是一个flv文件的结构:
复制代码 代码如下:
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类是一个抽象类,因为tag的种类有三种,为了统一管理,抽象出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;//数据区大小
public uint timestamp; //时间戳 单位ms
public int timestamp_ex;//时间戳扩展
public uint streamid;//id
public long offset;//偏移量
public byte[] data;//数据
//对tag进行读取
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;//采样的长度
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);
}
}
}
使用方法
用法很简单,new出来的时候把flv文件的stream对象传进去就行了,比如我这样的:
复制代码 代码如下:
flv flv = null;
using (filestream fs = new filestream("t31_stract.flv", filemode.open, fileaccess.read))
{
flv = new flv(fs);
}
之后就可以使用flv对象来分析当前flv的信息了。