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 );
    }
}

参考文章

http://blog.chinaunix.net/uid-24567872-id-3471535.html
http://blog.csdn.net/weed_hz/article/details/10232407
http://blog.chinaunix.net/uid-24567872-id-3471547.html

发布了20 篇原创文章 · 获赞 1 · 访问量 9844
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览