Flv解复用代码解析

Flv解复用代码解析


目录

  1. 总体流程
    1. main函数
    2. 处理函数process
    3. 解析函数
  2. 解析Flv header和Flv Tag
    1. 解析Flv header
    2. 解析Flv Tag
    3. 解析MetaData Tag
  3. dump H264、AAC和FLV

GitHub源码地址:flv-parser
Flv格式分析:Flv格式分析


1. 总体流程

  1. flv-parser项目能够解析flv格式文件,分离出H264和AAC数据,最后组装回flv格式文件并输出。

1. main函数

  1. 流程:
    1. 读取输⼊⽂件(flv类型的视频⽂件)
    2. 调⽤Process进⾏处理
    3. 退出
int main(int argc, char *argv[]) { //argv[1]是开始的第一个输入参数
    cout << "this is FLV parser test program!\ninput: flv, output:flv\n";

    if (argc != 3) {
        cout << "FlvParser.exe [input flv] [output flv]" << endl;
        return 0;
    }

    fstream fin; //fstream包含读写操作,对打开的文件可进行读写操作
    fin.open(argv[1], ios_base::in | ios_base::binary); //以二进制文件格式进行输入
    if (!fin)
        return 0;

    process(fin, argv[2]); //argv[2],输出flv文件

    fin.close();

    return 1;
}

2. 处理函数process

  1. 流程:
    1. 读取Flv⽂件
    2. 开始解析
    3. 打印解析信息
    4. 把解析之后的数据输出到另外⼀个⽂件中
void process(fstream &fin, const char *filename) {
    CFlvParser parser;

    int nBufSize = 2 * 1024 * 1024;
    int nFlvPos = 0;
    unsigned char *pBuf, *pBak; //用于存储读取的文本数据,不会有负数,所以使用unsigned
    pBuf = new unsigned char[nBufSize]; //从堆中创建2M内存
    pBak = new unsigned char[nBufSize];

    while (1) {
        int nReadNum = 0; //总读取size
        int nUsedLen = 0; //已使用的size
        fin.read((char *) pBuf + nFlvPos, nBufSize - nFlvPos); //读取2M数据
        nReadNum = fin.gcount(); //获取读取的size
        if (nReadNum == 0) //读取为0,说明文件已经读取完
            break;
        nFlvPos += nReadNum; //移动偏移量

        parser.parse(pBuf, nFlvPos, nUsedLen);
        if (nFlvPos != nUsedLen) {
            memcpy(pBak, pBuf + nUsedLen, nFlvPos - nUsedLen); //将未使用的size复制到pBak
            memcpy(pBuf, pBak, nFlvPos - nUsedLen); //从pBak复制回pBuf
        }
        nFlvPos -= nUsedLen;
    }
    parser.PrintInfo(); //打印解析信息
    parser.DumpH264("parser.264"); //dump h264文件
    parser.DumpAAC("parser.aac"); //dump aac文件

    //dump into flv
    parser.DumpFlv(filename); //输出flv格式文件

    delete[]pBak; //释放内存,使用delete[]
    delete[]pBuf;
}

3. 解析函数

  1. 流程:
    1. 解析flv的头部
    2. 解析flv的Tag
int CFlvParser::parse(unsigned char *pBuf, int nBufSize, int &nUsedLen) {
    int nOffset = 0;

    if (m_pFlvHeader == 0) {
        CheckBuffer(9); //flv header头为9字节
        m_pFlvHeader = create_flvHeader(pBuf + nOffset); //创建FLV header
        nOffset += m_pFlvHeader->m_headSize;
    }

    while (1) {
        CheckBuffer(15); // previousTagSize(4字节) + tag header(11字节)
        int nPrevSize = show_u32(pBuf + nOffset);
        nOffset += 4;

        Tag *pTag = create_tag(pBuf + nOffset, nBufSize - nOffset); //获取一个flv tag
        if (pTag == NULL) {
            nOffset -= 4; //如果tag为null,复原offset
            break;
        }
        nOffset += (11 + pTag->m_header.m_dataSize); //tag header + tag datasize

        m_tag.push_back(pTag);
    }

    nUsedLen = nOffset; //记录使用的size
    return 0;
}

2. 解析Flv header和Flv Tag

1. 解析Flv header

  1. flv header数据结构:
    typedef struct FlvHeader_s {
        int m_version;
        int m_haveVideo, m_haveAudio;
        int m_headSize;

        unsigned char *m_flvHeader;
    } FlvHeader;
  1. 解析Flv header
