ffmpeg之封装AAC

AAC是mp4的音频格式,而安防摄像机基本上使用G711等编码,想要封装成mp4供web预览,就需要跨越g711转AAC的这个难关。

ffmpeg作为音视频界的泰斗,可以帮助我们实现这一功能。

代码流程如下:

open_input_file 打开输入的文件供使用例如G711或者pcm
open_output_file 打开转码之后aac的音频文件供存储
init_resampler 初始化在采用的各种音频格式和参数
init_fifo 初始化换成队列为存储读取的音频文件存储为转码
write_output_file_header 写入转码文件头
read_decode_convert_and_store 解码读取的音频帧解码成pcm后保持带消息队列
load_encode_and_write 从消息队列中读取pcm音频文件,并编码成AAC

重点分析init_resampler/read_decode_convert_and_store/load_encode_and_write 这3个函数为转码的核心函数。

init_resampler主要是进行格式转换使用,AAC现在ffmpeg使用的格式是AV_SAMPLE_FMT_FLTP格式,其他格式好像都不行,而 一般PCM使用AV_SAMPLE_FMT_S16格式,所以需要进行格式转换。

		/* Convert the input samples to the desired output sample format.
		* This requires a temporary storage provided by converted_input_samples. */
		if (convert_samples((const uint8_t**)input_frame->extended_data, converted_input_samples,
			input_frame->nb_samples, resampler_context))
			goto cleanup;

read_decode_convert_and_store的核心是把读取的音频格式解码成pcm

static int decode_audio_frame(AVFrame *frame,
	AVFormatContext *input_format_context,
	AVCodecContext *input_codec_context,
	int *data_present, int *finished)
{
	/* Packet used for temporary storage. */
	AVPacket input_packet;
	int error;
	init_packet(&input_packet);

	/* Read one audio frame from the input file into a temporary packet. */
	if ((error = av_read_frame(input_format_context, &input_packet)) < 0) {
		/* If we are at the end of the file, flush the decoder below. */
		if (error == AVERROR_EOF)
			*finished = 1;
		else {
			fprintf(stderr, "Could not read frame (error '%d')\n",
				error);
			return error;
		}
	}

	/* Send the audio frame stored in the temporary packet to the decoder.
	* The input audio stream decoder is used to do this. */
	if ((error = avcodec_send_packet(input_codec_context, &input_packet)) < 0) {
		fprintf(stderr, "Could not send packet for decoding (error '%d')\n",
			error);
		return error;
	}

	/* Receive one frame from the decoder. */
	error = avcodec_receive_frame(input_codec_context, frame);
	/* If the decoder asks for more data to be able to decode a frame,
	* return indicating that no data is present. */
	if (error == AVERROR(EAGAIN)) {
		error = 0;
		goto cleanup;
		/* If the end of the input file is reached, stop decoding. */
	}
	else if (error == AVERROR_EOF) {
		*finished = 1;
		error = 0;
		goto cleanup;
	}
	else if (error < 0) {
		fprintf(stderr, "Could not decode frame (error '%d')\n",
			error);
		goto cleanup;
		/* Default case: Return decoded data. */
	}
	else {
		*data_present = 1;
		goto cleanup;
	}

cleanup:
	av_packet_unref(&input_packet);
	return error;
}

load_encode_and_write 的核心是把pcm编码成AAC编码

