使用ffmpeg实现MP3toPCM
流程解析
1. 注册协议、格式与编码器
// 打开pcm文件
FILE * pcmFile = fopen(pcmPath, "wb+");
// 注册解码器
avcodec_register_all();
av_register_all();
2. 打开媒体源
// 首先为mp3文件分配一个AVFormatContext数据结构
mavFormatContext = avformat_alloc_context();
LOGI("open ac file %s...", fileString);
// 打开文件,并解析文件,然后填充AVFormatContext数据结构
// avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options);
// 1. AVFormatContext数据结构 2. mp3文件名 3. 指定AVInputFormat,设置为NULL则自动检测 4.AVDictionary附加选项,一般设为NULL
int result = avformat_open_input(&mavFormatContext, fileString, NULL, NULL);
if (result != 0) {
LOGI("can't open file %s result %s", fileString, av_err2str(result));
return -1;
} else {
LOGI("open file %s success and result is %s", fileString, av_err2str(result));
}
// avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
// 读取一部分视音频数据并且获得一些相关的信息
result = avformat_find_stream_info(mavFormatContext, NULL);
if (result < 0) {
LOGI("fail avformat avformat_find_stream_info %s result %s", fileString, av_err2str(result));
return -1;
} else {
LOGI("avformat_find_stream_info success result is %s", fileString, av_err2str(result));
}
3. 寻找各个流,并且打开对应的解码器
// av_find_best_stream(AVFormatContext *ic, enum AVMediaType type, int wanted_stream_nb, int related_stream, AVCodec **decoder_ret, int flags)
// 获取mp3文件中音频对应的stream_index
// 1. AVFormatContext 2. 指定为查找音频AVMEDIA_TYPE_AUDIO 3. wanted_stream_nb指定的stream号,-1为自动检测 4. related_stream找相关的stream 5. decoder_ret如果不为NULL,则返回选择的stream的decoder 6. 相关的flags 7. 返回值:返回相关的index
mstream_index = av_find_best_stream(mavFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
LOGI("stream_index is %d", mstream_index);
if (mstream_index == -1) {
LOGI("no audio stream");
return -1;
}
AVStream* audioStream = mavFormatContext->streams[mstream_index];
//if (audioStream->time_base.den && audioStream->time_base.num)
mavCodecContext = audioStream->codec;
LOGI("avCodecContext->codec_id is %d AV_CODEC_ID_AAC is %d", mavCodecContext->codec_id, AV_CODEC_ID_AAC);
// 找音频流解码器
AVCodec* avCodec = avcodec_find_decoder(mavCodecContext->codec_id);
if (avCodec == NULL) {
LOGI("Unsupported codec");
return -1;
}
// 打开音频流解码器
result = avcodec_open2(mavCodecContext, avCodec, NULL);
if (result < 0) {
LOGI("avcodec_open2 fail avformat_find_stream_info result is %s", av_err2str(result));
return -1;
} else {
LOGI("avcodec_open2 sucess avformat_find_stream_info result is %s", av_err2str(result));
}
4. 初始化解码后的数据结构
if (!audioCodecIsSupported()) {
LOGI("because of audio Codec Is Not Supported so we will init swresampler...");
mswrContext = swr_alloc();
/*
struct SwrContext* swr_alloc_set_opts(
struct SwrContext * s, //如果为NULL则创建一个新的SwrContext,否则对已有的SwrContext进行参数设置
int64_t out_ch_layout, //输出的声道格式,AV_CH_LAYOUT_*,AV_CH_LAYOUT_STEREO(双声道)
enum AVSampleFormat out_sample_fmt, // 输出的采样格式,AV_SAMPLE_FMT_S16,16bit
int out_sample_rate, // 输出的采样率,44100
int64_t in_ch_layout, //输入的声道格式,
enum AVSampleFormat in_sample_fmt, // 输入的采样格式
int in_sample_rate, // 输入的采样率
int log_offset, // 日志相关
void * log_ctx // 日志相关
) */
mswrContext = swr_alloc_set_opts(mswrContext, av_get_default_channel_layout(OUT_PUT_CHANNELS), AV_SAMPLE_FMT_S16, 44100,
av_get_default_channel_layout(mavCodecContext->channels), mavCodecContext->sample_fmt, mavCodecContext->sample_rate, 0, NULL);
// 初始化上下文
if (!mswrContext || swr_init(mswrContext)) {
if (mswrContext)
swr_free(&mswrContext);
avcodec_close(mavCodecContext);
LOGI("init resampler failed...");
return -1;
}
}
5. 读取流内容(packet),解码(frame),重采样(out_buffer),写数据(fwrite)
// 读取流内容到packet中
while (av_read_frame(mavFormatContext, &mpacket) >= 0) {
if (mpacket.stream_index == mstream_index) {
// avcodec_decode_audio4(AVCodecContext * avctx, AVFrame * frame, int * got_frame_ptr, const AVPacket * avpkt)
// 1. 解码器 2. 输出数据frame 3. 是否获取到frame 4. 输入数据packet
int len = avcodec_decode_audio4(mavCodecContext, mpAudioFrame, &gotframe, &mpacket);
if (len < 0) {
LOGI("decode audio error, skip packet");
return -1;
}
if (gotframe) {
// av_samples_get_buffer_size (int *linesize, int nb_channels, int nb_samples, enum AVSampleFormat sample_fmt, int align)
// 1. linesize 2. 通道数 3. 单个声道的样本数 4. 输出采样格式 5. 对齐
// 这个函数得到的结果是错的
// AV_CH_LAYOUT_STEREO是3声道,av_get_default_channel_layout(OUT_PUT_CHANNELS)为3
//int out_buffer_size=av_samples_get_buffer_size(NULL, AV_CH_LAYOUT_STEREO,
// mpAudioFrame->nb_samples, out_sample_fmt, 1);
// 每一份frame的样本数 * 输出pcm的通道数 * 样本的采样格式(16bit)
int out_buffer_size=mpAudioFrame->nb_samples *
OUT_PUT_CHANNELS *
av_get_bytes_per_sample(out_sample_fmt);
LOGI("mpAudioFrame->nb_samples = %d", mpAudioFrame->nb_samples);
int numChannels = OUT_PUT_CHANNELS;
int numFrames = 0;
void* audioData;
// 重新采样
if (mswrContext) {
// swr_convert (struct SwrContext *s, uint8_t **out, int out_count, const uint8_t **in, int in_count)
// 1. SwrContext上下文 2. 输出buffer 3. 输出的单通道的样本数 4. 输入数据 5. 输入的单通道的样本数
numFrames = swr_convert(mswrContext, &out_buffer,
mpAudioFrame->nb_samples,
(const uint8_t **)mpAudioFrame->data,
mpAudioFrame->nb_samples);
if (numFrames < 0) {
LOGI("fail resample audio");
ret = -1;
break;
}
LOGI("index:%5d\t pts:%lld\t packet size:%d out_buffer_size: %d\n",index,mpacket.pts,mpacket.size, out_buffer_size);
//Write PCM
// 写到pcm文件中
fwrite(out_buffer, 1, 4608, pcmFile);
index++;
av_packet_unref(&mpacket);
}
}
LOGI(" free");
}
}
6. 释放资源
av_free(&mpacket); // 注意,这个函数只需调用一次,不然会出现段错误
LOGI("end");
av_free(mpAudioFrame);
swr_free(&mswrContext);
fclose(pcmFile);
av_free(out_buffer);
avcodec_close(mavCodecContext);
avformat_close_input(&mavFormatContext);
源码
int AccompanyDecoder::init(const char *fileString, const char* pcmPath) {
LOGI("enter AccompanyDecoder::init");
maudioBuffer = NULL;
mposition = -1.0f;
maudioBufferCursor = 0;
maudioBufferSize = 0;
mswrContext = NULL;
mswrBuffer = NULL;
mswrBufferSize = 0;
uint8_t *out_buffer;
int index = 0;
AVSampleFormat out_sample_fmt=AV_SAMPLE_FMT_S16;
FILE * pcmFile = fopen(pcmPath, "wb+");
// 注册解码器
avcodec_register_all();
av_register_all();
mavFormatContext = avformat_alloc_context();
LOGI("open ac file %s...", fileString);
// 打开文件,并解析文件,然后填充avformat
int result = avformat_open_input(&mavFormatContext, fileString, NULL, NULL);
if (result != 0) {
LOGI("can't open file %s result %s", fileString, av_err2str(result));
return -1;
} else {
LOGI("open file %s success and result is %s", fileString, av_err2str(result));
}
// 检查文件中的流信息
result = avformat_find_stream_info(mavFormatContext, NULL);
if (result < 0) {
LOGI("fail avformat avformat_find_stream_info %s result %s", fileString, av_err2str(result));
return -1;
} else {
LOGI("avformat_find_stream_info success result is %s", fileString, av_err2str(result));
}
mstream_index = av_find_best_stream(mavFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
LOGI("stream_index is %d", mstream_index);
if (mstream_index == -1) {
LOGI("no audio stream");
return -1;
}
AVStream* audioStream = mavFormatContext->streams[mstream_index];
//if (audioStream->time_base.den && audioStream->time_base.num)
mavCodecContext = audioStream->codec;
LOGI("avCodecContext->codec_id is %d AV_CODEC_ID_AAC is %d", mavCodecContext->codec_id, AV_CODEC_ID_AAC);
AVCodec* avCodec = avcodec_find_decoder(mavCodecContext->codec_id);
if (avCodec == NULL) {
LOGI("Unsupported codec");
return -1;
}
result = avcodec_open2(mavCodecContext, avCodec, NULL);
if (result < 0) {
LOGI("avcodec_open2 fail avformat_find_stream_info result is %s", av_err2str(result));
return -1;
} else {
LOGI("avcodec_open2 sucess avformat_find_stream_info result is %s", av_err2str(result));
}
if (!audioCodecIsSupported()) {
LOGI("because of audio Codec Is Not Supported so we will init swresampler...");
mswrContext = swr_alloc();
mswrContext = swr_alloc_set_opts(mswrContext, av_get_default_channel_layout(OUT_PUT_CHANNELS), AV_SAMPLE_FMT_S16, 44100,
av_get_default_channel_layout(mavCodecContext->channels), mavCodecContext->sample_fmt, mavCodecContext->sample_rate, 0, NULL);
if (!mswrContext || swr_init(mswrContext)) {
if (mswrContext)
swr_free(&mswrContext);
avcodec_close(mavCodecContext);
LOGI("init resampler failed...");
return -1;
}
}
LOGI("channels is %d sampleRate is %d", mavCodecContext->channels, mavCodecContext->sample_rate);
av_init_packet(&mpacket);
mpAudioFrame = av_frame_alloc();
int ret = 1;
av_init_packet(&mpacket);
int gotframe = 0;
out_buffer=(uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE*2);
while (av_read_frame(mavFormatContext, &mpacket) >= 0) {
if (mpacket.stream_index == mstream_index) {
int len = avcodec_decode_audio4(mavCodecContext, mpAudioFrame, &gotframe, &mpacket);
if (len < 0) {
LOGI("decode audio error, skip packet");
return -1;
}
if (gotframe) {
int out_buffer_size=av_samples_get_buffer_size(NULL, AV_CH_LAYOUT_STEREO,
mpAudioFrame->nb_samples, out_sample_fmt, 1);
LOGI("mpAudioFrame->nb_samples = %d", mpAudioFrame->nb_samples);
int numChannels = OUT_PUT_CHANNELS;
int numFrames = 0;
void* audioData;
// 重新采样
if (mswrContext) {
numFrames = swr_convert(mswrContext, &out_buffer,
mpAudioFrame->nb_samples,
(const uint8_t **)mpAudioFrame->data,
mpAudioFrame->nb_samples);
if (numFrames < 0) {
LOGI("fail resample audio");
ret = -1;
break;
}
LOGI("index:%5d\t pts:%lld\t packet size:%d out_buffer_size: %d\n",index,mpacket.pts,mpacket.size, out_buffer_size);
//Write PCM
fwrite(out_buffer, 1, 4608, pcmFile);
index++;
av_packet_unref(&mpacket);
}
}
LOGI("free");
}
}
av_free(&mpacket);
LOGI("end");
av_free(mpAudioFrame);
swr_free(&mswrContext);
fclose(pcmFile);
av_free(out_buffer);
avcodec_close(mavCodecContext);
avformat_close_input(&mavFormatContext);
return 0;
}
参考
1. 源码地址:https://github.com/mashenlyl/FFmpegDecoder
网友评论