ffmpeg-4.2.2:音频编码流程(pcm编码成MP3)

基于FFMPEG的音频编码器,可以将pcm数据编码成MP3。
主要是记录一下自己学习FFMPEG时总结的音频编码流程。
ffmpeg版本:ffmpeg-4.2.2
libmp3lame-version:3.100

流程图

在这里插入图片描述简单介绍下各个函数的功能:
avcodec_find_encoder():通过编码器ID查找编码器
avcodec_alloc_context3():初始化AVCodecContext
av_get_channel_layout():通过名字获取通道值(mono:单通道;stereo:双通道)
av_get_channel_layout_nb_channels():通过值获取通道数
avcodec_open2():打开编码器
av_packet_alloc():初始化AVPacket
av_frame_alloc():初始化AVFrame
av_frame_get_buffer():为AVFrame->data等分配内存
av_get_bytes_per_sample():计算一个采样的字节数
av_samples_get_buffer_size():计算一帧音频的字节数
av_frame_make_writable():检查AVFrame->data是否可写
avcodec_send_frame():编码音频:将一帧音频元数据发送给编码器
avcodec_receive_packet():编码音频:接收编码完成的AVPacket数据包

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>

static void encode(AVCodecContext *cdc_ctx, AVFrame *frame, AVPacket *pkt, FILE *fp_out)
{
	int ret = 0;

	if ((ret = avcodec_send_frame(cdc_ctx, frame)) < 0)
	{
		fprintf(stderr, "avcodec_send_frame failed.\n");
		exit(1);
	}

	while ((ret = avcodec_receive_packet(cdc_ctx, pkt)) >= 0)
	{
		printf("Write (size=%d) packet.\n", pkt->size);
		fwrite(pkt->data, 1, pkt->size, fp_out);
		av_packet_unref(pkt);
	}

	if ((ret != AVERROR(EAGAIN)) && (ret != AVERROR_EOF))
	{
		fprintf(stderr, "avcodec_receive_packet failed.\n");
		exit(1);
	}
}

void encode_audio(const char *input_file, const char *output_file)
{
	int ret = 0;
	int data_size = 0;
	AVCodec *codec = NULL;
	AVCodecContext *cdc_ctx = NULL;
	AVPacket *pkt = NULL;
	AVFrame *frame = NULL;
	FILE *fp_in, *fp_out;

	if ((codec = avcodec_find_encoder(AV_CODEC_ID_MP3)) == NULL)
	{
		fprintf(stderr, "avcodec_find_encoder_by_name failed.\n");
		goto ret1;
	}

	if ((cdc_ctx = avcodec_alloc_context3(codec)) == NULL)
	{
		fprintf(stderr, "avcodec_alloc_context3 failed.\n");
		goto ret1;
	}

#if 1 	/*encode zhu.pcm*/
	cdc_ctx->bit_rate = 192000;
	cdc_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP;
	cdc_ctx->sample_rate = 44100;
	cdc_ctx->channel_layout = av_get_channel_layout("stereo");
	cdc_ctx->channels = av_get_channel_layout_nb_channels(cdc_ctx->channel_layout);

#else 	/*encode 16k.pcm*/
	cdc_ctx->bit_rate = 64000;
	cdc_ctx->sample_fmt = AV_SAMPLE_FMT_S16P;
	cdc_ctx->sample_rate = 16000;
	cdc_ctx->channel_layout = av_get_channel_layout("mono");
	cdc_ctx->channels = av_get_channel_layout_nb_channels(cdc_ctx->channel_layout);
#endif

	if ((ret = avcodec_open2(cdc_ctx, codec, NULL)) < 0)
	{
		fprintf(stderr, "avcodec_open2 failed.\n");
		goto ret2;
	}

	if ((pkt = av_packet_alloc()) == NULL)
	{
		fprintf(stderr, "av_packet_alloc failed.\n");
		goto ret3;
	}

	if ((frame = av_frame_alloc()) == NULL)
	{
		fprintf(stderr, "av_frame_alloc failed.\n");
		goto ret4;
	}
	frame->nb_samples = cdc_ctx->frame_size;
	frame->format = cdc_ctx->sample_fmt;
	frame->channel_layout = cdc_ctx->channel_layout;

	if ((ret = av_frame_get_buffer(frame, 0)) < 0)
	{
		fprintf(stderr, "av_frame_get_buffer failed.\n");
		goto ret5;
	}

	if ((fp_in = fopen(input_file, "rb")) == NULL)
	{
		fprintf(stderr, "fopen %s failed.\n", input_file);
		goto ret5;
	}
	if ((fp_out = fopen(output_file, "wb")) == NULL)
	{
		fprintf(stderr, "fopen %s failed.\n", output_file);
		goto ret6;
	}

#if 1 		/*cdc_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP*/
	data_size = av_get_bytes_per_sample(cdc_ctx->sample_fmt);

	while (feof(fp_in) == 0)
	{
		int i = 0, ch = 0;

		if ((ret = av_frame_make_writable(frame)) < 0)
		{
			fprintf(stderr, "frame is not writable.\n");
			goto ret7;
		}

		for (i = 0; i < frame->nb_samples; i++)
		{
			for (ch = 0; ch < cdc_ctx->channels; ch++)
			{
				fread(frame->data[ch] + data_size * i, 1, data_size, fp_in);
			}
		}

		encode(cdc_ctx, frame, pkt, fp_out);
	}

#else 		/*cdc_ctx->sample_fmt = AV_SAMPLE_FMT_S16P*/
	data_size = av_samples_get_buffer_size(NULL, cdc_ctx->channels, cdc_ctx->frame_size, cdc_ctx->sample_fmt, 1);
	printf("data_size = %d\n", data_size);
	while (feof(fp_in) == 0)
	{
		if ((ret = av_frame_make_writable(frame)) < 0)
		{
			fprintf(stderr, "frame is not writable.\n");
			goto ret7;
		}
		
		fread(frame->data[0], 1, data_size, fp_in);
		encode(cdc_ctx, frame, pkt, fp_out);
	}
#endif

	encode(cdc_ctx, NULL, pkt, fp_out);


	fclose(fp_out);
	fclose(fp_in);
	av_frame_free(&frame);
	av_packet_free(&pkt);
	avcodec_close(cdc_ctx);
	avcodec_free_context(&cdc_ctx);
	return;
ret7:
	fclose(fp_out);
ret6:
	fclose(fp_in);
ret5:
	av_frame_free(&frame);
ret4:
	av_packet_free(&pkt);
ret3:
	avcodec_close(cdc_ctx);
ret2:
	avcodec_free_context(&cdc_ctx);
ret1:
	exit(1);
}

int main(int argc, const char *argv[])
{
	if (argc < 3)
	{
		fprintf(stderr, "Uage:<input file> <output file>\n");
		exit(0);
	}

	encode_audio(argv[1], argv[2]);
	
	return 0;
}

注:

  1. 音频的一帧等于若干个采样,AVCodecContext->frame_size就是每帧的采样数。采样率为44100,表示一秒钟有44100个采样,那一秒钟音频就有44100 / frame_size帧。
  2. AVPacket和pcm文件中左右声道的数据是LRLRLR储存的,但是AVFrame中不同的采样格式,储存不一样。如S16P,LR都保存在AVFrame->data[0]中,所以计算出一帧的字节数 ,从pcm文件中读出拷贝到data[0]中即可;FLTP格式,则L在AVFrame->data[0]中,R在AVFrame->data[1]中,则需要一个采样一个采样的读取,LR分别储存在data[0]和data[1]中

下载

项目主页

Github:https://github.com/newbie-plan/encode_audio

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值