[关闭]
@ltlovezh 2019-08-05T12:25:43.000000Z 字数 2497 阅读 790

FFmpeg时间戳

未分类


FFmpeg内部有多种时间戳,基于不同的时间基准。理解这些时间概念,有助于通过FFmpeg进行音视频开发。

在我看来,FFmpeg有两个时间基准:AV_TIME_BASE和AVStream->time_base。

内部时间基准:AV_TIME_BASE

AV_TIME_BASE表示1S对应多长时间,AV_TIME_BASE_Q则是对应的分数形式,如下所示:

  1. /**
  2. * Internal time base represented as integer
  3. *
  4. */
  5. #define AV_TIME_BASE 1000000
  6. /**
  7. * Internal time base represented as fractional value
  8. */
  9. #define AV_TIME_BASE_Q (AVRational){1, AV_TIME_BASE}

AVFormatContext中的参数多是基于AV_TIME_BASE的,如下所示:

  1. /**
  2. * Position of the first frame of the component, in
  3. * AV_TIME_BASE fractional seconds.
  4. * 第一帧的开始时间戳
  5. */
  6. int64_t start_time;
  7. /**
  8. * Duration of the stream, in AV_TIME_BASE fractional
  9. * seconds.
  10. * 时长
  11. */
  12. int64_t duration;

比如:通过AVFormatContext计算出音视频文件的时长:

  1. /**
  2. * Convert an AVRational to a `double`.
  3. */
  4. static inline double av_q2d(AVRational a){
  5. return a.num / (double) a.den;
  6. }
  7. // 单位秒
  8. AVFormatContext->duration * av_q2d(AV_TIME_BASE_Q)

所以,基于内部时间基准的时间戳转换很简单:

  1. // 时间戳转换到秒
  2. time_in_seconds = timestamp * av_q2d(AV_TIME_BASE_Q)
  3. // 秒转换到时间戳
  4. timestamp = time_in_seconds * AV_TIME_BASE

流时间基准:AVStream->time_base

AVStream表示AVFormatContext中一条具体的流结构,比如:音频流、视频流和字幕流等。AVStream->time_base也是分数结构AVRational,只不过不再是固定值,而是依赖于具体流。
AVStreamAVPacketAVFrame中的时间戳多是基于对应的流时间基准。

例如:AVStream中的帧开始时间戳和流时长:

  1. /**
  2. * This is the fundamental unit of time (in seconds) in terms
  3. * of which frame timestamps are represented.
  4. * 表示一秒对应多长时间
  5. */
  6. AVRational time_base;
  7. /**
  8. * Decoding: pts of the first frame of the stream in presentation order, in stream time base.
  9. */
  10. int64_t start_time;
  11. /**
  12. * Decoding: duration of the stream, in stream time base.
  13. */
  14. int64_t duration;

要计算出流时长信息(秒),如下所示:

  1. //计算出AVStream多少秒
  2. duration(秒) = AVStream->duration * av_q2d(AVStream->time_base);

比如:某视频流的time_base是1/90000,即:90000表示1秒。那么2700000实际表示30秒。

再看一下AVFrame中的时间戳:

  1. /**
  2. * frame timestamp estimated using various heuristics, in stream time base
  3. *
  4. */
  5. int64_t best_effort_timestamp;
  6. /**
  7. * Presentation timestamp in time_base units (time when frame should be shown to user).
  8. */
  9. int64_t pts;

一般情况下,两者都可以表示PTS时间戳,且都是基于流时间基准。

  1. // 用秒表示PTS
  2. timestamp(秒) = av_frame_get_best_effort_timestamp(AVFrame) * av_q2d(AVStream->time_base);

不同时间基准之间的转换

为了在不同时间基准间进行转换,FFmpeg提供了av_rescale_q函数进行转换

  1. /**
  2. * Rescale a 64-bit integer by 2 rational numbers.
  3. * The operation is mathematically equivalent to `a * bq / cq`.
  4. * a表示要原始值,bq表示原来的时间基准;cq表示要转换到的时间基准
  5. */
  6. int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq) av_const;

例如:把流时间戳转换到内部时间戳:

  1. // 把某个视频帧的pts转换成内部时间基准
  2. av_rescale_q(AVFrame->pts, AVStream->time_base, AV_TIME_BASE_Q)

ffmpeg中进行seek时(av_seek_frame),时间戳必须基于流时间基准,比如:seek到第5秒。

  1. // 首先计算出基于视频流时间基准的时间戳
  2. int64_t timestamp_in_stream_time_base = av_rescale_q(5 * AV_TIME_BASE, AV_TIME_BASE_Q, video_stream_->time_base);
  3. // 然后seek
  4. av_seek_frame(av_format_context, video_stream_index, timestamp_in_stream_time_base, AVSEEK_FLAG_BACKWARD);
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注