手写解复用(二):FLV解析

1、本文的基本作用:引导理解封装格式,但更重要的是要自己去阅读协议,上手实践;
2、本文的最大价值:久之,皆已遗忘;顾之,皆已忆起。

一、基本知识

1、结构概览

2、总体结构

FLV是由FLV Header和FLV Body组成的,FLV Body是由一个个Tag组成的,每个Tag是由Tag Header和Tag Data组成的,音视频数据以及一些配置信息就放在Tag中。

3、FLV Header

FLV Header,记录了FLV的类型, 版本等信息, 是FLV的开头, 一般都差不多, 占9Bytes

4、Tag Header

Tag Header,记录了时间戳, Tag数据的长度等。

5、Tag Data

Tag Data需根据不同的类型来解析,代码中有详细的注释,可参考。

二、重点代码

1、解析FLV Header

static void DealFlvHeader(unsigned char* const headerData) {
    flvHeader.version = data[0];

    data += 1;

    flvHeader.flags_audio = data[0] >> 2 & 0x1;
    flvHeader.flags_video = data[0] & 0x1;

    data += 1;

    flvHeader.headersize = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
}

2、解析Tag Header

static void DealTagHeader() {
    header.type = data[0];
    data += 1;

    header.data_size = (data[0] << 16) | (data[1] << 8) | data[2];
    data += 3;

    header.timestamp = (data[0] << 16) | (data[1] << 8) | data[2];
    data += 3;

    header.timestamp_extended = data[0];
    data += 1;

    header.streamid = (data[0] << 16) | (data[1] << 8) | data[2];
}

3、解析Tag Data

static void DealTagData() {
    switch (tagType) {
        case 0x12:
            DealScriptTagData(tagData, tagSize);
            break;

        case 0x9:
            DealVideoTagData(tagData, tagSize);
            break;

        case 0x8:
            DealAudioTagData(tagData);
            break;

        default:
            break;
    }
}

3.1 解析Script Data

static void DealScriptTagData() {
    amfType = data[0];
        
    amfIndex += 1;
        
    data += 1;
    tagDataSize -= 1;
}

3.2 解析Video Data

static void DealVideoTagData() {
    vTagHeader.freameType = data[0] >> 4 & 0xf;
    vTagHeader.codecId = data[0] & 0xf;
}

3.1 解析Audio Data

static void DealAudioTagData() {
    audioHeader.soundFormat = (data[0] >> 4) & 0xf;
    audioHeader.soundRate = (data[0] >> 2) & 0x3;
    audioHeader.soundSize = (data[0] >> 1) & 0x1;
    audioHeader.soundType = data[0] & 0x1;
}

三、完整源码

纯C代码,gcc编译就能直接运行,打印格式模仿FlvParser的形式。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <arpa/inet.h>

#define PRINTF_DEBUG
#define PRINTF_PACKET_DATA 0

#define MAX_SIGNATURE_LEN 3
#define MAX_PRE_TAG_SIZE_LEN 4
#define MIN_FLV_HEADER_LEN 9
#define MAX_TAG_HEADER_LEN 11
#define MAX_PARSE_TAG_NUM 15
#define MAX_AMF_STR_SIZE 255

/************************************************************************************************************
**                                        flv header: 记录了flv的类型, 版本等信息, 是flv的开头, 一般都差不多, 占9bytes
**
-------------------------------------------------------------------------------------------------------------
**        字段名称               |    长度(bytes)    |        有关描述
-------------------------------------------------------------------------------------------------------------
**        signature              |    3             |        文件标识, 总是为"FLV", 0x46 0x4c 0x56
**        version                |    1             |        版本(目前为0x01)
**        flag                   |    3             |        文件的标志位说明. 前5位保留, 必须为0; 

                                                            第6位为音频Tag: 1表示有音频; 第七位保留, 为0; 第8位为视频Tag: 1表示有视频
**        headersize             |    4             |        整个header的长度, 一般为9(版本为0x01时); 大于9表示下面还有扩展信息
************************************************************************************************************/
typedef struct t_flv_header {
    unsigned char signature[MAX_SIGNATURE_LEN+1];
    unsigned char version;
    unsigned char : 5;
    unsigned char flags_audio: 1;
    unsigned char : 1;
    unsigned char flags_video: 1; 

    int headersize;
} T_FLV_HEADER;

