ffmpeg-3.4编译出lib库和头文件
配置文件可以是这样 config.sh
主要是 --enable-encoders --enable-swresample
#!/bin/bash
export PREFIX=./../ffmpeg
./configure \
--disable-yasm \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-debug \
--disable-zlib \
--disable-bzlib \
--disable-static \
--disable-stripping \
--enable-ffmpeg \
--enable-shared \
--enable-gpl \
--enable-small \
--target-os=linux \
--arch=arm \
--enable-cross-compile \
--cross-prefix=arm-linux- \
--cc=arm-linux-gcc \
--prefix=$PREFIX \
--enable-encoders \
--enable-decoders \
--enable-muxers \
--enable-demuxers \
--enable-parsers \
--enable-bsfs \
--enable-protocols \
--disable-filters \
--disable-avfilter \
--disable-swscale \
--enable-swresample \
--disable-devices \
--disable-postproc \
--enable-network \
--enable-indev=v4l2
Pcm2Aac.h 仅供参考,详见注释
#ifdef __cplusplus
extern "C"
{
#endif
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
#ifdef __cplusplus
};
#endif
class Pcm2AAC
{
public:
Pcm2AAC();
~Pcm2AAC();
bool Init(int i_nPcmSampleRate, AVSampleFormat i_ePcmSampleFmt, int i_nPcmChannels);
void AddData(char *i_pData, int i_nSize);
void FlushData(void);
bool GetData(char *&o_pData, int &o_nSize);
private:
void AddADTS(int i_nPktLen);
int m_nPcmSize;
int m_nPcmChannel;
int m_nPcmSampleRate;
AVSampleFormat m_ePcmFormat;
char *m_aPcmPointer[AV_NUM_DATA_POINTERS];
char *m_pPcmData;
char *m_pOutData;
AVPacket *m_pPacket;
AVFrame *m_pFrame;
AVCodec *m_pCodec;
AVCodecContext *m_pCodecCtx;
SwrContext *m_pSwrCtx;
int64_t m_s64Pts;
int m_nFrameSize;
int m_nFrameCnt;
};
Pcm2Aac.cpp 仅供参考,详见注释
#include "Pcm2AAC.h"
// refer to ffmpeg/libavformat/adtsenc.c
#define ADTS_HEADER_SIZE 7
#define ADTS_MAX_FRAME_BYTES ((1 << 13) - 1) // 8K
Pcm2AAC::Pcm2AAC()
{
m_nPcmSize = 0;
m_pPacket = NULL;
m_pFrame = NULL;
m_pCodec = NULL;
m_pCodecCtx = NULL;
m_pSwrCtx = NULL;
m_s64Pts = 0;
m_nFrameSize = 0;
m_nFrameCnt = 0;
for(int i = 0; i < AV_NUM_DATA_POINTERS; i++)
{
m_aPcmPointer[i] = new char[1024 * 10];
}
m_pPcmData = new char[1024 * 10];
m_pOutData = new char[1024 * 10];
memset(m_pPcmData, 0, 1024 * 10);
memset(m_pOutData, 0, 1024 * 10);
}
Pcm2AAC::~Pcm2AAC()
{
// just for test
// 1秒钟处理了多少帧
int nFramesPerSecond = m_nPcmSampleRate / m_nFrameSize; // 44100/1024=43
printf("nFramesPerSecond = %d\n", nFramesPerSecond);
// 1帧需要多少时间(毫秒)
double fFrameTime = 1000.00 / nFramesPerSecond; // 1000/43=23.26ms
printf("fFrameTime = %.2fms\n", fFrameTime);
// 时间总长 = 总帧数 * 1帧所需时间
float fDuration = m_nFrameCnt * fFrameTime / 1000; // 2628*23.25=61.12s
printf("This AAC total frames = %d, duration = %.2fs\n", m_nFrameCnt, fDuration);
if(m_pPacket != NULL)
{
av_packet_free(&m_pPacket);
}
if(m_pFrame != NULL)
{
av_frame_free(&m_pFrame);
}
if(m_pCodecCtx != NULL)
{
avcodec_free_context(&m_pCodecCtx);
}
if(m_pSwrCtx != NULL)
{
swr_free(&m_pSwrCtx);
}
for(int i = 0; i < AV_NUM_DATA_POINTERS; i++)
{
if(m_aPcmPointer[i] != NULL)
{
delete[] m_aPcmPointer[i];
}
}
if(m_pPcmData != NULL)
{
delete[] m_pPcmData;
}
if(m_pOutData != NULL)
{
delete[] m_pOutData;
}
}
bool Pcm2AAC::Init(int i_nPcmSampleRate, AVSampleFormat i_ePcmSampleFmt, int i_nPcmChannels)
{
m_nPcmSampleRate = i_nPcmSampleRate;
m_ePcmFormat = i_ePcmSampleFmt;
m_nPcmChannel = i_nPcmChannels;
avcodec_register_all();
// 寻找AAC的编码器
m_pCodec = avcodec_find_encoder(AV_CODEC_ID_AAC);
if(m_pCodec == NULL)
{
fprintf(stderr, "Codec not found\n");
return false;
}
// 初始化编码器上下文, 主要是通道数, 采样率, 采样格式
m_pCodecCtx = avcodec_alloc_context3(m_pCodec);
if(m_pCodecCtx == NULL)
{
fprintf(stderr, "Could not allocate audio codec context\n");
return false;
}
m_pCodecCtx->channels = m_nPcmChannel;
m_pCodecCtx->channel_layout = av_get_default_channel_layout(m_nPcmChannel);
m_pCodecCtx->sample_rate = m_nPcmSampleRate;
// 新的FFmpeg, 只支持AV_SAMPLE_FMT_FLTP这一种AAC音频格式
m_pCodecCtx->sample_fmt = AV_SAMPLE_FMT_FLTP;
m_pCodecCtx->bit_rate = 64000;
// Allow the use of the experimental AAC encoder
m_pCodecCtx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
// 打开编码器
if(avcodec_open2(m_pCodecCtx, m_pCodec, NULL) < 0)
{
fprintf(stderr, "Could not open codec\n");
return false;
}
// 这个编码器的frame_size很关键, 表示每个声道每次只能编码frame_size个采样点, m_pFrame->nb_samples不能比它大
// 否则会报错: more samples than frame size (avcodec_encode_audio2)
printf("m_pCodecCtx->frame_size = %d\n", m_pCodecCtx->frame_size);
m_nFrameSize = m_pCodecCtx->frame_size;
// av_packet_alloc() 为一个NULL指针分配内存并将其值置为默认
m_pPacket = av_packet_alloc();
if(m_pPacket == NULL)
{
return false;
}
m_pFrame = av_frame_alloc();
if(m_pFrame == NULL)
{
return false;
}
// 设置格式转换
m_pSwrCtx = swr_alloc_set_opts(NULL, m_pCodecCtx->channel_layout, m_pCodecCtx->sample_fmt, m_pCodecCtx->sample_rate,
m_pCodecCtx->channel_layout, m_ePcmFormat, m_nPcmSampleRate, 0, NULL);
if(m_pSwrCtx == NULL)
{
fprintf(stderr, "Could not allocate resample context\n");
return false;
}
if(swr_init(m_pSwrCtx) < 0)
{
fprintf(stderr, "Could not open resample context\n");
return false;
}
return true;
}
void Pcm2AAC::AddData(char *i_pData, int i_nSize)
{
int ret = 0;
int nSampleSize = 0;
memcpy(m_pPcmData + m_nPcmSize, i_pData, i_nSize);
m_nPcmSize += i_nSize;
nSampleSize = av_get_bytes_per_sample(m_ePcmFormat);
if(m_nPcmSize <= (nSampleSize * m_nFrameSize * m_nPcmChannel))
{
return;
}
memcpy(m_aPcmPointer[0], m_pPcmData, nSampleSize * m_nFrameSize * m_nPcmChannel);
m_nPcmSize -= (nSampleSize * m_nFrameSize * m_nPcmChannel);
memcpy(m_pPcmData, m_pPcmData + nSampleSize * m_nFrameSize * m_nPcmChannel, m_nPcmSize);
m_pFrame->pts = m_s64Pts;
m_s64Pts += m_nFrameSize; // fixme
m_pFrame->nb_samples = m_nFrameSize;
m_pFrame->format = m_pCodecCtx->sample_fmt;
m_pFrame->channel_layout = m_pCodecCtx->channel_layout;
m_pFrame->sample_rate = m_pCodecCtx->sample_rate;
if(av_frame_get_buffer(m_pFrame, 0) < 0)
{
fprintf(stderr, "Could not allocate audio data buffers\n");
return ;
}
// 转换格式
if(swr_convert(m_pSwrCtx, m_pFrame->extended_data, m_pFrame->nb_samples, (const uint8_t**)m_aPcmPointer, m_pFrame->nb_samples) < 0)
{
fprintf(stderr, "Could not convert input samples (error )\n");
if(m_pFrame != NULL)
{
av_frame_unref(m_pFrame);
}
return ;
}
// 放进编码器
ret = avcodec_send_frame(m_pCodecCtx, m_pFrame);
if(ret < 0)
{
fprintf(stderr, "Error sending the frame to the encoder\n");
if(m_pFrame != NULL)
{
av_frame_unref(m_pFrame);
}
return;
}
if(m_pFrame != NULL)
{
av_frame_unref(m_pFrame);
}
}
void Pcm2AAC::FlushData(void)
{
// 发送NULL表示刷新packet, 意味着流的结束
avcodec_send_frame(m_pCodecCtx, NULL);
}
bool Pcm2AAC::GetData(char *&o_pData, int &o_nSize)
{
// 从编码器取出编码完的数据
int ret = avcodec_receive_packet(m_pCodecCtx, m_pPacket);
if(ret < 0)
{
return false;
}
// 由于编码后的AAC流没有ADTS头, 会导致保存的文件无法被播放器识别, 所以需要手动写入ADTS头
// 在每个AAC原始流前面加上一个7字节的ADTS头
AddADTS(m_pPacket->size + ADTS_HEADER_SIZE);
m_nFrameCnt++;
memcpy(m_pOutData + ADTS_HEADER_SIZE, m_pPacket->data, m_pPacket->size);
o_nSize = m_pPacket->size + ADTS_HEADER_SIZE;
o_pData = m_pOutData;
av_packet_unref(m_pPacket);
return true;
}
// ADTS全称是(Audio Data Transport Stream), 是AAC的一种十分常见的传输格式
void Pcm2AAC::AddADTS(int i_nPktLen)
{
int nProfile = 1; // AAC LC, 低复杂度规格
int nFreqIdx = 4; // default 44.1kHz
int nChanCfg = m_nPcmChannel;
if(i_nPktLen > ADTS_MAX_FRAME_BYTES)
{
fprintf(stderr, "ADTS frame size too large: %d (max %d)\n", i_nPktLen, ADTS_MAX_FRAME_BYTES);
return;
}
switch(m_nPcmSampleRate)
{
case 96000:
nFreqIdx = 0;
break;
case 88200:
nFreqIdx = 1;
break;
case 64000:
nFreqIdx = 2;
break;
case 48000:
nFreqIdx = 3;
break;
case 44100:
nFreqIdx = 4;
break;
case 32000:
nFreqIdx = 5;
break;
case 24000:
nFreqIdx = 6;
break;
case 22050:
nFreqIdx = 7;
break;
case 16000:
nFreqIdx = 8;
break;
case 12000:
nFreqIdx = 9;
break;
case 11025:
nFreqIdx = 10;
break;
case 8000:
nFreqIdx = 11;
break;
case 7350:
nFreqIdx = 12;
break;
default:
break;
}
// Fill in ADTS header, 7 bytes
m_pOutData[0] = 0xFF;
m_pOutData[1] = 0xF1;
m_pOutData[2] = ((nProfile) << 6) + (nFreqIdx << 2) + (nChanCfg >> 2);
m_pOutData[3] = (((nChanCfg & 3) << 6) + (i_nPktLen >> 11));
m_pOutData[4] = ((i_nPktLen & 0x7FF) >> 3);
m_pOutData[5] = (((i_nPktLen & 7) << 5) + 0x1F);
m_pOutData[6] = 0xFC;
}
main.cpp 仅供参考,详见注释
#include
#include "Pcm2AAC.h"
int main(int argc, char *argv[])
{
// 输入的PCM文件
FILE *pInFile = fopen("./in.pcm", "rb");
// 输出的AAC文件
FILE *pOutFile = fopen("./out.aac", "ab");
char aFrameBuf[1024] = {0}; // AVCodecContext->frame_size = 1024
char *pOutData = NULL;
int nReadSize = 0;
int nOutSize = 0;
Pcm2AAC objPcm2AAC;
bool bEOF = false;
// 我手头上的PCM是AV_SAMPLE_FMT_S16格式(有符号的16位), 采样率44100, 两声道的
if(!(objPcm2AAC.Init(44100, AV_SAMPLE_FMT_S16, 2)))
{
printf("objPcm2AAC Init error\n");
return -1;
}
printf("converting start\n");
while(true)
{
nReadSize = fread(aFrameBuf, 1, sizeof(aFrameBuf), pInFile);
if(nReadSize > 0)
{
// 把PCM数据放进编码器
objPcm2AAC.AddData(aFrameBuf, nReadSize);
}
else
{
// 刷新编码器, 标志着PCM流结束
objPcm2AAC.FlushData();
bEOF = true;
}
while(true)
{
// 从编码器取出AAC数据
if(!(objPcm2AAC.GetData(pOutData, nOutSize)))
{
break;
}
fwrite(pOutData, 1, nOutSize, pOutFile);
}
if(bEOF)
{
break;
}
}
printf("converting end\n");
fclose(pOutFile);
fclose(pInFile);
return 0;
}
编译
arm-linux-g++ -std=c++11 main.cpp Pcm2Aac.cpp -o pcm2aac -I/ffmpeg/include -I./ -L/ffmpeg/lib -lpthread -lavdevice -lavformat -lavcodec -lavutil -lswresample
运行
~# ./pcm2aac
m_pCodecCtx->frame_size = 1024
converting start
converting end
nFramesPerSecond = 43
fFrameTime = 23.26ms
This AAC total frames = 2628, duration = 61.12s
[aac @ 0xf33350] Qavg: 678.742
得到的out.aac文件可以拿去PC上播放
in.pcm可以到这里去取
https://download.csdn.net/download/cfl927096306/12584097
参考
https://blog.csdn.net/g0415shenw/article/details/81606813