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; //用于存储读取的文本数据,不会有负数,所以使用unsignedpBuf = new unsigned char[nBufSize]; //从堆中创建2M内存pBak = new unsigned char[nBufSize];while (1) {int nReadNum = 0; //总读取sizeint nUsedLen = 0; //已使用的sizefin.read((char *) pBuf + nFlvPos, nBufSize - nFlvPos); //读取2M数据nReadNum = fin.gcount(); //获取读取的sizeif (nReadNum == 0) //读取为0,说明文件已经读取完break;nFlvPos += nReadNum; //移动偏移量parser.parse(pBuf, nFlvPos, nUsedLen);if (nFlvPos != nUsedLen) {memcpy(pBak, pBuf + nUsedLen, nFlvPos - nUsedLen); //将未使用的size复制到pBakmemcpy(pBuf, pBak, nFlvPos - nUsedLen); //从pBak复制回pBuf}nFlvPos -= nUsedLen;}parser.PrintInfo(); //打印解析信息parser.DumpH264("parser.264"); //dump h264文件parser.DumpAAC("parser.aac"); //dump aac文件//dump into flvparser.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 headernOffset += 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 tagif (pTag == NULL) {nOffset -= 4; //如果tag为null,复原offsetbreak;}nOffset += (11 + pTag->m_header.m_dataSize); //tag header + tag datasizem_tag.push_back(pTag);}nUsedLen = nOffset; //记录使用的sizereturn 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);    //时间戳 低24bitheader.m_TSEx = show_u8(pBuf + 7);          //时间戳的扩展字段, 高8bitheader.m_StreamID = show_u24(pBuf + 8);     //流idheader.m_TotalTS =(unsigned int) ((header.m_TSEx << 24)) + header.m_timeStamp; //转换成uint32_t类型,nTSEx为高位,往左移动24位,然后加上nTimeStampcout << "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长度大于nLeftLenreturn NULL;}Tag *pTag;switch (header.m_type) { //根据tag type匹配对应类型,格式参考blog:https://blog.csdn.net/weixin_41910694/article/details/109564752case 0x09:pTag = new CVideoTag(&header, pBuf, nLeftLen, this); //解析视频tagbreak;case 0x08:pTag = new CAudioTag(&header, pBuf, nLeftLen, this); //解析音频tagbreak;case 0x12:pTag = new CMetaDataTag(&header, pBuf, nLeftLen, this); //解析metadata tagbreak;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表示AVCif (m_header.m_type == 0x09 && m_codecID == 7) { //如果type=9并且codeId为7,那么表示h264parse_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 NALUint nAVCPacketType = pd[1];int nCompositionTime = CFlvParser::show_u24(pd + 2);if (nAVCPacketType == 0) { //AVCPacketType=0那么data数据为AVCDecoderConfigurationRecoderparse_H264_configuration(pParser, pd);} else if (nAVCPacketType == 1) { //AVCPacketType=1那么表示有一个或多个NALUparse_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),实际计算结果一直是4bit(3) reserved = ‘111’b;                   [5]uint32_t(5) numOfSequenceParameterSets; [5] SPS 的个数,计算方法是 numOfSequenceParameterSets & 0x1F,实际计算结果一直为1for (i=0; i< numOfSequenceParameterSets; i++) {uint32_t(16) sequenceParameterSetLength ;   [6,7]bit(8*sequenceParameterSetLength) sequenceParameterSetNALUnit;}uint32_t(8) numOfPictureParameterSets;      PPS 的个数,一直为1for (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); //sequenceParameterSetLengthpps_size = CFlvParser::show_u16(pd + 11 + (2 + sps_size) + 1); //pictureParameterSetLengthm_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); //sequenceParameterSetNALUnitmemcpy(m_media + 4 + sps_size, &g_h264StartCode, 4);memcpy(m_media + 4 + sps_size + 4, pd + 11 + 2 + sps_size + 2 + 1, pps_size); //pictureParameterSetNALUnitreturn 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;// aacstatic 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 rawif (nAACPacketType == 0) { //值为0表示AAC sequence header,数据是AudioSpecificConfigparse_audio_specificConfig(pParser, pd);} else if (nAACPacketType == 1) { //值为1表示AAC raw,数据是Raw AAC frame dataparse_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编码级别,audioObjectTypem_sampleRateIndex = ((pd[2] & 0x07) << 1) | (pd[3] >> 7); //4bit,真正的采样率索引,samplingFrequencyIndexm_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/107735932write_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包的typem_amf1_size = show_u16(pd + 1); //amf1包的value_sizeif (m_amf1_type != 2) {printf("no metadata\n");return;}//解析scriptif (strncmp((const char *) "onMetaData", (const char *) (pd + 3), 10) == 0) { //从第3字节开始为amf1的valueparse_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表示onMetaDataarrayLen = show_u32(pd + offset); //ECMAArrayLengthoffset += 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); //stringLengthoffset += 2;char name[nameLen + 1];memset(name, 0, sizeof(name));memcpy(name, &pd[offset], nameLen); //stringDataname[nameLen + 1] = '\0';offset += nameLen; //跳过字段名占用的长度uint8_t amfType = pd[offset++]; //typeswitch (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); //todostrValue.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,continuecontinue;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,continuecontinue;CAudioTag *pAudioTag = (CAudioTag *) (*it_tag);if (pAudioTag->m_soundFormat != 10) //如果不是AAC格式,continuecontinue;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 headerf.write((char *) m_pFlvHeader->m_flvHeader, m_pFlvHeader->m_headSize);unsigned int nLastTagSize = 0;// 写 flv tagvector<Tag *>::iterator it_tag;for (it_tag = m_tag.begin(); it_tag < m_tag.end(); it_tag++) {unsigned int nn = write_u32(nLastTagSize); //第一个previousTagSizef.write((char *) &nn, 4);//检查重复的start codeif ((*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;
}

