ffmpeg根据输入的参数截取视频

一、概述

  在日常的开发中,经常会遇到视频截取的需求,如:视频编辑。用ffmpeg的视频截取功能非常简单,只需要调用av_frame_seek函数即可。

但是需要理解av_frame_seek参数的含义,其主要功能是对音视频流的位置进行定位。

int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags); 
  功能:
  将文件指针定位到指定时间戳(timestamp)附近的关键帧,为后续的解码操作做准备。
  参数说明:
    • s:输入的媒体格式上下文(AVFormatContext)。
    • stream_index:目标流的索引(例如视频流或音频流)。若设为 -1,则会尝试在所有流中寻找时间戳最接近的关键帧。
    • timestamp:目标时间戳,其单位由 AVStream.time_base 决定。
    • flags:控制定位行为的标志,常用标志如下:
      • AVSEEK_FLAG_BACKWARD:向后查找最近的关键帧(适用于定位到指定时间之前)。
      • AVSEEK_FLAG_ANY:允许定位到非关键帧(可能会导致解码错误,需谨慎使用)。
      • AVSEEK_FLAG_FRAME:基于帧号而非时间戳进行定位(需配合 AVSEEK_FLAG_BYTE 使用)。
    返回值:
      成功时返回 0,失败时返回负错误码

二、代码示例

  • 计算开始pts和结束pts。计算公式为pts=要移动的时间/timebase。av_seek_frame是使用pts移动位置的
    if (begin_seconds > 0 && end_seconds > 0)
    {
        //计算开始的pts和结束的pts
       //pts = seconds/(num/den)=seconds*(den/num)
        if (vs && vs->time_base.num > 0)
        {
            double t = (double)vs->time_base.den / (double)vs->time_base.num;//den分母/num分子
            begin_pts = begin_seconds * t;
            end_pts = end_seconds * t;
        }
        if (as && as->time_base.num > 0)
        {
            begin_audio_pts = begin_seconds * ((double)as->time_base.den / (double)as->time_base.num);
        }
      
    }

     

  • 使用av_seek_frame集合pts进行音视频位置移动
      //移动到关键帧seek
        if (vs)
        {
            re = av_seek_frame(ic, vs->index, begin_pts, AVSEEK_FLAG_FRAME | AVSEEK_FLAG_BACKWARD);
            PrintError(re, "av_seek_frame");
        }

     

  • 截取到结尾
    if (begin_seconds > 0 && end_seconds > 0)
    {
        AVStream* in_stream = ic->streams[pkt.stream_index];
        AVStream* out_stream = nullptr;
        long long offset_pts = 0; //偏移pts,用于截断的开头pts运算
        if (vs && pkt.stream_index == vs->index)
        {
            qDebug() << "视频:";
            //超过设定的时间就退出
            if (pkt.pts > end_pts)
            {
                av_packet_unref(&pkt);
                break;
            }
            out_stream = oc->streams[0];
            offset_pts = begin_pts;
        }
        else if (as && pkt.stream_index == as->index)
        {
            qDebug() << "音频:";
            out_stream = oc->streams[1];
            offset_pts = begin_audio_pts;
        }
        //重新计算pts dts duration
        //`a * bq(输入basetime) / cq(输出basetime)`
        if (out_stream)
        {
            pkt.pts = av_rescale_q_rnd(pkt.pts - offset_pts, in_stream->time_base,
                out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)
            );
            pkt.dts = av_rescale_q_rnd(pkt.dts - offset_pts, in_stream->time_base,
                out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)
            );
            pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
        }
        pkt.pos = -1;
    }

     

posted on 2025-05-28 13:12  飘杨......  阅读(31)  评论(0)    收藏  举报