音视频解封装demo:将FLV文件解封装(demux)得到文件中的H264数据和AAC数据(纯手工,不依赖第三方开源库)

时间:2024-07-12 15:22:21

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. 参考链接

【参考文章】

  • FLV格式详解_JT同学的博客-****博客_flv格式**(推荐!!!)**

  • FLV封装格式介绍及解析 - 简书

  • 音视频封装:FLV格式详解和打包H264、AAC方案(上) - 云+社区 - 腾讯云

  • 音视频封装:FLV格式详解和打包H264、AAC方案(下) - 云+社区 - 腾讯云

【参考源码】

  • https://github.com/ImSjt/H.264toFLV.git

  • https://github.com/rainfly123/flvmuxer

【工具下载】

  • 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同学的博客-****博客_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,