CFlvParser::FlvHeader *CFlvParser::create_flvHeader(unsigned char *pBuf) {
    FlvHeader *pHeader = new FlvHeader;
    pHeader->m_version = pBuf[3];
    pHeader->m_haveAudio = (pBuf[4] >> 2) & 0x01;    //是否有音频,音频标识在第5bit,比如 0000 0101,右移两位就是0000 01,& 0x01就可以得到值了
    pHeader->m_haveVideo = (pBuf[4] >> 0) & 0x01;    //是否有视频
    pHeader->m_headSize = show_u32(pBuf + 5);   //头部长度,从第6字节开始,所以需要移动5字节,flv header头为9字节

    pHeader->m_flvHeader = new unsigned char[pHeader->m_headSize];
    memcpy(pHeader->m_flvHeader, pBuf, pHeader->m_headSize);

    return pHeader;
}

2. 解析Flv Tag

  1. 先解析Tag header,再根据tag type匹配对应类型。
  2. 格式参考:Flv格式分析
CFlvParser::Tag *CFlvParser::create_tag(unsigned char *pBuf, int nLeftLen) {
    TagHeader header;   //开始解析标签头部
    header.m_type = show_u8(pBuf + 0);          //类型
    header.m_dataSize = show_u24(pBuf + 1);     //标签body的长度
    header.m_timeStamp = show_u24(pBuf + 4);    //时间戳 低24bit
    header.m_TSEx = show_u8(pBuf + 7);          //时间戳的扩展字段, 高8bit
    header.m_StreamID = show_u24(pBuf + 8);     //流id
    header.m_TotalTS =
            (unsigned int) ((header.m_TSEx << 24)) + header.m_timeStamp; //转换成uint32_t类型,nTSEx为高位,往左移动24位,然后加上nTimeStamp
    cout << "total TS : " << header.m_TotalTS << endl;
    //cout << "nLeftLen : " << nLeftLen << " , m_dataSize : " << pTag->header.m_dataSize << endl;
    if ((header.m_dataSize + 11) > nLeftLen) { //如果tag header + tag datasize长度大于nLeftLen
        return NULL;
    }

    Tag *pTag;
    switch (header.m_type) { //根据tag type匹配对应类型,格式参考blog:https://blog.csdn.net/weixin_41910694/article/details/109564752
        case 0x09:
            pTag = new CVideoTag(&header, pBuf, nLeftLen, this); //解析视频tag
            break;
        case 0x08:
            pTag = new CAudioTag(&header, pBuf, nLeftLen, this); //解析音频tag
            break;
        case 0x12:
            pTag = new CMetaDataTag(&header, pBuf, nLeftLen, this); //解析metadata tag
            break;
        default:
            pTag = new Tag();
            pTag->init(&header, pBuf, nLeftLen);
    }

    return pTag;
}
  1. 其中Tag的数据结构为:
    class Tag {
    public:
        Tag() : m_tagHeader(NULL), m_tagData(NULL), m_media(NULL), m_mediaLen(0) {}

        void init(TagHeader *pHeader, unsigned char *pBuf, int nLeftLen);

        TagHeader m_header;
        unsigned char *m_tagHeader; //记录tag header原始数据
        unsigned char *m_tagData; //记录tag data原始数据
        unsigned char *m_media;
        int m_mediaLen;
    };
  1. Tag Header数据结构为:
    struct TagHeader {
        int m_type;
        int m_dataSize;
        int m_timeStamp;
        int m_TSEx;
        int m_StreamID;

        unsigned int m_TotalTS;

        TagHeader() : m_type(0), m_dataSize(0), m_timeStamp(0), m_TSEx(0), m_StreamID(0), m_TotalTS(0) {}

        ~TagHeader() {}
    };

1. 解析Video Tag

  1. 当Tag header的type值为8时表示Video Tag,Video Tag数据结构为:
    class CVideoTag : public Tag {
    public:
        CVideoTag(TagHeader *pHeader, unsigned char *pBuf, int nLeftLen, CFlvParser *pParser);

        int m_frameType;
        int m_codecID;

        int parse_H264_tag(CFlvParser *pParser);

        int parse_H264_configuration(CFlvParser *pParser, unsigned char *pTagData);

        int parse_nalu(CFlvParser *pParser, unsigned char *pTagData);
    };
  1. Video Tag第1字节是4bit的帧类型和4bit的视频编码类型,如果视频编码类型为AVC则说明是h264的类型。
