[✔️]FFMpeg视频处理

554 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 16 天,点击查看活动详情

核心逻辑

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->ptsPresentation timestamp in time_base units (time when frame should be shown to user).
可以作为帧时间的判断依据,注意单位是time_base
pkt_pts废弃字段,同pts
pkt_dts值来自触发此帧的packet.dts