1. 程式人生 > >flv檔案格式及h264 aac流封裝成flv

flv檔案格式及h264 aac流封裝成flv

FLV檔案格式

FLV是流媒體封裝格式,我們可以將其資料看為二進位制位元組流。
FLV包括檔案頭(Flv Header)和檔案體(Flv Body)兩部分,其中檔案體由一系列的Tag及Tag Size對組成。Tag又可以分成三類:audio,video,script,分別代表音訊流,視訊流,指令碼流(關鍵字或者檔案資訊之類)。

FLV Header
第1-3位元組:為檔案標識(Signature),總為“FLV”(0x46 0x4C 0x56);
第4位元組:為版本,目前為1(0x01);
第5個位元組:前5位保留必須為0,第6位表示是否存在音訊Tag,第7位保留必須為0,第8位表示是否存在視訊 Tag。
第6-9個位元組:表示從File Header開始到File Body開始的位元組數
這裡寫圖片描述

這裡寫圖片描述

Flv Body
FLV File Body的總體佈局
這裡寫圖片描述
這裡寫圖片描述

FLV File Body的解析:
FLV body由若干個tag 組成;
每個tag header前面有4bytes記錄著上一個tag的長度;
每一個tag第一部分是tag header,tag header長度為11bytes;

tag header的結構
這裡寫圖片描述

第1個位元組:記錄著tag的型別,音訊(0x8),視訊(0x9),指令碼(0x12);
第2到4位元組: 是資料區的長度,也就是tag data的長度;
第5到7位元組: 是時間戳,單位是毫秒;
第8個位元組: 是擴充套件時間戳,時間戳不夠長的時候用;
第9到11位元組:streamID,但是總為0;

這11個位元組就是tag header的結構。

tag data
這裡寫圖片描述

tag data如果是音訊資料,第一個byte記錄audio資訊:
前4bits表示音訊格式(全部格式請看官方文件):
·0 – 未壓縮
·1 – ADPCM
·2 – MP3
·4 – Nellymoser 16-kHz mono
·5 – Nellymoser 8-kHz mono
·10 – AAC
下面兩個bits表示samplerate:
·0 – 5.5KHz
·1 – 11kHz
·2 – 22kHz
·3 – 44kHz
下面1bit表示取樣長度:
·0 – snd8Bit
·1 – snd16Bit
下面1bit表示型別:
·0 – sndMomo
·1 – sndStereo
之後是資料。

如果是視訊資料,第一個byte記錄video資訊:
前4bits表示型別:
·1– keyframe
·2 – inner frame
·3 – disposable inner frame (h.263 only)
·4 – generated keyframe
後4bits表示解碼器ID:
·2 – seronson h.263
·3 – screen video
·4 – On2 VP6
·5 – On2 VP6 with alpha channel
·6 – Screen video version 2
·7 – AVC (h.264)
之後是資料。

如果tag data是指令碼資料,Script Tag Data,該型別Tag又通常被稱為Metadata(元資料) Tag,會放一些關於FLV視訊和音訊的引數資訊,如duration、width、height等。通常該型別Tag會跟在File Header後面作為第一個Tag出現,而且只有一個。

封裝為flv檔案
參考工作伺服器 simplest_librtmp_example 中的simplest_librtmp_receive 的程式碼。
封裝為flv檔案還是比較簡單的。

程式碼參考

void CFlvPushClient::SendMsg( uint32_t uiCode,PushMetaData& stPushMetaData )
{
    m_szFlvHeader[0] = 'F';
    m_szFlvHeader[1] = 'L';
    m_szFlvHeader[2] = 'V';
    m_szFlvHeader[3] = 0x01;
    m_szFlvHeader[4] = stPushMetaData.ucFlags;
    m_szFlvHeader[5] = 0x00;
    m_szFlvHeader[6] = 0x00;
    m_szFlvHeader[7] = 0x00;
    m_szFlvHeader[8] = 0x09;

#ifdef _GRECORD_FLV

    GAL_DEBUG_PRINT << "write FLV Header ...";

    m_stFile.WriteData( m_szFlvHeader, 9 );
    // previous tag size.
    char pts[] = { (char)0x00, (char)0x00, (char)0x00, (char)0x00 };
    m_stFile.WriteData( pts, 4 );
#endif
}

