/*
 * Analogue TV Player plugin for VDR
 *
 * Copyright (C) 2003, 2005 Andreas Kool <akool@gmx.de>
 *
 * This code is distributed under the terms and conditions of the
 * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
 *
 * Version: $Id: device.c,v 1.94 2006/01/21 09:05:31 akool Exp $
 * Release: $Name:  $
 *
 */

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
extern "C" {
#ifdef boolean
#define HAVE_BOOLEAN
#endif
#include <jpeglib.h>
#undef boolean
}

#include <stdint.h>
#include <netinet/in.h>
#include "player-analogtv.h"

#include "device.h"
#include "encoder.h"
#include "i18n.h"

#include "pvr.h"
#include "redremote.h"

#define SOURCE          "C"

#if VDRVERSNUM < 10300
#define CA            32001
#else
#define CA             0xa0
#endif

#define TS             0x47 // Synchronisation Byte

static char *ciao = ". analogTV giving up!";

static int receivers = 0;
static int pass = 0;


cAnalogtvDevice::cAnalogtvDevice(void) // 4. VDR call
{
  register char *p, *p1;

  d(3, "cAnalogtvDevice::cAnalogtvDevice()");

  // an den folgenden beiden Merkmalen (in der "channels.conf") werden
  // analogTV Kanäle erkannt:

  source = cSource::FromString(SOURCE);
  ca = CA;

  encoder = 0;

  pids[0] = pids[1] = pids[2] = -1;

  fd_pvr = -1;


  // old (pre 0.9.3) or even no values?

  if ((s.brightness < 0) || (s.brightness > 34))
    s.brightness = isPVR ? 17 : 20; // 60%

  if ((s.contrast < 0) || (s.contrast > 34))
    s.contrast = isPVR ? 18 : 14;   // 40%

  if ((s.colour < 0) || (s.colour > 34))
    s.colour = isPVR ? 17 : 16;     // 47%

  if ((s.hue < 0) || (s.hue > 34))
    s.hue = isPVR ? 17 : 15;        // 45%

  if ((freqOffset < 0) || (freqOffset > 32))
    freqOffset = 16;

  if ((p = strchr(s.finetune, '='))) {
    if ((p1 = strchr(p, '|'))) {
      if ((int)(p1 - p) > 3) { // old, pre 0.9.3 values
        strcpy(s.finetune, "|");
        d(0, "SORRY: Please do the whole finetuning again!");
      }
    }
  }

  // old (pre 0.9.7) values?

  if (!s.filter)
    s.filter = 1;
  else
    s.filter = 7;

  sip = NULL;

#ifdef NEED_OWN_SIP       // dont use this with AutoPID-patch applied!
#if VDRVERSNUM < 10300
  sip = new cSIProcessor(NULL);
  d(1, "cSIProcessor created");
#else
  sip = new cSchedules();
  d(1, "cSchedules created");
#endif
#endif

  redremote = new cRedRemote();
}

void cAnalogtvDevice::StopEncoder()
{
  d(3, "cAnalogtvDevice::StopEncoder()");

  if (encoder) {
    delete encoder;
    encoder = NULL;
  }

  EncoderRunning = 0;
}

cAnalogtvDevice::~cAnalogtvDevice()
{
  d(3, "cAnalogtvDevice::~cAnalogtvDevice()");

  StopEncoder();

#ifdef NEED_OWN_SIP       // dont use this with AutoPID-patch applied!
  if (sip) {
    delete sip;
    sip = NULL;
  }
#endif
}

int cAnalogtvDevice::getOffset(char *ID)
{
  register char *p;

  if ((p = strstr(s.finetune, ID)))
    if ((p = strchr(p, '=')))
      return atoi(p + 1);

  return 16;
}

bool cAnalogtvDevice::SetPid(cPidHandle *Handle, int Type, bool On)
{
  d(3, "cAnalogtvDevice::SetPid(Pid=%d, handle=%d, used=%d, Type=%d, On=%d)",
    Handle->pid, Handle->handle, Handle->used, Type, On);

#if VDRVERSNUM > 10128 // vdr-1.1.29 introduced the PCR-Pid
  if ((Type > 4) && (Type < 8))
    pids[Type - 5] = On ? Handle->pid : -1;
  else {
    d(0, "Invalid/unknown Type %d in setPid()", Type);
    return false;
  }
#else
  if ((Type > 3) && (Type < 6))
    pids[Type - 4] = On ? Handle->pid : -1;
  else {
    d(0, "Invalid/unknown Type %d in setPid()", Type);
    return false;
  }
#endif

  if (isPVR && s.usePVR350 && !receivers) {
    if (On) {
      if (!pass) {
        if (setPassThrough(1))
          d(0, "Cannot enable PassThrough mode -- continuing anyway");
        else
          pass = 1;
      }
    }
    else {
      if (pass) {
        if (setPassThrough(0))
          d(0, "Cannot disable PassThrough mode -- continuing anyway");
        else
          pass = 0;
      }
    }
  }

  return true;
}

bool cAnalogtvDevice::saveRead(register int bytes)
{
  register int l = 0, r;
  register int w = bytes;

  if (pass)
    return false;

  while (w > 0) {
    r = read(fd_pvr, buf + l, w);

    if (r == -1) {
      d(0, "Read error on %s, %d:%s", video_device, errno, strerror(errno));
      return false;
    }

    w -= r;
    l += r;

    if (w > 0)
      usleep(USLEEP);
  }

  bytestotal += bytes;

  return true;
}

bool cAnalogtvDevice::TSalign(void)
{
  register int n = 0;

  do {
    if (!saveRead(TS_SIZE))
      return false;

    if (buf[0] != TS) { // we must align on the next TS boundary
      n += TS_SIZE;

      do {
        if (!saveRead(1))
          return false;
        n++;
      } while (buf[0] != TS);

      if (!saveRead(TS_SIZE - 1))
        return false;

      n += TS_SIZE - 1;
    }
  } while (buf[0] != TS);

  skipped += n;
  bufferFilled = true;

  if (n)
    d(1, "Skipped %d bytes to sync on TS packet", n);

  return true;
}

