本文共 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/