CFlvParser::CVideoTag::CVideoTag(TagHeader *pHeader, unsigned char *pBuf, int nLeftLen, CFlvParser *pParser) {
    init(pHeader, pBuf, nLeftLen); //存储tag header和tag data原始数据

    unsigned char *pd = m_tagData;
    m_frameType = (pd[0] & 0xf0) >> 4; //帧类型
    m_codecID = pd[0] & 0x0f;          //视频编码类型,值为7表示AVC
    if (m_header.m_type == 0x09 && m_codecID == 7) { //如果type=9并且codeId为7,那么表示h264
        parse_H264_tag(pParser); //解析h264 tag data
    }
}
  1. 第2字节表示AVCPacketType。
    1. 值为0表示AVC sequence header,解析方式
    2. 1表示AVC NALU。
  2. 按照不同的AVCPacketType进行解析。
int CFlvParser::CVideoTag::parse_H264_tag(CFlvParser *pParser) {
    unsigned char *pd = m_tagData; //pd[0]表示帧类型和编码ID
    //pd[1]表示AVCPacketType,值为0表示AVC sequence header,1表示AVC NALU
    int nAVCPacketType = pd[1];
    int nCompositionTime = CFlvParser::show_u24(pd + 2);

    if (nAVCPacketType == 0) { //AVCPacketType=0那么data数据为AVCDecoderConfigurationRecoder
        parse_H264_configuration(pParser, pd);
    } else if (nAVCPacketType == 1) { //AVCPacketType=1那么表示有一个或多个NALU
        parse_nalu(pParser, pd);
    } else {

    }
    return 1;
}
1. 解析AVCDecoderConfigurationRecoder
/**
AVCDecoderConfigurationRecord {
    uint32_t(8) configurationVersion = 1;  [0]
    uint32_t(8) AVCProfileIndication;       [1]
    uint32_t(8) profile_compatibility;      [2]
    uint32_t(8) AVCLevelIndication;         [3]
    bit(6) reserved = ‘111111’b;            [4]
    uint32_t(2) lengthSizeMinusOne;         [4] 计算方法是 1 + (lengthSizeMinusOne & 3),实际计算结果一直是4
    bit(3) reserved = ‘111’b;                   [5]
    uint32_t(5) numOfSequenceParameterSets; [5] SPS 的个数,计算方法是 numOfSequenceParameterSets & 0x1F,实际计算结果一直为1
    for (i=0; i< numOfSequenceParameterSets; i++) {
        uint32_t(16) sequenceParameterSetLength ;   [6,7]
        bit(8*sequenceParameterSetLength) sequenceParameterSetNALUnit;
    }
    uint32_t(8) numOfPictureParameterSets;      PPS 的个数,一直为1
    for (i=0; i< numOfPictureParameterSets; i++) {
        uint32_t(16) pictureParameterSetLength;
        bit(8*pictureParameterSetLength) pictureParameterSetNALUnit;
    }
}
 */
int CFlvParser::CVideoTag::parse_H264_configuration(CFlvParser *pParser, unsigned char *pTagData) {
    unsigned char *pd = pTagData;
    // 跨过 Tag Data的VIDEODATA(1字节) AVCVIDEOPACKET(AVCPacketType(1字节) 和 CompositionTime(3字节) 5字节)
    pParser->m_nalUnitLength = (pd[9] & 0x03) + 1; //m_nalUnitLength = 1 + (lengthSizeMinusOne & 3),表示NALU的长度

    int sps_size, pps_size;
    sps_size = CFlvParser::show_u16(pd + 11); //sequenceParameterSetLength
    pps_size = CFlvParser::show_u16(pd + 11 + (2 + sps_size) + 1); //pictureParameterSetLength

    m_mediaLen = 4 + sps_size + 4 + pps_size;
    m_media = new unsigned char[m_mediaLen];
    memcpy(m_media, &g_h264StartCode, 4);
    memcpy(m_media + 4, pd + 11 + 2, sps_size); //sequenceParameterSetNALUnit
    memcpy(m_media + 4 + sps_size, &g_h264StartCode, 4);
    memcpy(m_media + 4 + sps_size + 4, pd + 11 + 2 + sps_size + 2 + 1, pps_size); //pictureParameterSetNALUnit

    return 1;
}
  1. 其中m_nalUnitLength表示用几个字节来存储NALU的长度,因为一个tag可能包含多个nalu, 所以每个nalu前面有NalUnitLength字节表示每个nalu的长度
    a. 如果NALULengthSizeMinusOne是0,那么每个NALU使用一个字节的前缀来指定长度,那么每个NALU包的最大长度是255字节,
    b. 使用2个字节的前缀来指定长度,那么每个NALU包的最大长度是64K字节,但分辨率达到1280*720 的图像编码出的I帧,可能大于64K;
    c. 3字节是比较好的,但是因为一些原因(例如对齐)没有被广泛支持,因此4字节长度的前缀是目前使用最多的方式。
