博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ffmpeg之封装AAC
阅读量:2755 次
发布时间:2019-05-13

本文共 6308 字,大约阅读时间需要 21 分钟。

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;	}

 

转载地址:http://ymvad.baihongyu.com/

你可能感兴趣的文章
Android开发者该学习哪些东西提高竞争力?Android篇
查看>>
Android开发究竟该如何学习,跳槽薪资翻倍
查看>>
Android开发者出路在哪?成功拿下大厂offer
查看>>
Android开发者必须收藏的8个开源库,看看这篇文章吧!
查看>>
Android开发者该学习哪些东西提高竞争力?系列篇
查看>>
Android开发者跳槽面试,offer拿到手软
查看>>
Android开发者面试如何系统复习?分享PDF高清版
查看>>
Android性能优化之APK优化,附赠课程+题库
查看>>
Android性能优化常见问题,已开源
查看>>
Android性能优化面试题集锦,完整PDF
查看>>
Android技术功底不够如何去面试,已拿offer附真题解析
查看>>
深入解析android核心组件和应用框架,Android面试题及解析
查看>>
深度剖析原理!你有过迷茫吗?面试必会
查看>>
漫谈MySQL权限安全,面试必会
查看>>
灵魂一问-如何彻底防止APK反编译?醍醐灌顶!
查看>>
灵魂拷问!Android面试知识点总结宝典助你通关!醍醐灌顶!
查看>>
焦虑的移动互联网开发者如何破局?BAT大厂面试总结
查看>>
爱了爱了!从草根到百万年薪程序员的十年风雨之路,小白也能看明白
查看>>
牛笔了!我拿到了梦寐以求的字节跳动和腾讯双offer!吊打面试官系列!
查看>>
疯狂涨知识!三年经验Android开发面经总结,醍醐灌顶!
查看>>