h264和aac 封装成flv

简介
FLV(Flash Video)是现在非常流行的流媒体格式,由于其视频文件体积轻巧、封装播放简单等特点,使其很适合在网络上进行应用,目前主流的视频网站无一例外地使用了FLV格式。另外由于当前浏览器与Flash Player紧密的结合,使得网页播放FLV视频轻而易举,也是FLV流行的原因之一。

FLV是流媒体封装格式,我们可以将其数据看为二进制字节流。总体上看,FLV包括文件头(File Header)和文件体(File Body)两部分,其中文件体由一系列的Tag及Tag Size对组成。

FLV格式解析


header
头部分由一下几部分组成
Signature(3 Byte)+Version(1 Byte)+Flags(1 Bypte)+DataOffset(4 Byte)

signature 占3个字节
固定FLV三个字符作为标示。一般发现前三个字符为FLV时就认为他是flv文件。
Version 占1个字节
标示FLV的版本号。这里我们看到是1
Flags 占1个字节
内容标示。第0位和第2位,分别表示 video 与 audio 存在的情况.(1表示存在,0表示不存在)。截图看到是0x05,也就是00000101,代表既有视频,也有音频。
DataOffset 4个字节
表示FLV的header长度。这里可以看到固定是9
body
FLV的body部分是由一系列的back-pointers + tag构成

back-pointers 固定4个字节,表示前一个tag的size。
tag 分三种类型,video、audio、scripts。
tag组成
tag type+tag data size+Timestamp+TimestampExtended+stream id+ tag data

type 1个字节。8为Audio,9为Video,18为scripts
tag data size 3个字节。表示tag data的长度。从streamd id 后算起。
Timestreamp 3个字节。时间戳
TimestampExtended 1个字节。时间戳扩展字段
stream id 3个字节。总是0
tag data 数据部分
我们根据实例来分析:
看到第一个TAG
type=0x12=18。这里应该是一个scripts。
size=0x000125=293。长度为293。
timestreamp=0x000000。这里是scripts,所以为0
TimestampExtended =0x00。
stream id =0x000000

具体的代码
1.写文件头

int CFlvEncoder::OpenFile(std::string flvFileName)
{
    AutoLock(&mutex_Lock);
    m_flvFile = fopen(flvFileName.c_str(),"wb");
    if (m_flvFile)
    {
        buffer buf;
        put_tag( &buf, (char*)"FLV" ); // Signature
        put_byte( &buf, 1 );    // Version
        put_byte( &buf, 0x05 );    // Video Only
        put_be32( &buf, 9 );    // DataOffset
        put_be32( &buf, 0 );    // PreviousTagSize0
        WriteFile(&buf);
        return 1;
    }    
    return -1;
}


2.写metadata

