Flv解复用代码解析
Flv解复用代码解析
目录
- 总体流程
- main函数
- 处理函数process
- 解析函数
- 解析Flv header和Flv Tag
- 解析Flv header
- 解析Flv Tag
- 解析MetaData Tag
- dump H264、AAC和FLV
GitHub源码地址:flv-parser
Flv格式分析:Flv格式分析
1. 总体流程
- flv-parser项目能够解析flv格式文件,分离出H264和AAC数据,最后组装回flv格式文件并输出。
1. main函数
- 流程:
- 读取输⼊⽂件(flv类型的视频⽂件)
- 调⽤Process进⾏处理
- 退出
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
- 流程:
- 读取Flv⽂件
- 开始解析
- 打印解析信息
- 把解析之后的数据输出到另外⼀个⽂件中
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. 解析函数
- 流程:
- 解析flv的头部
- 解析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
- flv header数据结构:
typedef struct FlvHeader_s {int m_version;int m_haveVideo, m_haveAudio;int m_headSize;unsigned char *m_flvHeader;} FlvHeader;
- 解析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
- 先解析Tag header,再根据tag type匹配对应类型。
- 格式参考: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;
}
- 其中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;};
- 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
- 当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);};
- 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}
}
- 第2字节表示AVCPacketType。
- 值为0表示AVC sequence header,解析方式
- 1表示AVC NALU。
- 按照不同的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;
}
- 其中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;
}
- 根据m_nalUnitLength获取NALU的长度,然后拷贝到m_media。
2. 解析Audio Tag
- 当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);
};
- Tag Data的第0字节包含:
a. 4bit的音频格式
b. 2bit的采样率
c. 1bit的采样精度
d. 1bit的判断是否为立体声 - 当音频格式值为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}
}
- 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
- 从第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
- 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
- 当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;
};
- MetaData由两个AFM包组成。
- 第⼀个AMF包:
a. 第0字节表示AMF包类型,⼀般总是0x02,表示字符串。
b. 第1-2字节为UI16类型值,标识字符串的⻓度,⼀般总是0x000A。
c. 从第3字节开始为amf1的value,为"onMetaData"。 - 第⼆个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;
}
- 打印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解复用代码解析相关推荐
- ffmpeg系列-解复用流程解析
从我的笔记ffmpeg-mov格式与分离器实现详解一文中,我们已经知道了mov的demuxer相关实现.本文主要来分析demuxer的流程. 1.结构流程图 从上面的结构图中我们可以看到AVForma ...
- Vue生命周期详解 对应代码解析
-使用GitHub阅览 对于Vue的实例,比如 const app = new Vue({...}) 浏览器解析到这段代码的时候,自动执行beforeCreate => created => ...
- 分析FLV文件分析和解析器的开源代码
分析一下GitHub上一份FLV文件分析和解析器的开源代码 GitHub源码地址:功能强大的 FLV 文件分析和解析器 :可以将flv文件的视频tag中的h264类型数据和音频tag中的aac类型数据 ...
- FFmpeg从入门到牛掰(一):解复用(demux)讲解
转载请注明出处:https://blog.csdn.net/impingo 项目地址:https://github.com/im-pingo/pingos 解复用讲解 概念 解复用操作 函数调用流程 ...
- 【FFmpeg】FFmpeg 相关术语简介 ( 容器 | 媒体流 | 数据帧 | 数据包 | 编解码器 | 复用 | 解复用 )
文章目录 一.FFmpeg 简介 二.FFmpeg 相关术语 1.容器 2.媒体流 3.数据帧 4.数据包 5.编解码器 6.复用 7.解复用 博客资源 一.FFmpeg 简介 FFmpeg 是 &q ...
- 番外篇15:libevent简单理解(附libevent官方代码解析,和跨平台服务器、客户端链接代码)
文章目录 一.事件event和事件管理器event_base介绍 二.libevent流程简介(注册->检测->分派) 三.libevent的好处 四.代码比较 4.1 原来reactor ...
- 组合模式详解附有代码案例分析(包含透明组合模式、安全组合模式的代码示例)
组合模式 一.组合模式的概念和角色 (一).组合模式的概念 (二).组合模式的角色 二.组合模式的应用场景 三.透明组合模式的代码示例 四.安全组合模式的代码示例 五.组合模式的优缺点 (一).优点 ...
- 模板方法模式详解附有代码案例分析(包含模板方法模式重构JDBC操作业务代码示例)
模板方法模式 一.模板方法模式的概念和角色 (一).模板方法模式的概念 (二).模板方法模式的角色 二.模板方法模式的应用场景 三. 模板方法模式的代码示例 四.模板方法模式重构JDBC操作业务 五. ...
- 【四】【vlc-android】播放控制交互与demux解复用层、媒体数据流拉取层的具体数据传递和控制流程源码分析
1.VLC中有很多demux/mux/encoder/decoder模块,因此需要先了解这些模块的加载原理,模块的加载原理基本一致,因此举例分析MP4解复用模块如何加载完成的,主要流程如下: // v ...
最新文章
- Go 语言编程 — gormigrate GORM 的数据库迁移助手
- 百度NeurIPS全球顶会冠军团队,带你7日从零实践强化学习
- VTK:相互作用之ImageRegion
- 外挂学习之路(8)--- 释放技能call
- c++ map的存储结构_「软帝学院」java集合类框架map及相关常见问题二
- MarkDown语法, 快捷键,Dos命令
- 新iPhone同款?谷歌Pixel 4渲染图曝光:“浴霸”相机模组抢眼
- mysql三高讲解(二)2.9: mysql示例数据库sakia database的使用
- Laravel 安装mysql、表增加模拟数据、生成控制器
- [转载] Java获取嵌套的json串里的返回结果
- sqlite简单笔记
- 六年级上册计算机教材分析,人教版六年级上册数学教材分析
- euht网络登录_基于EUHT技术的城轨高速线路车地无线网络解决方案
- 对多个Excel表中的数据进行合并计算
- Android开发丶openinstall的集成和使用(推广二维码)
- 虚拟展厅三维交互体验满足用户多场景营销需求
- 云原生爱好者周刊:美国国家安全局发布网络安全指南
- 【T3】打印单据(非新打印)表头显示不全
- 材料学专业跨考计算机,2019年985材料跨考同济大学计算机上岸考研初复试经验分享!...
- 电功率与力功率的学习
热门文章
- 最优化方法的Matlab实现
- matlab 斜抛 空气阻力,运用MATLAB对运动学、动力学问题进行过程分析
- java getfiles_Java8中的Files和Paths
- Tsukinai的第十七个程序(梅森尼数)
- 不属于String类所有的方法是?
- SpringMVC学习-IDEA创建maven项目添加webapp怎么配置
- 9个value_counts()的小技巧,提高Pandas 改进数据分析效率
- tp5 时间间隔查询问题
- 【字符编码详解】ASCII、GB2312、GBK、UTF-8、UTF-16编码与Unicode字符集
- URL地址的两种格式