技术在于交流、沟通,转载请注明出处并保持作品的完整性。
原文:https://blog.csdn.net/hiwubihe/article/details/81260923
[音频编解码系列文章]
- 音频编解码基础
- FFMPEG实现音频重采样
- FFMPEG实现PCM编码(采用封装格式实现)
- FFMPEG实现PCM编码(不采用封装格式实现)
- FAAC库实现PCM编码
- FAAD库实现RAW格式AAC解码
- FAAD库实现RAW格式AAC封装成ADTS格式
- FAAD库实现ADTS格式解码
- FFMPEG实现对AAC解码(采用封装格式实现)
- 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;
}