阅读量:3
1、README
前言
注意:flv是不支持h.265封装的。目前解封装功能正常,所得到的H.264文件与AAC文件均可正常播放。
a. demo使用
$ make clean && make DEBUG=1 $ $ $ ./flv_demux_h264_aac Usage: ./flv_demux_h264_aac avfile/test1.flv ./flv_demux_h264_aac avfile/test2.flv
b. 参考链接
【参考文章】
【参考源码】
【工具下载】
SpecialAAAC.exe:https://sourceforge.net/projects/aacstreamanalysis/
H264BSAnalyzer.exe:https://github.com/latelee/H264BSAnalyzer/tree/master/release
FlvParse.exe:https://github.com/ty6815/AvStackDocs/tree/master/media%20format/flv
c. demo目录架构
$ tree . ├── aac_adts.c ├── aac_adts.h ├── avfile │ ├── test1.aac │ ├── test1.flv │ ├── test1.h264 │ ├── test2.aac │ ├── test2.flv │ └── test2.h264 ├── docs │ ├── FLV封装格式介绍及解析 - 简书.mhtml │ ├── FLV格式详解_JT同学的博客-CSDN博客_flv格式.mhtml │ ├── 笔记---H.264里的SEI - 简书.mhtml │ ├── 音视频封装:FLV格式详解和打包H264、AAC方案(上) - 云+社区 - 腾讯云.mhtml │ └── 音视频封装:FLV格式详解和打包H264、AAC方案(下) - 云+社区 - 腾讯云.mhtml ├── flv.c ├── flv_format.h ├── flv.h ├── h264_nalu.h ├── main.c ├── Makefile ├── README.md ├── reference_code │ └── Flv_to_h264_AAC_Demux.zip └── tools ├── FlvParse.exe ├── H264BSAnalyzer.exe └── SpecialAAAC.exe
2、主要代码片段
flv_format.h
/*************************************************************** * describe: Flv file format description(Mainly for H.264 & AAC) * author: linriming * e-mail: linriming20@163.com ***************************************************************/ #ifndef __FLV_FORMAT_H__ #define __FLV_FORMAT_H__ #include <stdint.h> #define AUDIO_SUPPORT(x) (x << 2) /* bit[2] in flvheader's type_flag */ #define VIDEO_SUPPORT(x) (x << 0) /* bit[0] in flvheader's type_flag */ #define SIZE_FLV_HEADER sizeof(struct flvHeader) /* 9 Bytes */ #define SIZE_FLV_TAG_HEADER sizeof(struct flvTagHeader) /* 11 Bytes */ #define SIZE_PREVIOUS_TAG_SIZE sizeof(uint32_t) /* 4 Bytes */ /* FLV tag type */ typedef enum{ FLVTAG_TYPE_AUDIO = 0x08, FLVTAG_TYPE_VIDEO = 0x09, FLVTAG_TYPE_SCRIPT = 0x12, }flvTagType; /* AMF data type in <Script Tag> */ typedef enum{ AMF_DATA_TYPE_NUMBER = 0x00, AMF_DATA_TYPE_BOOL = 0x01, AMF_DATA_TYPE_STRING = 0x02, AMF_DATA_TYPE_OBJECT = 0x03, AMF_DATA_TYPE_NULL = 0x05, AMF_DATA_TYPE_UNDEFINED = 0x06, AMF_DATA_TYPE_REFERENCE = 0x07, AMF_DATA_TYPE_MIXEDARRAY = 0x08, AMF_DATA_TYPE_OBJECT_END = 0x09, AMF_DATA_TYPE_ARRAY = 0x0a, AMF_DATA_TYPE_DATE = 0x0b, AMF_DATA_TYPE_LONG_STRING = 0x0c, AMF_DATA_TYPE_UNSUPPORTED = 0x0d, } amfDataType; /* audio tag */ typedef enum{ SFI_LINEAR_PCM_PLATFORM_ENDIAN = 0, SFI_ADPCM = 1, SFI_MP3 = 2, SFI_LINEAR_PCM_LITTLE_ENDIAN = 3, SFI_NELLYMOSER_16KHZ_MONO = 4, SFI_NELLYMOSER_8KHZ_MONO = 5, SFI_NELLYMOSER = 6, SFI_G711A = 7, SFI_G711MU = 8, SFI_RESERVED = 9, SFI_AAC = 10, SFI_SPEEX = 11, SFI_MP3_8KHZ = 14, SFI_DEVIVE_SPECIFIC_SOUND = 15, }soundFormatIndex; typedef enum{ SRI_5_5KHZ = 0, SRI_11KHZ = 1, SRI_22KHZ = 2, SRI_44KHZ = 3, }soundSamplerateIndex; typedef enum{ SSI_8BIT = 0, SSI_16BIT = 1, }soundSizeIndex; typedef enum{ STI_MONO = 0, STI_STEREO = 1, }soundTypeIndex; #define AAC_PACKET_TYPE_SEQUENCE_HEADER (0) #define AAC_PACKET_TYPE_RAW (1) typedef enum{ AAC_MAIN = 1, AAC_LC = 2, AAC_SSR = 3, }aacProfileIndex; typedef enum{ AAC_96KHz = 0x0, AAC_88_2KHz = 0x1, AAC_64KHz = 0x2, AAC_48KHz = 0x3, AAC_44_1KHz = 0x4, AAC_32KHz = 0x5, AAC_24KHz = 0x6, AAC_22_05KHz = 0x7, AAC_16KHz = 0x8, AAC_12KHz = 0x9, AAC_11_025KHz = 0xa, AAC_8KHz = 0xb, AAC_RESERVED = 0xc, }aacSamplerateIndex; typedef enum{ AAC_CHANNEL_SPECIAL = 0x0, AAC_CHANNEL_MONO = 0x1, AAC_CHANNEL_STEREO = 0x2, AAC_CHANNEL_3 = 0x3, AAC_CHANNEL_4 = 0x4, AAC_CHANNEL_5 = 0x5, AAC_CHANNEL_5_1 = 0x6, AAC_CHANNEL_7_1 = 0x7, AAC_CHANNELRESERVED = 0x8, }aacChannelIndex; #define AVC_PACKET_TYPE_SEQUENCE_HEADER (0) #define AVC_PACKET_TYPE_NALU (1) #define AVC_PACKET_TYPE_END_OF_SEQUENCE (2) /* next for video tag */ #define VIDEOTAG_FRAMETYPE_KEYFRAME (1) #define VIDEOTAG_FRAMETYPE_INTER_FRAME (2) #define VIDEOTAG_FRAMETYPE_DISPOSABLE_INTER_FRAME (3) #define VIDEOTAG_FRAMETYPE_GENERATED_KEYFRAME (4) #define VIDEOTAG_FRAMETYPE_VIDEO_INFO_FRAME (5) #define VIDEOTAG_CODECID_JPEG (1) #define VIDEOTAG_CODECID_SORENSON_H263 (2) #define VIDEOTAG_CODECID_SCREEN_VIDEO (3) #define VIDEOTAG_CODECID_ON2_VP6 (4) #define VIDEOTAG_CODECID_ON2_VP6_WITH_ALPHA_CHANNEL (5) #define VIDEOTAG_CODECID_SCREEN_VIDEO_VERSION_2 (6) #define VIDEOTAG_CODECID_AVC (7) #pragma pack(push) #pragma pack(1) /* 1 bytes align */ typedef struct flvHeader{ uint8_t signature[3]; /* signature bytes always 'F' 'L' 'V': 0x46 0x4C 0x56 */ uint8_t version; /* file version, always 0x01 */ uint8_t type_flag; /* bit[7:3] and bit[1] always 0, bit[2] for aduio, bit[0] for video */ uint32_t data_offset; /* size of header, 00 00 00 09(big-endian) for version 1 */ }T_FlvHeader, *PT_FlvHeader; /* 9 bytes totally */ typedef struct flvTagHeader{ uint8_t TagType; /* Type of this tag. Value are 8(audio), 9(video), 18(script), other(reserved). */ uint8_t DataSize[3]; /* Length of the data in the Data filed. */ uint8_t Timestamp[3]; /* Time in milliseconds at which the data in this applies. 0 in first tag in the FLV file. */ uint8_t TimestampExtended; /* Extension of Timestamp field to form a SI32 value, it is upper 8 bits. */ uint8_t StreamID[3]; /* Always 0 */ }T_FlvTagHeader, *PT_FlvTagHeader; /* 11 bytes total */ typedef struct flvTag{ T_FlvTagHeader flvheader; /* tag header */ uint8_t flvdata[0]; /* tag data index */ }T_FlvTag, *PT_FlvTag; typedef struct avcVideoPacket{ T_FlvTagHeader flvheader; uint8_t flvdata[0]; /* flv tag data index */ }T_AvcVideoPacket, *PT_AvcVideoPacket; #pragma pack(pop) #endif /* __FLV_FORMAT_H__ */
flv.c
#include "h264_nalu.h" #include "aac_adts.h" #include "flv.h" int flv_demux_h264_aac(const char *flvFileName) { FILE *fpH264 = NULL; FILE *fpAAC = NULL; FILE *fpFLV = NULL; uint8_t *flvBuf = NULL; // note!!!: it maybe happen the 'Segmentation fault', as 1024 is too long for 'sps', but it maybe save the 'sei' or other unused data if first nalu isn't 'sps'. uint8_t spsBuf[64] = {0}; uint8_t ppsBuf[64] = {0}; uint32_t spsLen = 0; uint32_t ppsLen = 0; int ret = -1; uint32_t flvTagDataSize = 0; PT_FlvTag ptFlvTag = NULL; const char *h264FileName = "out_video.h264"; const char *aacFileName = "out_audio.aac"; /* open file */ fpFLV = fopen(flvFileName, "rb"); if (!fpFLV) { printf("open %s error!\n", flvFileName); } fpH264 = fopen(h264FileName, "wb"); if (!fpH264) { printf("open %s error!\n", h264FileName); } fpAAC = fopen(aacFileName, "wb"); if (!fpAAC) { printf("open %s error!\n", aacFileName); } /* alloc memory */ flvBuf = (uint8_t *)malloc(MAX_FLV_BUF_SIZE); if (!flvBuf) { printf("malloc for 'flvBuf' error!\n"); } /* parse and save some data first. */ fread(flvBuf, SIZE_FLV_HEADER, 1, fpFLV); ret = strncmp((const char *)flvBuf, "FLV", 3); if(ret != 0) { printf("\033[31m It's not a FLV file!\n\033[0m"); return -1; } DEBUG("\033[32m" "=====================================\n" "Signature: '%c' '%c' '%c'\n" "Version : 0x%02x\n" "Audio : %s\n" "Video : %s\n" "=====================================\n" "\033[0m", flvBuf[0], flvBuf[1], flvBuf[2], flvBuf[3], (flvBuf[4] & 0x04) ? "yes" : "no", (flvBuf[4] & 0x01) ? "yes" : "no"); fread(flvBuf, SIZE_PREVIOUS_TAG_SIZE, 1, fpFLV); DEBUG("\033[37m[Pre Tag Size]: %d \033[0m\n", (flvBuf[0] << 24) |\ (flvBuf[1] << 16) |\ (flvBuf[2] << 8) |\ flvBuf[3]); while(1) { ret = fread(flvBuf, SIZE_FLV_TAG_HEADER, 1, fpFLV); if (ret <= 0) { DEBUG(">>> FLV file end! <<<\n"); break; } ptFlvTag = (PT_FlvTag)flvBuf; flvTagDataSize = (ptFlvTag->flvheader.DataSize[0] << 16) |\ (ptFlvTag->flvheader.DataSize[1] << 8) |\ ptFlvTag->flvheader.DataSize[2]; fread(ptFlvTag->flvdata, flvTagDataSize, 1, fpFLV); switch (ptFlvTag->flvheader.TagType) { case FLVTAG_TYPE_SCRIPT: { DEBUG("\033[34m[Script Tag] \033[0m\n"); /* nothing to do! */ break; } case FLVTAG_TYPE_AUDIO: { char *packetTypeStr = NULL; int aacPacketType = ptFlvTag->flvdata[1]; switch(aacPacketType) { case AAC_PACKET_TYPE_SEQUENCE_HEADER: packetTypeStr = "[AAC sequence header]"; /* nothing to do! */ break; case AAC_PACKET_TYPE_RAW: packetTypeStr = "[AAC raw]"; uint8_t headerbuf[AAC_ADTS_HEADER_SIZE] = {0}; uint32_t headerbufLen = 0; uint32_t datasize = ((ptFlvTag->flvheader.DataSize[0] << 16) |\ (ptFlvTag->flvheader.DataSize[1] << 8) |\ ptFlvTag->flvheader.DataSize[2]); generateAdtsHeader(AAC_ADTS_HEADER_SIZE + (datasize - 2), headerbuf, &headerbufLen); fwrite(headerbuf, headerbufLen, 1, fpAAC); fwrite(&ptFlvTag->flvdata[2], datasize - 2, 1, fpAAC); break; default: printf("[%s:%d] Unknown AAC packet type!", __FUNCTION__, __LINE__); goto exit; } DEBUG("\033[36m[Audio Tag] \t %s\033[0m\n", packetTypeStr); break; } case FLVTAG_TYPE_VIDEO: { char *packetTypeStr = NULL; char *frameTypeStr = NULL; uint8_t startCode[4] = {0x00, 0x00, 0x00, 0x01}; int isKeyFrame = 0; int type = 0; isKeyFrame = (ptFlvTag->flvdata[0] & 0xF0) == 0x10 ? 1 : 0; type = ptFlvTag->flvdata[1]; switch(type) { case AVC_PACKET_TYPE_SEQUENCE_HEADER: packetTypeStr = "[AVC squence header]"; frameTypeStr = "[key frame]"; spsLen = ((ptFlvTag->flvdata[11] << 8) | ptFlvTag->flvdata[12]); memcpy(spsBuf, &ptFlvTag->flvdata[13], spsLen); //printf("spsLen: %d\n", spsLen); //for(int i = 0; i < spsLen; i++) // printf("0x%02x ", spsBuf[i]); //printf("\n"); ppsLen = ((ptFlvTag->flvdata[13 + spsLen + 1] << 8) | ptFlvTag->flvdata[13+spsLen+2]); memcpy(ppsBuf, &ptFlvTag->flvdata[13 + spsLen + 3], ppsLen); //printf("ppsLen: %d\n", ppsLen); //for(int i = 0; i < ppsLen; i++) // printf("0x%02x ", ppsBuf[i]); //printf("\n"); break; case AVC_PACKET_TYPE_NALU: packetTypeStr = "[AVC NALU]"; /* one or more NALU. */ uint32_t naluDataSize = 0; uint32_t curPos = 0; curPos = 5; naluDataSize = (ptFlvTag->flvdata[curPos + 0] << 24) |\ (ptFlvTag->flvdata[curPos + 1] << 16) |\ (ptFlvTag->flvdata[curPos + 2] << 8) |\ ptFlvTag->flvdata[curPos + 3]; if(isKeyFrame) { frameTypeStr = "[key frame]"; if((ptFlvTag->flvdata[curPos + 4] & 0x1f) == NALU_TYPE_SEI &&\ (ptFlvTag->flvdata[curPos + 5] & 0x1f) == 0x05/* SEI: payloadType: user_data_unregistered() */) { /* has SEI */ /* SEI */ fwrite(startCode, sizeof(startCode), 1, fpH264); fwrite(&ptFlvTag->flvdata[curPos + 4], naluDataSize, 1, fpH264); /* SPS */ fwrite(startCode, sizeof(startCode), 1, fpH264); fwrite(spsBuf, spsLen, 1, fpH264); /* PPS */ fwrite(startCode, sizeof(startCode), 1, fpH264); fwrite(ppsBuf, ppsLen, 1, fpH264); /* update the second NALU data size. */ curPos += (4 + naluDataSize); naluDataSize = (ptFlvTag->flvdata[curPos + 0] << 24) |\ (ptFlvTag->flvdata[curPos + 1] << 16) |\ (ptFlvTag->flvdata[curPos + 2] << 8) |\ ptFlvTag->flvdata[curPos + 3]; /* IDR */ fwrite(startCode, sizeof(startCode), 1, fpH264); fwrite(&ptFlvTag->flvdata[curPos + 4], naluDataSize, 1, fpH264); } else { /* SPS */ fwrite(startCode, sizeof(startCode), 1, fpH264); fwrite(spsBuf, spsLen, 1, fpH264); /* PPS */ fwrite(startCode, sizeof(startCode), 1, fpH264); fwrite(ppsBuf, ppsLen, 1, fpH264); /* IDR */ fwrite(startCode, sizeof(startCode), 1, fpH264); fwrite(&ptFlvTag->flvdata[curPos + 4], naluDataSize, 1, fpH264); } } else { frameTypeStr = "[inter frame]"; /* SLICE */ fwrite(startCode, sizeof(startCode), 1, fpH264); fwrite(&ptFlvTag->flvdata[curPos + 4], naluDataSize, 1, fpH264); } break; case AVC_PACKET_TYPE_END_OF_SEQUENCE: packetTypeStr = "[AVC end of squence]"; frameTypeStr = "[key frame]"; break; default: printf("[%s:%d] Unknown AVC packet type!", __FUNCTION__, __LINE__); goto exit; } DEBUG("\033[35m[Video Tag] \t %s \t %s\033[0m\n", packetTypeStr, frameTypeStr); break; } default: { printf("\033[33m[Unknown Tag]\033[0m\n"); break; } } fread(flvBuf, SIZE_PREVIOUS_TAG_SIZE, 1, fpFLV); DEBUG("\033[37m[PreTagSize] \t %d\033[0m\n", (flvBuf[0] << 24) |\ (flvBuf[1] << 16) |\ (flvBuf[2] << 8) |\ flvBuf[3]); } exit: if(fpFLV) fclose(fpFLV); if(fpH264) {fflush(fpH264); fclose(fpH264);} if(fpAAC) {fflush(fpAAC); fclose(fpAAC);} if(flvBuf) free(flvBuf); return 0; }
main.c
#include <stdio.h> #include "flv.h" int main(int argc, char *argv[]) { if(argc == 1) { printf("Usage: \n" " %s avfile/test1.flv\n" " %s avfile/test2.flv\n", argv[0], argv[0]); return -1; } flv_demux_h264_aac(argv[1]); printf("\033[32m""Success!\n""\033[0m"); return 0; }