核心逻辑
while(av_read_frame()==0){ // 成功读取到一帧的数据 if(avcodec_send_packet()==0){ // 成功发送获取数据包的请求后,需要循环接收数据包 while(avcodec_receive_frame()==0){ //成功接收到一帧的数据包 // 处理一帧的音视频 } } }
以上都是伪代码,言简意赅的说明了视频的处理流程。
av_read_frame
返回流的下一帧。此函数返回存储在文件中的内容,并且不会验证解码器有哪些有效帧。
它会分裂并为每个调用返回一个帧。不会的省略有效帧之间的无效数据,以便使解码器获得最大值可以解码的信息。
如果pkt->buf为NULL,则该数据包在下一个之前有效av_read_frame()或直到avformat_close_input()。否则,数据包无限期有效。在这两种情况下,必须使用不再需要av_packet_unref时。对于视频,数据包包含正好一帧。对于音频,如果每个帧帧具有已知的固定大小(例如PCM或ADPCM数据)。如果音频帧具有可变大小(例如MPEG音频),则它包含一帧。
pkt->pts、pkt->dts和pkt->持续时间始终设置为正确AVStream.time_base单位中的值(如果格式不能提供它们)。pkt->pts可以是AV_NOPTS_VALUE,如果视频格式具有B帧,因此如果没有,最好依赖pkt->dts解压缩有效载荷。
return 0(如果正常),在错误或文件结束时<0
avcodec_send_packet
avcodec_receive_frame
avformat_seek_file
int avformat_seek_file( AVFormatContext *s, // 媒体文件句柄 // 流索引 // 只有在 flags 包含 AVSEEK_FLAG_FRAME 的时候才是设置某个流的读取位置 // 其他情况都是把这个流的time_base作为参考 int stream_index, int64_t min_ts, // 跳转到的最小的时间,或时间单位,或字节单位,或帧数序号(第几帧) int64_t ts, // 目标seek位置,单位同上,通常填INT64_MIN即可。 int64_t max_ts, // 跳转到的最大的时间,单位同上,通常填 INT64_MAX 即可。 // seek 方式 // AVSEEK_FLAG_BYTE,按字节大小进行seek。 // AVSEEK_FLAG_FRAME,按帧数大小进行seek。 // AVSEEK_FLAG_ANY,会seek到非IDR,解码会出现马赛克、花屏现象。 // AVSEEK_FLAG_BACKWARD,往 ts 后面找最近的IDR int flags);
返回值>=0成功,否则为错误码
seek之后,有时会发现read_frame的pts是0,frame竟然是起点,因为seek只能到关键帧,如果gop过长,也会导致回到起点,如果视频时长过短,也会因为gop多长导致这个问题
seek的timestrap参数的详细规则:
- 当stream_index为-1时,会选择默认流,时间戳是以AV_TIME_BASE为基准
double sec = 1.25;//1.25秒 int64_t timestamp = sec * AV_TIME_BASE; av_seek_frame(pFormatCtx,-1,timestamp,AVSEEK_FLAG_BACKWARD);
- 当strea_index为索引流时,timestrap是packet.pts的时间戳
double sec = 1.25; int64_t pts = sec / av_q2d(pFormatCtx->streams[vStreamIndex]->time_base); av_seek_frame(pFormatCtx,vStreamIndex,pts,AVSEEK_FLAG_BACKWARD);
AVFrame
| 字段 | 解释 |
| frame->pts | Presentation timestamp in time_base units (time when frame should be shown to user). 可以作为帧时间的判断依据,注意单位是time_base |
| pkt_pts | 废弃字段,同pts |
| pkt_dts | 值来自触发此帧的packet.dts |