1、README
前言
注意:flv是不支持h.265封装的。目前解封装功能正常,所得到的H.264文件与AAC文件均可正常播放。
a. demo使用
bash
$ 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 format/flv
c. demo目录架构
bash
$ 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
c
/***************************************************************
* 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
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
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;
}