void CFlvEncoder::SetParamVideo(uint32_t nWidth,uint32_t nHeight,uint32_t nFrameRate )
{
    AutoLock(&mutex_Lock);
    if (!m_bSetparamVideo)
    {
        m_nWidth = nWidth;
        m_nHeight = nHeight;
        m_nFrameRate = nFrameRate;
        WriteMeta(nFrameRate,nWidth,nHeight);
        m_bSetparamVideo = true;
    }    
}
void CFlvEncoder::WriteMeta(uint32_t nFramerate,uint32_t nWidth,uint32_t nHeight)
{
    buffer *c = new buffer(1024);

    put_byte( c, FLV_TAG_TYPE_META ); // Tag Type "script data"

    uint32_t start = c->d_cur;
    put_be24( c, 0 ); // data length
    put_be24( c, 0 ); // timestamp
    put_be32( c, 0 ); // reserved

    put_byte( c, AMF_DATA_TYPE_STRING );
    put_amf_string( c, (char*)"onMetaData" );

    put_byte( c, AMF_DATA_TYPE_MIXEDARRAY );
    put_be32( c, 6 );


    put_amf_string( c, (char*)"width" );
    put_amf_double( c, nWidth );

    put_amf_string( c, (char*)"height" );
    put_amf_double( c, nHeight );

    put_amf_string( c, (char*)"framerate" );
    put_amf_double( c, (double)nFramerate );

    put_amf_string( c, (char*)"videocodecid" );
    put_byte( c, AMF_DATA_TYPE_STRING );
    put_amf_string( c, (char*)"avc1" );


    put_amf_string( c, (char*)"duration" );
    put_amf_double( c, 0 ); // written at end of encoding

    put_amf_string( c, (char*)"filesize" );
    put_amf_double( c, 0 ); // written at end of encoding


    put_amf_string( c, (char*)"" );
    put_byte( c, AMF_END_OF_OBJECT );

    unsigned int length = c->d_cur - start;
    rewrite_amf_be24( c, length - 10, start );

    put_be32( c, length + 1 ); // tag length
    WriteFile(c);
    delete c;
}
void CFlvEncoder::ReWriteMeta()
{
    buffer *c = new buffer(1024);

    fseek(m_flvFile, 0L, SEEK_END);
    uint32_t nFileSize = ftell(m_flvFile);
    fseek(m_flvFile,10+3,SEEK_SET);
    put_byte( c, FLV_TAG_TYPE_META ); // Tag Type "script data"

    uint32_t start = c->d_cur;
    put_be24( c, 0 ); // data length
    put_be24( c, 0 ); // timestamp
    put_be32( c, 0 ); // reserved

    put_byte( c, AMF_DATA_TYPE_STRING );
    put_amf_string( c, (char*)"onMetaData" );

    put_byte( c, AMF_DATA_TYPE_MIXEDARRAY );
    put_be32( c, 6 );


    put_amf_string( c, (char*)"width" );
    put_amf_double( c, m_nWidth );

    put_amf_string( c, (char*)"height" );
    put_amf_double( c, m_nHeight );

    put_amf_string( c, (char*)"framerate" );
    put_amf_double( c, (double)m_nFrameRate );

    put_amf_string( c, (char*)"videocodecid" );
    put_byte( c, AMF_DATA_TYPE_STRING );
    put_amf_string( c, (char*)"avc1" );


    put_amf_string( c, (char*)"duration" );
    double dduration = (m_nNowTic)/1000;
    put_amf_double( c, dduration ); // written at end of encoding

    put_amf_string( c, (char*)"filesize" );
    put_amf_double( c, nFileSize ); // written at end of encoding

    put_amf_string( c, (char*)"" );
    put_byte( c, AMF_END_OF_OBJECT );

    unsigned int length = c->d_cur - start;
    rewrite_amf_be24( c, length - 10, start );

    put_be32( c, length + 1 ); // tag length
    WriteFile(c);
    delete c;
}


3.写音视频数据
 

void CFlvEncoder::handleVideo(uint8_t* vidoebuf,uint32_t bufsize,int64_t TimeStamp)
{
    AutoLock(&mutex_Lock);
     if (!m_bSetparamVideo)
     {
         return;
     }
    
     int64_t nowTs = m_TimeRecalculate.GetNextVidoeTic(TimeStamp);
      int64_t offset = 0;
     bool bkey = false;

     if (!m_bVideobegin)
     {
     //获取到sps、pps、sei
         MallocNal(&m_pNalSPS);
         MallocNal(&m_pNalPPS);
         MallocNal(&m_pNalSEI);
         ParseH264Nal((char*)vidoebuf,bufsize,(char*)m_outbuf,(int)m_outlen,m_pNalSPS,m_pNalPPS,m_pNalSEI,bkey);
         if (!bkey)
         {
             return;
         }
         //写acv头
         WriteVidoeHeaders(m_pNalSPS,m_pNalPPS,m_pNalSEI);
         m_bVideobegin = true;
         m_bVideoHead = true;
         WriteVideoFrame(m_outbuf,m_outlen,bkey,nowTs,nowTs);
     }
     else if( m_bVideoHead)
     {
     //写视频的tag
        CheckH264key((char*)vidoebuf,bufsize,bkey);
        AVCParseNalUnits((char*)vidoebuf,bufsize,(char*)m_outbuf,(int*)&m_outlen);
        WriteVideoFrame(m_outbuf,m_outlen,bkey,nowTs,offset);
     } 
}

猜你喜欢

转载自blog.csdn.net/meitoothdu/article/details/89203938