static int encode_audio_frame(AVFrame *frame,
	AVFormatContext *output_format_context,
	AVCodecContext *output_codec_context,
	int *data_present)
{
	/* Packet used for temporary storage. */
	AVPacket output_packet;
	int error;
	init_packet(&output_packet);

	/* Set a timestamp based on the sample rate for the container. */
	if (frame) {
		frame->pts = pts;
		pts += frame->nb_samples;
	}

	/* Send the audio frame stored in the temporary packet to the encoder.
	* The output audio stream encoder is used to do this. */
	error = avcodec_send_frame(output_codec_context, frame);
	/* The encoder signals that it has nothing more to encode. */
	if (error == AVERROR_EOF) {
		error = 0;
		goto cleanup;
	}
	else if (error < 0) {
		fprintf(stderr, "Could not send packet for encoding (error '%d')\n",
			error);
		return error;
	}

	/* Receive one encoded frame from the encoder. */
	error = avcodec_receive_packet(output_codec_context, &output_packet);
	/* If the encoder asks for more data to be able to provide an
	* encoded frame, return indicating that no data is present. */
	if (error == AVERROR(EAGAIN)) {
		error = 0;
		goto cleanup;
		/* If the last frame has been encoded, stop encoding. */
	}
	else if (error == AVERROR_EOF) {
		error = 0;
		goto cleanup;
	}
	else if (error < 0) {
		fprintf(stderr, "Could not encode frame (error '%d')\n",
			error);
		goto cleanup;
		/* Default case: Return encoded data. */
	}
	else {
		*data_present = 1;
	}

	/* Write one audio frame from the temporary packet to the output file. */
	if (*data_present &&
		(error = av_write_frame(output_format_context, &output_packet)) < 0) {
		fprintf(stderr, "Could not write frame (error '%d')\n",
			error);
		goto cleanup;
	}

cleanup:
	av_packet_unref(&output_packet);
	return error;
}

重点API解读:

格式转换,主要是对pcm格式进行2次采样,改变采样率,采样位数, 采样通道

swr_alloc_set_opts:设置格式转换的初始化参数

	* Create a resampler context for the conversion.
	* Set the conversion parameters.
	* Default channel layouts based on the number of channels
	* are assumed for simplicity (they are sometimes not detected
	* properly by the demuxer and/or decoder).
	*/
	*resample_context = swr_alloc_set_opts(NULL,
		av_get_default_channel_layout(output_codec_context->channels),
		output_codec_context->sample_fmt,
		output_codec_context->sample_rate,
		av_get_default_channel_layout(input_codec_context->channels),
		input_codec_context->sample_fmt,
		input_codec_context->sample_rate,
		0, NULL);

av_samples_alloc:分配音频空间,主要分配转换之后保持的音频

重点参数如下

output_codec_context->channels:音频通道数

frame_size:音频采样数

output_codec_context->sample_fmt:音频采样深度

/* Allocate as many pointers as there are audio channels.
	* Each pointer will later point to the audio samples of the corresponding
	* channels (although it may be NULL for interleaved formats).
	*/
	if (!((*converted_input_samples) = (uint8_t * *) calloc(output_codec_context->channels,
		sizeof(**converted_input_samples)))) {
		fprintf(stderr, "Could not allocate converted input sample pointers\n");
		return AVERROR(ENOMEM);
	}

	/* Allocate memory for the samples of all channels in one consecutive
	* block for convenience. */
	if ((error = av_samples_alloc(*converted_input_samples, NULL,
		output_codec_context->channels,
		frame_size,
		output_codec_context->sample_fmt, 0)) < 0) {
		fprintf(stderr,
			"Could not allocate converted input samples (error '%d')\n",
			error);
		av_freep(&(*converted_input_samples)[0]);
		free(*converted_input_samples);
		return error;
	}

swr_convert :对音频进行二次采样,并输出结果

重点参数如下

resample_context:swr_alloc_set_opts产生的值

converted_data:av_samples_alloc分配的空间

frame_size:采样次数

input_data:输入的音频

frame_size:输入的采样次数

	/* Convert the samples using the resampler. */
	if ((error = swr_convert(resample_context,
		converted_data, frame_size,
		input_data, frame_size)) < 0) {
		fprintf(stderr, "Could not convert input samples (error '%d')\n",
			error);
		return error;
	}

猜你喜欢

转载自blog.csdn.net/g0415shenw/article/details/81154994