bool cAnalogtvDevice::OpenDvr(void)
{
  d(3, "cAnalogtvDevice::OpenDvr()");

#if 0
  if (!SetChannelDeviceDone) {
    d(1, "OpenDvr() IGNORED - SetChannelDevice() not yet called");
    return false;
  }
#endif

  vdr_ready = 0;

  bufferFilled = false;
  bytestotal = skipped = dupf = af = vf = pf = sf = TSerrors = CCErrors = 0;
  LastCC = -1;
  dropAudio = true;
  droppedAudio = 0;
  has_vi = has_ai = false;

  memset(PidsSeen, 0, sizeof PidsSeen);

  pts[AUDIO] = pts[VIDEO] = last_pts[AUDIO] = last_pts[VIDEO] = 0;
  dts[AUDIO] = dts[VIDEO] = last_dts[AUDIO] = last_dts[VIDEO] = 0;

  firstVideoPts = 0;


  if (pids[2] == -1) {
    Apid = pids[1]; // ptAudio
    Vpid = pids[0]; // ptVideo
    Ppid = -1;      // ptPcr

    d(1, "NEW Audio-PID=%d, Video-PID=%d", Apid, Vpid);
  }
  else {
    Apid = pids[2]; // ptAudio
    Vpid = pids[0]; // ptVideo
    Ppid = pids[1]; // ptPcr

  d(1, "NEW Audio-PID=%d, Video-PID=%d, PCR-PID=%d", Apid, Vpid, Ppid);
  }

  if (isPVR) {
    if (setPVRpids(Apid, Vpid, Ppid)) {
      d(0, "Cannot set Pids on PVR-card%s", ciao);
      return false;
    }
  }

#ifdef DBG
  log = fopen("/tmp/mpeg.log", "w");

  if (log != (FILE *)NULL)
    d(0, "W A R N I N G :  Full MPEG-stream dump ACTIVE!");
#endif

  if (EncoderRunning) { // Encoder already running, nice!
    flush_buffer(); // this have to stay exactly here!
    EncoderNeeded = 1;

    if (isPVR && s.stutteringHack)
      setPVR(INPUT, videosource, -1, -1, -1, -1, -1, -1, -1);

    return true;
  }
  else {
    if (s.mpeg_encoder == PVRTS) {
      if ((fd_pvr = open(video_device, O_RDONLY)) >= 0) {
        if (TSalign())
          EncoderNeeded = 1;
        else
          return false;

        return true;
      }

      d(0, "Cannot open %s, %d:%s", video_device, errno, strerror(errno));
      return false;
    }

    encoder = new cEncoder();

    if ((s.mpeg_encoder == FFMPEGTS) || (s.mpeg_encoder == NVRECTS)) {
      if ((fd_pvr = open(FIFO, O_RDONLY | O_NONBLOCK)) >= 0) {
        tsBuffer = new cTSBuffer(fd_pvr, KILOBYTE(256), CardIndex() + 1);

        return true;
      }

      d(0, "Cannot open %s, %d:%s", FIFO, errno, strerror(errno));
      return false;
    }

    EncoderNeeded = 1;

    return true;
  }
}

void cAnalogtvDevice::CloseDvr(void)
{
  register int i;

  d(3, "cAnalogtvDevice::CloseDvr()");

#if 0
  if (!SetChannelDeviceDone) {
    d(1, "CloseDvr() IGNORED - SetChannelDevice() not yet called");
    return;
  }
#endif

  dropPES = 1;
  EncoderNeeded = 0;
  SetChannelDeviceDone = 0;

  if (bytestotal) {
    d(1, "TS: Stats video (%4d): %d packets", Vpid, vf);
    d(1, "TS: Stats audio (%4d): %d packets", Apid, af);
    if (Ppid != -1)
      d(1, "TS: Stats PCR   (%4d): %d packets", Ppid, pf);

    // Pid 0 : PROGRAM_ASSOCIATION_TABLE_PID
    // Pid 1 : CONDITIONNAL_ACCESS_TABLE_PID

    for (i = 0; i < 0x2000; i++)
      if (PidsSeen[i] && (i != Apid) && (i != Vpid) && (i != Ppid))
        d(1, "TS: Stats       (%4d): %d packets", i, PidsSeen[i]);

    d(1, "TS: Stats      skipped: %d packets", sf);
    d(1, "TS: Stats   duplicates: %d packets", dupf);
    d(1, "TS: Stats got %d TS errors, %d TS continuity errors", TSerrors, CCErrors);
    d(1, "TS: Stats %lld bytes total, %d skipped", bytestotal, (sf * TS_SIZE) + skipped);
  }

#ifdef DBG
  if (log != (FILE *)NULL)
    fclose(log);
#endif

  if (isPVR && s.stutteringHack)
    setPVR(INPUT, videosource, -1, -1, -1, -1, -1, -1, -1);

  if ((s.EncoderAlwaysUp || PVRPES_ALWAYS) && // PVRPES _is_ always up
      (s.mpeg_encoder != PVRTS))              // PVRTS is never always up
    return;

  if (s.mpeg_encoder != PVRTS)
    StopEncoder();

  if ((s.mpeg_encoder == PVRTS) || (s.mpeg_encoder == FFMPEGTS) || (s.mpeg_encoder == NVRECTS)) {
    if (fd_pvr >= 0)
      close(fd_pvr);

    fd_pvr = -1;

    if ((s.mpeg_encoder == FFMPEGTS) || (s.mpeg_encoder == NVRECTS)) {
      if (tsBuffer)
        delete tsBuffer;

      tsBuffer = NULL;
    }
  }
}

uint64_t cAnalogtvDevice::transPtsDts(uint8_t *pts)
{
  uint64_t wts;

  wts = ((uint64_t)((pts[0] & 0x0E) << 5) | ((pts[1] & 0xFC) >> 2)) << 24;
  wts |= (((pts[1] & 0x03) << 6)          | ((pts[2] & 0xFC) >> 2)) << 16;
  wts |= (((pts[2] & 0x02) << 6)          | ((pts[3] & 0xFE) >> 1)) << 8;
  wts |= (((pts[3] & 0x01) << 7)          | ((pts[4] & 0xFE) >> 1));

  return wts * 300ULL;
}

void cAnalogtvDevice::AVsync(uchar *b, int type)
{
  auto int diff;

  if (!pts[AUDIO] || !pts[VIDEO]) // not known yet ...
    return;

  if (pts[VIDEO] > pts[AUDIO])
    diff = pts[VIDEO] - pts[AUDIO];
  else
    diff = pts[AUDIO] - pts[VIDEO];

  if ((type == VIDEO) && (diff > 40820400)) // 0:00:01.511
    memcpy(pts, LastPts[type], 5);

  {
    auto FILE *fo = fopen("/tmp/pts.log", "a");
    auto char  aspts[16];

    if (fo != (FILE *)NULL) {
      formatpts(diff, aspts);

      fprintf(fo, "a-pts=%10lld, v-pts=%10lld, diff=%10d, %s%s\n",
        pts[AUDIO], pts[VIDEO], diff, aspts,
        ((type == VIDEO) && (diff > 40820400)) ? " KORR" : "");
      fclose(fo);
    }
  }
}