/************************************************************************************************************
**                                        tag header
**
-------------------------------------------------------------------------------------------------------------
**        字段名称               |    长度(bytes)    |        有关描述
-------------------------------------------------------------------------------------------------------------
**        type                   |    1             |        数据类型, (0x12)为脚本类型; (0x08)为音频类型; (0x09)为视频类型
**        data_size              |    3             |        数据区长度
**        timestamp              |    3             |        时间戳, 类型为(0x12)的tag时间戳一直为0, (0xFFFFFF)可以表示长度为4小时, 单位为毫秒.
**        timestamp_extended     |    1             |        将时间戳扩展为4bytes, 代表高8位, 一般都为0, 长度为4小时的flv一般很少见了
**        streamid               |    3             |        总为0
************************************************************************************************************/
typedef struct t_flv_tag_header {
    int type;
    int data_size;
    int timestamp;
    int timestamp_extended;
    int streamid;
} T_FLV_TAG_HEADER;

/************************************************************************************************************
**                                        video tag header
**
-------------------------------------------------------------------------------------------------------------
**        字段名称               |    长度(bytes)    |        有关描述
-------------------------------------------------------------------------------------------------------------
**        FreameType             |    4(bits)             |  FrameType为数据类型, 1为关键帧, 2为非关键帧, 3为h263的非关键帧,
                                                            4为服务器生成关键帧, 5为视频信息或命令帧.
**        CodecId                |    4(bits)             |  CodecID为包装类型, 1为JPEG, 2为H263, 3为Screen video, 
                                                            4为On2 VP6, 5为On2 VP6, 6为Screen videoversion 2, 7为AVC

CodecID=2, 为H263VideoPacket;

CodecID=3, 为ScreenVideopacket;

CodecID=4, 为VP6FLVVideoPacket;

CodecID=5, 为VP6FLVAlphaVideoPacket;

CodecID=6, 为ScreenV2VideoPacket;

CodecID=7, 为AVCVideoPacket.

** AVCVideoPacket Format: | AVCPacketType(8)| CompostionTime(24) | Data | 
    如果AVCPacketType=0x00, 为AVCSequence Header;--描述了SPS以及PPS的信息;
    如果AVCPacketType=0x01, 为AVC NALU;
    如果AVCPacketType=0x02, 为AVC end ofsequence;
    CompositionTime为相对时间戳: 如果AVCPacketType=0x01, 为相对时间戳; 其它, 均为0;

    Data为负载数据:
    如果AVCPacketType=0x00, 为AVCDecorderConfigurationRecord;
    如果AVCPacketType=0x01, 为NALUs;
    如果AVCPacketType=0x02, 为空.

    AVCDecorderConfigurationRecord格式, 包括文件的信息: 

    | cfgVersion(8) | avcProfile(8) | profileCompatibility(8) |avcLevel(8) | reserved(6) | lengthSizeMinusOne(2) | reserved(3) | numOfSPS(5) |spsLength(16) | sps(n) | numOfPPS(8) | ppsLength(16) | pps(n) |

************************************************************************************************************/
typedef struct t_flv_tag_video_header {
    unsigned char freameType:4, codecId:4;
} T_FLV_TAG_VIDEO_HEADER;

