当前位置: 移动技术网 > IT编程>开发语言>c# > c#实现flv解析详解示例

c#实现flv解析详解示例

2019年07月18日  | 移动技术网IT编程  | 我要评论
先上效果图:   工具类 在解析的过程中,我们会和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的信息了。

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网