本篇文章对自己项目中的网络媒体流解码流程进行了梳理和总结。本文中的方法不同于一般打开文件或流进行读写的流程,不需要通过avformat_open_input,avformat_find_stream_info等操作获取AVFormatContext,然后遍历不同流信息。此处直接通过解析音视频sequence header包,获取相应的参数来初始化对应的AVCodec和AVCodecContext,然后使用AVCodecContext进行后续的解码操作。由于项目中直接获取到了音视频包,所以也省去了av_read_frame操作。
一般流程:
前提:av_register_all()
1. 构造AVCodec
2. 构造AVCodecContext
3. 解码(注意输入的数据格式)
音频解码(关键函数 avcodec_decode_audio4):
1. 构造AVCodec(AAC为例)
codec = avcodec_find_decoder(AV_CODEC_ID_AAC);
2. 构造AVCodecContext
codec_ctx = avcodec_alloc_context3(codec); // these params are neccesary when decoding codec_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; codec_ctx->sample_rate = 44100; codec_ctx->channels = channels; codec_ctx->channel_layout = AV_CH_LAYOUT_MONO; if (channels == 2) { codec_ctx->channel_layout = AV_CH_LAYOUT_STEREO; } // 可选参数aac audio sequence header,当前不设置可以正常解析 //codec_ctx->extradata = (uint8_t*)extradata; //codec_ctx->extradata_size = extradata_size; // 别忘了open codec context if ((ret=avcodec_open2(codec_ctx, codec, NULL)) < 0) { error("avcodec_open2 audio codec(AAC) failed."); return ret; }
3. 解码(注意输入的数据格式)
AVPacket pkt; av_init_packet(&pkt); pkt.data = (uint8_t*)unit->bytes; pkt.size = unit->size;
frame = av_frame_alloc(); do { int got_frame = 0; // pkt should be raw aac audio data, that is, without aac header.
// 纯aac音频数据,不包含任何容器头或编码格式头 ret = avcodec_decode_audio4(codec_ctx, frame, &got_frame, &pkt); trace("ret=%d, got_frame=%d, decoded frame size=%d", ret, got_frame, frame->linesize[0]); if (ret < 0) { char err[1000]; av_make_error_string(err, 1000, ret); error("avcodec_decode_audio4 failed. err=%s", err); break; } if (!got_frame) { ret = ERROR_AAC_NO_FRAME; error("avcodec_decode_audio4 no frame."); break; } } while (false); av_free_packet(&pkt);
视频解码(关键函数 avcodec_decode_video2):
1. 构造AVCodec(H.264为例)
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
2. 构造AVCodecContext
codec_ctx = avcodec_alloc_context3(codec); // extradata(video sequence header SPS, PPS) is neccesary when decoding codec_ctx->extradata = (uint8_t*)extradata; codec_ctx->extradata_size = extradata_size; // 别忘了 open codec ctx if (avcodec_open2(codec_ctx, codec, NULL) < 0) { error("avcodec_open2 video codec(H264) failed."); return -1; }
3. 解码(注意输入的数据格式)
AVPacket pkt; av_init_packet(&pkt); pkt.data = (uint8_t*)unit->bytes; pkt.size = unit->size; frame = av_frame_alloc(); do { int got_picture = 0; // pkt should be NAL unit, that is, with 1 byte header.
// 如果是ibmf(iso basic media file format)格式,需要在nalu unit前面加上可变字长的Unit length字段 // 如果是annexb格式,没有测试过,如果纯nalu数据解码不正常,可以尝试加入nalu的starting code(0x000001/0x00000001)测试。 ret = avcodec_decode_video2(codec_ctx, frame, &got_picture, &pkt); trace("ret=%d, got_picture=%d, decoded frame size=%d", ret, got_picture, frame->linesize[0]*3/2); if (ret < 0) { error("avcodec_decode_video2 failed. ret=%d", ret); break; } if (!got_picture) { ret = ERROR_H264_NO_FRAME; error("avcodec_decode_video2 no frame."); break; } } while (false); av_free_packet(&pkt);