bool cAnalogtvDevice::get_ainfo(uchar *b, int l)
{
  return true;
}

bool cAnalogtvDevice::get_vinfo(uchar *b, int l)
{
  return true;
}

bool cAnalogtvDevice::getPES(uchar *b, int l)
{
  register int c = 3, type = -1;
  register int check = 0;
  auto     uint8_t streamID;
  auto     uint8_t flags1 = 0;
  auto     uint8_t flags2 = 0;
  auto     uint8_t pes_hlength = 0;
  auto     unsigned short *pl;
  auto     int plength, mpeg = 0;
#if 0
  auto     char aspts1[16], aspts2[16];
#endif
#ifdef DBG
  auto     char    spts[16];
#endif

  if (!memcmp(b, "\0\0\1", 3)) { // Synchronisation Bytes
    streamID = b[c++];

    switch (streamID) {
      case AUDIO_STREAM_S :
      case AUDIO_STREAM_E : type = AUDIO;
                            if (!has_ai)
                              has_ai = get_ainfo(b, l);
                            break;

      case VIDEO_STREAM_S :
      case VIDEO_STREAM_E : type = VIDEO;
                            if (!has_vi)
                              has_vi = get_vinfo(b, l);
                            break;
    }

    pl = (unsigned short *)(b + c);
    plength = ntohs(*pl);
    c += 2;

#ifdef DBG
    if (log != (FILE *)NULL) {
      fprintf(log, "PES: streamID: 0x%02x=", streamID);
      switch (streamID) {
        case GROUP_START_CODE : fprintf(log, "GROUP_START_CODE"); break;
        case PROG_STREAM_MAP  : fprintf(log, "PROG_STREAM_MAP "); break;
        case PRIVATE_STREAM1  : fprintf(log, "PRIVATE_STREAM1 "); break;
        case PADDING_STREAM   : fprintf(log, "PADDING_STREAM  "); break;
        case PRIVATE_STREAM2  : fprintf(log, "PRIVATE_STREAM2 "); break;
        case AUDIO_STREAM_S   : fprintf(log, "AUDIO_STREAM_S  "); break;
        case AUDIO_STREAM_E   : fprintf(log, "AUDIO_STREAM_E  "); break;
        case VIDEO_STREAM_S   : fprintf(log, "VIDEO_STREAM_S  "); break;
        case VIDEO_STREAM_E   : fprintf(log, "VIDEO_STREAM_E  "); break;
        case ECM_STREAM       : fprintf(log, "ECM_STREAM      "); break;
        case EMM_STREAM       : fprintf(log, "EMM_STREAM      "); break;
        case DSM_CC_STREAM    : fprintf(log, "DSM_CC_STREAM   "); break;
        case ISO13522_STREAM  : fprintf(log, "ISO13522_STREAM "); break;
        case PROG_STREAM_DIR  : fprintf(log, "PROG_STREAM_DIR "); break;
      }
      fprintf(log, ", plength=%d\n", plength);
    }
#endif

    if (type != -1) {
      flags1 = b[c++];

      if ((flags1 & 0xC0) == 0x80)
        mpeg = 2;
      else {
        mpeg = 1;
        flags2 = 0;
      }

      if (mpeg == 2) {
        flags2 = b[c++];
        pes_hlength = b[c++];
      }
      else { // mpeg 1
        check = flags1;
        pes_hlength = 1;

        while ((check == 0xFF) && (c < l)) {
          check = b[c++];
          pes_hlength++;
        }

        if ((check & 0xC0) == 0x40) {
          check = b[c++];
          pes_hlength++;

          check = b[c++];
          pes_hlength++;
        }

        if ((check & 0x30) && check != 0xFF) {
          flags2 = (check & 0xF0) << 2;
          c--;
        }
      }

#ifdef DBG
      if (log != (FILE *)NULL)
        fprintf(log, "PES: MPEG-%d, pes_hlength=%d, %s\n",
          mpeg, pes_hlength, type ? "VIDEO" : "AUDIO");
#endif

      if (((flags2 & PTS_DTS_FLAGS) == PTS_ONLY) ||
          ((flags2 & PTS_DTS_FLAGS) == PTS_DTS)) {
        pts[type] = transPtsDts(b + c);


        // The "vdrsync" approach: drop audio until audio-pts is
        // greater than first video-pts

        if (dropAudio) {
          if ((type == VIDEO) && !firstVideoPts)
            firstVideoPts = pts[type];
          else if (type == AUDIO)
            if (pts[type] >= firstVideoPts) {
              dropAudio = false;

	      if (droppedAudio)
                d(1, "skipped %d audio-packets to sync A/V", droppedAudio);
            }
        }

#if 0 //XXX try#4: no success :-(
        switch (type) {
          case VIDEO : if (!firstVideoPts)
               	         firstVideoPts = pts[type];
                       break;

          case AUDIO : if (pts[type] >= firstVideoPts) {
               	         dropAudio = false;
               	         firstVideoPts = 0;

	      		 if (droppedAudio)
                	   d(1, "skipped %d audio-packets to sync A/V", droppedAudio);
               	       }
                       else
               	         dropAudio = true;

        }
#endif

#if 0 //XXX try#3: no success :-(
        if ((type == AUDIO) &&
            (last_pts[AUDIO] != (uint64_t)-1)) {
          uint64_t diff;

          if (pts[AUDIO] > last_pts[AUDIO])
            diff = pts[AUDIO] - last_pts[AUDIO];
          else
            diff = last_pts[AUDIO] - pts[AUDIO];

          if (diff > 5120000) { // this _must_ be wrong!
            memcpy(b + c, LastPts[AUDIO], 5);
            formatpts(pts[AUDIO], aspts1);
            formatpts(last_pts[AUDIO], aspts2);
            fprintf(stderr, "\n\033[KAudio-PTS fixed: %s -> %s\n", aspts1, aspts2);
          }
        }
#endif

        if (pts[type] > last_pts[type])
          status[type] = type ? 'V' : 'A';
        else if (pts[type] < last_pts[type])
          status[type] = type ? 'v' : 'a';
        else // same?
          status[type] = '?';

        // AVsync(b + c, type); //XXX try#2: no success :-(

        last_pts[type] = pts[type];
        memcpy(LastPts[type], b + c, 5);

        c += 5;

        if ((flags2 & PTS_DTS_FLAGS) == PTS_DTS) {
          dts[type] = transPtsDts(b + c); c += 5;

          // memcpy(b + c - 5 - 5, b + c - 5, 5); // DTS -> PTS //XXX try#1: no success :-(
        }

#ifdef DBG
        if (log != (FILE *)NULL) {
          auto int other = 1 - type;

          fprintf(log, "PES: %c-pts:%fs, %c-pts:%fs, diff=%fs\n",
            (type ? 'V' : 'A'), pts[type] / 90000.,
            (other ? 'V' : 'A'), pts[other] / 90000.,
            (pts[type] - pts[other]) / 90000.);
        }
#endif
      }

#ifdef DBG
      if (log != (FILE *)NULL)
        if (flags2 & ESCR_FLAG) {
          // memcpy(escr, b, 6); c += 6;
          fprintf(log, "PES: ESCR!\n");
        }
#endif

#ifdef DBG
      if (log != (FILE *)NULL) {
        if (((flags2 & PTS_DTS_FLAGS) == PTS_ONLY) ||
            ((flags2 & PTS_DTS_FLAGS) == PTS_DTS)) {

          formatpts(pts[type], spts);
          fprintf(log, "PES: %s-PTS=%s", (type ? "Video" : "Audio"), spts);

          if ((flags2 & PTS_DTS_FLAGS) == PTS_DTS) {
            formatpts(dts[type], spts);
            fprintf(log, " DTS=%s", spts);
          }

          fprintf(log, ")\n");
        }
      }
#endif

    }

    return streamID == GROUP_START_CODE;
  }

  return false;
}

