FFMPEG实现PCM编码(不采用封装格式实现)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hiwubihe/article/details/81260923

技术在于交流、沟通,转载请注明出处并保持作品的完整性。

原文:https://blog.csdn.net/hiwubihe/article/details/81260923

[音频编解码系列文章]

  1. 音频编解码基础
  2. FFMPEG实现音频重采样
  3. FFMPEG实现PCM编码(采用封装格式实现)
  4. FFMPEG实现PCM编码(不采用封装格式实现)
  5. FAAC库实现PCM编码
  6. FAAD库实现RAW格式AAC解码
  7. FAAD库实现RAW格式AAC封装成ADTS格式
  8. FAAD库实现ADTS格式解码
  9. FFMPEG实现对AAC解码(采用封装格式实现)
  10. FFMPEG实现对AAC解码(不采用封装格式实现)

在上篇“FFMPEG实现PCM编码(采用封装格式实现)”实现了通过FFMPEG对PCM数据编码,实现上把AAC作为一种封装格式实现的。本篇将采用一种更加简洁的方式,只用FFMPEG中的编码库,不用封装库实现PCM编码成AAC,当然这种前提是对AAC的格式需要了解。AAC有原始格式,ADTS和ADIF格式,原始格式一般解码不出来,因为没有音频相关的参数信息,后面介绍FAAD库的使用时,可以知道这种格式可以手动的把参数传输给FAAD库,完成解码。一般的播放器如vlc是解码不了这种音频的。本篇将首先读取一帧PCM数据,然后送给编码器,编码器完成一帧的编码后,在每一帧前面加上ADTS头,从而完成把PCM编码成ADTS格式的AAC文件。

ADTS格式标准"ISO-IEC-13818-7",可以参考。格式主要有下面表格

                                                          Table 8 – Syntax of adts_fixed_header()

Syntax

No. of bits

Mnemonic

adts_fixed_header()

 

 

{

 

 

        syncword;

12

bslbf

        ID;

1

bslbf

        layer;

2

uimsbf

        protection_absent;

1

bslbf

        profile;

2

uimsbf

        sampling_frequency_index;

4

uimsbf

        private_bit;

1

bslbf

        channel_configuration;

3

uimsbf

        original/copy;

1

bslbf

        home;

1

bslbf

}

 

 

                                                                Table 9 – Syntax of adts_variable_header()

Syntax

No. of bits

Mnemonic

adts_variable_header()

 

 

{

 

 

        copyright_identification_bit;

1

bslbf

        copyright_identification_start;

1

bslbf

        frame_length;

13

bslbf

        adts_buffer_fullness;

11

bslbf

        number_of_raw_data_blocks_in_frame;

2

uimsfb

}

 

 

帧数据加入ADTS头代码

unsigned bool MakeAdtsHeader(unsigned char *pHead,int *HeadSize,int iProfile,int iSampleRate,int iChan ,int iFramelen)
{
	unsigned char *data = pHead;
	int iHeadLen		= *HeadSize;
	if(iHeadLen<7)
	{
		return false;
	}
	*HeadSize = iHeadLen = 7;

	int profile   = (iProfile - 1) & 0x3;
	int sr_index  = FindAdtsSRIndex(iSampleRate);
	int framesize = iFramelen + iHeadLen;

	memset(data, 0, iHeadLen);

	data[0] += 0xFF; /* 8b: syncword */

	data[1] += 0xF0; /* 4b: syncword */
	/* 1b: mpeg id = 0 */
	/* 2b: layer = 0 */
	data[1] += 1; /* 1b: protection absent */

	data[2] += ((profile << 6) & 0xC0); /* 2b: profile */
	data[2] += ((sr_index << 2) & 0x3C); /* 4b: sampling_frequency_index */
	/* 1b: private = 0 */
	data[2] += ((iChan >> 2) & 0x1); /* 1b: channel_configuration */

	data[3] += ((iChan << 6) & 0xC0); /* 2b: channel_configuration */
	/* 1b: original */
	/* 1b: home */
	/* 1b: copyright_id */
	/* 1b: copyright_id_start */
	data[3] += ((framesize >> 11) & 0x3); /* 2b: aac_frame_length */

	data[4] += ((framesize >> 3) & 0xFF); /* 8b: aac_frame_length */

	data[5] += ((framesize << 5) & 0xE0); /* 3b: aac_frame_length */
	data[5] += ((0x7FF >> 6) & 0x1F); /* 5b: adts_buffer_fullness */

	data[6] += ((0x7FF << 2) & 0x3F); /* 6b: adts_buffer_fullness */
	/* 2b: num_raw_data_blocks */

	return true;
}

DEMO实例代码