/************************************************************************************************************
**                                        AVCDecoderConfigurationRecord
**
-------------------------------------------------------------------------------------------------------------
**        字段名称               |    长度(bytes)    |        有关描述
-------------------------------------------------------------------------------------------------------------
**        configurationVersion   |    1             |        配置版本占用8位, 一定为1
**        AVCProfileIndication   |    1             |        profile_idc占用8位, 从H.264标准SPS第一个字段profile_idc拷贝而来, 指明所用profile
**        profile_compatibility  |    1             |        占用8位, 从H.264标准SPS拷贝的冗余字
**        AVCLevelIndication     |    1             |        level_idc占用8位, 从H.264标准SPS第一个字段level_idc拷贝而来, 指明所用 level
**        reserved           |    6b            |        保留位占6位, 值一定为'111111'
**        lengthSizeMinusOne     |    2b            |        占用2位, 表示NAL单元头的长度, 0表示1字节, 1表示2字节, 2表示3字节, 3表示4字节
**        reserved  	         |    3b            |        保留位占3位, 值一定为'111'
**        numOfSPS  	         |    5b            |        numOfSequenceParameterSets占用5位, 表示当前SPS的个数
**        SPSLength          |    2             |        sequenceParameterSetLength占用16位, SPS占用的长度
**        SPSData            |    *             |        
**        numOfPPS  	         |    5b            |        numOfPictureParameterSets占用8位, 表示当前PPS的个数
**        PPSLength          |    2             |        pictureParameterSetLength占用16位, PPS占用的长度
**        PPSData            |    *             |        numOfPictureParameterSets占用8位, 表示当前PPS的个数
************************************************************************************************************/
typedef struct t_flv_tag_avc_dec_cfg {
    unsigned char configurationVersion;
    unsigned char AVCProfileIndication;
    unsigned char profile_compatibility;
    unsigned char AVCLevelIndication;
    unsigned char :6, lengthSizeMinusOne:2;

    unsigned char :3, numOfSequenceParameterSets:5;
    unsigned short spsLen;
    unsigned char *spsData;

    unsigned char numOfPictureParameterSets;
    unsigned short ppsLen;
    unsigned char *ppsData;
} T_FLV_TAG_AVC_DEC_CFG;

/************************************************************************************************************
**                                        avc video packet header
**
-------------------------------------------------------------------------------------------------------------
**        字段名称               |    长度(bytes)    |        有关描述
------------------------------------------------------------------------------------------------------------
**        AVCPacketType占用1字节 |    1             |        
**        CompositionTime        |    3             |

AVCVideoPacket同样包括Packet Header和Packet Body两部分:
Packet Header:
        AVCPacketType占用1字节, 仅在AVC时有此字段
            0, AVC sequence header (SPS、PPS信息等)
            1, AVC NALU
             2, AVC end of sequence (lower level NALU sequence ender is not required or supported)

        CompositionTime占用24位, 相对时间戳, 如果AVCPacketType=0x01为相对时间戳; 其它, 均为0;
        该值表示当前帧的显示时间, tag的时间为解码时间, 显示时间等于解码时间+CompositionTime.
************************************************************************************************************/
typedef struct t_flv_tag_avc_video_packet {
    unsigned char avcPacketType;

    int compositionTime;

    union videoPacket {
        T_FLV_TAG_AVC_DEC_CFG avcDecCfg;
    } vp;
} T_FLV_TAG_AVC_VIDEO_PACKET;

typedef struct t_flv_tag_audio_header {
    unsigned char soundFormat:4, soundRate:2, soundSize:1, soundType:1;
} T_FLV_TAG_AUDIO_HEADER;

typedef struct t_flv_tag_aac_spec_cfg {
    unsigned char audioObjectType:5;
    unsigned char samplingFreqIndex:4, channelCfg:2;
} T_FLV_TAG_AAC_SPEC_CFG;

typedef struct t_flv_tag_aac_audio_packet {
    unsigned char aacPacketType;

    union audioPacket {
        T_FLV_TAG_AAC_SPEC_CFG aacSpecCfg;
    } ap;
} T_FLV_TAG_AAC_AUDIO_PACKET;

static void inline printfNSpace(int num) {
    printf("%*s", num, " ");
}

static double dealAmfNumber(unsigned char *amfNum) {
    double d = 0;

    unsigned char *dp = (unsigned char *)&d;

    dp[0] = amfNum[7];
    dp[1] = amfNum[6];
    dp[2] = amfNum[5];
    dp[3] = amfNum[4];
    dp[4] = amfNum[3];
    dp[5] = amfNum[2];
    dp[6] = amfNum[1];
    dp[7] = amfNum[0];

    return d;
}