2. 解析NALU
int CFlvParser::CVideoTag::parse_nalu(CFlvParser *pParser, unsigned char *pTagData) {
    unsigned char *pd = pTagData;
    int nOffset = 0;

    m_media = new unsigned char[m_header.m_dataSize + 10];
    m_mediaLen = 0;

    nOffset = 5;
    while (1) {
        if (nOffset >= m_header.m_dataSize) //如果解析完了一个tag,跳出循环
            break;
        //一个tag可能包含多个nalu, 所以每个nalu前面有NalUnitLength字节表示每个nalu的长度
        int nNaluLen;  //nNaluLen表示时间nalu数据长度
        switch (pParser->m_nalUnitLength) {
            case 4:
                nNaluLen = CFlvParser::show_u32(pd + nOffset);
                break;
            case 3:
                nNaluLen = CFlvParser::show_u24(pd + nOffset);
                break;
            case 2:
                nNaluLen = CFlvParser::show_u16(pd + nOffset);
                break;
            default:
                nNaluLen = CFlvParser::show_u8(pd + nOffset);
        }
        memcpy(m_media + m_mediaLen, &g_h264StartCode, 4);
        memcpy(m_media + m_mediaLen + 4, pd + nOffset + pParser->m_nalUnitLength, nNaluLen);
        m_mediaLen += (4 + nNaluLen); //m_mediaLen = startcode + nNaluLen(实际数据长度)
        nOffset += (pParser->m_nalUnitLength + nNaluLen); //一个nalu整体长度 = NalUnitLength + NaluLen
    }

    return 1;
}
  1. 根据m_nalUnitLength获取NALU的长度,然后拷贝到m_media。

2. 解析Audio Tag

  1. 当Tag header的type值为8时表示Audio Tag,Audio Tag的数据结构为:
class CAudioTag : public Tag {
public:
    CAudioTag(TagHeader *pHeader, unsigned char *pBuf, int nLeftLen, CFlvParser *pParser);

    int m_soundFormat;
    int m_soundRate;
    int m_soundSize;
    int m_soundType;

    // aac
    static int m_aacProfile;
    static int m_sampleRateIndex;
    static int m_channelConfig;

    int parse_AAC_tag(CFlvParser *pParser);

    int parse_audio_specificConfig(CFlvParser *pParser, unsigned char *pTagData);

    int parse_rawAAC(CFlvParser *pParser, unsigned char *pTagData);
};
  1. Tag Data的第0字节包含:
    a. 4bit的音频格式
    b. 2bit的采样率
    c. 1bit的采样精度
    d. 1bit的判断是否为立体声
  2. 当音频格式值为10表示AAC,进行解析AAC Tag
CFlvParser::CAudioTag::CAudioTag(TagHeader *pHeader, unsigned char *pBuf, int nLeftLen, CFlvParser *pParser) {
    init(pHeader, pBuf, nLeftLen);

    unsigned char *pd = m_tagData;
    m_soundFormat = (pd[0] & 0xf0) >> 4; //音频格式
    m_soundRate = (pd[0] & 0x0c) >> 2;   //采样率
    m_soundSize = (pd[0] & 0x02) >> 1;   //采样精度
    m_soundType = (pd[0] & 0x01);        //是否立体声
    if (m_soundFormat == 10) // m_soundFormat=10时表示AAC
    {
        parse_AAC_tag(pParser); //解析AAC tag
    }
}
  1. Tag Data的第1字节表示AACPacketType,值为0表示AAC sequence header,值为1表示AAC raw data。
int CFlvParser::CAudioTag::parse_AAC_tag(CFlvParser *pParser) {
    unsigned char *pd = m_tagData;
    int nAACPacketType = pd[1]; //值为0表示AAC sequence header,值为1表示AAC raw

    if (nAACPacketType == 0) { //值为0表示AAC sequence header,数据是AudioSpecificConfig
        parse_audio_specificConfig(pParser, pd);
    } else if (nAACPacketType == 1) { //值为1表示AAC raw,数据是Raw AAC frame data
        parse_rawAAC(pParser, pd);
    } else {

    }

    return 1;
}
1. 解析AudioSpecificConfig
  1. 从第2字节开始,包含:
    a. 5bit的AAC编码级别
    b. 4bit的采样率索引
    c. 4bit的通道数量
