zoukankan      html  css  js  c++  java
  • MPEG-PS封装格式

    据传输媒体的质量不同,MPEG-2中定义了两种复合信息流:传送流(TS:TransportStream)和节目流(PS:ProgramStream)

    PS文件分为3层:ps层(Program Stream)、pes层(Packet Elemental Stream)、es层(Elementary Stream)。es层就是音视频数据,pes层是在音视频数据上加了时间戳等对数据帧的说明信息,ps层是在pes层上加入了数据流识别和传输的必要信息。

    1.Ps和Ts的区别

    S流与PS流的区别在于TS流的包结构是固定长度的,而PS流的包结构是可变长度的。
     PS包与TS包在结构上的这种差异,导致了它们对传输误码具有不同的抵抗能力,因而应用的环境也有所不同。TS码流由于采用了固定长度的包结构,当传输误码破坏了某一TS包的同步信息时,接收机可在固定的位置检测它后面包中的同步信息,从而恢复同步,避免了信息丢失。而PS包由于长度是变化的,一旦某一PS包的同步信息丢失,接收机无法确定下一包的同步位置,就会造成失步,导致严重的信息丢失。因此,在信道环境较为恶劣,传输误码较高时,一般采用TS码流;而在信道环境较好,传输误码较低时,一般采用PS码流。由于TS码流具有较强的抵抗传输误码的能力,因此目前在传输媒体中进行传输的MPEG-2码流基本上都采用了TS码流的包格式。
      MPEG2-PS主要应用于存储的具有固定时长的节目,如DVD电影,而MPEG-TS则主要应用于实时传送的节目,比如实时广播的电视节目。这两种格式的主要区别是什么呢?你将DVD上的VOB文件的前面一截剪掉(或者干脆就是数据损坏),那么就会导致整个文件无法解码,而电视节目是你任何时候打开电视机都能解码(收看)的,所以,MPEG2-TS格式的特点就是要求从视频流的任一片段开始都是可以独立解码的。PS主要用于播放或编辑系统, TS主要用于数据传输。
     

    2.Ps文件结构

    一个完整的MPEG-2文件就是一个PS流文件。使用Elecard StreamAnalyzer打开一个MPEG-2文件,得到下面信息。

    可以看出来,正如我们上面所说的,整个文件分为3层。首先整个文件被分为了一个个的ProgramPack,然后ProgramPack里面包含了ProgramPack header和Pes包,Pes包里又包含了Pes header和音频编码数据(MPEG-2 Audio)或视频编码数据(MPEG-2 Video)。
    下面就分别来分析PS文件的 Ps和Pes包。

    2.1.Ps层

    Ps层主要由pack header和数据组成,pack header中各个bit的意义如下

    我们可以通过分析一个示例文件来了解它

    其中system_clock_reference的意义如下
    SCR and SCR_ext together are the System Clock Reference, a counter driven at 27MHz, used as a reference to synchronize streams. The clock is divided by 300 (to match the 90KHz clocks such as PTS/DTS), the quotient is SCR (33 bits), the remainder is SCR_ext (9 bits)
    system_clock_reference_base的计算方法为:
    scr += packet_size * 90000LL / (mux_rate * 50LL);
    参考自:ffmpeg-3.3.1 Mpegenc.c
    基本信息了解完了,下面就开始定义这个结构了,一开始是采用了位域来定义的
    复制代码
    struct pack_header 
    {
        unsigned char pack_start_code[4]; 
        unsigned char system_clock_reference_base21 : 2;
        unsigned char marker_bit : 1;
        unsigned char system_clock_reference_base1 : 3;
        unsigned char fix_bit : 2;   
        unsigned char system_clock_reference_base22;
        unsigned char system_clock_reference_base31 : 2;
        unsigned char marker_bit1 : 1;
        unsigned char system_clock_reference_base23 : 5;
        unsigned char system_clock_reference_base32;
        unsigned char system_clock_reference_extension1 : 2;
        unsigned char marker_bit2 : 1;
        unsigned char system_clock_reference_base33 : 5;
        unsigned char marker_bit3 : 1;
        unsigned char system_clock_reference_extension2 : 7;
        unsigned char program_mux_rate1;
        unsigned char program_mux_rate2;
        unsigned char marker_bit5 : 1;
        unsigned char marker_bit4 : 1;
        unsigned char program_mux_rate3 : 6;
        unsigned char pack_stuffing_length : 3;
        unsigned char reserved : 5;
    
        pack_header()
        {
            pack_start_code[0] = 0x00;
            pack_start_code[1] = 0x00;
            pack_start_code[2] = 0x01;
            pack_start_code[3] = 0xBA;
            fix_bit = 0x01;
            marker_bit = 0x01;
            marker_bit1 = 0x01;
            marker_bit2 = 0x01;
            marker_bit3 = 0x01;
            marker_bit4 = 0x01;
            marker_bit5 = 0x01;
            reserved = 0x1F;
            pack_stuffing_length = 0x00;
            system_clock_reference_extension1 = 0;
            system_clock_reference_extension2 = 0;
        }
    
        void getSystem_clock_reference_base(UINT64 &_ui64SCR)
        {
            _ui64SCR = (system_clock_reference_base1 << 30) | (system_clock_reference_base21 << 28)
                | (system_clock_reference_base22 << 20) | (system_clock_reference_base23 << 15)
                | (system_clock_reference_base31 << 13) | (system_clock_reference_base32 << 5)
                | (system_clock_reference_base33);
        }
    
        void setSystem_clock_reference_base(UINT64 _ui64SCR)
        {
            system_clock_reference_base1 = (_ui64SCR >> 30) & 0x07;
            system_clock_reference_base21 = (_ui64SCR >> 28) & 0x03;
            system_clock_reference_base22 = (_ui64SCR >> 20) & 0xFF;
            system_clock_reference_base23 = (_ui64SCR >> 15) & 0x1F;
            system_clock_reference_base31 = (_ui64SCR >> 13) & 0x03;
            system_clock_reference_base32 = (_ui64SCR >> 5) & 0xFF;
            system_clock_reference_base33 = _ui64SCR & 0x1F;
        }
    
        void getProgram_mux_rate(unsigned int &_uiMux_rate)
        {
            _uiMux_rate = (program_mux_rate1 << 14) | (program_mux_rate2 << 6) | program_mux_rate3;
        }
    
        void setProgram_mux_rate(unsigned int _uiMux_rate)
        {
            program_mux_rate1 = (_uiMux_rate >> 14) & 0xFF;
            program_mux_rate2 = (_uiMux_rate >> 6) & 0xFF;
            program_mux_rate3 = _uiMux_rate & 0x3F;
        }
    };
    复制代码

    这样的好处是可以直接通过

    pack_header header;
    header.setProgram_mux_rate(25200);
    header.setSystem_clock_reference_base(0);
    os.write((char *)&header, sizeof(header));

    来写入文件,但是不方便抽象成类,所以就参考ffmpeg使用了put_bits的方式

    复制代码
    class PackHeader : public HeaderBase
    {
    public:
        UINT64 SCRBase;
        UINT8 SCRExt;
        UINT32 programMuxRate;
        UINT8 stuffingLength;
    
        PackHeader();
        virtual ~PackHeader();
        int Serialize();
    };
    复制代码

    然后在类中加一个序列化函数,来将整个类序列化

    复制代码
    int PackHeader::Serialize()
    {
        int calcBinaryBitLen = 32 //pack_start_code
            + 2 // '01'
            + 3 //system_clock_reference_base [32..30]
            + 1 //marker_bit
            + 15 //system_clock_reference_base [29..15]
            + 1 //marker_bit
            + 15 //system_clock_reference_base [14..0]
            + 1 //marker_bit
            + 9 //system_clock_reference_extension
            + 1 //marker_bit
            + 22 // program_mux_rate
            + 1 //marker_bit
            + 1 //marker_bit
            + 5 //reserved
            + 3; //pack_stuffing_length
    
        if (stuffingLength > 0)
        {
            for (int i = 0; i < stuffingLength; i++)
            {
                calcBinaryBitLen += 8;
            }
        }
    
        if ((calcBinaryBitLen / 8) > binaryLen)
        {
            if (binary)
                delete[] binary;
    
            binary = new BYTE[calcBinaryBitLen / 8];
        }
    
        binaryLen = calcBinaryBitLen / 8;
    
        BYTE* p = binary;
        bits_buffer_t bw;
    
        bits_initwrite(&bw, binaryLen, p);
        bits_write(&bw, 32, PACK_HEADER_START_CODE); //pack_start_code
        bits_write(&bw, 2, 0x1); // '01'
        bits_write(&bw, 3, (SCRBase >> 30) & 0x07); //system_clock_reference_base [32..30]
        bits_write(&bw, 1, 1); //marker_bit
        bits_write(&bw, 15, (SCRBase >> 15) & 0x7FFF);  //system_clock_reference_base [29..15]
        bits_write(&bw, 1, 1);  //marker_bit
        bits_write(&bw, 15, SCRBase & 0x7FFF); //system_clock_reference_base [14..0]
        bits_write(&bw, 1, 1); //marker_bit
        bits_write(&bw, 9, SCRExt); //system_clock_reference_extension
        bits_write(&bw, 1, 1); //marker_bit
        bits_write(&bw, 22, programMuxRate & 0x3FFFFF);  // program_mux_rate
        bits_write(&bw, 1, 1); //marker_bit
        bits_write(&bw, 1, 1); //marker_bit
        bits_write(&bw, 5, 0x1F); //reserved
        bits_write(&bw, 3, stuffingLength & 0x07);  //pack_stuffing_length
    
        if (stuffingLength > 0)
        {
            for (int i = 0; i < stuffingLength; i++)
            {
                bits_write(&bw, 8, 0xFF); //stuffing
            }
        }
        return 1;
    }
    复制代码

    对于DVD而言,一般开始的pack里面还有一个System header

    我们也可以通过分析一个示例文件来了解它

    2.2.Pes层

    Pes层由编码的音频或视频数据(es)加上Pes头组成的,Pes头主要是通过PTS和DTS来提供音视频同步的信息,Pes头的各个bit的意义如下所示

    Pes头之后紧跟着的就是编码的音频或视频数据(es)了,对于DVD而言,一个program pack的大小问0x800,所以一帧MPEG-2视频被分在多个Pes包里,不够一个包的就写在下一帧的第一个pack里,或在Pes Header后面填充FF(PES_header_data_length要加上填充的字节数)。

  • 相关阅读:
    ant 软件包不存在报错
    在 Internet Explorer 中使用 Windows 窗体控件
    智能客户端
    Back to the Future with Smart Clients
    "Automation 服务器不能创建对象" 的解决方案
    Top 10 Reasons for Developers to Create Smart Clients
    Updater Application Block for .NET
    Smart Client Application Model and the .NET Framework 1.1
    Security and Versioning Models in the Windows Forms Engine Help You Create and Deploy Smart Clients
    智能客户端技术总结(二)
  • 原文地址:https://www.cnblogs.com/cnhk19/p/15007384.html
Copyright © 2011-2022 走看看