/*******************************************************************************
Copyright (c) wubihe Tech. Co., Ltd. All rights reserved.
--------------------------------------------------------------------------------

Date Created:	2014-10-25
Author:			wubihe QQ:1269122125 Email:[email protected]
Description:	代码实现PCM编码AAC 不采用封装格式
--------------------------------------------------------------------------------
Modification History
DATE          AUTHOR          DESCRIPTION
--------------------------------------------------------------------------------

********************************************************************************/
#include <stdio.h>

#define __STDC_CONSTANT_MACROS

#ifdef _WIN32
//Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#ifdef __cplusplus
};
#endif
#endif

#define INPUT_FILE_NAME			("huangdun_r48000_FMT_S16_c2.pcm")
#define ADTS_HEAD_LEN			(7)


//AAC的编码级别 profile
#define MAIN       1
#define LC         2
#define SSR        3
#define LTP        4
#define HE_AAC     5
#define ER_LC     17
#define ER_LTP    19
#define LD        23
#define DRM_ER_LC 27 /* special object type for DRM */

static int adts_sample_rates[] = {96000,88200,64000,48000,44100,32000,24000,22050,16000,12000,11025,8000,7350,0,0,0};

static int FindAdtsSRIndex(int sr)
{
	int i;

	for (i = 0; i < 16; i++)
	{
		if (sr == adts_sample_rates[i])
			return i;
	}
	return 16 - 1;
}

unsigned bool MakeAdtsHeader(unsigned char *pHead,int *HeadSize,int iProfile,int iSampleRate,int iChan ,int iFramelen)
{
	unsigned char *data = pHead;
	int iHeadLen		= *HeadSize;
	if(iHeadLen<7)
	{
		return false;
	}
	*HeadSize = iHeadLen = 7;

	int profile   = (iProfile - 1) & 0x3;
	int sr_index  = FindAdtsSRIndex(iSampleRate);
	int framesize = iFramelen + iHeadLen;

	memset(data, 0, iHeadLen);

	data[0] += 0xFF; /* 8b: syncword */

	data[1] += 0xF0; /* 4b: syncword */
	/* 1b: mpeg id = 0 */
	/* 2b: layer = 0 */
	data[1] += 1; /* 1b: protection absent */

	data[2] += ((profile << 6) & 0xC0); /* 2b: profile */
	data[2] += ((sr_index << 2) & 0x3C); /* 4b: sampling_frequency_index */
	/* 1b: private = 0 */
	data[2] += ((iChan >> 2) & 0x1); /* 1b: channel_configuration */

	data[3] += ((iChan << 6) & 0xC0); /* 2b: channel_configuration */
	/* 1b: original */
	/* 1b: home */
	/* 1b: copyright_id */
	/* 1b: copyright_id_start */
	data[3] += ((framesize >> 11) & 0x3); /* 2b: aac_frame_length */

	data[4] += ((framesize >> 3) & 0xFF); /* 8b: aac_frame_length */

	data[5] += ((framesize << 5) & 0xE0); /* 3b: aac_frame_length */
	data[5] += ((0x7FF >> 6) & 0x1F); /* 5b: adts_buffer_fullness */

	data[6] += ((0x7FF << 2) & 0x3F); /* 6b: adts_buffer_fullness */
	/* 2b: num_raw_data_blocks */

	return true;
}