int CFlvParser::CAudioTag::parse_audio_specificConfig(CFlvParser *pParser, unsigned char *pTagData) {
    unsigned char *pd = m_tagData;

    m_aacProfile = ((pd[2] & 0xf8) >> 3) - 1; //5bit,AAC编码级别,audioObjectType
    m_sampleRateIndex = ((pd[2] & 0x07) << 1) | (pd[3] >> 7); //4bit,真正的采样率索引,samplingFrequencyIndex
    m_channelConfig = (pd[3] >> 3) & 0x0f; //4bit,通道数量

    m_media = NULL;
    m_mediaLen = 0;

    return 1;
}
2. 解析AAC raw data
  1. AAC raw data需要制作ADTS header,可以参考博客:AAC音频基础知识及码流解析
int CFlvParser::CAudioTag::parse_rawAAC(CFlvParser *pParser, unsigned char *pTagData) {
    uint64_t bits = 0;
    int dataSize = m_header.m_dataSize - 2; // 减去两字节的 audio tag data信息部分

    //制作元数据,见ADTS头格式, https://blog.csdn.net/weixin_41910694/article/details/107735932
    write_u64(bits, 12, 0xFFF);
    write_u64(bits, 1, 0);
    write_u64(bits, 2, 0);
    write_u64(bits, 1, 1);
    write_u64(bits, 2, m_aacProfile);
    write_u64(bits, 4, m_sampleRateIndex);
    write_u64(bits, 1, 0);
    write_u64(bits, 3, m_channelConfig);
    write_u64(bits, 1, 0);
    write_u64(bits, 1, 0);
    write_u64(bits, 1, 0);
    write_u64(bits, 1, 0);
    write_u64(bits, 13, 7 + dataSize);
    write_u64(bits, 11, 0x7FF);
    write_u64(bits, 2, 0);

    m_mediaLen = 7 + dataSize;
    m_media = new unsigned char[m_mediaLen];
    unsigned char p64[8];
    p64[0] = (unsigned char) (bits >> 56);
    p64[1] = (unsigned char) (bits >> 48);
    p64[2] = (unsigned char) (bits >> 40);
    p64[3] = (unsigned char) (bits >> 32);
    p64[4] = (unsigned char) (bits >> 24);
    p64[5] = (unsigned char) (bits >> 16);
    p64[6] = (unsigned char) (bits >> 8);
    p64[7] = (unsigned char) (bits);

    memcpy(m_media, p64 + 1, 7);
    memcpy(m_media + 7, pTagData + 2, dataSize);

    return 1;
}

3. 解析MetaData Tag

  1. 当Tag header的type值为18时表示MetaData Tag,MetaData Tag的数据结构为:
class CMetaDataTag : public Tag {
public:
    CMetaDataTag(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen, CFlvParser *pParser);

    double hex_str2double(const unsigned char *hex, const unsigned int length);

    int parse_meta(CFlvParser *pParser);

    void print_meta();

    uint8_t m_amf1_type;
    uint32_t m_amf1_size;
    uint8_t m_amf2_type;
    unsigned char *m_meta;
    unsigned int m_length;

    double m_duration;
    double m_width;
    double m_height;
    double m_videodatarate;
    double m_framerate;
    double m_videocodecid;

    double m_audiodatarate;
    double m_audiosamplerate;
    double m_audiosamplesize;
    bool m_stereo;
    double m_audiocodecid;

    string m_major_brand;
    string m_minor_version;
    string m_compatible_brands;
    string m_encoder;

    double m_filesize;
};
  1. MetaData由两个AFM包组成。
  2. 第⼀个AMF包:
    a. 第0字节表示AMF包类型,⼀般总是0x02,表示字符串。
    b. 第1-2字节为UI16类型值,标识字符串的⻓度,⼀般总是0x000A。
    c. 从第3字节开始为amf1的value,为"onMetaData"。
  3. 第⼆个AMF包:
    a. 第1个字节表示AMF包类型,⼀般总是0x08,表示数组。
    b. 第2-5个字节为UI32类型值,表示数组元素的个数。
    c. 后⾯即为各数组元素的封装,数组元素为元素名称和值组成的对。详细参考博客:FLV格式解析
