File:  [DVB] / multiplexer / output.c
Revision 1.15: download - view: text, annotated - select for diffs
Sat Jan 15 20:39:50 2005 UTC (19 years, 4 months ago) by oskar
Branches: MAIN
CVS tags: version-1-1-7, version-1-1-6, version-1-1-5, version-1-1-4, version-1-1-3, version-1-1-2, version-1-1-1, version-1-1-0, version-1-0-8, version-1-0-7, version-1-0-6, HEAD
add command --nit to control network pid in pat

/*
 * ISO 13818 stream multiplexer
 * Copyright (C) 2001 Convergence Integrated Media GmbH Berlin
 * Copyright (C) 2004 Oskar Schirmer (schirmer@scara.com)
 *
 * 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
 */

/*
 * Module:  Output
 * Purpose: Handle output buffers, write to stdout occasionally.
 *
 * Besides functions to detect states of the output buffer, and to
 * write data from it, support in filling the output buffer is
 * provided to the splicers.
 */

#include "global.h"
#include "error.h"
#include "output.h"

static refr_ctrl refc;
static refr_data refd;

static int outf;

static boolean outtrigger;
static int outdelta;
static t_msec trigger_msec_output;

static int next_size;

static t_msec statistics_msec;
static t_msec statistics_next;
static int statistics_load;
static int statistics_bursts;
static int statistics_refd_min;
static int statistics_refd_max;
static int statistics_time_min;
static int statistics_time_max;
static int statistics_burst_min;
static int statistics_burst_max;

boolean output_init (void)
{
  int r;
  struct stat outstat;
  outtrigger = FALSE;
  trigger_msec_output = TRIGGER_MSEC_OUTPUT;
  next_size = HIGHWATER_OUT;
  statistics_msec = 0;
  if (!list_create (refc,MAX_CTRL_OUTB)) {
    return (FALSE);
  }
  if (!list_create (refd,MAX_DATA_OUTB)) {
    return (FALSE);
  }
  r = -1;
  outf = STDOUT_FILENO;

  if (outf >= 0) {
    r = fstat (outf,&outstat);
  }
  if (r != 0) {
    warn (LERR,"Cannot open",EOUT,5,outf,r);
    warn (LERR,strerror(errno),EOUT,5,1,errno);
    return (FALSE);
  }
  r = fcntl(outf, F_GETFL);
  if (r >= 0) {
    r = fcntl(outf, F_SETFL, r | O_NONBLOCK);
  }
  if (r < 0) {
    warn (LERR,"Cannot fcntl",EOUT,5,2,errno);
    return (FALSE);
  }
  if (!S_ISREG (outstat.st_mode)) {
    timed_io = TRUE;
  }
  return (outf >= 0);
}

/* Calculate the free space in the output buffer
 * Return: Number of bytes
 */
static int output_free (void)
{
  register int r;
  r = list_full (refc) ? 0 : list_free (refd);
  warn (LDEB,"Free",EOUT,1,r,list_free (refd));
  return (r);
}

/* Determine whether there is probably enough space in the output buffer
 * Base for this decision is a guessed expected size "next_size", which
 * will be adapted by the functions that finally know the correct value.
 * Thus it is possible that processing data is started and then postponed
 * due to lack of space (see function output_pushdata).
 * Return: TRUE if probably enough space, FALSE otherwise
 */
boolean output_acceptable (void)
{
  warn (LDEB,"Acceptable",EOUT,2,0,next_size);
  return (output_free () >= next_size);
}

/* Reserve space in the output buffer.
 * size is the number of bytes wanted.
 * if timed==TRUE, then push is a time stamp (millisec),
 * otherwise a time stamp for greedy processing is calculated locally.
 * Precondition: size>0
 * Return: Pointer to data block, if available, NULL otherwise
 */
byte *output_pushdata (int size,
    boolean timed,
    int push)
{
  ctrl_buffer *c;
  byte *d;
  next_size = mmax (size,HIGHWATER_OUT);
  if (size > list_free (refd)) {
    warn (LDEB,"Pushdata (Postpone)",EOUT,3,1,size);
    return (NULL);
  }
  if (size > list_freeinend (refd)) {
    refd.in = 0;
  }
  if (size > list_free (refd)) {
    warn (LDEB,"Pushdata (Postpone)",EOUT,3,2,size);
    return (NULL);
  }
  warn (LDEB,"Pushdata",EOUT,3,3,size);
  warn (LDEB,"Pushdata Index",EOUT,3,4,refd.in);
  c = &refc.ptr[refc.in];
  c->index = refd.in;
  c->length = size;
  c->msecpush = timed
              ? push
              : (msec_now () - (outtrigger ? outdelta : trigger_msec_output));
  list_incr (refc.in,refc,1);
  d = &refd.ptr[refd.in];
  list_incr (refd.in,refd,size);
  if (statistics_msec > 0) {
    int tmp;
    tmp = list_size (refd);
    if (tmp > statistics_refd_max) {
      statistics_refd_max = tmp;
    }
    if (timed) {
      tmp = push + outdelta - msec_now ();
      if (tmp > statistics_time_max) {
        statistics_time_max = tmp;
      }
    }
  }
  next_size = HIGHWATER_OUT;
  return (d);
}

/* Set trigger timing value.
 */
void output_settriggertiming (t_msec time)
{
  trigger_msec_output = time;
}

/* Check whether data is available to be written to stdout.
 * If so, set the poll struct accordingly.
 * Check the time stamp and set the timeout^ accordingly.
 * Return: TRUE, if data is available to be written immediately,
 *         FALSE otherwise.
 */ 