static void DealFlvHeader(unsigned char* const headerData) {
    unsigned char *data = NULL;

    T_FLV_HEADER flvHeader = {0};

    data = headerData;

    memset(&flvHeader, 0x0, sizeof(T_FLV_HEADER));

    memcpy(flvHeader.signature, data, MAX_SIGNATURE_LEN);

    flvHeader.signature[MAX_SIGNATURE_LEN] = '\0';

    data += MAX_SIGNATURE_LEN;

    flvHeader.version = data[0];

    data += 1;

    flvHeader.flags_audio = data[0] >> 2 & 0x1;
    flvHeader.flags_video = data[0] & 0x1;

    data += 1;

    flvHeader.headersize = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];

    if (0x1 != flvHeader.version) {
        printf("version is not 1, todo...\n");
    }

#ifdef PRINTF_DEBUG
    printf("+FLV Header\n");
    
    printfNSpace(4);
    
    printf("signature: %s, version: %d, flags_audio: %d, flags_video: %d, headersize: %d\n",
            flvHeader.signature, flvHeader.version, flvHeader.flags_audio, flvHeader.flags_video, flvHeader.headersize);
#endif
}

static void DealTagHeader(unsigned char* const headerData, T_FLV_TAG_HEADER *tagHeader) {
    static int videoTagNum = 0;
    static int audioTagNum = 0;

    unsigned char *data = NULL;

    T_FLV_TAG_HEADER header = {0};

    data = headerData;

    memset(&header, 0x0, sizeof(T_FLV_TAG_HEADER));

    header.type = data[0];

    data += 1;

    header.data_size = (data[0] << 16) | (data[1] << 8) | data[2];

    data += 3;

    header.timestamp = (data[0] << 16) | (data[1] << 8) | data[2];

    data += 3;

    header.timestamp_extended = data[0];

    data += 1;

    header.streamid = (data[0] << 16) | (data[1] << 8) | data[2];

    memcpy(tagHeader, &header, sizeof(T_FLV_TAG_HEADER));

#ifdef PRINTF_DEBUG
    switch (tagHeader->type) {
        case 0x12:
            printfNSpace(4);
            printf("+Script Tag\n");
            
            break;
            
        case 0x9:
            videoTagNum++;
            
            printfNSpace(4);
            printf("+Video Tag[%d]\n", videoTagNum);

            break;

        case 0x8:
            audioTagNum++;
            
            printfNSpace(4);
            printf("+Audio Tag[%d]\n", audioTagNum);

            break;

        default:
            break;
    }

    printfNSpace(8);
    printf("+Tag Header\n");
    
    printfNSpace(12);
    printf("type: %d, data_size: %d, timestamp: %d, timestamp_extended: %d, streamid: %d\n",
            tagHeader->type, tagHeader->data_size, tagHeader->timestamp, tagHeader->timestamp_extended, tagHeader->streamid);
#endif
}