Flv解复用代码解析相关推荐

  1. ffmpeg系列-解复用流程解析

    从我的笔记ffmpeg-mov格式与分离器实现详解一文中,我们已经知道了mov的demuxer相关实现.本文主要来分析demuxer的流程. 1.结构流程图 从上面的结构图中我们可以看到AVForma ...

  2. Vue生命周期详解 对应代码解析

    -使用GitHub阅览 对于Vue的实例,比如 const app = new Vue({...}) 浏览器解析到这段代码的时候,自动执行beforeCreate => created => ...

  3. 分析FLV文件分析和解析器的开源代码

    分析一下GitHub上一份FLV文件分析和解析器的开源代码 GitHub源码地址:功能强大的 FLV 文件分析和解析器 :可以将flv文件的视频tag中的h264类型数据和音频tag中的aac类型数据 ...

  4. FFmpeg从入门到牛掰(一):解复用(demux)讲解

    转载请注明出处:https://blog.csdn.net/impingo 项目地址:https://github.com/im-pingo/pingos 解复用讲解 概念 解复用操作 函数调用流程 ...

  5. 【FFmpeg】FFmpeg 相关术语简介 ( 容器 | 媒体流 | 数据帧 | 数据包 | 编解码器 | 复用 | 解复用 )

    文章目录 一.FFmpeg 简介 二.FFmpeg 相关术语 1.容器 2.媒体流 3.数据帧 4.数据包 5.编解码器 6.复用 7.解复用 博客资源 一.FFmpeg 简介 FFmpeg 是 &q ...

  6. 番外篇15:libevent简单理解(附libevent官方代码解析,和跨平台服务器、客户端链接代码)

    文章目录 一.事件event和事件管理器event_base介绍 二.libevent流程简介(注册->检测->分派) 三.libevent的好处 四.代码比较 4.1 原来reactor ...

  7. 组合模式详解附有代码案例分析(包含透明组合模式、安全组合模式的代码示例)

    组合模式 一.组合模式的概念和角色 (一).组合模式的概念 (二).组合模式的角色 二.组合模式的应用场景 三.透明组合模式的代码示例 四.安全组合模式的代码示例 五.组合模式的优缺点 (一).优点 ...

  8. 模板方法模式详解附有代码案例分析(包含模板方法模式重构JDBC操作业务代码示例)

    模板方法模式 一.模板方法模式的概念和角色 (一).模板方法模式的概念 (二).模板方法模式的角色 二.模板方法模式的应用场景 三. 模板方法模式的代码示例 四.模板方法模式重构JDBC操作业务 五. ...

  9. 【四】【vlc-android】播放控制交互与demux解复用层、媒体数据流拉取层的具体数据传递和控制流程源码分析

    1.VLC中有很多demux/mux/encoder/decoder模块,因此需要先了解这些模块的加载原理,模块的加载原理基本一致,因此举例分析MP4解复用模块如何加载完成的,主要流程如下: // v ...

最新文章

  1. Go 语言编程 — gormigrate GORM 的数据库迁移助手
  2. 百度NeurIPS全球顶会冠军团队,带你7日从零实践强化学习
  3. VTK:相互作用之ImageRegion
  4. 外挂学习之路(8)--- 释放技能call
  5. c++ map的存储结构_「软帝学院」java集合类框架map及相关常见问题二
  6. MarkDown语法, 快捷键,Dos命令
  7. 新iPhone同款?谷歌Pixel 4渲染图曝光:“浴霸”相机模组抢眼
  8. mysql三高讲解(二)2.9: mysql示例数据库sakia database的使用
  9. Laravel 安装mysql、表增加模拟数据、生成控制器
  10. [转载] Java获取嵌套的json串里的返回结果
  11. sqlite简单笔记
  12. 六年级上册计算机教材分析,人教版六年级上册数学教材分析
  13. euht网络登录_基于EUHT技术的城轨高速线路车地无线网络解决方案
  14. 对多个Excel表中的数据进行合并计算
  15. Android开发丶openinstall的集成和使用(推广二维码)
  16. 虚拟展厅三维交互体验满足用户多场景营销需求
  17. 云原生爱好者周刊:美国国家安全局发布网络安全指南
  18. 【T3】打印单据(非新打印)表头显示不全
  19. 材料学专业跨考计算机,2019年985材料跨考同济大学计算机上岸考研初复试经验分享!...
  20. 电功率与力功率的学习

热门文章

  1. 最优化方法的Matlab实现
  2. matlab 斜抛 空气阻力,运用MATLAB对运动学、动力学问题进行过程分析
  3. java getfiles_Java8中的Files和Paths
  4. Tsukinai的第十七个程序(梅森尼数)
  5. 不属于String类所有的方法是?
  6. SpringMVC学习-IDEA创建maven项目添加webapp怎么配置
  7. 9个value_counts()的小技巧,提高Pandas 改进数据分析效率
  8. tp5 时间间隔查询问题
  9. 【字符编码详解】ASCII、GB2312、GBK、UTF-8、UTF-16编码与Unicode字符集
  10. URL地址的两种格式