c#实现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类,主要的类,里面包括一个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的信息了。

(0)

相关推荐

  • 用C#来解析PDF文件

    1. 介绍 这个项目让你可以去读取并解析一个PDF文件,并将其内部结构展示出来. PDF文件的格式标准文档可以从Adobe那儿获取到. 这个项目基于"PDF指南,第六版,Adobe便携文档格式1.7 2006年11月". 它是一个恐怕有1310页的大部头. 本文提供了对这份文档的简洁概述. 与此相关的项目定义了用来读取和解析PDF文件的C#类. 为了测试这些类,附带的测试程序PdfFileAnalyzer让你可以去读取一个PDF文件,分析它并展示和保存结果. 程序将PDF文件分割成单独

  • C#解析json文件的实现代码

    C# 解析 json JSON(全称为JavaScript Object Notation) 是一种轻量级的数据交换格式.它是基于JavaScript语法标准的一个子集. JSON采用完全独立于语言的文本格式,可以很容易在各种网络.平台和程序之间传输.JSON的语法很简单,易于人阅读和编写,同时也易于机器解析和生成. JSON与XML的比较 ◆可读性 JSON和XML的可读性相比较而言,由于XML提供辅助的标签,更加适合人阅读和理解.◆文件大小与传输 XML允许使用方便的标签,所以文件尺寸是要比

  • C#下解析HTML的两种方法介绍

    在搜索引擎的开发中,我们需要对Html进行解析.本文介绍C#解析HTML的两种方法.AD: 在搜索引擎的开发中,我们需要对网页的Html内容进行检索,难免的就需要对Html进行解析.拆分每一个节点并且获取节点间的内容.此文介绍两种C#解析Html的方法. C#解析Html的第一种方法:用System.Net.WebClient下载Web Page存到本地文件或者String中,用正则表达式来分析.这个方法可以用在Web Crawler等需要分析很多Web Page的应用中.估计这也是大家最直接,

  • asp.net C#生成和解析二维码的实例代码

    类库文件我们在文件最后面下载 [ThoughtWorks.QRCode.dll 就是类库] 使用时需要增加: 复制代码 代码如下: using ThoughtWorks.QRCode.Codec; using ThoughtWorks.QRCode.Codec.Data; using ThoughtWorks.QRCode.Codec.Util; 主要源代码: 1.生成二维码 复制代码 代码如下: QRCodeEncoder qrCodeEncoder = new QRCodeEncoder()

  • xml 封装与解析(javascript和C#中)

    1.xml的解析(javascript中): 具体代码如下,解析的结果root为Dom树. 复制代码 代码如下: if (window.ActiveXObject){ var doc=new ActiveXObject("Microsoft.XMLDOM"); doc.async="false"; doc.loadXML(strXml); }else{ var parser=new DOMParser(); var doc=parser.parseFromStrin

  • C#正则实现Ubb解析类的代码

    解析得到的代码能通过XHTML 1.0 STRICT验证; 包含了标题,链接,字体,对齐,图片,引用,列表等方面的功能.  Ubb.ReadMe.htm UBB代码说明 标题 [h1]标题一[/h1] 标题一 [h2]标题二[/h2] 标题二 [h1]标题三[/h1] 标题三 [h4]标题四[/h4] 标题四 [h5]标题五[/h5] 标题五 [h6]标题六[/h6] 标题六 链接 [url]www.unibetter.com[/url] unibetter.com [url]http://ww

  • c#中XML解析文件出错解决方法

    1.内容中含有xml预定好的实体,如"<"和"&",对xml来说是禁止使用的,针对这种字符,解决方式是使用CDATA部件以"<![CDATA[" 标记开始,以"]]>"标记结束,是CDATA内部内容被解析器忽略.具体说明参考<XML CDATA是什么?>. 2.内容中含有低位非打印字符,解析时会报错:""(十六进制值 0x1D)是无效的字符.加载或保存XML时引发的异常

  • C# 解析 Excel 并且生成 Csv 文件代码分析

    今天工作中遇到一个需求,就是获取 excel 里面的内容,并且把 excel 另存为 csv,因为本人以前未接触过,所以下面整理出来的代码均来自网络,具体参考链接已丢失,原作者保留所有权利! 例子: 复制代码 代码如下: using System; using System.Data; namespace ExportExcelToCode {     class ExcelOperater     {         public void Operater()         {      

  • C#解析Lrc歌词文件过程详解

    看到很多人解析歌词文件时写了一大片的字符处理代码,而且看得不是很明白,所以自己研究了一下,  首先来了解下Lrc文件  时间格式:  1.标准格式: [分钟:秒.毫秒] 歌词  注释:括号.冒号.点号全都要求英文输入状态:  2.其他格式①:[分钟:秒] 歌词:  3.其他格式②:[分钟:秒:毫秒] 歌词,与标准格式相比,秒后边的点号被改成了冒号. 标准格式:  其格式为"[标识名:值]".大小写等价.以下是预定义的标签. [ar:艺人名] [ti:曲名] [al:专辑名]  [by:

  • C#解析JSON实例

    本文以实例形式讲述了C#解析JSON的方法,C#封装了对XML和JSON解析的类库,使用相当方便!具体用法如下: 1.主要用到的类: 主要用到了JavaScriptSerializer类,该类在System.Web.Script.Serialization命名空间(在System.Web.Extensions.dll 中),需要把.NET版本修改为 .NET Framework 4(默认是.NET Framework 4 Client Profile)才能在Add Reference的 .NET

随机推荐