boolean output_available (unsigned int *nfds,
    struct pollfd *ufds,
    t_msec *timeout)
{
  t_msec t, now;
  boolean avail;
  t = -1;
  avail = FALSE;
  now = msec_now ();
  if (list_empty (refc)) {
    warn (LDEB,"Available Empty",EOUT,4,1,0);
    /* outtrigger = FALSE; */
    /* and discontinuity things */
  } else {
    t = refc.ptr[refc.out].msecpush - now;
    if (!outtrigger) {
      if (list_partialfull (refd)
       || (-t >= trigger_msec_output)) {
        outdelta = -t;
        warn (LDEB,"Available Trigger",EOUT,4,2,outdelta);
        outtrigger = TRUE;
      }
    }
    if (outtrigger) {
      t += outdelta;
      if (t <= 0) {
        warn (LDEB,"Available",EOUT,4,3,t);
        ufds->fd = outf;
        ufds->events = POLLOUT;
        *nfds += 1;
        avail = TRUE;
        t = -1;
      }
    } else {
      t += trigger_msec_output;
    }
  }
  if ((statistics_msec > 0) && (statistics_load > 0)) {
    t_msec s;
    s = statistics_next - now;
    if (s < 0) {
      s = 0;
    }
    if ((t < 0) || (s < t)) {
      t = s;
    }
  }
  *timeout = t;
  return (avail);
}

/* Set statistics generation frequency, time=0 to switch off.
 */
void output_set_statistics (t_msec time)
{
  int tmp;
  statistics_msec = time;
  if (time > 0) {
    statistics_next = msec_now () + time;
  }
  statistics_load = 0;
  statistics_bursts = 0;
  statistics_refd_min = statistics_refd_max = list_size (refd);
  statistics_burst_min = statistics_burst_max = 0;
  statistics_time_min = statistics_time_max =
      list_empty (refc) ? 0 :
        (tmp = refc.in,
         refc.ptr[list_incr (tmp,refc,-1)].msecpush + outdelta - msec_now ()); 
}

/* Generate statistics, if the time is right for this.
 */
void output_gen_statistics (void)
{
  if (statistics_msec > 0) {
    t_msec now;
    int tmp;
    now = msec_now ();
    if (now >= statistics_next) {
      fprintf (stderr, "Stat: now:%8d out:%8d/%4d buf:%8d..%8d time:%6d..%6d burst:%6d..%6d\n",
          now, statistics_load, statistics_bursts,
          statistics_refd_min, statistics_refd_max,
          statistics_time_min, statistics_time_max,
          statistics_burst_min, statistics_burst_max);
      statistics_load = 0;
      statistics_bursts = 0;
      statistics_refd_min = statistics_refd_max = list_size (refd);
      statistics_burst_min = statistics_burst_max;
      statistics_burst_max = 0;
      statistics_time_min = statistics_time_max =
          list_empty (refc) ? 0 :
            (tmp = refc.in,
             refc.ptr[list_incr (tmp,refc,-1)].msecpush + outdelta - now),
      statistics_next = now + statistics_msec;
    }
  }
}

/* Prepare to finish.
 * provoke output buffer flush
 */
void output_finish (void)
{
  outtrigger = TRUE;
  output_set_statistics (0);
}

/* Write some data to stdout from the output buffer.
 * Write as much data as available unwrapped and with identical time stamp.
 * Precondition: poll has stated data or error for stdout
 */
void output_something (boolean writeable)
{
  t_msec msec;
  int i, l, o;
  o = refc.out;
  msec = refc.ptr[o].msecpush;
  i = refc.ptr[o].index;
  l = 0;
  if (writeable) {
    do {
      l += refc.ptr[o].length;
    } while ((l < MAX_WRITE_OUT)
          && (list_incr (o,refc,1) != refc.in)
          && (refc.ptr[o].msecpush == msec)
          && (refc.ptr[o].index == (i+l))); /* and < MAX_WRITE_OUT */
    warn (LDEB,"Something",EOUT,0,1,l);
    if (statistics_msec > 0) {
      if (l < statistics_burst_min) {
        statistics_burst_min = l;
      }
      if (l > statistics_burst_max) {
        statistics_burst_max = l;
      }
    }
    l = write (outf,&refd.ptr[i],l);
  }
  warn (LDEB,"Some Written",EOUT,0,2,l);
  if (l > 0) {
    statistics_load += l;
    statistics_bursts += 1;
    o = refc.out;
    do {
      if (l < refc.ptr[o].length) {
        refc.ptr[o].length -= l;
        refc.ptr[o].index += l;
        warn (LDEB,"Some Left",EOUT,0,3,refc.ptr[o].length);
        l = 0;
      } else {
        warn (LDEB,"Some Done",EOUT,0,4,o);
        l -= refc.ptr[o].length;
        refc.out = list_incr (o,refc,1);
      }
    } while (l > 0);
    if (list_empty (refc)) {
      refd.out = refd.in = 0;
    } else {
      refd.out = refc.ptr[o].index;
    }
    if (statistics_msec > 0) {
      int tmp, tin;
      tmp = list_size (refd);
      if (tmp < statistics_refd_min) {
        statistics_refd_min = tmp;
      }
      tmp = list_empty (refc) ? 0 :
        (tin = refc.in,
         refc.ptr[list_incr (tin,refc,-1)].msecpush + outdelta - msec);
      if (tmp < statistics_time_min) {
        statistics_time_min = tmp;
      }
    }
  }
}


LinuxTV legacy CVS <linuxtv.org/cvs>