unsigned short int cAnalogtvDevice::getPid(void)
{
  return ((buf[1] & PID_MASK_HI) << 8) | buf[2];
}

bool cAnalogtvDevice::validPid(unsigned short int pid)
{
  if (pid == Apid)
    af++;
  else if (pid == Vpid)
    vf++;
  else if (pid == Ppid)
    pf++;
  else {
    sf++;

    PidsSeen[pid]++;

    return false;
  }

  return true;
}

bool cAnalogtvDevice::getTS(void)
{
  auto int  c;
  auto bool res;

  if (buf[1] & TRANSPORT_ERROR)
    TSerrors++;

  c = 4;

  if (buf[3] & ADAPTION_FIELD) {
#if 0
    if (buf[5] & PCR_FLAG) {
      memcpy(pcr, buf + 6, 6);

      fprintf(stderr, "PCR: %02x %02x %02x %02x %02x %02x\033[K\n",
        pcr[0], pcr[1], pcr[2], pcr[3], pcr[4], pcr[5]);
    }
#endif
    if (buf[5] & OPCR_FLAG)
      memcpy(opcr, buf + 12, 6);

    c += (buf[4] + 1);

    if (c > 187)
      c = 0;
  }

  // We've found the start of a PES packet

#ifdef DBG
  if (log != (FILE *)NULL) {
    fprintf(log, "TS: Continuity=%d (Last=%d)", (buf[3] & CONTINUITY_MASK), LastCC);
    if (buf[3] & ADAPTION_FIELD) {
      fprintf(log, " ADAPTION_FIELD(length=%d) ", buf[4]);
    }
    if (buf[3] & PAYLOAD)
      fprintf(log, " PAYLOAD(length=%d) ", TS_SIZE - c);
    if (buf[1] & PAYLOAD_START)
      fprintf(log, " PAYLOAD_START ");
    if (c && ((TS_SIZE - c) > 3) && (buf[c + 3] == GROUP_START_CODE))
      fprintf(log, " GROUP_START_CODE ");

    fprintf(log, "\n");
  }
#endif

  if (c && (buf[3] & PAYLOAD) && (buf[1] & PAYLOAD_START) && (TS_SIZE - c) > 13)
    res = getPES(buf + c, TS_SIZE - c);

  return c && ((TS_SIZE - c) > 3) && (buf[c + 3] == GROUP_START_CODE);
}

double cAnalogtvDevice::get_idle(void)
{
  static double last_uptime = -1, last_idle;
  static double system_idle = 0.0;
  auto   double uptime, idle, period;
  auto   char buf[80];
  auto   ssize_t r;
  auto   int fd;

  if ((fd = open("/proc/uptime", O_RDONLY)) < 0)
    return system_idle;

  r = read(fd, buf, sizeof(buf) - 1);

  close(fd);

  if (r == -1)
    return system_idle;

  buf[r] = 0;

  sscanf(buf, "%lf %lf", &uptime, &idle);

  period = uptime - last_uptime;

  if (period > 0.5) {
    if (last_uptime >= 0.0)
      system_idle = 0.5 * (system_idle + (idle - last_idle) / period);

    last_idle = idle;
    last_uptime = uptime;
  }

  return system_idle;
}

void cAnalogtvDevice::formatpts(int64_t pts, char *out)
{
  pts = pts / 300;
  pts &= (MAX_PTS - 1);

  sprintf(out, "%d:%02d:%02d.%03d",
    (unsigned int)(pts / 90000.) / 3600,
    ((unsigned int)(pts / 90000.) % 3600) / 60,
    ((unsigned int)(pts / 90000.) % 3600) % 60,
    (((unsigned int)(pts / 90.) % 3600000) % 60000) % 1000);
}

void cAnalogtvDevice::DisplayStatus(void)
{
  register int      neg = 0;
  static   int      countDown = 16;
  auto     double   system_load;
  auto     char     aspts[16], vspts[16], dspts[16];
  auto     uint64_t bytesdropped = (sf * TS_SIZE) + skipped;
  auto     int64_t  dpts = pts[AUDIO] - pts[VIDEO];

  if (--countDown > 0)
    return;

  countDown = 16;

  system_load = 100 * (1.0 - get_idle());

  if (dpts < 0) {
    dpts = -dpts;
    neg = 1;
  }

  formatpts(pts[AUDIO], aspts);
  formatpts(pts[VIDEO], vspts);
  formatpts(dpts, dspts);

  fprintf(stderr, "%.1f MB %c:%s %c:%s %c-%c:%s D:%.2f%% CPU:%4.1f%%\033[K\r",
      bytestotal / (double)(1 << 20),
      status[AUDIO], aspts,
      status[VIDEO], vspts,
      (neg ? status[VIDEO] : status[AUDIO]),
      (neg ? status[AUDIO] : status[VIDEO]),
      dspts,
      100.0 * bytesdropped / bytestotal,
      system_load);
}

