ffdemux_mpegts中时间戳的处理问题

ffdemux_mpegts是gstreamer的demux plugin,基于ffmpeg,在使用的时候发现处理实时流存在问题。

先来描述一下问题,采用gst-launch命令启动转码,命令如下:

gst-launch-0.10 udpsrc multicast-group=239.1.80.80 port=49500 ! queue ! ffdemux_mpegts name=demuxer ! queue ! ffdec_mp3 ! queue ! ffenc_mp2 ! queue ! ffmux_mpegts name=muxer preload=10000 maxdelay=500000 muxrate=3600000 ! udpsink host=239.100.100.100 port=12345 demuxer. ! queue ! queue ! mpeg2dec ! queue ! mpeg2enc format=3 bitrate=2900 closed-gop=true sequence-header-every-gop=true ! muxer. --gst-debug-level=2 > udp188-log.txt 2>&1 &

采用了ffdemux_mpegts来解复用mpegts流。采用udpsrc作为ts流的源,来自实时的数字电视。运行过程中会结束转码,而不是持续的运行。把debug的level设置为5,发现如下内容:

ffmpeg gstffmpegdemux.c:1378:gst_ffmpegdemux_loop:<demuxer> pkt pts:26:30:43.583444444
ffmpeg gstffmpegdemux.c:1378:gst_ffmpegdemux_loop:<demuxer> pkt pts:0:00:00.025755556
ffmpeg gstffmpegdemux.c:1548:gst_ffmpegdemux_loop:<demuxer> dropping buffer out of segment, stream eos
ffmpeg gstffmpegdemux.c:407:gst_ffmpegdemux_is_eos: stream 0 0xb561e6c8 eos:0

其中有pts:26:30:43.583444444,也就是pts已经达到了其最大值,因为是33位的,且按照90kHZ计时,刚好是26小时30分钟多一点。接下来就从0开始计数,这让ffdemux_mpegts以为流已经结束,因此发出了eos的events。由于音频和视频分别有自己的pts,另外由于pts并不是单调递增的,所以当音频和视频都满足了eos的条件就造成转码“结束”。

以上描述的是一个问题,还有另外一个问题,就是gstream采用64位的时间戳,且单位为纳秒,而pts只有33位,且单位是按照90kHZ来算的,因此简单的让gstreamer的时间戳等于pts是不可行的,以下是在gstffmpegutils.h中定义的函数:

static inline guint64
gst_ffmpeg_time_ff_to_gst (gint64 pts, AVRational base)
{
  guint64 out;

  if (pts == AV_NOPTS_VALUE){
      out = GST_CLOCK_TIME_NONE;
  } else {
      AVRational bq = { 1, GST_SECOND };
      out = av_rescale_q (pts, base, bq);
  }

  return out;
}

上面的函数在gstffmpegdemux.c中的loop函数里被调用,用于gstreamer的时间戳的计算。

由于gstreamer的时间戳是64为的,而pts是33位的,且pts并不是严格单调递增的,因此转换就更复杂一点。

ffdemux_mpegts中时间戳的处理问题

如上面所示表示PTS按取值被分成两个区域,其中1区域占据平分成3个部分的两边,2区域占据当中。

  • region1_right = 5秒,region1_left=95438(95443-5)秒。
  • region2_left = 10秒,region2_right=15433(95443-10)秒。
  • 当状态为1区域,如果pts_timestamp的值落在region2_left和region2_right之间,则变为2区域状态,并且gst_last_timestamp = max_pts*循环次数。
  • 当状态为2区域,如果遇上pts_timestamp的值落在region1_left和region1_right之间,则变为1区域状态。
  • 如果是1区域状态:
    • 如果pts_timestamp值在1.1区域则gst_timestamp = gst_last_timestamp + max_pts + pts_timestamp。
    • 如果pts_timestamp值在1.2区域则gst_timestamp = gst_last_timestamp + pts_timestamp。
  • 如果是2区域状态,则gst_timestamp = gst_last_timestamp + pts_timestamp。
64位无符号数,单位是纳秒,能表示约5,6百年(我自己计算的,如果不准请提示),因此起始时间设置为0,可以放心使用。

为了避免累积误差,gst_last_timestamp的计算采用pts_max*循环次数,然后再转换,而不是gst_last_timestamp += pts_max的转换。这就需要一个变量记载pts循环的次数,pts循环一次指的是pts达到最大值从零开始。

如下是ubuntu10.10 server版本源代码的修改:

60a61,65
>
  gint region;
  guint64 pts_region1_left, pts_region1_right;
  guint64 pts_region2_left, pts_region2_right;
  guint64 gst_last_timestamp, pts_accumulate;
979a985,991
  stream->pts_accumulate = 0llu;
  //stream->gst_last_timestamp = (8589934591llu * 100000llu) / 9llu;//0llu;
  stream->gst_last_timestamp = 0llu;
  stream->pts_region1_right = 5000000000llu; //5s
  stream->pts_region1_left = 95438000000000llu; //95438s, 95443-5
  stream->pts_region2_left = 10000000000llu; //10s
  stream->pts_region2_right = 95433000000000llu; //max pts is about 95443s
1029a1042,1047
      if (tmp < stream->pts_region1_right || tmp > stream->pts_region1_left) {
          stream->region = 1;
      } else {
          stream->region = 2;
      }
>
1364a1383,1404
      if (stream->region == 1) {
          if (timestamp > stream->pts_region2_left && timestamp < stream->pts_region2_right) {
              GST_DEBUG_OBJECT (demux, "enter region 2");
              stream->pts_accumulate += 1;
              stream->gst_last_timestamp = (stream->pts_accumulate * 8589934591llu * 100000llu) / 9llu;
              stream->region = 2;
          }
      } else if (stream->region == 2) {
          if (timestamp < stream->pts_region1_right || timestamp > stream->pts_region1_left) {
              GST_DEBUG_OBJECT (demux, "enter region 1");
              stream->region = 1;
          }
      }
      if (stream->region == 1) {
          if (timestamp < stream->pts_region2_left) {
              timestamp += (stream->gst_last_timestamp + (8589934591llu * 100000llu) / 9llu);
          } else if (timestamp > stream->pts_region2_right) {
              timestamp += stream->gst_last_timestamp;
          }
      } else if (stream->region == 2) {
              timestamp += stream->gst_last_timestamp;
      }
1381,1382c1421,1422
  if (demux->start_time != -1 && demux->start_time > timestamp)
      goto drop;
---
  //if (demux->start_time != -1 && demux->start_time > timestamp)
  //  goto drop;
1388,1389c1428,1429
  if (demux->segment.stop != -1 && timestamp > demux->segment.stop)
      goto drop;
---
  //if (demux->segment.stop != -1 && timestamp > demux->segment.stop)
  //  goto drop;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值