您的位置:首页 > 其它

【FFmpeg 3.x API应用四】音频编码

2018-02-13 16:58 393 查看

摘要

这篇文章介绍音频编码,示例程序是读取上一节生成的PCM格式的音频文件,对其进行编码输出。

初始化

这个必备的操作就不多说了。

void AudioEncoding::init()
{
avcodec_register_all();
}


配置编解码器CodecContext

查找编码器。在视频编码那一节我们使用
avcodec_find_encoder_by_name("libx264")
函数根据编码器名字查找对应的编码器,在这里我们可以使用编码器ID来查找对应的编码器,指定MP3编码器。

根据编码器申请CodecContext。

配置CodecContext各项参数。

打开CodecContext,完成初始化。

bool AudioEncoding::initCodecContext()
{
// 根据编码器ID查找对应的编码器
const AVCodec *enc = avcodec_find_encoder(AV_CODEC_ID_MP3);
if (!enc) {
printf("Failed to find codec\n");
return true;
}

mCodecCtx = avcodec_alloc_context3(enc);
if (!mCodecCtx) {
printf("Failed to allocate the codec context\n");
return true;
}

mCodecCtx->bit_rate = 128000;   //码率 128k
mCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16P;  //使用上一节生成的pcm文件,对应的采样格式
mCodecCtx->sample_rate = 44100; //采样率
mCodecCtx->channel_layout = AV_CH_LAYOUT_STEREO;  //双声道立体声
mCodecCtx->channels = 2; //声道数

//完成初始化
if (avcodec_open2(mCodecCtx, enc, NULL) < 0) {
printf("Failed to open encodec\n");
return true;
}
return false;
}


音频编码,保存mp3文件

申请一个frame结构,对其进行相应的配置。

循环读取pcm文件数据进行处理。

把读取的pcm数据填充到frame中,即解码写文件的逆过程。

对frame结构进行编码,接收编码后的packet。

持续接收延时编码后的数据packet。

bool AudioEncoding::readFrameProc(const char * input, const char * output)
{
//输入文件out_s16le.pcm,输出文件Out.mp3
FILE *pcmFd = fopen(input, "rb");
FILE *outFd = fopen(output, "wb");
if (!outFd || !pcmFd) {
fprintf(stderr, "Could not open file\n");
return true;
}

//申请frame
AVFrame *frame = av_frame_alloc();
if (!frame) {
printf("Failed to allocate video frame\n");
return true;
}

//配置frame
frame->format = mCodecCtx->sample_fmt;
frame->nb_samples = mCodecCtx->frame_size;
frame->channel_layout = mCodecCtx->channel_layout;

//为frame申请存放媒体数据的buff
if (av_frame_get_buffer(frame, 0) < 0) {
printf("Failed to allocate the video frame data\n");
return true;
}

int num = 0;
AVPacket pkt;
//循环读取pcm文件
while (!feof(pcmFd)) {

av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;

if (av_frame_make_writable(frame)) {
return true;
}
int sampleBytes = av_get_bytes_per_sample(mCodecCtx->sample_fmt);

// AV_SAMPLE_FMT_S16P
// 读取pcm文件,填充frame.data结构
for (int i = 0; i < frame->nb_samples; i++)
for (int ch = 0; ch < mCodecCtx->channels; ch++)
fread(frame->data[ch] + i*sampleBytes, 1, sampleBytes, pcmFd);

//音频编码,发送frame接收packet
avcodec_send_frame(mCodecCtx, frame);
int ret = avcodec_receive_packet(mCodecCtx, &pkt);

if (!ret) {
printf("Write frame %3d (size=%5d)\n", num++, pkt.size);
fwrite(pkt.data, 1, pkt.size, outFd);
av_packet_unref(&pkt);
}
}

printf("------------- get delayed data --------------------\n");

//持续接收编码延迟的数据
for (;;) {
avcodec_send_frame(mCodecCtx, NULL);
int ret = avcodec_receive_packet(mCodecCtx, &pkt);
if (ret == 0) {
printf("Write frame %3d (size=%5d)\n", num++, pkt.size);
fwrite(pkt.data, 1, pkt.size, outFd);
av_packet_unref(&pkt);
}
else if (ret == AVERROR_EOF) {
printf("Write frame complete\n");
break;
}
else {
printf("Error encoding frame\n");
break;
}

}

fclose(outFd);
fclose(pcmFd);
av_frame_free(&frame);

return false;
}


释放系统资源

AudioEncoding::~AudioEncoding()
{
avcodec_free_context(&mCodecCtx);
}


示例程序代码

上述示例的完整代码可以从Github下载: https://github.com/lmshao/FFmpeg-Basic
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息