c\#flv 해석 상세 예시 구현
16698 단어 flv 해석
도구 클래스
해석 하 는 과정 에서 우 리 는 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 클래스,주요 클래스,헤더 와 많은 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 클래스,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 의 종 류 는 세 가지 가 있 기 때문에 통일 적 인 관 리 를 위해 태그 류 를 추상 화 합 니 다.
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 클래스,태그 클래스 를 계승 하고 구성원 변수 값 을 추가 하여 스 크 립 트 정 보 를 저장 합 니 다.
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
";
ScriptObject.indent += 2;
foreach (KeyValuePair<string, object> kv in values)
{
str += new string(' ', ScriptObject.indent)
+ kv.Key + ": " + kv.Value + "\r
";
}
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++;
str += o + ",";
}
if (str.Length > 1)
str = str.Substring(0, str.Length - 1);
str += "\r
]";
return str;
}
}
}
VideoTag 클래스비디오 태그 클래스:
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 의 정 보 를 분석 할 수 있 습 니 다.