void CFlvPushClient::Write_Flv( SrsSharedPtrMessage* msg )
{
    if ( msg->is_metadata() && m_ucMetadata == 0 )
    {
        m_ucMetadata = 1;
        GAL_DEBUG_PRINT << "write metadata ...";
        char szTagHeader[11];
        memset(szTagHeader,0,11);
        szTagHeader[0] = 0x12;
        char* pszSize = (char*)&msg->size;
        szTagHeader[1] = pszSize[2];
        szTagHeader[2] = pszSize[1];
        szTagHeader[3] = pszSize[0];
        m_stFile.WriteData( szTagHeader,11 );

        m_stFile.WriteData( msg->payload, msg->size );

        char pre_size[4];
        memset( pre_size,0,4 );
        char* pp = (char*)&msg->size;
        pre_size[0] = pp[3];
        pre_size[1] = pp[2];
        pre_size[2] = pp[1];
        pre_size[3] = pp[0];
        m_stFile.WriteData( pre_size, 4 );
    }
    else if ( msg->is_audio() )
    {
        int64_t llTimestamp = msg->timestamp;
        int64_t delta = llTimestamp - m_llTimestampLast;
        if ( delta < CONST_MAX_JITTER_MS_NEG || delta > CONST_MAX_JITTER_MS )
        {
            delta = DEFAULT_FRAME_TIME_MS;
            GAL_WARN_PRINT << "detected last_pts = " << m_llTimestampLast << " pts = " << llTimestamp << " diff = " << delta << " last_time = " << m_llTimestampLastCorrect;
        }

        m_llTimestampLastCorrect = FPC_MAX( 0, m_llTimestampLastCorrect + delta );
        m_llTimestampLast = llTimestamp;

        GAL_DEBUG_PRINT << "write audio ... correct time = " << m_llTimestampLastCorrect;
        char szTagHeader[11];
        memset(szTagHeader,0,11);
        szTagHeader[0] = 0x08;
        char* pszSize = (char*)&msg->size;
        szTagHeader[1] = pszSize[2];
        szTagHeader[2] = pszSize[1];
        szTagHeader[3] = pszSize[0];
        int32_t iTimestamp = (int32_t)m_llTimestampLastCorrect;
        char* pszTimestamp = (char*)&iTimestamp;
        szTagHeader[4] = pszTimestamp[2];
        szTagHeader[5] = pszTimestamp[1];
        szTagHeader[6] = pszTimestamp[0];
        szTagHeader[7] = (iTimestamp >> 24) & 0xFF; 
        m_stFile.WriteData( szTagHeader,11 );

        m_stFile.WriteData( msg->payload, msg->size );

        char pre_size[4];
        memset( pre_size,0,4 );
        char* pp = (char*)&msg->size;
        pre_size[0] = pp[3];
        pre_size[1] = pp[2];
        pre_size[2] = pp[1];
        pre_size[3] = pp[0];
        m_stFile.WriteData( pre_size, 4 );
    }
    else if ( msg->is_video() )
    {
        int64_t llTimestamp = msg->timestamp;
        int64_t delta = llTimestamp - m_llTimestampLast;
        if ( delta < CONST_MAX_JITTER_MS_NEG || delta > CONST_MAX_JITTER_MS )
        {
            delta = DEFAULT_FRAME_TIME_MS;
            GAL_WARN_PRINT << "detected last_pts = " << m_llTimestampLast << " pts = " << llTimestamp << " diff = " << delta << " last_time = " << m_llTimestampLastCorrect;
        }

        m_llTimestampLastCorrect = FPC_MAX( 0, m_llTimestampLastCorrect + delta );
        m_llTimestampLast = llTimestamp;

        GAL_DEBUG_PRINT << "write video ... correct time = " << m_llTimestampLastCorrect;
        char szTagHeader[1];
        memset(szTagHeader,0,11);
        szTagHeader[0] = 0x09;
        char* pszSize = (char*)&msg->size;
        szTagHeader[1] = pszSize[2];
        szTagHeader[2] = pszSize[1];
        szTagHeader[3] = pszSize[0];
        int32_t iTimestamp = (int32_t)m_llTimestampLastCorrect;
        char* pszTimestamp = (char*)&iTimestamp;
        szTagHeader[4] = pszTimestamp[2];
        szTagHeader[5] = pszTimestamp[1];
        szTagHeader[6] = pszTimestamp[0];
        szTagHeader[7] = (iTimestamp >> 24) & 0xFF; 
        m_stFile.WriteData( szTagHeader,11 );

        m_stFile.WriteData( msg->payload, msg->size );

        char pre_size[4];
        memset( pre_size,0,4 );
        char* pp = (char*)&msg->size;
        pre_size[0] = pp[3];
        pre_size[1] = pp[2];
        pre_size[2] = pp[1];
        pre_size[3] = pp[0];
        m_stFile.WriteData( pre_size, 4 );
    }
}

參考文章