如何读取Flv视频文件的时间

来源:互联网 发布:网络填表终结者破解版 编辑:程序博客网 时间:2024/06/05 18:05

Mobicast闪播在搜索时需要反馈Flv的时间长度,可是如果使用flvmdi来将每个文件注入metadata再读取出时间,那么陈奕迅的歌词“明年今日”就相当适合我的软件了。
得自己写程序读取Flv的时间长度。
上网搜索,发现鲜有这样的文章,多数都是教你用as来读取metadata的时间,但是如果没有注入metadata,那就没办法了。
以下教程适用大多数可以读写文件的编程语言。读者需要有一定的文件读写的基础知识。补充一句,其实,大家如果不懒,英文又还过得去,可以直接看看Flv的文档,那么自然就很清晰该怎么做了,也用不着来听我罗嗦了,呵呵。
要看中文教程?好,follow me!
一、头文件
Flv的头文件格式比较简单,由9个字节组成.

前3个字节是文件格式标识 0x464C56.
第4个字节也是标识文件的版本号 0x01.
第5个字节 0x05
该字节前5个bit是保留的必须是0
          第6个bit音频类型标志(TypeFlagsAudio)
          第7个bit也是保留的必须是0
          第8个bit视频类型标志(TypeFlagsVideo)
第6-9的四个字节保留,为的是以后flv版本的升级扩展 0x00000009。

二、文件tag和内容
Flv文件除去头文件,就是由n个tag及内容组成。
tag1 内容1,tag2 内容2 ..... tagn 内容n
我们要读取的信息如时间就在tag里面。下面我来分析一下tag。
tag的格式如下:
1字节的类型。tag的类型目前有三种,分别为音频tag(0x08),视频tag(0x09),脚本tag(0x12)。(图例中是0x12)
3个字节的数据大小,即上面所说的内容的大小 0x000920
3个字节的时间戳  数据的时间戳(单位级为毫秒,如果要转为秒要除以1000) 0x000000
1个字节的时间戳扩展 0x00
3个字节的streamID,目前都是0 0x000000
之后的0x02开始的就是数据内容了,如脚本数据内容metadata,音频数据内容,视频数据内容。
大家是否还有注意到,在0x12 tag类型前面有4个字节 0x00000000,这个是表示前一个tag的大小。由于这里是第一个tag,所以无所谓前面的tag的大小,就为0了。
知道了Flv的文件格式,要读取时间那是易如反掌了。
什么?不会读取字节?汗,请打开搜索引擎,输入“xx语言 读取字节”。

 下面给出程序:

flv.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="flv.aspx.cs" Inherits="flv" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>flvinfo</title>
    <style type="text/css">
       body{font-size:12px;}
       td{height:20px; font-size:12px; padding:3px;}
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <div style=" background:#cccccc; padding-left:20px;">
    <br />
    <div style=" background:#cccccc; text-align:left; padding:3px;">
        <strong>Flv文件的结构:</strong><br />
        <table border="1" bordercolor="#000000" cellpadding="0" cellspacing="0" style="border-collapse:collapse;">
            <tr>
                <td style="height: 20px" colspan="9">&nbsp;Flv的头文件格式比较简单,由9个字节组成.&nbsp;</td>
                <td style="height: 20px" colspan="4">&nbsp;tag1</td>
                <td style="height: 20px">&nbsp;tag2</td>
                <td style="height: 20px" colspan="3">&nbsp;tag3</td>
                <td style="height: 20px" colspan="3">&nbsp;tag4</td>
                <td style="height: 20px">&nbsp;tag5</td>
                <td style="height: 20px" colspan="3">&nbsp;tag6</td>
                <td style="height: 20px;"></td>
            </tr>
            <tr>
                <td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td> 6</td><td>7</td><td>8</td><td>9</td><td>10</td><td>11</td><td>12</td><td>13</td>
                <td>14</td><td>15</td><td> 16</td><td>17</td><td>18</td><td>19</td><td>20</td><td>21</td><td>22</td><td>23</td><td>24</td><td>25...</td>
            </tr>
            <tr>
              <td style="background-color: #ffff66; height: 20px;">46</td><td style="background-color: #ffff66; height: 20px;">4C</td>
              <td style="background-color: #ffff66; height: 20px;">56</td><td style="background-color: #ffff66; height: 20px;">01</td>
              <td style="background-color: #ffff66; height: 20px;">05</td><td style="background-color: #ffff66; height: 20px;">00</td>
              <td style="background-color: #ffff66; height: 20px;">00</td><td style="background-color: #ffff66; height: 20px;">00</td>
              <td style="background-color: #ffff66; height: 20px;">09</td><td style="background-color: #72ABD8; height: 20px;">00</td>
              <td style="background-color: #72ABD8; height: 20px;">00</td><td style="background-color: #72ABD8; height: 20px;">00</td>
              <td style="background-color: #72ABD8; height: 20px;">00</td><td style="background-color: #72ABD8; height: 20px;">12</td>
              <td style="background-color: #72ABD8; height: 20px;">00</td><td style="background-color: #72ABD8; height: 20px;">09</td>
              <td style="background-color: #72ABD8; height: 20px;">20</td><td style="background-color: #72ABD8; height: 20px;">00</td>
              <td style="background-color: #72ABD8; height: 20px;">00</td><td style="background-color: #72ABD8; height: 20px;">00</td>
              <td style="background-color: #72ABD8; height: 20px;">00</td><td style="background-color: #72ABD8; height: 20px;">00</td>
              <td style="background-color: #72ABD8; height: 20px;">00</td><td style="background-color: #72ABD8; height: 20px;">00</td>
              <td style="height: 20px;">02...</td>
            </tr>
            <tr>
                <td style="height: 20px; text-align: left;" colspan="3">格式标识</td>
                <td style="height: 20px; text-align: left;">版本号</td>
                <td style="height: 20px; text-align: left;">
                    该字节前5个bit是保留的必须是0<br />
                    第6个bit音频类型标志(TypeFlagsAudio)<br />
                    第7个bit也是保留的必须是0<br />
                    第8个bit视频类型标志(TypeFlagsVideo)</td>
                <td style="height: 20px; text-align: left;" colspan="4">字节保留,为以后<br />flv版本升级扩展</td>
                <td style="height: 20px; text-align: left;" colspan="4">
                    前一个tag大小<br />
                    由于这里是第<br />
                    一个tag所以无<br />
                    所谓前面的tag<br />
                    大小,就为0了</td>
                <td style="height: 20px; text-align: left;">脚本tag(0x12)<br />音频tag(0x08)<br />视频tag(0x09)</td>
                <td style="height: 20px; text-align: left;" colspan="3">数据大小<br />内容大小</td>
                <td style="height: 20px; text-align: left;" colspan="3" >时间戳<br />单位级毫秒,<br />如果要转为<br />秒要除以1000</td>
                <td style="height: 20px; text-align: left;"> 时间戳扩展</td>
                <td style="height: 20px; text-align: left;" colspan="3">streamID<br />目前都是0<br />0x000000</td>
                <td style="height: 20px; text-align: left;">
                    metadata<br />
                    数据内容<br />
                    脚本数据内容<br />
                    音频数据内容<br />
                    视频数据内容</td>
            </tr>
        </table>
        </div>
        <div style=" background:#cccccc; text-align:left; padding:3px;">
           <asp:Label ID="flvInfo" runat="server" Text="暂无信息"></asp:Label>
        </div>
        <div style=" background:#cccccc; text-align:left; padding:3px;">
           <asp:Button ID="Button1" runat="server" Text="rename" BackColor="DarkGray" BorderColor="#000000" BorderStyle="Solid" BorderWidth="1px" OnClick="Button1_Click" />
        </div>
        </div>
    </form>
</body>
</html>

flv.aspx.cs:

using System;
using System.IO;
using System.Text;