static void DealScriptTagData(unsigned char* const tagData, unsigned int tagDataSize) {
    int i = 0;
    int amfType = 0;
    int amfIndex = 0;
    int valueType = 0;
    int valueSize = 0;
    int keySize = 0;
    int arrayCount = 0;
    int amfStringSize = 0;

    double amfNum = 0;

    unsigned char amfStr[MAX_AMF_STR_SIZE+1] = {0};

    unsigned char *data = NULL;

    data = tagData;

    for (;;) {
        if (tagDataSize <= 3) {
            break;
        }
        
        amfType = data[0];
        
        amfIndex += 1;
        
        data += 1;
        tagDataSize -= 1;

#ifdef PRINTF_DEBUG
        printfNSpace(12);
        printf("AMF%d type: %d\n", amfIndex, amfType);
#endif
        
        switch (amfType) {
            case 2:
                amfStringSize = (data[0] << 8) | data[1];

#ifdef PRINTF_DEBUG
                printfNSpace(12);
                printf("AMF%d String size: %d\n", amfIndex, amfStringSize);
#endif
                
                data += 2;
                tagDataSize -= 2;
                
                memset(amfStr, 0x0, sizeof(amfStr));
                
                memcpy(amfStr, data, amfStringSize);
                
                amfStr[amfStringSize] = '\0';
                    
#ifdef PRINTF_DEBUG
                printfNSpace(12);
                printf("AMF%d String: %s\n", amfIndex, amfStr);
#endif
                
                data += amfStringSize;
                tagDataSize -= amfStringSize;

                break;
            
            // object, 键值对, AMF_OBJECT
            case 3:
#ifdef PRINTF_DEBUG
                printfNSpace(12);
                printf("+Metadata\n");
#endif
                // for end 00 00 09
                while (tagDataSize-3 >= 0)  {
                    // for end 00 00 09
                    if ((data[0] == 0x0) && (data[1] == 0x0) && (data[2] == 0x9)) {
                        break;
                    }
                    
                    keySize = (data[0] << 8) | data[1];
                    data += 2;
                    tagDataSize -= 2;
                    
                    memset(amfStr, 0x0, sizeof(amfStr));

                    memcpy(amfStr, data, keySize);

                    amfStr[keySize] = '\0';

#ifdef PRINTF_DEBUG
                    printfNSpace(16);
                    printf("%s: ", amfStr);
#endif
                    data += keySize;
                    tagDataSize -= keySize;
                    
                    valueType = data[0];
                    
                    data += 1;
                    tagDataSize -= 1;
                    
                    if (0 == valueType) {
                        amfNum = dealAmfNumber(data);
#ifdef PRINTF_DEBUG
                        printf("%lf\n", amfNum);
#endif

                        data += 8;
                        tagDataSize -= 8;
                    } else if (1 == valueType) {
#ifdef PRINTF_DEBUG
                        printf("%d\n", data[0]);
#endif
                        data += 1;
                        tagDataSize -= 1;
                    } else if (2 == valueType) {
                        valueSize = (data[0] << 8) | data[1];
                        
                        data += 2;
                        tagDataSize -= 2;
                        
                        memset(amfStr, 0x0, sizeof(amfStr));

                        memcpy(amfStr, data, valueSize);

                        amfStr[valueSize] = '\0';

#ifdef PRINTF_DEBUG
                        printf("%s\n", amfStr);
#endif
                        
                        data += valueSize;
                        tagDataSize -= valueSize;
                    } else {
                        //printf("now can not parse value type: %d\n", valueType);
                        
                        return;
                    }
                }

                break;
            
            // 数组
            case 8:
                arrayCount = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];

#ifdef PRINTF_DEBUG
                printfNSpace(12);
                printf("AMF%d Metadata count: %d\n", amfIndex, arrayCount);
                
                printfNSpace(12);
                printf("+Metadata\n");
#endif
                
                data += 4;
                tagDataSize -= 4;
                
                for (i=0; i<arrayCount; i++) {
                    keySize = (data[0] << 8) | data[1];
                    
                    data += 2;
                    tagDataSize -= 2;
                    
                    memset(amfStr, 0x0, sizeof(amfStr));

                    memcpy(amfStr, data, keySize);

                    amfStr[keySize] = '\0';
                
#ifdef PRINTF_DEBUG
                    printfNSpace(16);
                    printf("%s: ", amfStr);
#endif
                    
                    data += keySize;
                    tagDataSize -= keySize;
                    
                    valueType = data[0];
                    
                    data += 1;
                    tagDataSize -= 1;
                    
                    if (0 == valueType) {
                        amfNum = dealAmfNumber(data);
#ifdef PRINTF_DEBUG
                        printf("%lf\n", amfNum);
#endif

                        data += 8;
                        tagDataSize -= 8;
                    } else if (1 == valueType) {
#ifdef PRINTF_DEBUG
                        printf("%d\n", data[0]);
#endif
                        data += 1;
                        tagDataSize -= 1;
                    } else if (2 == valueType) {
                        valueSize = (data[0] << 8) | data[1];
                        
                        data += 2;
                        tagDataSize -= 2;
                        
                        memset(amfStr, 0x0, sizeof(amfStr));

                        memcpy(amfStr, data, valueSize);

                        amfStr[valueSize] = '\0';

#ifdef PRINTF_DEBUG
                        printf("%s\n", amfStr);
#endif
                        
                        data += valueSize;
                        tagDataSize -= valueSize;
                    } else {
                        //printf("now can not parse value type: %d\n", valueType);
                        
                        return;
                    }
                }

                break;
                
            default:
                break;
        }
    }
}