CFlvParser::CMetaDataTag::CMetaDataTag(CFlvParser::TagHeader *pHeader, uint8_t *pBuf, int nLeftLen,
                                       CFlvParser *pParser) {

    init(pHeader, pBuf, nLeftLen);
    uint8_t *pd = pBuf;
    m_amf1_type = show_u8(pd + 0); //amf1包的type
    m_amf1_size = show_u16(pd + 1); //amf1包的value_size

    if (m_amf1_type != 2) {
        printf("no metadata\n");
        return;
    }

    //解析script
    if (strncmp((const char *) "onMetaData", (const char *) (pd + 3), 10) == 0) { //从第3字节开始为amf1的value
        parse_meta(pParser);
    }

}
int CFlvParser::CMetaDataTag::parse_meta(CFlvParser *pParser) {
    uint8_t *pd = m_tagData;
    int dataSize = m_header.m_dataSize;

    uint32_t arrayLen = 0;
    uint32_t offset = 13; // Type + Value_Size + Value = 13字节

    uint32_t nameLen = 0;
    double doubleValue = 0;
    string strValue = "";
    bool boolValue = false;
    uint32_t valueLen = 0;
    uint8_t u8Value = 0;

    if (pd[offset++] == 0x08) { // 0x08表示onMetaData
        arrayLen = show_u32(pd + offset); //ECMAArrayLength
        offset += 4; //跳过 [ECMAArrayLength]占用的字节
        printf("ArrayLen = %d\n", arrayLen);
    } else {
        printf("metadata format error!!!");
        return -1;
    }

    for (uint32_t i = 0; i < arrayLen; i++) {
        doubleValue = 0;
        boolValue = false;
        strValue = "";
        //读取字段长度
        nameLen = show_u16(pd + offset); //stringLength
        offset += 2;

        char name[nameLen + 1];
        memset(name, 0, sizeof(name));
        memcpy(name, &pd[offset], nameLen); //stringData
        name[nameLen + 1] = '\0';
        offset += nameLen; //跳过字段名占用的长度

        uint8_t amfType = pd[offset++]; //type
        switch (amfType) {
            case 0x0: //Number type, 就是double类型,占用8字节
                doubleValue = hex_str2double(&pd[offset], 8);
                offset += 8; //跳过8字节
                break;
            case 0x1: //boolean type,bool类型,占用1字节
                u8Value = show_u8(pd + offset);
                offset += 1;
                if (u8Value != 0x00) {
                    boolValue = true;
                } else {
                    boolValue = false;
                }
                break;
            case 0x2: //string type,占2字节
                valueLen = show_u16(pd + offset);
                offset += 2;
                strValue.append(pd + offset, pd + offset + valueLen); //todo
                strValue.append("");
                offset += valueLen;
                break;
            default:
                printf("un handle amfType:%d\n", amfType);
                break;
        }

        if (strncmp(name, "duration", 8) == 0) {
            m_duration = doubleValue;
        } else if (strncmp(name, "width", 5) == 0) {
            m_width = doubleValue;
        } else if (strncmp(name, "height", 6) == 0) {
            m_height = doubleValue;
        } else if (strncmp(name, "videodatarate", 13) == 0) {
            m_videodatarate = doubleValue;
        } else if (strncmp(name, "framerate", 9) == 0) {
            m_framerate = doubleValue;
        } else if (strncmp(name, "videocodecid", 12) == 0) {
            m_videocodecid = doubleValue;
        } else if (strncmp(name, "audiodatarate", 13) == 0) {
            m_audiodatarate = doubleValue;
        } else if (strncmp(name, "audiosamplerate", 15) == 0) {
            m_audiosamplerate = doubleValue;
        } else if (strncmp(name, "audiosamplesize", 15) == 0) {
            m_audiosamplesize = doubleValue;
        } else if (strncmp(name, "stereo", 6) == 0) {
            m_stereo = boolValue;
        } else if (strncmp(name, "audiocodecid", 12) == 0) {
            m_audiocodecid = doubleValue;
        } else if (strncmp(name, "major_brand", 11) == 0) {
            m_major_brand = strValue;
        } else if (strncmp(name, "minor_version", 13) == 0) {
            m_minor_version = strValue;
        } else if (strncmp(name, "compatible_brands", 17) == 0) {
            m_compatible_brands = strValue;
        } else if (strncmp(name, "encoder", 7) == 0) {
            m_encoder = strValue;
        } else if (strncmp(name, "filesize", 8) == 0) {
            m_filesize = doubleValue;
        }
    }

    print_meta();
    return 1;
}
  1. 打印MetaData
