分析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.pswp.cn/news/379122.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/379122.shtml
英文地址,請注明出處:http://en.pswp.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.…