public partial class flv : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
       //
    }

    public struct FlvInfo
    {
        //头文件
        public string Expand;       //3个字节,格式, (前3个字节文件格式标识.)
        public string Version;      //1个字节,版本, (第4个字节也是标识文件的版本号)            
        public string Flags;        //1个字节,标志, (该字节前5个bit是保留的必须是0,第6个bit音频类型标志(TypeFlagsAudio,第7个bit也是保留的必须是0,第8个bit视频类型标志(TypeFlagsVideo))
        public string VerUpgrade;   //4个字节,升级, (第6-9的四个字节保留,为的是以后flv版本的升级扩展 0x00000009.)
        //tag
        public string FrontTagSize; //4个字节,前一个tag的大小
        public string TagClass;     //1个字节,TAG类型,tag的类型目前有三种,分别为音频tag(0x08),视频tag(0x09),脚本tag(0x12)。
        public string ContentSize;  //3个字节,数据大小,即上面所说的内容的大小
        public string AllTime;      //3个字节,时间戳,数据的时间戳(单位级为毫秒,如果要转为秒要除以1000)
        public string TimeExpand;   //1个字节,时间戳扩展
        public string StreamID;     //3个字节,streamID,目前都是0 0x000000
    }

    /// <summary>
    /// 获取Flv文件开始的24个字节
    /// </summary>
    /// <param name="FileName">文件名</param>
    /// <returns>返回字节数组</returns>
    private byte[] getBegin24(string FileName)
    {
        FileStream fs = new FileStream(FileName, FileMode.Open, FileAccess.Read);
        Stream stream = fs;
        stream.Seek(0, SeekOrigin.Begin);
        const int seekPos = 64;
        int rl = 0;
        byte[] Info = new byte[seekPos];
        rl = stream.Read(Info, 0, seekPos);
        fs.Close();
        stream.Close();
        return Info;
    }

    /// <summary>
    /// 获取Flv相关信息,对上面返回的字节数组分段取出,并保存到FlvInfo结构中返回。
    /// </summary>
    /// <param name = "Info">从Flv文件中截取的二进制信息</param>
    /// <returns>返回一个FlvInfo结构</returns>
    private FlvInfo getFlvInfo(byte[] Info)
    {
        FlvInfo flvInfo = new FlvInfo();
        int i;
        int j;
        int position = 0;//循环的起始值
        int currentIndex = 0;//Info的当前索引值

        ////获取格式
        j = 0;
        byte[] bytExpand = new byte[3];//将格式部分读到一个单独的数组中
        for (i = currentIndex; i < currentIndex + 3; i++)
        {
            bytExpand[j] = Info[i];
            position++;
            j++;
        }
        currentIndex = position;
        flvInfo.Expand = this.byteToString(bytExpand);

        //获取版本
        j = 0;
        byte[] bytVersion = new byte[1];//将版本部分读到一个单独的数组中
        for (i = currentIndex; i < currentIndex + 1; i++)
        {
            bytVersion[j] = Info[i];
            position++;
            j++;
        }
        currentIndex = position;
        flvInfo.Version = this.byteToString(bytVersion);


        //获取Flags
        j = 0;
        byte[] bytFlags = new byte[1];//将Flags部分读到一个单独的数组中
        for (i = currentIndex; i < currentIndex + 1; i++)
        {
            bytFlags[j] = Info[i];
            position++;
            j++;
        }
        currentIndex = position;
        flvInfo.Flags = this.byteToString(bytFlags);

        //获取VerUpgrade
        j = 0;
        byte[] bytVerUpgrade = new byte[4];//将VerUpgrade部分读到一个单独的数组中
        for (i = currentIndex; i < currentIndex + 4; i++)
        {
            bytVerUpgrade[j] = Info[i];
            position++;
            j++;
        }
        currentIndex = position;
        flvInfo.VerUpgrade = this.byteToString(bytVerUpgrade);

        //获取FrontTagSize
        j = 0;
        byte[] bytFrontTagSize = new byte[4];//将FrontTagSize部分读到一个单独的数组中
        for (i = currentIndex; i < currentIndex + 4; i++)
        {
            bytFrontTagSize[j] = Info[i];
            position++;
            j++;
        }
        currentIndex = position;
        flvInfo.FrontTagSize = this.byteToString(bytFrontTagSize);

        //TagClass
        j = 0;
        byte[] bytTagClass = new byte[1];//将FrontTagSize部分读到一个单独的数组中
        for (i = currentIndex; i < currentIndex + 1; i++)
        {
            bytTagClass[j] = Info[i];
            position++;
            j++;
        }
        currentIndex = position;
        flvInfo.TagClass = this.byteToString(bytTagClass);

        //ContentSize
        j = 0;
        byte[] bytContentSize = new byte[3];//将ContentSize部分读到一个单独的数组中
        for (i = currentIndex; i < currentIndex + 3; i++)
        {
            bytContentSize[j] = Info[i];
            position++;
            j++;
        }
        currentIndex = position;
        flvInfo.ContentSize = this.byteToString(bytContentSize);

        //AllTime
        j = 0;
        byte[] bytAllTime = new byte[3];//将AllTime部分读到一个单独的数组中
        for (i = currentIndex; i < currentIndex + 3; i++)
        {
            bytAllTime[j] = Info[i];
            position++;
            j++;
        }
        currentIndex = position;
        flvInfo.AllTime = this.byteToString(bytAllTime);

        //TimeExpand
        j = 0;
        byte[] bytTimeExpand = new byte[1];//将TimeExpand部分读到一个单独的数组中
        for (i = currentIndex; i < currentIndex + 1; i++)
        {
            bytTimeExpand[j] = Info[i];
            position++;
            j++;
        }
        currentIndex = position;
        flvInfo.TimeExpand = this.byteToString(bytTimeExpand);

        //StreamID
        j = 0;
        byte[] bytStreamID = new byte[3];//将StreamID部分读到一个单独的数组中
        for (i = currentIndex; i < currentIndex + 3; i++)
        {
            bytStreamID[j] = Info[i];
            position++;
            j++;
        }
        currentIndex = position;
        flvInfo.StreamID = this.byteToString(bytStreamID);

        return flvInfo;
    }

    /// <summary>
    /// 将字节数组转换成字符串,上面程序用到的方法.
    /// </summary>
    /// <param name = "b">字节数组</param>
    /// <returns>返回转换后的字符串</returns>
    private string byteToString(byte[] b)
    {
        Encoding enc = Encoding.GetEncoding("GB2312");
        string str = enc.GetString(b);
        str = str.Substring(0, str.IndexOf('/0') >= 0 ? str.IndexOf('/0') : str.Length);//去掉无用字符
        return str;
    }

    /// <summary>
    /// 更改文件名
    /// </summary>
    /// <param name="filePath">文件名</param>
    /// <returns></returns>
    private bool ReName(string filePath)
    {
        if (File.Exists(filePath))
        {
            FlvInfo flvInfo = new FlvInfo();
            flvInfo = this.getFlvInfo(this.getBegin24(filePath));//读出文件信息

            if (flvInfo.Expand.Trim().Length == 0)
            {
                flvInfo.Expand = "未知格式";
            }
            if (flvInfo.AllTime.Trim().Length == 0)
            {
                flvInfo.AllTime = "未知时间";
            }
            try
            {
                ////更名
                //File.Move(filePath, filePath.Substring(0, filePath.ToLower().LastIndexOf("//")).Trim() + "//" + "(" + flvInfo.Expand.Trim() + ")" + flvInfo.AllTime.Trim() + ".flv");
                //Response.Write("<script>alert('Flv改名成功!');</script>");
                string b = "<table border='1' bordercolor='#000000' cellpadding='0' cellspacing='0' style='border-collapse:collapse;'>"
                         + "    <tr>"
                         + "        <td>格式标识</td><td>版本号</td><td>前5个bit</td><td>字节保留</td><td>前一个tag大小</td>"
                         + "        <td>脚本tag</td><td>数据大小</td><td>时间戳</td><td>时间扩展</td><td>streamID</td>"
                         + "    </tr>"
                         + "    <tr>"
                         + "        <td>" + flvInfo.Expand + "</td>"
                         + "        <td>" + flvInfo.Version + "</td>"
                         + "        <td>" + flvInfo.Flags + "</td>"
                         + "        <td>" + flvInfo.VerUpgrade + "</td>"
                         + "        <td>" + flvInfo.FrontTagSize + "</td>"
                         + "        <td>" + flvInfo.FrontTagSize + "</td>"
                         + "        <td>" + flvInfo.ContentSize + "</td>"
                         + "        <td>" + flvInfo.AllTime + "</td>"
                         + "        <td>" + flvInfo.TimeExpand + "</td>"
                         + "        <td>" + flvInfo.StreamID + "</td>"
                         + "    </tr>"
                         + "</table>";
                //Response.Write(b);
                this.flvInfo.Text = b;
                return true;

            }
            catch (Exception e)
            {
                Response.Write(e.ToString());
                return false;
            }
        }
        else
        {
            Response.Write("<script>alert('Flv文件不存在!');</script>");
            return false;
        }
    }
    protected void Button1_Click(object sender, EventArgs e)
    {
        ReName(@"D:/Ffmpeg/ps1.flv");
    }
}

原创粉丝点击