1
int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
int *got_picture_ptr,
const AVPacket *avpkt);
作用:
是解码一帧视频数据。输入一个压缩编码的结构体AVPacket,输出一个解码后的结构体AVFrame。
int *got_picture_ptr :如果没有可以解压缩的帧,则为零,否则为非零。
ffmpeg 源代码简单分析 : avcodec_decode_video2()
2
SwsContext 三兄弟
目的:
想將某個PixelFormat轉換至另一個PixelFormat,例如,將YUV420P轉換成YUYV422,或是想變換圖的大小。
可以完成图片像素格式的转换, 图片的拉伸等工作
ffmpeg-struct SwsContext使用心得
3
avcodec_send_frame
用于将 AVFrame 结构体所封装的图像数据传入codec
avcodec_receive_packet
获取输出的码流,并保存在传入的AVpacket结构中。
avcodec_send_packet
用于将 AVpacket结构体所封装的二进制流传入解码器
avcode_receive_frame
可以从解码器中获取解码输出的图像帧结构
4
av_read_frame 读取完整帧
关于如何实现完整帧的?
这个看对应解复用器的实现,比如ff_flv_demuxer的 flv_read_packet, 是根据封装格式来的。
5
avformat_open_input
注意申请 Context 的空间
ic = avformat_alloc_context(); //分配 avformatcontext
6
avformat_find_stream_Info
avformat_find_stream_Info
7
stream_component_open
stream_component_open函数分析
8
**int av_interleaved_write_frame(AVFormatContext s, AVPacket pkt);
说明:
将数据包写⼊输出媒体⽂件,并确保正确的交织(保持packet dts的增⻓性)。
该函数会在内部根据需要缓存packet,以确保输出⽂件中的packet按dts递增的顺序正确交织。如果⾃⼰
进⾏交织则应调⽤av_write_frame()。
参数:
s 媒体⽂件句柄
pkt
要写⼊的packet。
如果packet使⽤引⽤参考计数的内存⽅式,则此函数将获取此引⽤权(可以理解为
move了reference),并在内部在合适的时候进⾏释放。此函数返回后,调⽤者不得
通过此引⽤访问数据。如果packet没有引⽤计数,libavformat将进⾏复制。
此参数可以为NULL(在任何时候,不仅在结尾),以刷新交织队列。
Packet的stream_index字段必须设置为s-> streams中相应流的索引。
时间戳记(pts,dts)必须设置为stream’s timebase中的正确值(除⾮输出格式⽤
AVFMT_NOTIMESTAMPS标志标记,然后可以将其设置为AV_NOPTS_VALUE)。
同⼀stream后续packet的dts必须严格递增(除⾮输出格式⽤
AVFMT_TS_NONSTRICT标记,则它们只必须不减少)。duration也应设置(如果已
知)。
返回值:
成功时为0,错误时为负AVERROR。即使此函数调⽤失败,Libavformat仍将始终释放该
packet。
9
av_compare_ts
int av_compare_ts(int64_t ts_a, AVRational tb_a, int64_t ts_b, AVRational tb_b);
返回值:
-1 ts_a 在ts_b之前
1 ts_a 在ts_b之后
0 ts_a 在ts_b同⼀位置
⽤伪代码:return ts_a == ts_b ? 0 : ts_a < ts_b ? -1 : 1
10
avformat_write_header
a:会更新时间基
int avformat_write_header(AVFormatContext *s, AVDictionary **options)
{
int ret = 0;
int already_initialized = s->internal->initialized;
int streams_already_initialized = s->internal->streams_initialized;
if (!already_initialized)
if ((ret = avformat_init_output(s, options)) < 0)
return ret;
if (!(s->oformat->flags & AVFMT_NOFILE) && s->pb)
avio_write_marker(s->pb, AV_NOPTS_VALUE,
AVIO_DATA_MARKER_HEADER);
if (s->oformat->write_header) {
ret = s->oformat->write_header(s);//调用复用器接口
if (ret >= 0 && s->pb && s->pb->error < 0)
ret = s->pb->error;
if (ret < 0)
goto fail;
flush_if_needed(s);
}
if (!(s->oformat->flags & AVFMT_NOFILE) && s->pb)
avio_write_marker(s->pb, AV_NOPTS_VALUE,
AVIO_DATA_MARKER_UNKNOWN);
if (!s->internal->streams_initialized) {
if ((ret = init_pts(s)) < 0)
goto fail;
}
return streams_already_initialized;
fail:
if (s->oformat->deinit)
s->oformat->deinit(s);
return ret;
}
最终调⽤到复⽤器的 write_header,⽐如
AVOutputFormat ff_flv_muxer = {
.name = "flv",
.long_name = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),
.mime_type = "video/x-flv",
.extensions = "flv",
.priv_data_size = sizeof(FLVContext),
.audio_codec = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 :
AV_CODEC_ID_ADPCM_SWF,
.video_codec = AV_CODEC_ID_FLV1,
.init = flv_init,
.write_header = flv_write_header, // 写头部
.write_packet = flv_write_packet,
.write_trailer = flv_write_trailer, // 写⽂件尾部
.check_bitstream= flv_check_bitstream,
.codec_tag = (const AVCodecTag* const []) {
flv_video_codec_ids, flv_audio_codec_ids, 0
},
.flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |
AVFMT_TS_NONSTRICT,
.priv_class = &flv_muxer_class,
};
11
av_packet_rescale_ts
将packet中的各时间值从输入流封装格式时间基转到输出流封装格式时间基
AVStream.time_base是AVPacket中pts和dts的时间单位,输入流与输出流中time_base按如下方式确定:
对于输入流:打开输入文件后,调用avformat_find_stream_info()可获取到每个流中的time_base
对于输出流:打开输出文件后,调用avformat_write_header()可根据输出文件封装格式确定每个流的time_base并写入输出文件中