int main()
{
	int iReturn = 0;

	int iAdtsHeadLen = ADTS_HEAD_LEN;
	unsigned char szAdtsHead[ADTS_HEAD_LEN];
	int iAacProfile  = LC;

	//各种不同格式对应字节数
	static int mapSampleBytes[AV_SAMPLE_FMT_NB]
	={1,2,4,4,8,1,2,4,4,8};

	//PCM原始数据格式
	uint64_t iInputLayout				= AV_CH_LAYOUT_STEREO;
	int      iInputChans				= av_get_channel_layout_nb_channels(iInputLayout);
	AVSampleFormat eInputSampleFormat   = AV_SAMPLE_FMT_S16;
	int	     iInputSampleRate			= 48000;
	//不同样本格式长度
	int iInputSampleBytes				= mapSampleBytes[eInputSampleFormat];

	//注册所有编解码器
	avcodec_register_all();

	//打开编码器
	AVCodec *pCodec			  = NULL;
	AVCodecContext *pCodecCtx = NULL;


	pCodec = avcodec_find_encoder(AV_CODEC_ID_AAC);
	if (!pCodec) 
	{
		printf("Codec not found\n");
		return -1;
	}
	pCodecCtx = avcodec_alloc_context3(pCodec);
	if (!pCodecCtx) 
	{
		printf("Could not allocate video codec context\n");
		return -1;
	}
	//音频编码 不同格式支持不同的PCM原始数据,如果数据格式不同就需要重采样
	//如PCM编码MP3格式的音频文件,原始PCM是交叉存储方式则需要重采样,因为MP3编码器不支持
	//具体反应在avcodec_open2会失败 下面设置是告诉编码器PCM的格式,如果PCM与下面设置不同需要重采样

	pCodecCtx->codec_id			= AV_CODEC_ID_AAC;
	pCodecCtx->codec_type		= AVMEDIA_TYPE_AUDIO;
	pCodecCtx->sample_fmt		= eInputSampleFormat;
	pCodecCtx->sample_rate		= iInputSampleRate;
	pCodecCtx->channel_layout	= iInputLayout;
	pCodecCtx->channels			= iInputChans;
	if ((iReturn = avcodec_open2(pCodecCtx, pCodec, NULL)) < 0) 
	{
		printf("Could not open codec\n");
		return -1;
	}


	//打开输入文件
	FILE *pInputFile = fopen(INPUT_FILE_NAME, "rb");
	if(pInputFile == NULL)
	{
		printf("Could not open File:%s \n",INPUT_FILE_NAME);
		return -1;
	}

	//打开输出文件
	FILE *pOutputFile = fopen("huangdun.aac", "wb");
	if(pInputFile == NULL)
	{
		printf("Could not open File:%s \n","huangdun.aac");
		return -1;
	}

	//1帧数据样本数(AAC单通道1024)
	int iFrameSamples = 1024;


	//原始数据帧
	AVFrame* pRawframe  = NULL;
	//原始帧一Planer的大小 非平面分布的情况就是缓存总大小
	int iRawLineSize = 0;
	//原始帧缓存大小
	int iRawBuffSize = 0;
	//原始帧缓存
	uint8_t *pRawBuff= NULL;

	// 存储原始数据 
	iRawLineSize = 0;
	iRawBuffSize  = av_samples_get_buffer_size(&iRawLineSize, iInputChans, iFrameSamples, eInputSampleFormat, 0);
	pRawBuff = (uint8_t *)av_malloc(iRawBuffSize);

	//原始数据保存在AVFrame结构体中
	pRawframe = av_frame_alloc();

	pRawframe->nb_samples	= iFrameSamples;
	pRawframe->format		= eInputSampleFormat;
	pRawframe->channels     = iInputChans;


	iReturn = avcodec_fill_audio_frame(pRawframe, iInputChans, eInputSampleFormat, (const uint8_t*)pRawBuff, iRawBuffSize, 0);
	if(iReturn<0)
	{
		return -1;
	}

	//编码以后的数据是AVPacket
	AVPacket pkt;
	av_new_packet(&pkt,iRawBuffSize);

	//统计读取样本数
	long long lReadTotalSamples = 0;
	//每次读取样本数
	int iReadSamples;
	//是否编码成功
	int got_frame =0;



	
	//读取数据 保存在pConvertframe->data
	int iRealRead = fread(pRawBuff, 1, iRawBuffSize, pInputFile);
	while(iRealRead>0)
	{
		iReadSamples = iRealRead/(iInputSampleBytes*iInputChans);
		
		pRawframe->pts		    = lReadTotalSamples;
		got_frame				= 0;
		//Encode
		if(avcodec_encode_audio2(pCodecCtx, &pkt,pRawframe, &got_frame)<0)
		{
			printf("Failed to encode!\n");
			return -1;
		}
		if (got_frame==1)
		{
			printf("Succeed to encode 1 frame! \tsize:%5d\n",pkt.size);
			//一帧前面头ADTS
			MakeAdtsHeader(szAdtsHead,&iAdtsHeadLen,iAacProfile,iInputSampleRate,iInputChans ,pkt.size);
			fwrite(szAdtsHead, iAdtsHeadLen, 1, pOutputFile);
			fwrite(pkt.data, 1, pkt.size, pOutputFile);
			//解码数据写文件
			av_free_packet(&pkt);
		}

		//统计样本数以转换前为准 转换前后样本数是一样的
		lReadTotalSamples += (iReadSamples);

		iRealRead = fread(pRawBuff, 1, iRawBuffSize, pInputFile);
	}

	//刷新解码缓存
	//Flush Encoder
	got_frame = 1;
	while(got_frame)
	{
		if(avcodec_encode_audio2(pCodecCtx, &pkt, NULL, &got_frame)<0)
		{
			printf("Failed to encode!\n");
			return -1;
		}
		if (got_frame) 
		{
			MakeAdtsHeader(szAdtsHead,&iAdtsHeadLen,iAacProfile,iInputSampleRate,iInputChans ,pkt.size);
			fwrite(szAdtsHead, iAdtsHeadLen, 1, pOutputFile);
			fwrite(pkt.data, 1, pkt.size, pOutputFile);
			//输出文件
			av_free_packet(&pkt);
		}
	}


	fclose(pInputFile);
	fclose(pOutputFile);
	avcodec_close(pCodecCtx);
	av_free(pCodecCtx);
	av_frame_free(&pRawframe);

	return 0;

}

猜你喜欢

转载自blog.csdn.net/hiwubihe/article/details/81260923