bool cAnalogtvDevice::checkContinuity(void)
{
  register int cc = buf[3] & CONTINUITY_MASK;

  if (LastCC == -1)
    LastCC = cc;
  else if (cc == LastCC) {
    dupf++;
    return false;
  }
  else {
    LastCC++;

    if (LastCC == 16)
      LastCC = 0;

    if (cc != LastCC) {
      CCErrors++;
      LastCC = cc;
    }
  }
  return true;
}

bool cAnalogtvDevice::GetTSPacket(uchar *&Data)
{
  register int  r, n1 = 0, l = 0, w = TS_SIZE;
#if 0
  register int  n = 0, firstTime = !vdr_ready;
#endif
  auto     bool skip, res;

#if 0
  if (!SetChannelDeviceDone) {
    d(1, "GetTSPacket() IGNORED - SetChannelDevice() not yet called");
    return false;
  }
#endif

  if (pass) {
    return false;

    d(1, "GetTSPacket() sleeping, PassThrough is active!");
    while (pass)
      usleep(999999); // return false;
  }

  if (!vdr_ready) {
    d(2, "cAnalogtvDevice::GetTSPacket() -- first call");

    vdr_ready = 1;
    dropPES = 0;
  }

  if (s.mpeg_encoder == PVRTS) {
    do {
      skip = false;

      if (bufferFilled)
        bufferFilled = false;
      else {
        if (!saveRead(TS_SIZE))
          return false;

        if (buf[0] != TS) { // not a TS ??
          if (!TSalign())
            return false;

          bufferFilled = false;
        }

        pid = getPid();

        skip = !validPid(pid);

        if (!skip && !checkContinuity())
          skip = true;

        skip = false; //XXX
      }
    } while (skip);

    Data = buf;

    getTS();

    if (s.debug)
      DisplayStatus();

    return true;
  }
  else if ((s.mpeg_encoder == FFMPEGTS) || (s.mpeg_encoder == NVRECTS)) {
    if (tsBuffer) {
#if VDRVERSNUM < 10313
      r = tsBuffer->Read();

      if (r >= 0) {
#else
      if (true) { 
#endif
        Data = tsBuffer->Get();
        return true;
      }
      else if (FATALERRNO) {
        LOG_ERROR;
        return false;
      }
      return true;
    }
    return false;
  }
  else { // PVRPES or PES-Softencoder (mp1e, ffmpeg ...)
    do {
      skip = false;
      n1 = buf[0] = 0;

      while (ReaderLoopRunning && (buf[0] != TS)) {
        r = read_buffer(buf, 1);

        if (r < 1)
          usleep(USLEEP);
        else
          n1++;
      }

      if (ReaderLoopRunning) {
        if (n1 > 1)
          d(1, "skipped %d byte to sync on next TS packet", n1);

        bytestotal += n1;

        l = 1;
        w = TS_SIZE - 1;

        while (ReaderLoopRunning && (w > 0)) {

          r = read_buffer(buf + l, w);

          if (r < w)
            usleep(USLEEP);

          w -= r;
          l += r;
        }

        if (ReaderLoopRunning) {
          bytestotal += TS_SIZE - 1;

          pid = getPid();

#ifdef DBG
          if (log != (FILE *)NULL)
            fprintf(log, "TS: Pid=%d (Audio:%s, Video:%s, skipped:%s)\n",
              pid, (pid == Apid) ? "YES" : "NO", (pid == Vpid) ? "YES" : "NO",
              ((pid == Apid) || (pid == Vpid)) ? "NO" : "YES");
#endif

          skip = !validPid(pid);

          if (!skip && !checkContinuity())
            skip = true;

          if (!skip) {
            res = getTS();

            if (!skip && dropAudio && (pid == Apid)) {
              skip = true;
              sf++;
              droppedAudio++;
            }

#if 0
            if (s.syncHack)
              if (firstTime && !res) { // find next GOP
                skip = true;
                sf++;
                n++;
              }
#endif
          }
        }
      }
    } while (skip && ReaderLoopRunning);

    if (ReaderLoopRunning) {

#if 0
      if (n)
        d(1, "skipped %d byte to sync on next GOP", n * TS_SIZE);
#endif

      if (s.debug)
        DisplayStatus();

      Data = buf;
      return true;
    }
    else
      return false;
  }
}

bool cAnalogtvDevice::ProvidesSource(int Source) const
{
  d(3, "cAnalogtvDevice::ProvidesSource(Source=%d)", Source);

  return source == Source;
}

#if VDRVERSNUM >= 10300
bool cAnalogtvDevice::ProvidesTransponder(const cChannel *Channel) const
{
  d(3, "cAnalogtvDevice::ProvidesTransponder(Transponder=%d)", Channel->Transponder());

  return false; // can't provide any actual transponder
}
#endif

#if VDRVERSNUM >= 10300
int cAnalogtvDevice::ProvidesCa(const cChannel *Channel) const
{
  d(3, "cAnalogtvDevice::ProvidesCa(Channel->Ca()=%d), ca=%d)", Channel->Ca(), ca);

  return ca == Channel->Ca();
}
#else
int cAnalogtvDevice::ProvidesCa(int Ca) const
{
  d(3, "cAnalogtvDevice::ProvidesCa(Ca=%d, ca=%d)", Ca, ca);

  return ca == Ca;
}
#endif

bool cAnalogtvDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
{
  auto bool result = false;
  auto bool hasPriority = Priority < 0 || Priority > this->Priority();
  auto bool needsDetachReceivers = true;
  auto int  freq = Channel->Frequency();

  if (freq == 1) // seit VDR-1.2.6 wird Frequenz==0 nicht mehr akzeptiert
    freq = 0;

  d(3, "cAnalogtvDevice::ProvidesChannel(Receiving()=%d, frequency=%d, Channel->Frequency()=%d, Channel->Ca()=%d)",
      Receiving(), frequency, freq, Channel->Ca());

  // if (Receiving(true))
  //   d(1, "Receiver attached");

#if VDRVERSNUM >= 10300
  if (ProvidesSource(Channel->Source()) && ProvidesCa(Channel)) {
#else
  if (ProvidesSource(Channel->Source()) && ProvidesCa(Channel->Ca())) {
#endif
    if (Receiving()) {
      if ((frequency == freq) || (freq == -1)) {
        needsDetachReceivers = false;
        result = true;
      }
      else
        result = hasPriority;
    }
    else {
      needsDetachReceivers = false;
      result = true;
    }
  }

  if (NeedsDetachReceivers)
    *NeedsDetachReceivers = needsDetachReceivers;

  d(3, "cAnalogtvDevice::ProvidesChannel(): NeedsDetachReceivers=%d, result=%d, hasPriority=%d",
    needsDetachReceivers, result, hasPriority);

  return result;
}

bool cAnalogtvDevice::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY)
{
  auto char buffer[PATH_MAX];

  sprintf(buffer, "/dev/video%d", videodev);

  d(1, "GrabImage(FileName=%s, Jpeg=%d, Quality=%d, X=%d, Y=%d) => %s",
    FileName, Jpeg, Quality, SizeX, SizeY, buffer);

  int videoDev = open(buffer, O_RDWR);

  if (videoDev < 0)
    LOG_ERROR_STR(buffer);

  if (videoDev >= 0) {
    int result = 0;
    struct video_mbuf mbuf;
    result |= ioctl(videoDev, VIDIOCGMBUF, &mbuf);

    if (result == 0) {
      int msize = mbuf.size;
      unsigned char *mem = (unsigned char *)mmap(0, msize, PROT_READ | PROT_WRITE, MAP_SHARED, videoDev, 0);

      if (mem && mem != (unsigned char *)-1) {
        // set up the size and RGB
        struct video_capability vc;
        result |= ioctl(videoDev, VIDIOCGCAP, &vc);
        struct video_mmap vm;
        vm.frame = 0;

        if ((SizeX > 0) && (SizeX <= vc.maxwidth) &&
            (SizeY > 0) && (SizeY <= vc.maxheight)) {
          vm.width = SizeX;
          vm.height = SizeY;
        }
        else {
          vm.width = vc.maxwidth;
          vm.height = vc.maxheight;
        }
        vm.format = VIDEO_PALETTE_RGB24;
        result |= ioctl(videoDev, VIDIOCMCAPTURE, &vm);
        result |= ioctl(videoDev, VIDIOCSYNC, &vm.frame);
        // make RGB out of BGR:
        int memsize = vm.width * vm.height;
        unsigned char *mem1 = mem;
        for (int i = 0; i < memsize; i++) {
          unsigned char tmp = mem1[2];
          mem1[2] = mem1[0];
          mem1[0] = tmp;
          mem1 += 3;
        }

        if (Quality < 0)
          Quality = 255; //XXX is this 'best'???

        isyslog("grabbing to %s (%s %d %d %d)", FileName, Jpeg ? "JPEG" : "PNM", Quality, vm.width, vm.height);

        FILE *f = fopen(FileName, "wb");

        if (f) {
          if (Jpeg) {
            // write JPEG file:
            struct jpeg_compress_struct cinfo;
            struct jpeg_error_mgr jerr;
            cinfo.err = jpeg_std_error(&jerr);
            jpeg_create_compress(&cinfo);
            jpeg_stdio_dest(&cinfo, f);
            cinfo.image_width = vm.width;
            cinfo.image_height = vm.height;
            cinfo.input_components = 3;
            cinfo.in_color_space = JCS_RGB;

            jpeg_set_defaults(&cinfo);
            jpeg_set_quality(&cinfo, Quality, true);
            jpeg_start_compress(&cinfo, true);

            int rs = vm.width * 3;
            JSAMPROW rp[vm.height];
            for (int k = 0; k < vm.height; k++)
              rp[k] = &mem[rs * k];
            jpeg_write_scanlines(&cinfo, rp, vm.height);
            jpeg_finish_compress(&cinfo);
            jpeg_destroy_compress(&cinfo);
          }
          else {
            // write PNM file:
            if (fprintf(f, "P6\n%d\n%d\n255\n", vm.width, vm.height) < 0 ||
                fwrite(mem, vm.width * vm.height * 3, 1, f) < 0) {
              LOG_ERROR_STR(FileName);
              result |= 1;
            }
          }
          fclose(f);
        }
        else {
          LOG_ERROR_STR(FileName);
          result |= 1;
        }
        munmap(mem, msize);
      }
      else
        result |= 1;
    }
    close(videoDev);
    return result == 0;
  }
  return false;
}

bool cAnalogtvDevice::setup(int freq, const char *name, int amixer)
{
  register char  *p, *hwp;
  auto     char   fn[PATH_MAX], t[PATH_MAX];
  auto     FILE  *f;
  auto     int    hw = -1, abort = 0;
  auto     int    needTuning = 0, needNorm = 0, needSource = 0, needPictureProperties = 0;
  auto     int    needCodec = 0;

  if (videodev != lastvideodev) { // other VideoDevice?

    sprintf(video_device, "/dev/video%d", videodev);

    d(1, "NEW videodev=%s", video_device);

    if (v4l == -1) {
      v4l = v4l_version();

      switch (v4l) {
        case -1 : d(0, "Cannot detect V4L version%s", ciao);
             	  return false;

        case -2 : d(0, "No such device! Please check your channels.conf - entry parameters (Cxx)!%s", ciao);
             	  return false;
      }

      d(1, "Detected V4L version %d", v4l);
    }

    s.encode = VIDEO | AUDIO;
    hwp = "Unknown";

    if (lv == 4) {
      sprintf(fn, "/proc/video/dev/video%d", videodev);

      if ((f = fopen(fn, "r")) != (FILE *)NULL) {
        while (fgets(t, sizeof t, f)) {

          if ((p = strchr(t, '\n')))
            *p = 0;

          if (!memcmp(t, "hardware", 8)) {
            if ((p = strchr(t + 8, ':'))) {
              hw = strtol(p + 2, (char **)NULL, 0);

              switch (hw) { // to be extended!
                case 0	     	          : hwp = "Hauppauge PVR"; // The ivtv-driver does not set the "hardware" field ...
              	   			    break;

                case VID_HARDWARE_BT848   : hwp = "Brooktree A/V Capture";
              	   			    break;

                case VID_HARDWARE_SAA7146 : hwp = "Philips Multimedia bridge";
              	   			    d(0, "You are trying to use your DVB-card as capture device! Please check your channels.conf - entry parameters (Cxx)!");
              				    abort = 1;
              	   			    break;

                case VID_HARDWARE_TRUST   : hwp = "Trust FM Radio";
    	      	   			    s.encode = AUDIO;
              	   			    break;

                case VID_HARDWARE_OV511	: hwp = "OV511 USB camera";
    	      	   			    s.encode = VIDEO;
              	   			    break;
              }
            }
            d(1, "Detected hardware %s", hwp);
          }
          else if (!memcmp(t, "name", 4)) {
            if ((p = strchr(t + 4, ':'))) {
              strcpy(capdev, p + 2);
              d(1, "Detected capture device %s", capdev);
            }
          }
          else if (!memcmp(t, "type", 4)) {
            if (!strstr(t + 4, "VID_TYPE_TUNER")) {
              d(0, "This device does'nt have a tuner");

              if (frequency > 1) { // a webcam or s-video input does'nt *need* a tuner
                d(0, "Which is required for frequency=%d", frequency);
                abort = 1;
              }
            }
            else
              d(0, "This device have a tuner");

            if (!strstr(t + 4, "VID_TYPE_CAPTURE")) {
              d(0, "This device cannot capture!");
              abort = 1;
            }
            else
              d(0, "This device is able for capturing");

            if (!memcmp(t + strlen(t) - 21, "VID_TYPE_MPEG_ENCODER", 21))
              d(0, "This device have a onboard MPEG-Encoder");
            else if (isPVR) {
              d(0, "You selected a Hauppauge PVR-x50 card, but this is'nt a PVR-card (Or you dont have patched the ivtv-driver)!");
              abort = 1;
            }
          }
        }
        fclose(f);
      }
    }
    else if (lv == 6) {
      sprintf(fn, "/sys/class/video4linux/video%d/name", videodev);

      if ((f = fopen(fn, "r")) != (FILE *)NULL) {
        while (fgets(t, sizeof t, f)) {

          if ((p = strchr(t, '\n')))
            *p = 0;

          strcpy(capdev, t);
          d(1, "Detected capture device %s", capdev);

          if (!memcmp(capdev, "Vanilla iTVC15 card", 19)) {
            hwp = "Hauppauge PVR";
            d(1, "Detected hardware %s", hwp);
            d(0, "This device have a tuner");
            d(0, "This device is able for capturing");
            d(0, "This device have a onboard MPEG-Encoder");
          }
        }
        fclose(f);
      }
    }

    if (abort) {
      d(0, "Fatal error(s) above%s", ciao);
      return false;
    }

    if (v4l == 2) {
      get_inputs();

      if ((videosource >= MAXSOURCES) || !*sources[videosource]) {
        d(0, "Invalid video source %d. Please check your channels.conf, flag 'D' in entry parameters%s", videosource, ciao);
        return false;
      }
    }

    lastvideodev = videodev;
  }

  if (freq != frequency) {
    frequency = freq;

    if (frequency) {
      strcpy(ID, name);
      freqOffset = getOffset(ID);
    }
    else
      freqOffset = 16;

    d(1, "NEW Frequency=%d + %d", frequency, (freqOffset - 16) * FREQDELTA);
    needTuning++;
  }

  if (videonorm != lastvideonorm) { // other VideoNorm
    lastvideonorm = videonorm;

    d(1, "NEW videonorm=%s", tvnorms[videonorm]);
    needNorm++;
  }

  if (videosource != lastvideosource) { // other VideoSource?
    lastvideosource = videosource;

    d(1, "NEW videosource=%d:%s", videosource, sources[videosource]);
    needSource++;
  }

  if ((s.brightness != sc.brightness) ||
      (s.contrast   != sc.contrast)   ||
      (s.colour     != sc.colour)     ||
      (s.hue        != sc.hue)) {

    d(1, "NEW picture properties");
    needPictureProperties++;
  }

  // If the frequency is in the range [101..1000] and input isn't TV then tune using redremote
  if (s.enabledigibox && needTuning && videosource &&
                   (frequency > 100 && frequency < 1000)) {
    redremote->Tune(freq);
    frequency = freq;
    needTuning = 0;
  }

  if (isPVR) { // On-board MPEG-Encoder

    if (PVRencoderSettingsChanged()) {
      d(1, "NEW PVR MPEG-Encoder parameter(s)");
      needCodec++;
    }

    if (setPVR((needCodec                           ? CODEC     : 0) |
               ((s.image_size != sc.image_size)     ? FORMAT    : 0) |
               (needSource                          ? INPUT     : 0) |
               (needTuning                          ? FREQUENCY : 0) |
               (needNorm                            ? STANDARD  : 0) |
               (needPictureProperties               ? CONTROL   : 0) |
               ((s.mixer_volume != sc.mixer_volume) ? VOLUME    : 0),
               videosource,
               frequency, freqOffset,
               videonorm,
               s.brightness, s.contrast, s.colour, s.hue)) {
      d(0, "Cannot configure PVR-card%s", ciao);
      return false;
    }

    if (s.avHack) // this fixes the "a/v async problem on the first time"
      setPVR(FREQUENCY, -1, frequency, freqOffset, -1, -1, -1, -1, -1);
  }
  else { // Software encoding of YUV frames
    if (amixer) {
      if (amixer != sc.mixer_line) { // other AudioSource?
        s.mixer_line = amixer;
        d(1, "NEW mixer input device=%d (found alternative mixer input device)", amixer);
      }
    }

    if (EncoderRunning) {
      if (needSource || EncoderSettingsChanged()) {
        d(1, "Encoder must be restarted");
        StopEncoder();
      }
    }

    if (needPictureProperties) {
      if (setPictureProperties(s.brightness, s.contrast, s.colour, s.hue)) {
        if (s.propHack)
          d(1, "setPictureProperties(%d,%d,%d,%d) FAILED%s- continuing anyway ...",
            s.brightness, s.contrast, s.colour, s.hue,
            (v4l == 1) ? " (please upgrade to V4L2!) " : " ");
        else
          d(0, "Could not set (all) picture properties");
      }
    }

    if (needTuning) {
      if (frequency) { // only "television" have a tuner
        if (tune(frequency, freqOffset)) {
          d(0, "Cannot tune%s", ciao);
          return false;
        }
      }
    }

    if (needNorm) {
      if (setNorm(videonorm)) {
        d(0, "Cannot set videonorm%s", ciao);
        return false;
      }
    }
  }

  return true;
}

char *channel(register int frequency)
{
  register int i = 0;
  static CHANNELNAMES channelnames[] = {
  {  48250, "K02" },
  {  55250, "K03" },
  {  62250, "K04" },
  {  69250, "X01" },
  {  76250, "X02" },
  {  83250, "X03" },
  { 105250, "S01" },
  { 112250, "S02" },
  { 119250, "S03" },
  { 126250, "S04" },
  { 133250, "S05" },
  { 140250, "S06" },
  { 147250, "S07" },
  { 154250, "S08" },
  { 161250, "S09" },
  { 168250, "S10" },
  { 175250, "K05" },
  { 182250, "K06" },
  { 189250, "K07" },
  { 196250, "K08" },
  { 203250, "K09" },
  { 210250, "K10" },
  { 217250, "K11" },
  { 224250, "K12" },
  { 231250, "S11" },
  { 238250, "S12" },
  { 245250, "S13" },
  { 252250, "S14" },
  { 259250, "S15" },
  { 266250, "S16" },
  { 273250, "S17" },
  { 280250, "S18" },
  { 287250, "S19" },
  { 294250, "S20" },
  { 303250, "S21" },
  { 311250, "S22" },
  { 319250, "S23" },
  { 327250, "S24" },
  { 335250, "S25" },
  { 343250, "S26" },
  { 351250, "S27" },
  { 359250, "S28" },
  { 367250, "S29" },
  { 375250, "S30" },
  { 383250, "S31" },
  { 391250, "S32" },
  { 399250, "S33" },
  { 407250, "S34" },
  { 415250, "S35" },
  { 423250, "S36" },
  { 431250, "S37" },
  { 439250, "S38" },
  { 447250, "S39" },
  { 455250, "S40" },
  { 463250, "S41" },
  { 471250, "21" },
  { 479250, "22" },
  { 487250, "23" },
  { 495250, "24" },
  { 503250, "25" },
  { 511250, "26" },
  { 519250, "27" },
  { 527250, "28" },
  { 535250, "29" },
  { 543250, "30" },
  { 551250, "31" },
  { 559250, "32" },
  { 567250, "33" },
  { 575250, "34" },
  { 583250, "35" },
  { 591250, "36" },
  { 599250, "37" },
  { 607250, "38" },
  { 615250, "39" },
  { 623250, "40" },
  { 631250, "41" },
  { 639250, "42" },
  { 647250, "43" },
  { 655250, "44" },
  { 663250, "45" },
  { 671250, "46" },
  { 679250, "47" },
  { 687250, "48" },
  { 695250, "49" },
  { 703250, "50" },
  { 711250, "51" },
  { 719250, "52" },
  { 727250, "53" },
  { 735250, "54" },
  { 743250, "55" },
  { 751250, "56" },
  { 759250, "57" },
  { 767250, "58" },
  { 775250, "59" },
  { 783250, "60" },
  { 791250, "61" },
  { 799250, "62" },
  { 807250, "63" },
  { 815250, "64" },
  { 823250, "65" },
  { 831250, "66" },
  { 839250, "67" },
  { 847250, "68" },
  { 855250, "69" },
  {      0, NULL } };

  while (channelnames[i].freq) {
    if (channelnames[i].freq == frequency)
      return channelnames[i].channelname;
    i++;
  }

  return "???";
}

bool cAnalogtvDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
{
  register bool cc = false;
  auto     int  B, C, D;
  auto     int  freq = Channel->Frequency();

  if (LiveView) {
    d(1, "You need a MPEG-decoder (full featured DVB-C/S/T- or DXR3-card, softdevice-, PVR-350- or Xine-plugin) to _view_ the analogTV-output. Please see analogTV-FAQ, Q#2! (LiveView - impossible!)%s", ciao);
    return false;
  }

  if (freq == 1) // seit VDR-1.2.6 wird Frequenz==0 nicht mehr akzeptiert
    freq = 0;

  d(3, "cAnalogtvDevice::SetChannelDevice(Receiving()=%d, Receiving(true)=%d), frequency=%d", Receiving(), Receiving(true), freq);

  B = Channel->Bandwidth();
  C = Channel->CoderateH();
  D = Channel->CoderateL();

  if ((B == 3) && (C == 9) && (D == 9)) {
    d(1, "WARNING: Please adapt your channels.conf - you are still using the pre analogTV-0.9.11 syntax!");

    B = 2; // PAL
    C = Channel->Sid() - 10;
    D = Channel->Srate();

    d(1, "WARNING: Parameters=B%dC%dD%d, Srate=0, SID=<Service ID>", B, C, D);
  }

  if ((B < 0) || (B > 2))
    B = 2;

  videonorm = 2 - B; // 0 = PAL, 1 = NTSC, 2 = SECAM

  if (C < 0)
    C = 0;

  videodev = C;

  if (D < 0)
    D = 0;

  videosource = D;

  if (freq)
    d(1, "Channel: %s, SID=%d, %3.2f MHz (%s), %s", Channel->Name(), Channel->Sid(), freq / 1000.0, channel(freq), tvnorms[videonorm]);
  else
    d(1, "Channel: %s, SID=%d, %s", Channel->Name(), Channel->Sid(), tvnorms[videonorm]);
  d(1, "Video device: /dev/video%d", videodev);
  d(1, "Video source: %d:%s", videosource, sources[videosource]);

  if (Receiving(true))
    d(1, "Receiver #%d attached", ++receivers);

  if (!Receiving() || (frequency != freq)) // if we are receiving the channel is already set!
#if VDRVERSNUM >= 10318
    cc = setup(freq, Channel->GetChannelID().ToString(), Channel->Apid(1));
#else
    cc = setup(freq, Channel->GetChannelID().ToString(), Channel->Apid2());
#endif
  else {
    cc = true;
    d(1, "SetChannelDevice(Receiving()=%d, Receiving(true)=%d) frequency=%d already receiving", Receiving(), Receiving(true), freq);
  }

  SetChannelDeviceDone = cc ? 1 : 0;
  return cc;
}

void cAnalogtvDevice::SetVideoFormat(bool VideoFormat16_9)
{
  d(3, "cAnalogtvDevice::SetVideoFormat(VideoFormat16_9=%d)", VideoFormat16_9);

  wide = VideoFormat16_9;

  if (wide) {
    d(1, "Using 16:9 video format");
    s.aspect = 2;
  }
  else {
    d(1, "Using 4:3 video format");
    s.aspect = 1;
  }
}