void CFlvParser::CMetaDataTag::print_meta() {
    printf("\nduration: %0.2lfs, filesize: %.0lfbytes\n", m_duration, m_filesize);

    printf("width: %0.0lf, height: %0.0lf\n", m_width, m_height);
    printf("videodatarate: %0.2lfkbps, framerate: %0.0lffps\n", m_videodatarate, m_framerate);
    printf("videocodecid: %0.0lf\n", m_videocodecid);

    printf("audiodatarate: %0.2lfkbps, audiosamplerate: %0.0lfKhz\n",
           m_audiodatarate, m_audiosamplerate);
    printf("audiosamplesize: %0.0lfbit, stereo: %d\n", m_audiosamplesize, m_stereo);
    printf("audiocodecid: %0.0lf\n", m_audiocodecid);

    printf("major_brand: %s, minor_version: %s\n", m_major_brand.c_str(), m_minor_version.c_str());
    printf("compatible_brands: %s, encoder: %s\n\n", m_compatible_brands.c_str(), m_encoder.c_str());
}

3. dump H264、AAC和FLV

1. dump H264

int CFlvParser::dump_H264(const std::string &path) {
    fstream f;
    f.open(path.c_str(), ios_base::out | ios_base::binary); //以二进制文件输出

    vector<Tag *>::iterator it_tag;
    for (it_tag = m_tag.begin(); it_tag != m_tag.end(); it_tag++) {
        if ((*it_tag)->m_header.m_type != 0x09) //如果不是视频tag,continue
            continue;

        f.write((char *) (*it_tag)->m_media, (*it_tag)->m_mediaLen); //输出
    }
    f.close();

    return 1;
}

2. dump AAC

int CFlvParser::dump_AAC(const std::string &path) {
    fstream f;
    f.open(path.c_str(), ios_base::out | ios_base::binary); //以二进制方式输出

    vector<Tag *>::iterator it_tag;
    for (it_tag = m_tag.begin(); it_tag != m_tag.end(); it_tag++) {
        if ((*it_tag)->m_header.m_type != 0x08) //如果不是音频tag,continue
            continue;

        CAudioTag *pAudioTag = (CAudioTag *) (*it_tag);
        if (pAudioTag->m_soundFormat != 10) //如果不是AAC格式,continue
            continue;

        if (pAudioTag->m_mediaLen != 0)
            f.write((char *) (*it_tag)->m_media, (*it_tag)->m_mediaLen);
    }
    f.close();

    return 1;
}

3. dump FLV