/*
   Video Header = | FrameType(4) | CodecID(4) |
   VideoData = | FrameType(4) | CodecID(4) | VideoData(n) |
*/
static void DealVideoTagData(unsigned char* const tagData, unsigned int tagDataSize) {
    int i = 0;

    unsigned char *data = NULL;

    data = tagData;

    T_FLV_TAG_VIDEO_HEADER vTagHeader = {0};
    T_FLV_TAG_AVC_VIDEO_PACKET avcVideoPacket = {0};

    memset(&vTagHeader, 0x0, sizeof(vTagHeader));

    vTagHeader.freameType = data[0] >> 4 & 0xf;
    vTagHeader.codecId = data[0] & 0xf;

    data++;

#ifdef PRINTF_DEBUG
    printfNSpace(12);
    printf("FrameType: %d\n", vTagHeader.freameType);
    
    printfNSpace(12);
    printf("CodecId: %d\n", vTagHeader.codecId);
#endif

    /* now just avc(h264) */
    switch (vTagHeader.codecId) {
        case 0x07:
            memset(&avcVideoPacket, 0x0, sizeof(avcVideoPacket));
            
            avcVideoPacket.avcPacketType = data[0];
            avcVideoPacket.compositionTime = (data[1] << 16) | (data[2] << 8) | data[3];
            
            data += 4;
            
            if (0 == avcVideoPacket.avcPacketType) {
#ifdef PRINTF_DEBUG
                printfNSpace(12);
                printf("+AVCVideoPacket\n");
                
                printfNSpace(16);
                printf("AVCPacketType: %d\n", avcVideoPacket.avcPacketType);
                
                printfNSpace(16);
                printf("CompositionTime Offset: %d\n", avcVideoPacket.compositionTime);
#endif

                printfNSpace(16);
                printf("+AVCDecoderConfigurationRecord\n");
                
                avcVideoPacket.vp.avcDecCfg.configurationVersion = data[0];
                avcVideoPacket.vp.avcDecCfg.AVCProfileIndication = data[1];
                avcVideoPacket.vp.avcDecCfg.profile_compatibility = data[2];
                avcVideoPacket.vp.avcDecCfg.AVCLevelIndication = data[3];
                avcVideoPacket.vp.avcDecCfg.lengthSizeMinusOne = data[4] & 0x3;
                avcVideoPacket.vp.avcDecCfg.numOfSequenceParameterSets = data[5] & 0x1f;
                avcVideoPacket.vp.avcDecCfg.spsLen = (data[6] << 8) | data[7];
                
                // todo, parse sps
                
                data += (8+avcVideoPacket.vp.avcDecCfg.spsLen);
                
                avcVideoPacket.vp.avcDecCfg.numOfPictureParameterSets = data[0];
                avcVideoPacket.vp.avcDecCfg.ppsLen = (data[1] << 8) | data[2];
                
                // todo, parse pps

#ifdef PRINTF_DEBUG
            printfNSpace(20);
            printf("configurationVersion: %d\n", avcVideoPacket.vp.avcDecCfg.configurationVersion);
            
            printfNSpace(20);
            printf("AVCProfileIndication: %d\n", avcVideoPacket.vp.avcDecCfg.AVCProfileIndication);
            
            printfNSpace(20);
            printf("profile_compatibility: %d\n", avcVideoPacket.vp.avcDecCfg.profile_compatibility);
            
            printfNSpace(20);
            printf("AVCLevelIndication: %d\n", avcVideoPacket.vp.avcDecCfg.AVCLevelIndication);
            
            printfNSpace(20);
            printf("lengthSizeMinusOne: %d\n", avcVideoPacket.vp.avcDecCfg.lengthSizeMinusOne);
            
            printfNSpace(20);
            printf("numOfSequenceParameterSets: %d\n", avcVideoPacket.vp.avcDecCfg.numOfSequenceParameterSets);
            
            printfNSpace(20);
            printf("sequenceParameterSetLength: %d\n", avcVideoPacket.vp.avcDecCfg.spsLen);
            
            printfNSpace(20);
            printf("numOfPictureParameterSets: %d\n", avcVideoPacket.vp.avcDecCfg.numOfPictureParameterSets);
            
            printfNSpace(20);
            printf("pictureParameterSetLength: %d\n", avcVideoPacket.vp.avcDecCfg.ppsLen);
#endif
            } else {
#ifdef PRINTF_DEBUG
                printfNSpace(12);
                printf("+Video Data\n");
                
                printfNSpace(16);
                printf("AVCPacketType: %d\n", avcVideoPacket.avcPacketType);
                
                printfNSpace(16);
                printf("CompositionTime Offset: %d\n", avcVideoPacket.compositionTime);
                
                printfNSpace(16);
                printf("Data\n");
                
#if PRINTF_PACKET_DATA
                for (i=0; i<tagDataSize-(1+1+3); i++) {
                    if ((i!=0) && (i%16==0)) {
                        printf("\n");
                    }
                    
                    if (i%16==0) {
                        printfNSpace(20);
                    }
                    
                    printf("%02x ", data[i]);
                }
                
                printfNSpace(20);
                printf("\n");
#endif
#endif
            }
            
            break;
            
        default:
            break;
    }
}

