1. 视频文件解码过程
解码过程
步骤如下:
- 视频文件(封装格式,MP4/FLV/AVI 等)获取视频格式信息等
- 解复用为Stream 流, 准备解码用的Codec
- 将Stream 流 使用解码器解为Raw 格式针
1.1 音视频格式填充:
int ret = avformat_open_input(&format_ctx, filename, nullptr, nullptr); // 打开输入文件并将格式上下文赋给 format_ctx
// 填充stream 信息
if (avformat_find_stream_info(format_ctx, NULL) < 0) {std::cout << "no stream in files : " << std::endl;return -1;
}// find the video stream information
ret = av_find_best_stream(format_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
if (ret < 0) {fprintf(stderr, "Cannot find a video stream in the input file\n");return -1;
}
video_stream_index = ret;
video_stream = format_ctx->streams[video_stream_index];ret = av_find_best_stream(format_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
if (ret < 0) {std::cout<< "no audio stream " << ret << std::endl;
} else {audio_stream_index = ret;audio_stream = format_ctx->streams[audio_stream_index];
}
1.2 准备解码用的codec
const AVCodec * video_codec = avcodec_find_decoder(video_stream->codecpar->codec_id);
if (video_codec == nullptr) {std::cout << "we not found the code: " << video_decoder->id << std::endl;
} else {std::cout << "we found the video codec: " << video_codec->name << std::endl;
}
const AVCodec * audio_codec = avcodec_find_decoder(audio_stream->codecpar->codec_id);
if (audio_codec == nullptr) {std::cout << "we not found the code: " << audio_decoder->id << std::endl;
} else {std::cout << "we found the audio codec: " << audio_codec->name << std::endl;
}// codec context for decoder
AVCodecContext* video_dec_context = avcodec_alloc_context3(video_codec);
if (!video_dec_context) {std::cout << "video dec context alloc failed" << std::endl;
}
if ((ret = avcodec_parameters_to_context(video_dec_context, video_stream->codecpar)) < 0) {std::cerr << "codec copy failed" << std::endl;
}
std::cout<< "video format width: " << video_dec_context->width << std::endl;
std::cout<< "video format height: " << video_dec_context->height << std::endl;
std::cout << "video pixel format: " << video_dec_context->pix_fmt << std::endl;
video_width = video_dec_context->width;
video_height = video_dec_context->height;
video_fmt = video_dec_context->pix_fmt;
// open codec
ret = avcodec_open2(video_dec_context, video_codec, NULL);
if (ret < 0) {std::cerr << "video codec open failed" << std::endl;
}
Audio 的流程和video 的流程相近:
通过stream 信息->AVCodec->AVCodecContext , 再通过AVCodecContext 打开解码器(avcodec_open2)
1.3 解码过程:
在打开解码器后,创建AVPacket AVFrame
AVPacket 是解码前的包, AVFrame 是解码后的帧
AVPacket *packet;
packet = av_packet_alloc();AVFrame* frame = av_frame_alloc();while(1) {// 从里面获取一个packetret1 = av_read_frame(format_ctx, packet);if (ret1 < 0) {break;} else {/*if (packet->stream_index == video_stream_index) {std::cout << "Video: pts: " << packet->pts << " dts: " << packet->dts <<" duration: " << packet->duration << std::endl;} else {if (packet->stream_index != -1 && audio_stream_index == packet->stream_index) {std::cout << "Audio: pts: " << packet->pts << " dts: " << packet->dts <<" duration: " << packet->duration << std::endl;}}*/if (packet->stream_index == video_stream_index) {int result = decode_packet(video_dec_context, packet);if (result == 0) {//video_frame_count++;}} else {decode_packet(audio_dec_context, packet);av_frame_unref(frame);}}av_packet_unref(packet);}
// 解包过程
static int decode_packet(AVCodecContext *dec, const AVPacket *pkt)
{int ret = 0;// submit the packet to the decoderret = avcodec_send_packet(dec, pkt);if (ret < 0) {fprintf(stderr, "Error submitting a packet for decoding (%d)\n", ret);return ret;}// get all the available frames from the decoderwhile (ret >= 0) {ret = avcodec_receive_frame(dec, frame);if (ret < 0) {// those two return values are special and mean there is no output// frame available, but there were no errors during decodingif (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))return 0;// fprintf(stderr, "Error during decoding (%s)\n", av_err2str(ret));std::cerr << "Error during decoding " << ret << std::endl;return ret;}// // write the frame data to output fileif (dec->codec->type == AVMEDIA_TYPE_VIDEO) {static int video_frame_count = 0;/* copy decoded frame to destination buffer:* this is required since rawvideo expects non aligned data */// std::cout << "frame size: " << frame->linesize << std::endl;av_image_copy2(video_dst_data, video_dst_linesize,frame->data, frame->linesize,video_fmt, video_width, video_height);/* write to rawvideo file */size_t size = fwrite(video_dst_data[0], 1, video_dst_bufsize, video_dst_file);if (size == 0) {std::cerr << "write failed" << std::endl;return -1;} else {video_frame_count++;std::cout << " write to video frame count: " << video_frame_count << std::endl;}}//ret = output_video_frame(frame);// else// ret = output_audio_frame(frame);av_frame_unref(frame);}return ret;
}