分析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 body
        uint8_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 profile
        static 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;

    //h264
    int _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个字节的PreviousTagSize

        Tag* 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)//不是AAC
            continue;

        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);//流的id
    header.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 0
    char 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);
#else
    ret = *(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--;
    }

#endif

    return 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;
            else
                boolValue = 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 header
    if(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 ADTS
    WriteU64(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 code
        memcpy(_pMedia + _nMediaLen, &nH264StartCode, 4);
        //复制nalu数据
        memcpy(_pMedia + _nMediaLen + 4, pd + nOffset + pParser->_nNaluUintLength, nNaluLen);

        _nMediaLen += (4 + nNaluLen);
        nOffset += (pParser->_nNaluUintLength + nNaluLen);
    }
}


【该资源在win7——64位系统下验证通过。win10系统试试用win7兼容方式打】 解析flv二进制数据的小工具,tag header tag data等都分析出来了的 这个工具的主要功能是查看FLV文件结构,帮助我们理解FLV格式。另外,如果涉及到处理flv文件发,这个工具对于查看处理结果非常有帮助。因此我觉得有必要写一个使用说明,希望这个工具能够给大家提供帮助。 打后的界面如下图所示。 先说一下界面布局:左上方是FLV文件的结构树,右边是FLV文件的字节流数据;左侧结构树下面依次是结构树的信息等级选择、高速模式选择、文件分析用时及进度条等;下方是分析文件的地址显示以及文件选择按钮。下面详细介绍一下相关部分。 结构树及信息等级 FLV结构树是这个工具最重要的显示信息,用户可以直观的查看当前FLV文件的结构。FLVParse默认FLV文件结构树的形式为:File Header + Metadata Tag(1个) + Video or Audio Tags(按顺序)。 结构树的信息详细程度是按等级划分的,之所以要分等级,是为了区分显示信息的详细程度,因为不同程度的分析对于分析所用的时间影响是比较大的(主要在UI界面上),越详细的信息等级占用分析时间越长。一共有6个等级,按从简单到详细介绍如下。 only section position info —— 只有每个section的位置信息,如下图所示。其中每个section后的方括号里是位置信息(十六进制表示),每个“Pre Tag Size”后面的数字表示size的大小(十进制表示),Video&Audio Tag按照在文件中的顺序依次排序标号; file header info, metadata info —— 只有File Header + Metadata Tag的详细信息,如下图所示。其中File Header的详细结构信息会在子树中列出,并在每项后面标示该项的值;Metadata Tag类似,包含Tag Header和Tag Data两个子树,并且对应子项的详细信息也都列出; file header info, metadata info, tag position info —— 包含File Header + Metadata Tag的详细信息,Video&Audio Tags的位置信息,以及Pre Tag Size信息,如下图所示; file header info, metadata info, tag section position info —— 比上个等级多出Video&Audio Tags的Tag Header和Tag Data的位置信息,如下图所示; file header info, metadata info, tag header info —— 比上个等级多出Tag Header的详细子项信息,如下图所示; file header info, metadata info, tag info —— 比上个等级多出Tag Data的详细子项信息,如下图所示。 FLV字节流数据显示 右侧显示了FLV文件的数据,可以让用户方便地查询对应位置上的字节。每一行都以一个十六进制的位置始,该位置为相对于文件头的位置。每一行有十六个字节,每个字节按高4位和第4位显示2个十六进制的字符,用户可以滑动滚动条查看任意位置的字节。 当用户选中左边结构树中的某项时,右边数据会自动选中对应的数据区域(绿色),根据不同项的类型,选中的区域大小也会自动对应。 高速模式 这个选项是为了解决分析比较大的FLV文件时,用户等待时间过长的问题。 普通模式时,分析过程为阻塞模式,即主线程分析完毕后刷新界面,用户才可以继续操作。 高速模式时,为非阻塞模式,主线程分析一小部分后立即返回刷新界面,响应用户操作;另外一个线程会继续分析剩余大部分文件,直到分析完毕自动结束线程。因此高速模式时,用户会看到结构树的滚动条一直在滑动,这是因为后台分析线程在不断向结构树里添加子项。需要注意的是,当后台分析线程还没有结束,如果用户打新的文件进行分析,有可能出现错误的分析结果。这个目前没有进行测试,我想应该是这样的。 这里需要提一下,其实真正分析文件的时间并不会特别长,即使几百兆的文件,几十秒内应该没有问题,时间主要消耗在MFC的树型控件CTreeCtrl上。为了发效率,FLVParse使用了MFC控件,但是CTreeCtrl在结构比较复杂,子项比较多的时候,效率会出现比较大的下降。当子项超过10000的时候,再进行添加的时间大大变长,几乎到了无法忍受的程度,好在还算稳定,没有出现崩溃等现象。粗略估计,每次分析文件,花在更新UI界面上的时间要占总耗时的90%以上,而且对于越大的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值