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) {
// 从里面获取一个packet
ret1 = 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 decoder
ret = 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 decoder
while (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 decoding
if (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 file
if (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;
}