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

分析一下GitHub上一份FLV文件分析和解析器的开源代码
GitHub源码地址:功能强大的 FLV 文件分析和解析器

:可以将flv文件的视频tag中的h264类型数据和音频tag中的aac类型数据导出
(只限h264和aac)
(这个代码不太适合用于大文件的分析,因为所有数据都会一直存在内存中)

main.cpp

#include <iostream>
#include <fstream>
#include "FlvParser.h"using namespace std;void Pro(fstream &fin);int main()
{cout << "Hello World!" << endl;string infilename("test.flv");if(!infilename.length()){cout<<"!infilename.length() "<<endl;}fstream fin;fin.open(infilename, ios_base::in | ios_base::binary);if(!fin){cout<<"fin.open failed!"<<endl;return 0;}Pro(fin);fin.close();return 0;}void Pro(fstream& fin)
{FlvParser parser;int nBufSize = 2 * 1024 * 1024;int nFlvPos = 0;uint8_t *pBuf, *pBak;pBuf = new uint8_t[nBufSize];pBak = new uint8_t[nBufSize];while (1){int nReadNum = 0;int nUsedLen = 0;fin.read((char*)pBuf + nFlvPos, nBufSize - nFlvPos);//获取上次读取的字符数nReadNum = fin.gcount();//结束读取if(nReadNum == 0)break;nFlvPos += nReadNum;parser.Parse(pBuf, nFlvPos, nUsedLen);//判断没有处理nFlvPos长度,就前移剩下的数据到前面if(nFlvPos != nUsedLen){memcpy(pBak, pBuf + nUsedLen, nFlvPos - nUsedLen);memcpy(pBuf, pBak, nFlvPos - nUsedLen);}nFlvPos -= nUsedLen;}//输出文件信息parser.PrintInfo();//输出h264数据parser.DumpH264("parser.h264");//输出aac数据parser.DumpAAC("parser.aac");delete[] pBak;delete[] pBuf;}

FlvParser.h

#ifndef FLVPARSER_H
#define FLVPARSER_H#include <vector>
#include <stdint.h>
#include <string>using namespace  std;class FlvParser
{
public:FlvParser();virtual ~FlvParser();int Parse(uint8_t *pBuf, int nBufSize,int& nUsedLen);int PrintInfo();int DumpH264(const std::string& path);int DumpAAC(const std::string& path);private://flv头部信息typedef struct _FlvHeader{int nVersion; //版本int bHaveVideo;//是否有音频int bHaveAudio;//是否有视频int nHeadSize;//FLV头部长度//头部数据uint8_t *pFlvHeader;}FlvHeader;//Tag头部struct TagHeader{int nType;//类型int nDataSize;//body的大小int nTimeStamp;//时间戳int nTSEx;  //时间戳的扩展字节int nStreamID;//流的ID,总是0//完整的时间戳nTimeStamp和nTSEx拼装uint32_t nTotalTS;TagHeader():nType(0), nDataSize(0), nTimeStamp(0), nTSEx(0), nStreamID(0), nTotalTS(0){}~TagHeader(){}};class Tag{public:TagHeader _header;uint8_t* _pTagHeader;//tag头部uint8_t* _pTagData;//tag bodyuint8_t* _pMedia;//tag 元数据,改造后的数据int _nMediaLen;//数据长度public:Tag():_pTagHeader(0), _pTagData(0), _pMedia(0), _nMediaLen(0){}void Init(TagHeader* pHeader, uint8_t* pBuf, int nLeftLen);};class VideoTag: public Tag{public:int _nFrameType;//帧类型int _nCodecID;//视频编解码类型public:VideoTag(TagHeader* pHeader, uint8_t* pBuf, int nLeftLen, FlvParser* pParser);int ParseH264Tag(FlvParser *pParser);int ParseH264Configuration(FlvParser* pParser, uint8_t* pTagData);int ParseNalu(FlvParser* pParser, uint8_t* pTagData);};class AudioTag:public Tag{public:int _nSoundFormat; //编码类型int _nSoundRate;//采样率int _nSoundSize;//精度int _nSoundType;//类型static int _aacProfile;//AAC profilestatic int _sampleRateIndex;//采样率索引static int _channelConfig;//通道设置public:AudioTag(TagHeader* pHeader, uint8_t* pBuf, int nLeftLen, FlvParser* pParser);int ParseAACTag(FlvParser *pParser);int ParseAudioSpecificConfig(FlvParser* pParser, uint8_t *pTagData);int ParserRawAAC(FlvParser* pParser, uint8_t* pTagData);};class MetaDataTag: public Tag{public: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;public:MetaDataTag(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen, FlvParser *pParser);double hexStr2double(const unsigned char* hex, const unsigned int length);int parseMeta(FlvParser* pParser);void printMeta();};struct FlvStat{int nMetaNum, nVideoNum, nAudioNum;int nMaxTimeStamp;int nLengthSize;FlvStat(): nMetaNum(0), nVideoNum(0), nAudioNum(0), nMaxTimeStamp(0), nLengthSize(0){}~FlvStat(){}};//取4字节数值static uint32_t ShowU32(uint8_t* pBuf){return (pBuf[0] << 24) | (pBuf[1] << 16) | (pBuf[2] << 8) | pBuf[3];}//取3字节数值static uint32_t ShowU24(uint8_t* pBuf){return (pBuf[0] << 16) | (pBuf[1] << 8) | pBuf[2];}//取2字节数值static uint32_t ShowU16(uint8_t* pBuf){return (pBuf[0] << 8) | pBuf[1];}//取1字节数值static uint32_t ShowU8(uint8_t* pBuf){return pBuf[0];}//按位的数据写入到64位中,x 本来的值会往左移(高位移)//length : 多少位数据//value  : 实际的值static void WriteU64(uint64_t& x, int length, int value){uint64_t mask = 0xFFFFFFFFFFFFFFFF >> (64 - length);x = (x << length) | ((uint64_t)value & mask);}//字节序替换static uint32_t WriteU32(uint32_t n){uint32_t nn = 0;uint8_t *p = (uint8_t*)&n;uint8_t *pp= (uint8_t*)&nn;pp[0] = p[3];pp[1] = p[2];pp[2] = p[1];pp[3] = p[0];return nn;}friend class Tag;private:FlvHeader* CreateFlvHeader(uint8_t* pBuf);int DestroyFlvHeader(FlvHeader* pHeader);Tag* CreateTag(uint8_t* pBuf, int nLeftLen);int DestroyTag(Tag* pTag);int Stat();int StatVideo(Tag* pTag);int IsUserDataTag(Tag* pTag);private:FlvHeader* _pFlvHeader;vector<Tag*> _vpTag;FlvStat _sStat;//Videojj* _vjj;//h264int _nNaluUintLength;};#endif // FLVPARSER_H

FlvParser.cpp

#include "FlvParser.h"
#include <iostream>
#include <fstream>
#include <string.h>using namespace std;#define CheckBuffer(x) { if((nBufSize - nOffset) < (x)){ nUsedLen = nOffset; return 0;} }int FlvParser::AudioTag::_aacProfile;
int FlvParser::AudioTag::_sampleRateIndex;
int FlvParser::AudioTag::_channelConfig;static const uint32_t nH264StartCode = 0x01000000;FlvParser::FlvParser()
{_pFlvHeader = NULL;}FlvParser::~FlvParser()
{for(int i = 0; i < _vpTag.size(); i++){DestroyTag(_vpTag[i]);delete _vpTag[i];}}int FlvParser::Parse(uint8_t *pBuf, int nBufSize,int& nUsedLen)
{int nOffset = 0;//初始化flv解析器if(_pFlvHeader == 0){//CheckBuffer(9);//如果不够flv header长度就返回if((nBufSize - nOffset) < 9){nUsedLen = nOffset;return 0;}_pFlvHeader = CreateFlvHeader(pBuf + nOffset);nOffset += _pFlvHeader->nHeadSize;}while (1){//CheckBuffer(15);if((nBufSize - nOffset) < 15){nUsedLen = nOffset;return 0;}int nPrevSize = ShowU32(pBuf + nOffset);nOffset += 4;//移过4个字节的PreviousTagSizeTag* pTag = CreateTag(pBuf + nOffset, nBufSize - nOffset);if(pTag == NULL){nOffset -= 4;break;}nOffset += (11 + pTag->_header.nDataSize);_vpTag.push_back(pTag);}nUsedLen = nOffset;return 0;
}int FlvParser::PrintInfo()
{Stat();cout << "vnum: " << _sStat.nVideoNum << " , anum: " << _sStat.nAudioNum << " , mnum: " << _sStat.nMetaNum << endl;cout << "maxTimeStamp: " << _sStat.nMaxTimeStamp << " ,nLengthSize: " << _sStat.nLengthSize << endl;return 0;
}//输出h264文件
int FlvParser::DumpH264(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 = _vpTag.begin(); it_tag != _vpTag.end(); it_tag++){if((*it_tag)->_header.nType != 0x09)continue;f.write((char*)(*it_tag)->_pMedia, (*it_tag)->_nMediaLen);}f.close();return 1;
}//输出AAC文件
int FlvParser::DumpAAC(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 = _vpTag.begin(); it_tag != _vpTag.end(); it_tag ++){if((*it_tag)->_header.nType != 0x08){continue;}AudioTag* pAudioTag = (AudioTag*)(*it_tag);if(pAudioTag->_nSoundFormat != 10)//不是AACcontinue;if(pAudioTag->_nMediaLen != 0){f.write((char*)(*it_tag)->_pMedia, (*it_tag)->_nMediaLen);}}f.close();return 1;
}//创建保存Flv Header信息
FlvParser::FlvHeader* FlvParser::CreateFlvHeader(uint8_t *pBuf)
{FlvHeader* pHeader = new FlvHeader;pHeader->nVersion = pBuf[3]; //版本号pHeader->bHaveAudio = (pBuf[4] >> 2) & 0x01;//是否有音频pHeader->bHaveVideo = (pBuf[4] >> 0) & 0x01;//是否有视频pHeader->nHeadSize = ShowU32(pBuf + 5);// 头部长度//将原来的flv header 数据保存一份pHeader->pFlvHeader = new uint8_t[pHeader->nHeadSize];memcpy(pHeader->pFlvHeader, pBuf, pHeader->nHeadSize);return pHeader;
}//释放Flv Header信息相关内存
int FlvParser::DestroyFlvHeader(FlvHeader *pHeader)
{if(pHeader == NULL)return 0;delete pHeader->pFlvHeader;delete pHeader;pHeader = NULL;return 1;
}int FlvParser::Stat()
{for(int i = 0; i< _vpTag.size(); i++){switch (_vpTag[i]->_header.nType){case 0x08:_sStat.nAudioNum++;break;case 0x09:StatVideo(_vpTag[i]);break;case 0x12:_sStat.nMetaNum++;break;default:break;}}return 1;
}int FlvParser::StatVideo(Tag *pTag)
{_sStat.nVideoNum++;_sStat.nMaxTimeStamp = pTag->_header.nTimeStamp;//0x17 = 帧类型为 1 (h264的IDR 关键帧标识)//               7 (avc)//0x00 = AVC 包类型为 0 (AVC sequence header)if(pTag->_pTagData[0] == 0x17 && pTag->_pTagData[1] == 0x00){//获取nalu 长度使用多少个字节表示//注意:根据上面知道AVC sequence header包(包含SPS 、PPS)时,//帧类型是 1 ,是使用h264的IDR 关键帧相同标识的)_sStat.nLengthSize = (pTag->_pTagData[9] & 0x03) + 1;}return 1;
}//创建Tag
FlvParser::Tag* FlvParser::CreateTag(uint8_t *pBuf, int nLeftLen)
{//开始解析tag头部TagHeader header;header.nType = ShowU8(pBuf + 0);//类型header.nDataSize = ShowU24(pBuf + 1);//tag body的长度header.nTimeStamp = ShowU24(pBuf + 4);//时间戳,低24位header.nTSEx = ShowU8(pBuf + 7);//时间戳扩展字段,高8位header.nStreamID = ShowU24(pBuf + 8);//流的idheader.nTotalTS = (uint32_t)((header.nTSEx << 24)) + header.nTimeStamp;//合成完整的时间戳//如果这次数据不够整个tag长度(包括tag header 和 tag body)就不处理if((header.nDataSize + 11) > nLeftLen){return NULL;}Tag* pTag;switch (header.nType) {case 0x09://视频类型pTag = new VideoTag(&header, pBuf, nLeftLen, this);break;case 0x08://音频类型pTag = new AudioTag(&header, pBuf, nLeftLen, this);break;case 0x12://script 类型pTag = new MetaDataTag(&header, pBuf, nLeftLen, this);break;default:pTag = new Tag();pTag->Init(&header, pBuf, nLeftLen);break;}return pTag;}//释放Tag相应的内存
int FlvParser::DestroyTag(Tag* pTag)
{if(pTag->_pMedia != NULL)delete[] pTag->_pMedia;if(pTag->_pTagData != NULL)delete[] pTag->_pTagData;if(pTag->_pTagHeader != NULL)delete[] pTag->_pTagHeader;return 1;
}//tag 初始化
void FlvParser::Tag::Init(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen)
{//复制tag头部信息(解析好的)memcpy(&_header, pHeader, sizeof(TagHeader));//复制tag头部信息(原数据)_pTagHeader = new uint8_t[11];memcpy(_pTagHeader, pBuf, 11);//复制tag body_pTagData = new uint8_t[_header.nDataSize];memcpy(_pTagData, pBuf + 11, _header.nDataSize);
}FlvParser::MetaDataTag::MetaDataTag(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen, FlvParser *pParser)
{Init(pHeader, pBuf, nLeftLen);uint8_t* pd = _pTagData;m_amf1_type = ShowU8(pd);//第一个AMF 包类型一般为2(字符串)m_amf1_size = ShowU16(pd + 1);//字符串数据的长度//不是字符串就返回,不处理if(m_amf1_type != 2){printf("no metadata\n");return;}//判断字符串是否等于onMetaData就进行处理if(strncmp((const char*)"onMetaData", (const char*)(pd + 3), 10) == 0){parseMeta(pParser);}}//使用字符串的方式提取double类型值,直接将原来的double8个字节,字节序调转一下也一样的
double FlvParser::MetaDataTag::hexStr2double(const unsigned char* hex, const unsigned int length)
{double ret = 0;
#if 0char hexstr[length * 2];memset(hexstr, 0, sizeof(hexstr));for(uint32_t i = 0; i < length; i++){sprintf(hexstr + i * 2, "%02x", hex[i]);}sscanf(hexstr, "%llx", (unsigned long long*)&ret);
#elseret = *(double*)hex;uint8_t* ret_begin = (uint8_t*)&ret;uint8_t* ret_end = ret_begin + 7;for(int i = 0; i<4; i++){uint8_t temp = *ret_begin;*ret_begin = *ret_end;*ret_end = temp;ret_begin++;ret_end--;}#endifreturn ret;
}int FlvParser::MetaDataTag::parseMeta(FlvParser* pParser)
{uint8_t* pd = _pTagData;int dataSize = _header.nDataSize;uint32_t arrayLen = 0;//跨过第一个AMF结构,到第二个AFM结构uint32_t offset = 13;uint32_t nameLen = 0;double doubleValue = 0;string strValue = "";bool boolValue = false;uint32_t valueLen =0;uint8_t u8Value = 0;//判断类型是否等于8(数组)if(pd[offset++] == 0x08){//数组元素长度arrayLen = ShowU32(pd + offset);offset += 4;//跨过数组长度占用的4个字节cout<<"MetaData ArrayLen = " << arrayLen << endl;}else{cout<<"MetaData format error!" <<endl;return -1;}for(uint32_t i = 0; i < arrayLen; i++){doubleValue = 0;boolValue = false;strValue = "";//读取字段名称的长度nameLen = ShowU16(pd + offset);offset += 2;char name[nameLen + 1];//字段存储memset(name, 0, sizeof(name));//复制字段名称memcpy(name, &pd[offset], nameLen);offset += nameLen;//获取AFM类型uint8_t amfType = pd[offset++];switch (amfType) {case 0x0://double 8字节doubleValue = hexStr2double(&pd[offset], 8);offset += 8;break;case 0x1: //bool 1个字节u8Value = ShowU8(pd + offset);offset += 1;if(u8Value != 0)boolValue = true;elseboolValue = false;break;case 0x2://字符串valueLen = ShowU16(pd + offset);offset += 2;strValue.append(pd + offset, pd + offset + valueLen);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;}}printMeta();return 1;
}void FlvParser::MetaDataTag::printMeta()
{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());
}FlvParser::AudioTag::AudioTag(TagHeader* pHeader, uint8_t* pBuf, int nLeftLen, FlvParser* pParser)
{Init(pHeader, pBuf, nLeftLen);uint8_t *pd = _pTagData;_nSoundFormat = (pd[0] & 0xf0) >> 4;//音频格式 如:AAC_nSoundRate = (pd[0] & 0x0c) >> 2;//采样率_nSoundSize = (pd[0] & 0x02) >> 1;//采样精度_nSoundType = (pd[0] & 0x01);//是否立体声if(_nSoundFormat == 10)//AAC{ParseAACTag(pParser);}}int FlvParser::AudioTag::ParseAACTag(FlvParser *pParser)
{uint8_t* pd = _pTagData;//数据包的类型:音频配置信息,音频数据int nAACPacketType = pd[1];//AAC sequence headerif(nAACPacketType == 0){ParseAudioSpecificConfig(pParser, pd);}//AAC 数据(AAC RAW)else if(nAACPacketType == 1){ParserRawAAC(pParser, pd);}else{cout<<"ParseAACTag nAACPacketType = "<< nAACPacketType << endl;}return 1;
}int FlvParser::AudioTag::ParseAudioSpecificConfig(FlvParser* pParser, uint8_t *pTagData)
{uint8_t* pd = _pTagData;_aacProfile = ((pd[2] & 0xf8) >> 3);//5位 AAC 编码级别_sampleRateIndex = ((pd[2] & 0x07) << 1) | (pd[3] >> 7);//4bit 真正采样率索引_channelConfig = (pd[3] >> 3) & 0x0f; //4bit 通道数量printf("----- AAC info------\n");printf("profile:%d\n", _aacProfile);printf("sample rate index:%d\n", _sampleRateIndex);printf("channel config:%d\n", _channelConfig);_pMedia = NULL;_nMediaLen = 0;return 0;
}int FlvParser::AudioTag::ParserRawAAC(FlvParser* pParser, uint8_t* pTagData)
{uint64_t bits = 0;//数据长度,跳过tag data的第一个第二个字节int dataSize = _header.nDataSize - 2;//制作 AAC ADTSWriteU64(bits, 12, 0xFFF);WriteU64(bits, 1, 0);WriteU64(bits, 2, 0);WriteU64(bits, 1, 1);WriteU64(bits, 2, _aacProfile - 1);WriteU64(bits, 4, _sampleRateIndex);WriteU64(bits, 1, 0);WriteU64(bits, 3, _channelConfig);WriteU64(bits, 1, 0);WriteU64(bits, 1, 0);WriteU64(bits, 1, 0);WriteU64(bits, 1, 0);WriteU64(bits, 13, 7 + dataSize);WriteU64(bits, 11, 0x7FF);WriteU64(bits, 2, 0);//_nMediaLen = 7 + dataSize;_pMedia = new uint8_t[_nMediaLen];uint8_t p64[8];//调转字节序p64[0] = (uint8_t)(bits >> 56);//0,没用数据p64[1] = (uint8_t)(bits >> 48);//ADTS 头的0xfff开始位置p64[2] = (uint8_t)(bits >> 40);p64[3] = (uint8_t)(bits >> 32);p64[4] = (uint8_t)(bits >> 24);p64[5] = (uint8_t)(bits >> 16);p64[6] = (uint8_t)(bits >> 8);p64[7] = (uint8_t)(bits);//ADTS header,从p64 + 1 开始的memcpy(_pMedia, p64 + 1, 7);//AAC RAW 从 audio tag data的第3个字节开始的memcpy(_pMedia + 7, pTagData + 2, dataSize);return 1;
}//VideoTag构造
FlvParser::VideoTag::VideoTag(TagHeader* pHeader, uint8_t* pBuf, int nLeftLen, FlvParser* pParser)
{//初始化Init(pHeader, pBuf, nLeftLen);uint8_t *pd = _pTagData;_nFrameType = (pd[0] & 0xf0) >> 4;//帧类型 (HDR / 普通帧)_nCodecID = pd[0] & 0x0f;//视频编码类型(avc)//开始解析 (视频类型 的tag 、 avc 编码类型)if(_header.nType == 0x09 && _nCodecID == 7){ParseH264Tag(pParser);}
}//解析h264
FlvParser::VideoTag::ParseH264Tag(FlvParser *pParser)
{uint8_t* pd = _pTagData;int nAVCPacketType = pd[1];int nCompositionTime = FlvParser::ShowU24(pd + 2);//视频配置信息(sps、pps)if(nAVCPacketType == 0){ParseH264Configuration(pParser, pd);}//视频数据else if(nAVCPacketType == 1){ParseNalu(pParser, pd);}else{cout<<"Unknow ParseH264Tag nAVCPacketType = "<< nAVCPacketType << endl;}return 1;}FlvParser::VideoTag::ParseH264Configuration(FlvParser *pParser, uint8_t *pTagData)
{uint8_t* pd = pTagData;//获取nalu长度使用多少个字节进行存储pParser->_nNaluUintLength = (pd[9] & 0x03) + 1;int sps_size, pps_size;//sps(序列参数集)的长度sps_size = FlvParser::ShowU16(pd + 11);//pps(图像参数集)的长度pps_size = FlvParser::ShowU16(pd + 11 + (2 + sps_size) + 1);//制作元数据(4表示start code)_nMediaLen = 4 + sps_size + 4 + pps_size;_pMedia = new uint8_t[_nMediaLen];memcpy(_pMedia, &nH264StartCode, 4);memcpy(_pMedia + 4, pd + 11 + 2, sps_size);memcpy(_pMedia + 4 + sps_size, &nH264StartCode, 4);memcpy(_pMedia + 4 + sps_size + 4, pd + 11 + 2 + sps_size + 2 + 1, pps_size);return 1;
}FlvParser::VideoTag::ParseNalu(FlvParser *pParser, uint8_t *pTagData)
{uint8_t* pd = pTagData;int nOffset = 0;_pMedia = new uint8_t[_header.nDataSize + 10];_nMediaLen = 0;nOffset = 5;while (1){//如果解析完了一个TAG,就跳出循环if(nOffset >= _header.nDataSize)break;//计算nalu的长度//一个tag可能包含多个nalu,每个nalu前面都有NalUnitLength字节表示每个nalu的长度int nNaluLen;switch (pParser->_nNaluUintLength){case 4:nNaluLen = FlvParser::ShowU32(pd + nOffset);break;case 3:nNaluLen = FlvParser::ShowU24(pd + nOffset);break;case 2:nNaluLen = FlvParser::ShowU16(pd + nOffset);break;default:nNaluLen = FlvParser::ShowU8(pd + nOffset);break;}//添加nalu start codememcpy(_pMedia + _nMediaLen, &nH264StartCode, 4);//复制nalu数据memcpy(_pMedia + _nMediaLen + 4, pd + nOffset + pParser->_nNaluUintLength, nNaluLen);_nMediaLen += (4 + nNaluLen);nOffset += (pParser->_nNaluUintLength + nNaluLen);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/379122.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用pv操作描述如下前驱图_LinkedList实现分析(二)——常用操作

上一篇文章LinkedList实现分析(一)——LinkedList初探与对象创建介绍了LinkedList中的一些重要属性和构造方法&#xff0c;下面我们将详细介绍一下LinkedList提高的常用方法的实现原理元素添加###add(E e)方法往LinkedList添加元素&#xff0c;LinkedList提供了多重方式&#x…

C++多重继承与虚基类及与.NET的比较

多重继承前面我们介绍的派生类只有一个基类&#xff0c;称为单基派生或单一继承。在实际运用中&#xff0c;我们经常需要派生类同时具有多个基类&#xff0c;这种方法称为多基派生或多重继承。2.1 多重继承的声明&#xff1a;在 C 中&#xff0c;声明具有两个以上基类的派生类与…

Javascript的IE和Firefox兼容性汇编

window.event现有问题&#xff1a;使用 window.event 无法在 FF 上运行解决方法&#xff1a;FF 的 event 只能在事件发生的现场使用&#xff0c;此问题暂无法解决。可以这样变通&#xff1a;原代码(可在IE中运行)&#xff1a;<input type"button" name"someB…

Java Double类compareTo()方法与示例

双类compareTo()方法 (Double class compareTo() method) compareTo() method is available in java.lang package. compareTo()方法在java.lang包中可用。 compareTo() method is used to check equality or inequality for this Double-object against the given Double-obje…

平院实训门禁系统导入

这是我的配置&#xff08;如果是Win10最好每一步都管理员身份运行&#xff09; win7 SQLServer2008 VS2012 切记&#xff1a;注意&#xff1a;当你SQLserver创建数据库和VS连接数据库的时候得用同一种方式&#xff0c;要么都用window&#xff08;主机名&#xff09;&#xff0…

ffmpeg 解码音频(aac、mp3)输出pcm文件

ffmpeg 解码音频&#xff08;aac、mp3&#xff09;输出pcm文件 播放pcm可以参考&#xff1a; ffplay -ar 48000 -ac 2 -f f32le out.pcm main.c #include <stdio.h> #include <stdlib.h> #include <string.h>#include <libavutil/frame.h> #include …

Jquery getJSON()

getJSON与aspx 准备工作 Customer类 public class Customer{public int Unid { get; set; }public string CustomerName { get; set; }public string Memo { get; set; }public string Other { get; set; }}&#xff08;一&#xff09;ashx Customer customer new Customer { …

北京中信银行总行地址_中信银行拉萨分行举行“存款保险标识”启用和存款保险条例宣传活动...

11月NOV中信银行拉萨分行举行“存款保险标识”启用和《存款保险条例》宣传活动揭牌启用仪式111月Jul根据人民银行和总行关于“存款保险标识”启用工作相关要求&#xff0c;分行行领导高度重视“存款保险标识”启用和《存款保险条例》宣传活动工作&#xff0c;按照统一工作部署、…

Java ClassLoader getPackage()方法与示例

ClassLoader类的getPackage()方法 (ClassLoader Class getPackage() method) getPackage() method is available in java.lang package. getPackage()方法在java.lang包中可用。 getPackage() method is used to return the package that has been defined in ClassLoader or t…

C---编写程序:求出1~1000之间能被7或12整除,但不能同时被二者整除的所有整数,将结果保存在数组中,要求程序数据的输入、计算和输出均使用函数实现。

编写程序&#xff1a;求出1~1000之间能被7或12整除&#xff0c;但不能同时被二者整除的所有整数&#xff0c;将结果保存在数组中&#xff0c;要求程序数据的输入、计算和输出均使用函数实现。 编程思路&#xff1a;分别编写函数input()、cal()、output()实现数据的输入、计算和…

报告称我国成最大移民输出国 将形成投资产业链(关注)

时代特有的现象&#xff0c;我们应该予以关注 “现在国内房价这么高&#xff0c;政策也看不清&#xff0c;还不如逢高卖掉之前买的几套房子&#xff0c;一两套房子的钱办个投资移民&#xff0c;趁还年轻&#xff0c;拿到绿卡后享受一下美国本国待遇的高等教育了。”广州&#x…

转整型_156.Ruby烘焙大理石豆沙吐司解锁大理石花纹整型

好看又好吃的大理石豆沙面包。红豆馅均匀分布在松软细腻的面包体里&#xff0c;手撕着吃&#xff0c;一层层的甜美与温柔&#xff5e;关于吐司面包&#xff0c;我公众号里写过白吐司(基础款牛奶吐司&#xff0c;超绵鲜奶油吐司)和全麦吐司(基础款50%全麦吐司&#xff0c;经典燕…

ffmpeg 解码视频(h264、mpeg2)输出yuv420p文件

ffmpeg 解码视频&#xff08;h264、mpeg2&#xff09;输出yuv420p文件 播放yuv可以参考&#xff1a;ffplay -pixel_format yuv420p -video_size 768x320 -framerate 25 out.yuv main.c #include <stdio.h> #include <stdlib.h> #include <string.h>#includ…

VS2010 快捷键 (空格显示 绿点, Tab 显示箭头)

VS2010 有用的快捷键 &#xff1a; Ctrl r, ctrl w, 切换空格示。 转载于:https://www.cnblogs.com/fengye87626/archive/2012/11/21/2780716.html

C---编写程序:实现一个随堂测试,能进行加减乘除运算。要求如下:(1)随机产生两个1~10的正整数,在屏幕上输出题目,如:5+3=?(2)学生输入答案,程序检查学生输入答案是否正确,若正确,

编写程序&#xff1a;实现一个随堂测试&#xff0c;能进行加减乘除运算。要求如下&#xff1a; 1&#xff09;随机产生两个1~10的正整数&#xff0c;在屏幕上输出题目&#xff0c;如&#xff1a;53&#xff1f; 2&#xff09;学生输入答案&#xff0c;程序检查学生输入答案是…

分析一下mp4格式的trak -> mdia -> minf -> stbl -> stts、stsc 这两个box信息

分析一下mp4格式的trak -> mdia -> minf -> stbl -> stts、stsc 这两个box信息 &#xff08;因为这两个box在音频trak和视频trak 下都有的&#xff0c;而且都有一个数组的值是比较绕的&#xff09; 目录&#xff1a;stts&#xff1a;记录时间戳的&#xff0c;每个s…

利用SQL注入2分钟入侵网站

说起流光、溯雪、乱刀&#xff0c;可以说是大名鼎鼎无人不知无人不晓&#xff0c;这些都是小榕哥的作品。每次一提起小榕哥来&#xff0c;我的崇拜景仰就如滔滔江水&#xff0c;连绵不绝~~~~&#xff08;又来了&#xff01;&#xff09; 让我们崇拜的小榕哥最新又发布了SQL注入…

pip安装deb_技术|如何在 Ubuntu 上安装 pip

pip 是一个命令行工具&#xff0c;允许你安装 Python 编写的软件包。 学习如何在 Ubuntu 上安装 pip 以及如何使用它来安装 Python 应用程序。有许多方法可以在 Ubuntu 上安装软件。 你可以从软件中心安装应用程序&#xff0c;也可以从下载的 DEB 文件、PPA(LCTT 译注&#xff…

assubclass_Java类class asSubclass()方法及示例

assubclass类类asSubclass()方法 (Class class asSubclass() method) asSubclass() method is available in java.lang package. asSubclass()方法在java.lang包中可用。 asSubclass() method casts this Class object to denote a subclass of the class denoted by the given…

VB6.0 怎样启用控件comdlg32.ocx

VB6.0 怎样启用控件comdlg32.ocx 怎样启用控件comdlg32.ocx 2008-10-08 09:32 提问者&#xff1a; nefu_20061617 |浏览次数&#xff1a;1502次vbs文件中有代码Set ComDlg CreateObject("MSComdlg.CommonDialog")运行时发生错误ActiveX 部件不能创建对象: MSComdlg.…