FFMPEG解碼mp3到pcm
阿新 • • 發佈:2018-11-17
int Mp3Decoder::prepare() {
int ret=0;
//1 開啟檔案獲取fmt_ctx(用於解封裝)
if(avformat_open_input(&fmt_ctx,src_filename,NULL,NULL)<0){
fprintf(stderr, "Could not open source file %s\n", src_filename);
setErrorMsg("Could not open source file %s\n", src_filename);
return -1;
}
//解析stream資訊
if (avformat_find_stream_info(fmt_ctx,NULL)<0) {
fprintf(stderr, "Could not find stream information\n");
setErrorMsg( "Could not find stream information\n");
ret = -1;
goto close_fmt;
}
//找到音訊流 並且獲取相應的codec(用於解碼),這個函式是我們實現的後面分析
if (open_codec_context(&audio_stream_idx,&audio_dec_ctx,fmt_ctx,AVMEDIA_TYPE_AUDIO)>=0) {
audio_stream = fmt_ctx->streams[audio_stream_idx];
} else {
fprintf(stderr, "Could not find stream information\n");
setErrorMsg( "Could not find stream information\n");
ret = -1 ;
goto close_fmt;
}
// 如果包含流,獲取一些音訊的資訊 包括通道數,取樣率和編碼格式
if (audio_stream) {
enum AVSampleFormat sfmt = audio_dec_ctx->sample_fmt;
n_channels = audio_dec_ctx->channels;
sample_rate = audio_dec_ctx->sample_rate;
if (av_sample_fmt_is_planar(sfmt)) {
const char *packed = av_get_sample_fmt_name(sfmt);
ALOGW("Warning: the sample format the decoder produced is planar "
"(%s). This example will output the first channel only.\n",
packed ? packed : "?");
sfmt = av_get_packed_sample_fmt(sfmt);
n_channels = 1;
}
if ((ret = get_format_from_sample_fmt(&fmt, sfmt)) < 0)
goto close_fmt;
else {
goto end;
}
} else {
ret = -2;
}
close_fmt:
avformat_close_input(&fmt_ctx);
end:
return ret;
}
選擇流和獲取codec過程如下
int Mp3Decoder::open_codec_context(int *stream_idx, AVCodecContext **dec_ctx,
AVFormatContext *fmt_ctx, enum AVMediaType type) {
int ret, stream_index;
AVStream *st;
AVCodec *dec = NULL;
AVDictionary *ops = NULL;
//更具引數type 找到最合適的流索引
ret = av_find_best_stream(fmt_ctx,type,-1,-1,NULL,0);
if (ret<0) {
ALOGE( "Could not find %s stream in input file '%s'\n",
av_get_media_type_string(type), src_filename);
setErrorMsg("Could not find %s stream in input file '%s'\n",
av_get_media_type_string(type), src_filename);
return ret;
} else{
stream_index = ret;
st = fmt_ctx->streams[stream_index];
/* find decoder for the stream */ 根據stream 找到decoder
dec = avcodec_find_decoder(st->codecpar->codec_id);
if (!dec) {
ALOGE( "Failed to find %s codec\n",
av_get_media_type_string(type));
setErrorMsg("Failed to find %s codec\n",
av_get_media_type_string(type));
return AVERROR(EINVAL);
}
//建立一個codec context儲存解碼相關的上下文資訊
*dec_ctx = avcodec_alloc_context3(dec);
if (!*dec_ctx) { // alloc falis
ALOGE( "Failed to allocate the %s codec context\n",
av_get_media_type_string(type));
setErrorMsg( "Failed to allocate the %s codec context\n",
av_get_media_type_string(type));
return AVERROR(ENOMEM);
}
//新版要求使用codecpar,這裡做一些相容處理
if ((ret = avcodec_parameters_to_context(*dec_ctx,st->codecpar))<0){
ALOGE( "Failed to copy %s codec parameters to decoder context\n",
av_get_media_type_string(type));
setErrorMsg("Failed to copy %s codec parameters to decoder context\n",
av_get_media_type_string(type));
return ret;
}
/* Init the decoders, with or without reference counting */
av_dict_set(&ops,"refcounted_frames","0",0);
//繫結codec到codec上下文
if ((ret = avcodec_open2(*dec_ctx,dec,&ops))<0){
ALOGE( "Failed to open %s codec\n",
av_get_media_type_string(type));
setErrorMsg("Failed to open %s codec\n",
av_get_media_type_string(type));
return ret;
}
*stream_idx = stream_index;
}
return 0;
}
開始解碼的過程如下
int Mp3Decoder::start() {
int ret =0, got_frame;
AVFrame *frame = NULL;
AVPacket pkt;
// dump一下資訊,純粹為了除錯使用
av_dump_format(fmt_ctx,0,src_filename,0);
//申請一個frame用於存放解碼後的幀
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate frame\n");
setErrorMsg("Could not allocate frame\n");
ret = AVERROR(ENOMEM);
goto end;
}
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
//解封裝,讀取Packet(解封裝後的資料)
while (av_read_frame(fmt_ctx,&pkt)>=0) {
AVPacket orig_pkt = pkt;
do {
//解碼
ret = decode_packet(&pkt,frame,&got_frame,0);
pkt.data += ret;
pkt.size -= ret;
} while (pkt.size>0);
av_packet_unref(&orig_pkt);
}
pkt.data = NULL;
pkt.size = 0;
do {
ret = decode_packet(&pkt,frame,&got_frame,0);
} while (got_frame);
end:
//解封裝完成,釋放資源,如果支援重複start不釋放
av_frame_free(&frame);
avcodec_free_context(&audio_dec_ctx);
}
解碼資料的處理
int Mp3Decoder::decode_packet(AVPacket *pPkt,AVFrame *frame,int *got_frame, int cached) {
int ret =0;
int decoded = pPkt->size;
//本例只處理音訊流
if (pPkt->stream_index!=audio_stream_idx) {
pPkt->size = 0;
return ret;
}
*got_frame = 0;
//解碼packet資料到frame
ret = avcodec_decode_audio4(audio_dec_ctx,frame,got_frame,pPkt);
if (ret<0){
ALOGE("Error decoding audio frame (%s)\n", av_err2str(ret));
setErrorMsg("Error decoding audio frame (%s)\n", av_err2str(ret));
return ret;
}
/* Some audio decoders decode only part of the packet, and have to be
* called again with the remainder of the packet data.
* Sample: fate-suite/lossless-audio/luckynight-partial.shn
* Also, some decoders might over-read the packet. */
decoded = FFMIN(ret,pPkt->size);
if (*got_frame) {
size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample(
(AVSampleFormat) frame->format);
ALOGD("audio_frame%s n:%d nb_samples:%d pts:%s\n",
cached ? "(cached)" : "",
audio_frame_count++, frame->nb_samples,
av_ts2timestr(frame->pts, &audio_dec_ctx->time_base));
/* Write the raw audio data samples of the first plane. This works
* fine for packed formats (e.g. AV_SAMPLE_FMT_S16). However,
* most audio decoders output planar audio, which uses a separate
* plane of audio samples for each channel (e.g. AV_SAMPLE_FMT_S16P).
* In other words, this code will write only the first audio channel
* in these cases.
* You should use libswresample or libavfilter to convert the frame
* to packed data. */
// fwrite(frame->extended_data[0], 1, unpadded_linesize, audio_dst_file);
if (putBuffer) { //回撥輸出資料
ALOGE("data size %s",frame->extended_data);
putBuffer(frame->extended_data[0],1,unpadded_linesize,putBufferData);
}
}
/* If we use frame reference counting, we own the data and need
* to de-reference it when we don't use it anymore */
if (*got_frame & 0)
av_frame_unref(frame);
return decoded;
}