完整源码在transcode_video()函数中,下面摘出关键部分:
// 2. 滤镜处理 ret = filtering_frame(sctx->flt_ctx, frame_dec, frame_flt); if (ret == AVERROR_EOF) { av_log(NULL, AV_LOG_INFO, "filtering vframe EOF\n"); flt_finished = true; av_frame_free(&frame_flt); // flush encoder } else if (ret < 0) { av_log(NULL, AV_LOG_INFO, "filtering vframe error %d\n", ret); goto end; } flush_encoder: // 3. 编码 if (frame_flt != NULL) { // 3.1 设置帧类型。如果不设置,则使用输入流中的帧类型。 frame_flt->pict_type = AV_PICTURE_TYPE_NONE; } // 3.2 编码 ret = av_encode_frame(sctx->o_codec_ctx, frame_flt, &opacket); if (ret == AVERROR(EAGAIN)) // 需要读取新的packet喂给编码器 { //av_log(NULL, AV_LOG_INFO, "encode vframe need more packet\n"); goto end; } else if (ret == AVERROR_EOF) { av_log(NULL, AV_LOG_INFO, "encode vframe EOF\n"); enc_finished = true; goto end; } else if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "encode vframe error %d\n", ret); goto end; } // 3.3 更新编码帧中流序号,并进行时间基转换 // AVPacket.pts和AVPacket.dts的单位是AVStream.time_base,不同的封装格式其AVStream.time_base不同 // 所以输出文件中,每个packet需要根据输出封装格式重新计算pts和dts opacket.stream_index = sctx->stream_idx; av_packet_rescale_ts(&opacket, sctx->o_codec_ctx->time_base, sctx->o_stream->time_base); // 4. 将编码后的packet写入输出媒体文件 ret = av_interleaved_write_frame(sctx->o_fmt_ctx, &opacket); av_packet_unref(&opacket); 5.3 视频编码中的I/B/P帧类型做一个实验,修改5.1.2节frame_flt->pict_type值和5.1.1节enc_ctx->gop_size和enc_ctx->max_b_frames,将编码后视频帧I/B/P类型打印出来,观察实验结果。
我们选一个很短的视频文件用于测试(右键另存为):tnmil3.flv
实验结论如下:
将原始视频帧frame送入视频编码器后生成编码帧packet,那么
手工设置每一帧frame的帧类型为I/B/P,则编码后的packet的帧类型和frame中的一样。编码器是否设置“gop_size”和“max_b_frames”两个参数无影响。
将每一帧frame的帧类型设置为NONE,如果未设置编码器的“gop_size”(默认值-1)和“max_b_frames”(默认值0)两个参数,则编码器自动选择合适参数来进行编码,生成帧类型。
将每一帧frame的帧类型设置为NONE,如果设置了编码器的“gop_size”和“max_b_frames”两个参数,则编码器按照这两个参数来进行编码,生成帧类型。