static void DealAudioTagData(unsigned char* const tagData) {
    unsigned char *data = NULL;

    data = tagData;

    T_FLV_TAG_AUDIO_HEADER audioHeader = {0};
    T_FLV_TAG_AAC_AUDIO_PACKET aPacket = {0};

    memset(&audioHeader, 0x0, sizeof(T_FLV_TAG_AUDIO_HEADER));

    audioHeader.soundFormat = (data[0] >> 4) & 0xf;
    audioHeader.soundRate = (data[0] >> 2) & 0x3;
    audioHeader.soundSize = (data[0] >> 1) & 0x1;
    audioHeader.soundType = data[0] & 0x1;

#ifdef PRINTF_DEBUG
    printfNSpace(12);
    printf("SoundFormat: %d\n", audioHeader.soundFormat);

    switch (audioHeader.soundRate) {
        case 0:
            printfNSpace(12);
            printf("SoundRate: 5.5-KHz\n");
            break;
            
        case 1:
            printfNSpace(12);
            printf("SoundRate: 11-KHz\n");
            break;
        
        case 2:
            printfNSpace(12);
            printf("SoundRate: 22-KHz\n");
            break;
        
        case 3:
            printfNSpace(12);
            printf("SoundRate: 44-KHz\n");
            break;
        
        default:
            printfNSpace(12);
            printf("SoundRate: %d\n", audioHeader.soundRate);
    }

    switch (audioHeader.soundSize) {
        case 0:
            printfNSpace(12);
            printf("SoundSize: snd8bit\n");
            break;
        
        case 1:
            printfNSpace(12);
            printf("SoundSize: snd16bit\n");
            break;
        
        default:
            printfNSpace(12);
            printf("SoundSize: %d\n", audioHeader.soundSize);
    }

    switch (audioHeader.soundType) {
        case 0:
            printfNSpace(12);
            printf("SoundType: sndMono\n");
            break;
        
        case 1:
            printfNSpace(12);
            printf("SoundType: sndStereo\n");
            break;
        
        default:
            printfNSpace(12);
            printf("SoundSize: %d\n", audioHeader.soundSize);
    }
#endif

    data++;

    /* now just for aac */
    switch (audioHeader.soundFormat) {
        case 0xa:
            memset(&aPacket, 0x0, sizeof(T_FLV_TAG_AAC_AUDIO_PACKET));
            
            aPacket.aacPacketType = data[0];
            
            if (0 == aPacket.aacPacketType) {
#ifdef PRINTF_DEBUG
                printfNSpace(12);
                printf("+AACAudioData\n");
                
                printfNSpace(16);
                printf("AACPacketType: %d\n", aPacket.aacPacketType);
#endif
                aPacket.ap.aacSpecCfg.audioObjectType = (data[1] >> 3) & 0x1f;
                aPacket.ap.aacSpecCfg.samplingFreqIndex = ((data[1] & 0x7) << 1) | ((data[2] >> 7) & 0x1);
                aPacket.ap.aacSpecCfg.channelCfg = (data[2] >> 3) & 0xf;
                
#ifdef PRINTF_DEBUG
                printfNSpace(16);
                printf("+AudioSpecificConfig\n");
                
                printfNSpace(20);
                printf("AudioObjectType: %d\n", aPacket.ap.aacSpecCfg.audioObjectType);
                
                printfNSpace(20);
                printf("SamplingFrequencyIndex: %d\n", aPacket.ap.aacSpecCfg.samplingFreqIndex);
                
                printfNSpace(20);
                printf("ChannelConfiguration: %d\n", aPacket.ap.aacSpecCfg.channelCfg);
#endif
            } else {
#ifdef PRINTF_DEBUG
                printfNSpace(12);
                printf("+AACAudioData\n");
                
                printfNSpace(16);
                printf("AACPacketType: %d\n", aPacket.aacPacketType);
                
                printfNSpace(16);
                printf("Data(Raw AAC frame data)\n");
#endif
            }
            
            break;
            
        default:
            break;
    }
}

