avformat_seek_file使用

前言

    最近一个播放器项目基于ijkplayer做二次开发,是http-mp4点播,需要做精准seek,用到了avformat_seek_file接口,其他组开发人员的加密方案是:对video的2个nalu单元中间加了固定的N字节随机数据。

    这里简要介绍下ffmpeg下的这个接口使用、注意事项,以及http-mp4网络流精准seek的方案。

接口介绍

    int avformat_seek_file(AVFormatContext *s,

                                           int stream_index,

                                           int64_t min_ts,

                                           int64_t ts,

                                           int64_t max_ts,

                                           int flags);

解释:

1)AVFormatContext *s,AVFormatContext实例。

2)int stream_index,流索引,但是只有在 flags 包含 AVSEEK_FLAG_FRAME 的时候才是 设置某个流的读取位置。其他情况都只是把这个流的 time_base (时间基)作为参考。

3)int64_t min_ts,跳转到的最小的时间,或时间单位,或字节单位,或帧数序号(第几帧)。

4)int64_t ts,目标seek位置,单位同上,通常填INT64_MIN即可。

5)int64_t max_ts,跳转到的最大的时间,单位同上,通常填 INT64_MAX 即可。

6)int flags,seek方式,如下:

      AVSEEK_FLAG_BYTE,按字节大小进行seek。
      AVSEEK_FLAG_FRAME,按帧数大小进行seek。
      AVSEEK_FLAG_ANY,会seek到非IDR,解码会出现马赛克、花屏现象。
      AVSEEK_FLAG_BACKWARD往 ts 后面找最近的IDR

本地mp4

      经测试,avformat_seek_file接口,可通过将flag设置为AVSEEK_FLAG_BACKWARD,可seek到离ts最近的IDR帧(方向是ts的后面,本地明文mp4),参考示例:

      avformat_seek_file(s, -1, INT64_MIN, target_ts, INT64_MAX, AVSEEK_FLAG_BACKWARD)

      本地明文mp4,如果要做精准seek,则按如上方案实施即可,可找到target_ts后面最近的IDR帧,然后逐帧解码后,当视频的pts == target_ts,或者误差在指定范围内认为是精准seek即可。

     经验证,若网络流是明文mp4,亦可seek到最近的IDR帧

http-mp4

      然而,对于如上方案的加密网络流,http-mp4却不能通过avformat_seek_file接口来找到离target_ts后面最近的IDR帧,可通过服务器来实现,让服务器返回最近的IDR帧。

      此处,介绍一个本人自己经实践可行的方案

// 此处指http-mp4流
static bool is_http_stream(const char* filename)
{
    assert(filename);
    char* pos = strstr(filename, "http://");
    if (pos != NULL) {
        return true;
    }
    pos = strstr(filename, "https://");
    return pos != NULL;
}

      而后,在ijkplayer的ff_ffplay.c文件read_thread方法的seek_req判断里,增加如下代码:

        if (is->seek_req) {
            int64_t seek_target = is->seek_pos;
            if (is_http_stream(is->filename)) {
                int gop_size = is->viddec.avctx->gop_size;
                if (gop_size > 0) {
                    int64_t backward = gop_size * 1000 * 1000;
                    if (seek_target > backward) {
                        seek_target -= backward;
                    }
                }
            }
            int64_t seek_min    = is->seek_rel > 0 ? seek_target - is->seek_rel + 2: INT64_MIN;
            int64_t seek_max    = is->seek_rel < 0 ? seek_target - is->seek_rel - 2: INT64_MAX;
// FIXME the +-2 is due to rounding being not done in the correct direction in generation
//      of the seek_pos/seek_rel variables

            ffp_toggle_buffering(ffp, 1);
            ffp_notify_msg3(ffp, FFP_MSG_BUFFERING_UPDATE, 0, 0);
            ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek_max, is->seek_flags);

      关键的代码段逻辑:

1)即首先判断是否是http-mp4网络流(本地文件不走此逻辑)

2)通过VideoState结构体的Decoder类型字段viddec,找到AVCodecContext类型变量avctx

3)再通过avctx拿到本片源的gop_size

4)再将seek_target -= gop_size * 1000 * 1000,再按seek_target此值往回找IDR,可实现http-mp4的精准定位。

            if (is_http_stream(is->filename)) {
                int gop_size = is->viddec.avctx->gop_size;
                if (gop_size > 0) {
                    int64_t backward = gop_size * 1000 * 1000;
                    if (seek_target > backward) {
                        seek_target -= backward;
                    }
                }
            }

注意:

1.如果gop较小,比如1s一个关键帧,则精准定位按ijkplayer的代码基本就能满足需求;

2.但本项目中,遇到了10s甚至更长的gop大小,这对精准seek提出了更高的要求,增加了精准seek的难度 ,avformat_seek_file接口无法seek到最近的IDR帧,如果您也是遇到gop较大情形,而又没有后端服务支持,可参考本精准seek方案。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值