int CFlvParser::dump_Flv(const std::string &path) {
    fstream f;
    f.open(path.c_str(), ios_base::out | ios_base::binary);

    // 写Flv header
    f.write((char *) m_pFlvHeader->m_flvHeader, m_pFlvHeader->m_headSize);
    unsigned int nLastTagSize = 0;


    // 写 flv tag
    vector<Tag *>::iterator it_tag;
    for (it_tag = m_tag.begin(); it_tag < m_tag.end(); it_tag++) {
        unsigned int nn = write_u32(nLastTagSize); //第一个previousTagSize
        f.write((char *) &nn, 4);

        //检查重复的start code
        if ((*it_tag)->m_header.m_type == 0x09 && *((*it_tag)->m_tagData + 1) == 0x01) {
            bool duplicate = false;
            unsigned char *pStartCode = (*it_tag)->m_tagData + 5 + m_nalUnitLength;
            //printf("tagsize=%d\n",(*it_tag)->m_header.m_dataSize);
            unsigned nalu_len = 0;
            unsigned char *p_nalu_len = (unsigned char *) &nalu_len;
            switch (m_nalUnitLength) {
                case 4:
                    nalu_len = show_u32((*it_tag)->m_tagData + 5);
                    break;
                case 3:
                    nalu_len = show_u24((*it_tag)->m_tagData + 5);
                    break;
                case 2:
                    nalu_len = show_u16((*it_tag)->m_tagData + 5);
                    break;
                default:
                    nalu_len = show_u8((*it_tag)->m_tagData + 5);
                    break;
            }
            /*
            printf("nalu_len=%u\n",nalu_len);
            printf("%x,%x,%x,%x,%x,%x,%x,%x,%x\n",(*it_tag)->m_tagData[5],(*it_tag)->m_tagData[6],
                    (*it_tag)->m_tagData[7],(*it_tag)->m_tagData[8],(*it_tag)->m_tagData[9],
                    (*it_tag)->m_tagData[10],(*it_tag)->m_tagData[11],(*it_tag)->m_tagData[12],
                    (*it_tag)->m_tagData[13]);
            */

            unsigned char *pStartCodeRecord = pStartCode;
            int i;
            for (i = 0; i < (*it_tag)->m_header.m_dataSize - 5 - m_nalUnitLength - 4; ++i) {
                if (pStartCode[i] == 0x00 && pStartCode[i + 1] == 0x00 && pStartCode[i + 2] == 0x00 &&
                    pStartCode[i + 3] == 0x01) {
                    if (pStartCode[i + 4] == 0x67) {
                        //printf("duplicate sps found!\n");
                        i += 4;
                        continue;
                    } else if (pStartCode[i + 4] == 0x68) {
                        //printf("duplicate pps found!\n");
                        i += 4;
                        continue;
                    } else if (pStartCode[i + 4] == 0x06) {
                        //printf("duplicate sei found!\n");
                        i += 4;
                        continue;
                    } else {
                        i += 4;
                        //printf("offset=%d\n",i);
                        duplicate = true;
                        break;
                    }
                }
            }

            if (duplicate) {
                nalu_len -= i;
                (*it_tag)->m_header.m_dataSize -= i;
                unsigned char *p = (unsigned char *) &((*it_tag)->m_header.m_dataSize);
                (*it_tag)->m_tagHeader[1] = p[2];
                (*it_tag)->m_tagHeader[2] = p[1];
                (*it_tag)->m_tagHeader[3] = p[0];
                //printf("after,tagsize=%d\n",(int)show_u24((*it_tag)->m_tagHeader + 1));
                //printf("%x,%x,%x\n",(*it_tag)->m_tagHeader[1],(*it_tag)->m_tagHeader[2],(*it_tag)->m_tagHeader[3]);

                f.write((char *) (*it_tag)->m_tagHeader, 11);
                switch (m_nalUnitLength) {
                    case 4:
                        *((*it_tag)->m_tagData + 5) = p_nalu_len[3];
                        *((*it_tag)->m_tagData + 6) = p_nalu_len[2];
                        *((*it_tag)->m_tagData + 7) = p_nalu_len[1];
                        *((*it_tag)->m_tagData + 8) = p_nalu_len[0];
                        break;
                    case 3:
                        *((*it_tag)->m_tagData + 5) = p_nalu_len[2];
                        *((*it_tag)->m_tagData + 6) = p_nalu_len[1];
                        *((*it_tag)->m_tagData + 7) = p_nalu_len[0];
                        break;
                    case 2:
                        *((*it_tag)->m_tagData + 5) = p_nalu_len[1];
                        *((*it_tag)->m_tagData + 6) = p_nalu_len[0];
                        break;
                    default:
                        *((*it_tag)->m_tagData + 5) = p_nalu_len[0];
                        break;
                }
                //printf("after,nalu_len=%d\n",(int)show_u32((*it_tag)->m_tagData + 5));
                f.write((char *) (*it_tag)->m_tagData, pStartCode - (*it_tag)->m_tagData);
                /*
                printf("%x,%x,%x,%x,%x,%x,%x,%x,%x\n",(*it_tag)->m_tagData[0],(*it_tag)->m_tagData[1],(*it_tag)->m_tagData[2],
                        (*it_tag)->m_tagData[3],(*it_tag)->m_tagData[4],(*it_tag)->m_tagData[5],(*it_tag)->m_tagData[6],
                        (*it_tag)->m_tagData[7],(*it_tag)->m_tagData[8]);
                */
                f.write((char *) pStartCode + i, (*it_tag)->m_header.m_dataSize - (pStartCode - (*it_tag)->m_tagData));
                /*
                printf("write size:%d\n", (pStartCode - (*it_tag)->m_tagData) +
                        ((*it_tag)->m_header.m_dataSize - (pStartCode - (*it_tag)->m_tagData)));
                */
            } else {
                f.write((char *) (*it_tag)->m_tagHeader, 11);
                f.write((char *) (*it_tag)->m_tagData, (*it_tag)->m_header.m_dataSize);
            }
        } else {
            f.write((char *) (*it_tag)->m_tagHeader, 11);
            f.write((char *) (*it_tag)->m_tagData, (*it_tag)->m_header.m_dataSize);
        }

        nLastTagSize = 11 + (*it_tag)->m_header.m_dataSize;
    }
    unsigned int nn = write_u32(nLastTagSize);
    f.write((char *) &nn, 4);

    f.close();

    return 1;
}

你可能感兴趣的:(流媒体协议,音视频,c语言,开发语言)