static void DealTagData(unsigned char* const tagData, const int tagType, const unsigned int tagSize) {
#ifdef PRINTF_DEBUG
    printfNSpace(8);
    printf("%s\n", "+Tag Data");
#endif

    switch (tagType) {
        case 0x12:
            DealScriptTagData(tagData, tagSize);

            break;

        case 0x9:
            DealVideoTagData(tagData, tagSize);

            break;

        case 0x8:
            DealAudioTagData(tagData);

            break;

        default:
            break;
    }
}

int main(int argc, char *argv[]) {
    int dataLen = 0;
    int previousTagSize = 0;

    FILE *fp = NULL;

    unsigned char *tagData = NULL;

    unsigned char flvHeaderData[MIN_FLV_HEADER_LEN+1] = {0};
    unsigned char preTagSizeData[MAX_PRE_TAG_SIZE_LEN+1] = {0};
    unsigned char tagHeaderData[MAX_TAG_HEADER_LEN+1] = {0};

    T_FLV_TAG_HEADER tagHeader = {0};

    if (2 != argc) {
        printf("Usage: flvparse **.flv\n");

        return -1;
    }

    fp = fopen(argv[1], "rb");
    if (!fp) {
        printf("open file[%s] error!\n", argv[1]);

        return -1;
    }

    memset(flvHeaderData, 0x0, sizeof(flvHeaderData));

    dataLen = fread(flvHeaderData, 1, MIN_FLV_HEADER_LEN, fp);
    if (dataLen != MIN_FLV_HEADER_LEN) {
        printf("read flv header error!\n");
        
        fclose(fp);
        
        return -1;
    }

    flvHeaderData[MIN_FLV_HEADER_LEN] = '\0';

    DealFlvHeader(flvHeaderData);

#ifdef PRINTF_DEBUG
    printf("+FLV Body\n");
#endif

    while (1)
    {
        memset(preTagSizeData, 0x0, sizeof(preTagSizeData));
        
        dataLen = fread(preTagSizeData, 1, MAX_PRE_TAG_SIZE_LEN, fp);
        if (dataLen != MAX_PRE_TAG_SIZE_LEN) {
            break;
        }
        
        preTagSizeData[MAX_PRE_TAG_SIZE_LEN] = '\0';

#ifdef PRINTF_DEBUG
        printfNSpace(4);
        printf("previousTagSize: %d\n", (preTagSizeData[0]<<24) | (preTagSizeData[1]<<16) | (preTagSizeData[2]<<8) | preTagSizeData[3]);
#endif

        memset(tagHeaderData, 0x0, sizeof(tagHeaderData));
        
        dataLen = fread(tagHeaderData, 1, MAX_TAG_HEADER_LEN, fp);
        if (dataLen != MAX_TAG_HEADER_LEN) {
            continue;
        }
        
        memset(&tagHeader, 0x0, sizeof(T_FLV_TAG_HEADER));
        
        DealTagHeader(tagHeaderData, &tagHeader);

        tagData = (unsigned char*)malloc(tagHeader.data_size);
        if (!tagData) {
            continue;
        }
        
        memset(tagData, 0x0, tagHeader.data_size);
        
        dataLen = fread(tagData, 1, tagHeader.data_size, fp);
        if (dataLen != tagHeader.data_size) {
            continue;
        }
        
        DealTagData(tagData, tagHeader.type, tagHeader.data_size);
        
        free(tagData);
        tagData = NULL;
    }

    fclose(fp);

    return 0;
}

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FlightYe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值