Logo Search packages:      
Sourcecode: vdr-plugin-bitstreamout version File versions  Download package

pts.h

/*
 * pts.h:   P(resentation) T(ime) S(tamps) handling
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
 *
 * Copyright (C) 2003 Werner Fink, <werner@suse.de>
 */
#ifndef __PTS_H
#define __PTS_H

#ifndef _POSIX_SOURCE
# define _POSIX_SOURCE
#endif
#include <sys/time.h>
#include <unistd.h>
#include <vdr/config.h>
#include <vdr/device.h>
#include "types.h"

//#define DEBUG_PTS
#if defined(DEBUG) && !defined(DEBUG_PTS)
# define DEBUG_PTS
#endif
#ifdef  DEBUG_PTS
# define debug_pts(args...) fprintf(stderr, args)
#else
# define debug_pts(args...)
#endif
#define STC_STATERR     (~0U/90U)

enum ePTSstate { eOK = 0, eMISSED = -1, eDOSKIP = 1 };

00046 class cPTS {
private:
    uint_32 pts;
    uint_32 stc;
    off_t   off;
    off_t   min;
    struct timeval expire, syncstc, current, previous;
    uint_8  err;
    bool AVsync, Started, Synching, HasPTS;

    inline uint_32 PTSinMS(const uint_8 *pts)
    {
      return (uint_32)
      ((   ((uint_64)(pts[0] & 0x0E) << 29)
         | ((uint_64)(pts[1])        << 22)
         | ((uint_64)(pts[2] & 0xFE) << 14)
         | ((uint_64)(pts[3])        <<  7)
         | ((uint_64)(pts[4] & 0xFE) >>  1))/90ULL);
    }

    inline void timermark(void)
    {
        if (!timerisset(&previous))
          gettimeofday(&previous, NULL);
      if (!timerisset(&expire)) {
          static const struct timeval add = {2, 0};
          timeradd(&previous, &add, &expire);
      }
      if (!timerisset(&syncstc)) {
          static const struct timeval add = {0, 480*1000};
          timeradd(&previous, &add, &syncstc);
      }
    }

    inline bool timerexpired(void)
    {
      if (timerisset(&current)) {
          previous.tv_sec  = current.tv_sec;
          previous.tv_usec = current.tv_usec;
      }
      gettimeofday(&current, NULL);
      if (!timerisset(&expire)) {
          debug_pts("timerexpired() set expire\n");
          static const struct timeval add = {2, 0};
          timeradd(&current, &add, &expire);
          return false;
      }
      return timercmp(&current, &expire, >);
    }

    inline bool timersyncstc(void)
    {
      if (timerisset(&current)) {
          previous.tv_sec  = current.tv_sec;
          previous.tv_usec = current.tv_usec;
      }
      gettimeofday(&current, NULL);
      if (!timerisset(&syncstc)) {
          debug_pts("timersyncstc() set syncstc\n");
          static const struct timeval add = {0, 480*1000};
          timeradd(&current, &add, &syncstc);
          return false;
      }
      return timercmp(&current, &syncstc, >);
    }

    inline bool  dvbtime (void)
    {
#if defined(VDRVERSNUM) && VDRVERSNUM >= 10206
      cDevice* PrimaryDevice = cDevice::PrimaryDevice();
      sint_64 ret;

      debug_pts("dvbtime in\n");

      if (!PrimaryDevice) {   // No Device, always wrong
          err++;        // due missed reference
          goto out;
      }

      if ((ret = PrimaryDevice->GetSTC()) < 0) {
          err++;        // Device error
          goto out;
      }
      stc = (uint_32)(((uint_64)ret)/90ULL);

      //
      // The av711x DVB card/driver seems to have trouble
      // with large values (missing 33th bit aka > ~0U)
      //
      if (pts > STC_STATERR && stc < STC_STATERR+10000)
          stc += STC_STATERR;

      //
      // We have following cases
      //   off >  0 Earlier (we may play silent with duration `off')
      //   off <  0 Late    (we're in trouble, no time back)
      //   off == 0 Fit     (Strike)
      //
      off = pts - stc;
      debug_pts("dvbtime PTS=%u STC=%u off=%ld\n", pts, stc, off);

      return true;
    out:
      off = -1000;
      debug_pts("dvbtime out bad\n");
#endif
      return false;
    };

    //
    // timersyncstc() or timerexpired() must be called first
    //
    inline bool dvbAVsync (void)
    {
#if defined(VDRVERSNUM) && VDRVERSNUM >= 10206
      struct timeval sub;
      off_t diff, delay;

      debug_pts("dvbAVsync in\n");

      if (AVsync) {
          if (!dvbtime())
            goto err;
          debug_pts("AVsync STC=%d\n", stc);
          goto out;
      }

      timersub(&current, &previous, &sub);
      delay = (sub.tv_sec*1000 + sub.tv_usec/1000);

      diff = stc;       // Previous STC value

      if (!dvbtime())         // Fetch new STC value from DVB
          goto err;

      if ((diff = stc - diff) < 0)
          goto err;           // DVB AV are definitly not in sync

      //
      // The DVB STC delay should be in sync with
      // The system clock delay (at least 1ms).
      //
      debug_pts("dvbAVsync dSTC=%ld, dCLOCK=%ld\n", diff, delay);
      if ((diff < delay - 2) || (diff > delay + 2))
          goto err;

      //
      // Assume that DVB STC clock is now in sync with
      // the System clock.
      //
      AVsync = true;
    out:
      debug_pts("dvbAVsync out true\n");
      return true;
    err:
      debug_pts("dvbAVsync out bad\n");
#endif
      return false;
    };

    inline off_t less(void)
    {
      off_t ret = min;
      if (min >= 100)
          min -= 20;
      else
          min -= 10;
      return ret;
    };

public:
    inline bool  mark(const uint_8 *buf = (const uint_8 *)0)
    {
      if (buf) {
          pts = PTSinMS(buf);
          HasPTS = true;
      } else if (!HasPTS && timersyncstc())
          return true;        // Do not hanging around
      return HasPTS;
    };
    inline void  lead(const uint_32 t)
    {
      timerclear(&expire);
      err = 0;
      min = 200;
      Synching = false;
    };
    inline void  reset(void)
    {
      pts = stc = 0;
      timerclear(&current);
      timerclear(&expire);
      timerclear(&syncstc);
      timerclear(&previous);
      err = 0;
      off = -1000;
      min = 200;
      AVsync = false;
      Started = false;
      Synching = false;
      HasPTS = false;
    };
    inline const bool synch(bool s = false)
    {
      if (s) Synching = true;
      return Synching;
    };

    inline bool  stcsync(bool h = false)
    {
      if (h) HasPTS = true;         // Do not hanging around later

#if defined(VDRVERSNUM) && VDRVERSNUM >= 10206
      if (AVsync) {
          debug_pts("stcsync AV sync true\n");
          goto out;
      }

      if (!Started) {               // Timer should be started
          timermark();
          Started = true;           // Timer started
      }

      if (timersyncstc()) {
          debug_pts("stcsync timersyncstc()\n");
          goto out;                 // Timer expired
      }

      if (!dvbAVsync()) {
          debug_pts("stcsync !dvbAVsync()\n");
          goto bad;
      }
    bad:
      return false;
    out:
#endif
      return true;
    };

    inline bool  dvbcheck(const uint_32 delay)
    {
#if defined(VDRVERSNUM) && VDRVERSNUM >= 10206
      debug_pts("dvbcheck in\n");

      if (err > 10)
          goto out;                 // Ignore continuing error

      if (pts == 0) {
          debug_pts("dvbcheck pts == 0, HasPTS = %d\n", HasPTS);
          off = 0;
          goto out;                 // What should we compare?
      }

      if (timerexpired()) {
          debug_pts("dvbcheck timerexpired()\n");
          goto out;                 // Timer expired
      }

      if (!dvbAVsync()) {
          debug_pts("dvbcheck !dvbAVsync()\n");
          goto bad;
      }

      if (off >= less()) {          // 10ms which is 33cm
          timerclear(&expire);
          goto out;                 // Success
      }
    bad:
      pts += delay;                 // Next frame starts with this PTS
      debug_pts("dvbcheck out bad\n");
      return false;
    out:
      debug_pts("dvbcheck out true\n");
#endif
      return true;
    };

    inline off_t delay(off_t ignore)
    {
#if defined(VDRVERSNUM) && VDRVERSNUM >= 10206
      debug_pts("delay (abs(off=%ld) > ignore=%ld) == %d\n", off, ignore, (abs(off) > ignore));
      if (abs(off) > ignore)
          off = 0;
      return off;
#else
      return 0;
#endif
    };
};
#endif

Generated by  Doxygen 1.6.0   Back to index