/*
 *  demuxer.c
 *
 *  Copyright (C) Thomas Östreich - June 2001
 *
 *  This file is part of transcode, a video stream processing tool
 *      
 *  transcode 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, or (at your option)
 *  any later version.
 *   
 *  transcode 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 GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 */

#include "transcode.h"

#include <sys/errno.h>

#include "ioaux.h"
#include "aux_pes.h"
#include "seqinfo.h"
#include "demuxer.h"
#include "packets.h"

static int demux_mode=TC_DEMUX_SEQ_ADJUST;

int gop, gop_pts, gop_cnt;

/* ------------------------------------------------------------ 
 *
 * demuxer / synchronization thread 
 *
 * ------------------------------------------------------------*/

void tcdemux_thread(info_t *ipipe)
{

    int k, id=0, zz=0;

    int j, i, bytes;
    char *buffer=NULL;

    int payload_id=0, select=PACKAGE_ALL;

    double pts=0.0f, ref_pts=0.0f, resync_pts=-1.0f, pts_diff=0.0f, track_initial_pts=0.0f;
    double av_fine_pts1=-1.0f, av_fine_pts2=-1.0f, av_fine_diff=0.0f;

    uint32_t frame_based_lpts=0;
    
    int unit_seek=0, unit, track=0, is_track=0; 

    int resync_seq1=0, resync_seq2=INT_MAX, seq_dump, seq_seek;
    int keep_seq = 0;
    int hard_fps = 0;

    int has_pts_dts=0, demux_video=0, demux_audio=0;

    int flag_flush        = 0;
    int flag_force        = 0; 
    int flag_eos          = 0; 
    int flag_has_audio    = 0;
    int flag_append_audio = 0;
    int flag_notify       = 1;
    int flag_avsync       = 1; 
    int flag_skip         = 0; 
    int flag_sync_reset   = 0;
    int flag_sync_active  = 0;
    int flag_loop_all     = 0;
    int flag_av_fine_tune = 0;
    int flag_rewrite_scr  = 0;
    int flag_field_encoded= 0; 

    //for demux_mode=2
    int seq_picture_ctr=0, pack_picture_ctr=0, sequence_ctr=0, packet_ctr=0;
    
    char buf[256], *logfile;

    unsigned long i_pts, i_dts;
    
    unsigned int packet_size=VOB_PACKET_SIZE;

    seq_list_t *ptr=NULL;

    double fps;

    // allocate space
    if((buffer = (char *)calloc(1, packet_size))==NULL) {
      perror("out of memory");
      exit(1);
    }
    
    // copy info parameter to local variables
    
    unit_seek   = ipipe->ps_unit;
    resync_seq1 = ipipe->ps_seq1;
    resync_seq2 = ipipe->ps_seq2;
    track       = ipipe->track;
    
    //map track on substream id

    switch(ipipe->codec) {

    case TC_CODEC_SUB: 
      track+=0x20;
      flag_rewrite_scr=1;
      break;
    case TC_CODEC_AC3: 
      track+=0x80;
      break;
    case TC_CODEC_PCM: 
      track+=0xA0;
      break;
    case TC_CODEC_MP3: 
      track+=0xC0;
      break;
    case TC_CODEC_MPEG2:
      // for MPEG2 video, use this substream id for sync adjustment
      track=ipipe->subid;
      demux_video=1;
      break;
    }

    //0.6.0pre4
    if(demux_video==0) {
	
	if(ipipe->demux == TC_DEMUX_SEQ_FSYNC)
	    ipipe->demux=TC_DEMUX_SEQ_ADJUST;
	
	if(ipipe->demux == TC_DEMUX_SEQ_FSYNC2)
	    ipipe->demux=TC_DEMUX_SEQ_ADJUST2;
	
	//substream synchonized with video in "force frame rate" mode
	demux_audio=1;
    }
    
    if(ipipe->demux == TC_DEMUX_SEQ_FSYNC || ipipe->demux == TC_DEMUX_SEQ_FSYNC2 || ipipe->demux == TC_DEMUX_SEQ_LIST) {
	
      //allocate buffer
      if(flush_buffer_init(ipipe->fd_out, ipipe->verbose)<0) {
	fprintf(stderr, "[%s] flush buffer facility init failed\n", __FILE__);
	exit(1);
      }
      
      //need to open the logfile
      if(seq_init(ipipe->name, ipipe->fd_log, ipipe->fps, ipipe->verbose)<0) {
	fprintf(stderr, "[%s] sync mode init failed\n", __FILE__);
	exit(1);
      }
    }

    
    //new default behaving:
    //    if(unit_seek==0 && resync_seq1==0 && resync_seq2 == INT_MAX) flag_loop_all=1;
    //changes 0.6.0pre3:
    //tcprobe selects start unit --> switch to flag_loop_all always true
    // for any given unit
    if(resync_seq1==0 && resync_seq2 == INT_MAX) flag_loop_all=1;

    demux_mode  = ipipe->demux;
    select      = ipipe->select;
    fps         = ipipe->fps;
    logfile     = ipipe->name;
    keep_seq    = ipipe->keep_seq;
    hard_fps    = ipipe->hard_fps_flag;
    
    j=0;  //packet counter
    i=0;  //skipped packets counter
    k=0;  //unit counter
    
    // will be switched on as soon start of sequences to flush is reached 
    flag_flush=0;
    
    flag_notify=1;
    
    flag_avsync=0;
    flag_append_audio=0;

    if(keep_seq) flag_sync_active=1;

    unit=unit_seek;
    
    seq_seek = resync_seq1;
    seq_dump = resync_seq2 - resync_seq1;
    
    ++unit_seek;
    ++seq_seek;

    if(!flag_loop_all) {
      fprintf(stderr,"(%s) seeking to sequence %d:%d ...\n", __FILE__, unit, resync_seq1);
    }
    
    for(;;) {

      /* ------------------------------------------------------------ 
       *
       * (I) read a 2048k block
       *
       * ------------------------------------------------------------*/
      
      if((bytes=p_read(ipipe->fd_in, buffer, packet_size)) != packet_size) {    
	
	//program end code?
	if(bytes==4) {
	  if(scan_pack_header(buffer, MPEG_PROGRAM_END_CODE)) {
	    if(ipipe->verbose & TC_DEBUG) fprintf(stderr,"[%s] (pid=%d) program stream end code detected\n", __FILE__, getpid());
	    break;
	  }
	}
	
	if(bytes) fprintf(stderr, "(%s) invalid program stream packet size (%d/%d)\n", __FILE__, bytes, packet_size);
	
	break;
      }     

      // do not make any tests in pass-through mode
      if(demux_mode==TC_DEMUX_OFF) goto flush_packet;

      /* ------------------------------------------------------------ 
       *
       * (II) packet header ok?
       *
       * ------------------------------------------------------------*/

      
      if(!scan_pack_header(buffer, TC_MAGIC_VOB)) {
	
	if(flag_notify && (ipipe->verbose & TC_DEBUG)) fprintf(stderr,"[%s] (pid=%d) invalid packet header detected\n", __FILE__, getpid());
	
	// something else?
	
	if(scan_pack_header(buffer, MPEG_VIDEO) | scan_pack_header(buffer, MPEG_AUDIO)) {
	  
	    if(ipipe->verbose & TC_STATS) 
		fprintf(stderr, "[%s] (pid=%d) MPEG system stream detected\n", __FILE__, getpid());
	    
	    if(scan_pack_header(buffer, MPEG_VIDEO)) payload_id=PACKAGE_VIDEO;
	    if(scan_pack_header(buffer, MPEG_AUDIO)) payload_id=PACKAGE_AUDIO_MP3;
	    
	    // no further processing
	    goto flush_packet;
	} else {
	    
	    fprintf(stderr, "[%s] (pid=%d) '0x%02x%02x%02x%02x' not yet supported\n", __FILE__, getpid(), buffer[0] & 0xff, buffer[1] & 0xff, buffer[2] & 0xff, buffer[3] & 0xff);
	    break;
	}	      
      } else {
	  
	//MPEG1?
	if ((buffer[4] & 0xf0) == 0x20) {
	  
	  payload_id=PACKAGE_MPEG1;
	  flag_flush=1;
	  
	  if(ipipe->verbose & TC_STATS)
	    fprintf(stderr, "[%s] (pid=%d) MPEG-1 video stream detected\n", __FILE__, getpid());
	  
	  // no further processing
	  goto flush_packet;
	}
      }
      
      
      /* ------------------------------------------------------------ 
       *
       * (III) analyze packet contents
       *
       * ------------------------------------------------------------*/

	
      // proceed with a valid package, assume defaults
      
      flag_skip=0;          //do not skip
      has_pts_dts=0;        //no pts_dts stamp
      payload_id=0;         //payload unknown
      flag_sync_reset=0;    //no reset of syncinfo
      
      id = buffer[17] & 0xff;  //payload id byte
	
      //MPEG 2?
      if ((buffer[4] & 0xc0) == 0x40) {
	
	// do not change any flags
	
	if(ipipe->verbose & TC_STATS) {
	  //display info only once
	  fprintf(stderr, "[%s] (pid=%d) MPEG-2 video stream detected\n", __FILE__, getpid());
	}
      } else { 
	
	//MPEG1
	if ((buffer[4] & 0xf0) == 0x20) {
	  
	  payload_id=PACKAGE_MPEG1;
	  
	  if(ipipe->verbose & TC_STATS) {
	    //display info only once
	    fprintf(stderr, "[%s] (pid=%d) MPEG-1 video stream detected\n", __FILE__, getpid());
	  }
	} else {
	  
	  payload_id=PACKAGE_PASS;
	  
	  if(ipipe->verbose & TC_DEBUG)
	    fprintf(stderr, "[%s] (pid=%d) unknown stream packet id detected\n", __FILE__, getpid());
	}
	
	//flush all MPEG1 stuff
	goto flush_packet;
      }
      
      /* ------------------------------------------------------------ 
       *
       * (IV) audio payload
       *
       * ------------------------------------------------------------*/

      // check payload id
      // process this audio packet?
      
      // sync to AC3 audio mode? 
      if(id == P_ID_AC3) payload_id = PACKAGE_PRIVATE_STREAM;
	 
      // sync to MP3 audio mode?
      if(id >= 0xc0 && id <= 0xdf) payload_id = PACKAGE_AUDIO_MP3;

      // are we dealing with the right track?
      // check here:

      is_track=0;
      
      if(payload_id == PACKAGE_PRIVATE_STREAM) {
	
	//position of track code
	uint8_t *_buf=buffer+14;
	uint8_t *_tmp=_buf + 9 + _buf[8];
	
	is_track = ((*_tmp) == track) ? 1:0;
	
	if(ipipe->verbose & TC_STATS) 
	  fprintf(stderr, "substream [0x%x] %d\n", *_tmp, is_track);
	
	if(is_track==0) {
	  flag_skip=1;  //drop this packet
	} else {
	  flag_skip=0;
	  goto sync_track;
	}      
      }
      
      if(payload_id & PACKAGE_AUDIO_MP3) {
	
	is_track = ((id) == track) ? 1:0;
	if(is_track==0) flag_skip=1;  //drop this packet
	
	if(ipipe->verbose & TC_STATS) 
	  fprintf(stderr, "MPEG audio track [0x%x] %d\n", id, is_track);
      }
      
    sync_track:
      
      if(is_track) {
	
	
	if(flag_sync_active==0) {
	  
	  //first valid audio packet!
	  
	  // get pts time stamp:
	  tc_memcpy(buf, &buffer[20], 16);
	  has_pts_dts=get_pts_dts(buf, &i_pts, &i_dts);
	  
	  if(has_pts_dts) {
	    track_initial_pts=(double)i_pts/90000.;
	  } else {
	    //fallback to scr time stamp:
	    tc_memcpy(buf, &buffer[4], 6);
	    track_initial_pts = read_time_stamp(buf);
	  }
	  
	  if(resync_pts<0) {
	    pts_diff = track_initial_pts;
	  } else {
	    pts_diff = track_initial_pts - resync_pts;	  
	  }
	  
	  //pts_diff<0 is OK, the packets will be dropped to establish sync
	  //pts_diff>0 is not so simple, since we need to drop video 
	  //packets, already submitted to flush buffer
	  
	  //enable sync mode, check for PTS giant leap????
	  if(pts_diff < TC_DEMUX_MIN_PTS || pts_diff > TC_DEMUX_CRIT_PTS) 
	    flag_sync_active = 1;
	  
	  //0.6.0pre5: frame dropping handled by transcode
	  flag_sync_active=1;
	  
	  //unless this flag is on, the video sequence is dropped
	  //in TC_DEMUX_SEQ_FSYNC mode
	}
	
	
	// sync now, if requested:
	
	if(!unit_seek) flag_has_audio=1; //unit has audio packets
	
	
	// need to find the time difference of two audio packets
	// for fine-tuning AV sync.
	
	if(flag_av_fine_tune == 0) {
	  // get pts time stamp:
	  tc_memcpy(buf, &buffer[20], 16);
	  has_pts_dts=get_pts_dts(buf, &i_pts, &i_dts);
	  
	  if(av_fine_pts1<0) {
	    av_fine_pts1 = (double)i_pts/90000.;
	  } else {
	    av_fine_pts2 = (double)i_pts/90000.;
	    flag_av_fine_tune=1;
	  }
	  
	  //diff:
	  if(flag_av_fine_tune==1) {
	    av_fine_diff=av_fine_pts2-av_fine_pts1;
	    if(ipipe->verbose & TC_DEBUG) fprintf(stderr, "[%s] AV fine-tuning: %d ms\n", __FILE__, (int)(av_fine_diff*1000));
	  }
	  
	  //sanity check:
	  if(av_fine_diff<0) av_fine_diff=0.0f;
	  
	}
	
	//Pre-processing: check if we need to re-sync?

	if(demux_mode == TC_DEMUX_SEQ_FSYNC2 || demux_mode == TC_DEMUX_SEQ_ADJUST2) {
	    //new demux modes let transcode handle audio sync shift
	    flag_avsync=0;
	    flag_skip=0;
	}
	
	if(flag_avsync) {
		
	  // get pts time stamp:
	  tc_memcpy(buf, &buffer[20], 16);
	  has_pts_dts=get_pts_dts(buf, &i_pts, &i_dts);

	  if(has_pts_dts) {
	    pts=(double)i_pts/90000.;
	  } else {
	    //fallback to scr time stamp:
	    tc_memcpy(buf, &buffer[4], 6);
	    pts = read_time_stamp(buf);
	  }
		
	  pts_diff = pts - resync_pts;

	  //correction 
	  //FIXME
	  pts_diff += av_fine_diff;
	  
	  if(pts_diff<0) {
	    flag_skip=1;
		    
	    if(ipipe->verbose & TC_DEBUG) fprintf(stderr, "[%s] (pid=%d) audio packet %06d for PU [%d] skipped (%.4f)\n", __FILE__, getpid(), j, ((k==0)? 0:k-1), pts-resync_pts);
	  } else {
	    //reset
	    flag_skip=0;
	    flag_avsync=0;
	    if(ipipe->verbose) fprintf(stderr, "[%s] (pid=%d) AV sync established for PU [%d] at PTS=%.4f (%.4f)\n", __FILE__, getpid(), k-1, pts, pts-resync_pts);
	  }
	} 

	//Post-processing: more audio packets?
	    
	if(flag_append_audio) {
		
	  //need to flush a few audio packets, if video ahead
		
	  // get pts time stamp:
	  tc_memcpy(buf, &buffer[20], 16);
	  has_pts_dts=get_pts_dts(buf, &i_pts, &i_dts);
	  
	  if(has_pts_dts) {
	    pts=(double)i_pts/90000.;
	  } else {
	    //fallback to scr time stamp:
	    tc_memcpy(buf, &buffer[4], 6);
	    pts = read_time_stamp(buf);
	  }
	  
	  pts_diff = pts - resync_pts;
	  
	  if(pts_diff<0) {
	    
	    //append this packet
	    flag_skip=0;
	    
	    if(ipipe->verbose & TC_DEBUG) fprintf(stderr, "[%s] (pid=%d) audio packet %06d for PU [%d] appended (%.4f)\n", __FILE__, getpid(), j, ((k==0)? 0:k-1), pts-resync_pts);
	  } else {
	    //abort - all done
	    flag_eos=1;
	    if(ipipe->verbose) fprintf(stderr, "[%s] (pid=%d) AV sync abandoned for PU [%d] at PTS=%.4f (%.4f)\n", __FILE__, getpid(), k-1, pts, pts-resync_pts);
	  }
	}
      }

      //only go for audio in this phase
      if(flag_append_audio) goto flush_packet;

      /* ------------------------------------------------------------ 
       *
       * (V) misc payload
       *
       * ------------------------------------------------------------*/
      
      
      if(id == P_ID_PROG || id == P_ID_PADD)  {
	
	payload_id=PACKAGE_NAV;
	
	// get pts time stamp:
	tc_memcpy(buf, &buffer[20], 16);
	has_pts_dts=get_pts_dts(buf, &i_pts, &i_dts);
	
	if(has_pts_dts) {
	  pts=(double)i_pts/90000.;
	} else {
	  //fallback to scr time stamp:
	  tc_memcpy(buf, &buffer[4], 6);
	  pts = read_time_stamp(buf);
	}
	
	//do not dump this packet
	flag_skip=1;
      }
      
      /* ------------------------------------------------------------ 
       *
       * (VI) video payload
       *
       * ------------------------------------------------------------*/


      if(id == P_ID_MPEG) {
	
	payload_id = PACKAGE_VIDEO;
	
	// get pts time stamp:
	tc_memcpy(buf, &buffer[4], 6);
	pts = read_time_stamp(buf);
	
	//read full packet header 
	tc_memcpy(buf, &buffer[20], 16);
	has_pts_dts=get_pts_dts(buf, &i_pts, &i_dts);

	//need frame/field encoding information
	zz=scan_pack_ext(buffer);
	if(zz>0) flag_field_encoded=zz;

	//need precise number of pics in this sequence
	pack_picture_ctr = scan_pack_pics(buffer);

	seq_picture_ctr += pack_picture_ctr;

	frame_based_lpts = (seq_picture_ctr-1);

	//need this pack for subtitle PTS information
	if(flag_rewrite_scr && has_pts_dts) flag_force=1;

	// only process packets with pts/dts time stamp, since
	// they (all?) have a sequence start code

	if(has_pts_dts) {

	  if (ipipe->verbose & TC_STATS) fprintf(stderr, "[%s] (pid=%d) PTS-DTS detected in packet [%06d]\n", __FILE__, getpid(), j);
	  
	  // default first(=0) unit ?
	  if(k==0) {
	    
	    --unit_seek;
	    flag_sync_reset=1;

	    if(ipipe->verbose & TC_DEBUG) fprintf(stderr, "[%s] (pid=%d) MPEG sequence start code in packet %06d for PU [0]\n", __FILE__, getpid(), j);
	    
	    k++;			    
	  } 
	  
	  
	  if(pts<ref_pts) {
	    
	    --unit_seek;
	    flag_sync_reset=1;

	    // past next unit - abort
	    // or process all following units?
	    
	    if(unit_seek<0 && flag_loop_all==0) flag_eos=1;
	  
	    //experimental: try to resync
	    //flag_avsync = 1;

	    if(ipipe->verbose & TC_DEBUG) fprintf(stderr, "[%s] (pid=%d) PTS reset (%.3f->%.3f) in packet %06d for PU [%d]\n", __FILE__, getpid(), ref_pts, pts, j, k);
	  
	    k++;
	  }
	  
	  
	  // only decrement sequence counter in right unit, i.e.
	  // unit_seek=0;
	  if(!unit_seek) --seq_seek;
	  
	  // flush all sequences until the end of the unit
	  // or end of stream
	  // or resync_seq2 is reached
	  
	  if(seq_seek==0) {
	    
	    //re read packet header 
	    tc_memcpy(buf, &buffer[20], 16);
	    get_pts_dts(buf, &i_pts, &i_dts);

	    resync_pts = (double) i_pts / 90000;

	    if(!flag_flush) { 
	      
	      // need to dump requested seq_dump sequences
	      seq_seek=seq_dump;
	      flag_flush = 1;
	      
	      flag_avsync = 1;
	      
	      // may be a useful info for the user
	      if(ipipe->verbose) fprintf(stderr, "[%s] (pid=%d) processing PU [%d], on at PTS=%.4f sec\n", __FILE__, getpid(), k-1, resync_pts);
	      
	    } else {
				// finished, all sequences flushed, switch
			        // to audio packets post processing
	      flag_append_audio = 1;
	      flag_skip = 1;  //flush mode on, but do not write this one
	      
	      // may be a useful info for the user
	      if(ipipe->verbose) fprintf(stderr, "[%s] (pid=%d) processing PU [%d], off at PTS=%.4f sec\n", __FILE__, getpid(), k-1, resync_pts);
	    }
	  }
	  
	  //---------------------------------------------------
	  //gather information on the sequences before flushing 
	  //---------------------------------------------------

	  if((demux_mode == TC_DEMUX_SEQ_FSYNC || demux_mode == TC_DEMUX_SEQ_FSYNC2) && flag_flush) {
	    
	    ptr = seq_register(sequence_ctr);
	    
	    zz=(flag_field_encoded==3)?(seq_picture_ctr-pack_picture_ctr):
		(seq_picture_ctr-pack_picture_ctr)/2;

	    if(sequence_ctr)  seq_update(ptr->prev, i_pts, zz, packet_ctr, flag_sync_active, hard_fps);
	    
	    //init sequence information structure for current sequence
	    
	    ptr->pts = i_pts;
	    ptr->dts = i_dts;
	    ptr->pics_first_packet = pack_picture_ctr;
	    ptr->sync_reset = flag_sync_reset;
	    
	    //reset/update
	    seq_picture_ctr = 0;
	    packet_ctr = 0;
	    ++sequence_ctr;

	    //shift resync_pts, since sequence is dropped
	    if(flag_sync_active==0) resync_pts=(double) i_pts / 90000;
	    
	  } //end TC_DEMUX_SEQ_FSYNC mode
	  
	  //---------------------------------------------------
	  //print out sequence information for frame navigation
	  //---------------------------------------------------

	  if((demux_mode == TC_DEMUX_SEQ_LIST) && flag_flush) {
	    
	    ptr = seq_register(sequence_ctr);
	    
	    zz=(flag_field_encoded==3)?(seq_picture_ctr-pack_picture_ctr):
		(seq_picture_ctr-pack_picture_ctr)/2;

	    if(sequence_ctr)  seq_list(ptr->prev, i_pts, zz, packet_ctr, flag_sync_active);
	    
	    //init sequence information structure for current sequence
	    
	    ptr->pts = i_pts;
	    ptr->dts = i_dts;
	    ptr->pics_first_packet = pack_picture_ctr;
	    ptr->sync_reset = flag_sync_reset;
	    ptr->packet_ctr=j;

	    //reset/update
	    seq_picture_ctr = 0;
	    packet_ctr = 0;
	    ++sequence_ctr;

	    //shift resync_pts, since sequence is dropped
	    if(flag_sync_active==0) resync_pts=(double) i_pts / 90000;
	    
	  } //end TC_DEMUX_SEQ_LIST mode

	  //reset sync pts, if no audio/substream has been found (0.6.0pre4)
	  if(flag_sync_active==0 && demux_audio) {
	    resync_pts=(double) i_pts / 90000;
	    if(ipipe->verbose & TC_DEBUG) fprintf(stderr, "(%s) new initial PTS=%f\n", __FILE__, resync_pts);
	  }
	  
	}// PTS-DTS flag yes
	
	ref_pts=pts;
	
      }// MPEG video packet
      
      /* ------------------------------------------------------------ 
       *
       * (VII) evaluate scan results - flush packet
       *
       * ------------------------------------------------------------*/      
      
    flush_packet:

      
      if(ipipe->verbose & TC_STATS)
	fprintf(stderr, "INFO: j=%05d, i=%05d, skip=%d, flush=%d, force=%d, pay=%3d, sid=0x%02x, eos=%d\n", j, i, flag_skip, flag_flush, flag_force, payload_id, id, flag_eos); 

      //need to rewrite SCR pack header entry based on 
      //frame_based_pts information for transcode:
      if(flag_rewrite_scr) scr_rewrite(&buffer[4], ((flag_field_encoded==3)?frame_based_lpts:frame_based_lpts/2));
      
      //flush here:
      
      switch(demux_mode) {
	
      case TC_DEMUX_DEBUG:
	if((flag_flush && !flag_skip && (payload_id & select)) 
	   || flag_force) scan_pack_payload(buffer, j, ipipe->verbose);
	break;
	
      case TC_DEMUX_DEBUG_ALL:
	scan_pack_payload(buffer, j, ipipe->verbose);
	break;
	
      case TC_DEMUX_SEQ_FSYNC:
      case TC_DEMUX_SEQ_FSYNC2:

	if((flag_flush && !flag_skip && (payload_id & select)) || flag_force) {
	  
	  //count current sequence packets to be flushed
	  ++packet_ctr;
	  
	  if(ipipe->verbose & TC_STATS) fprintf(stderr,"flushing packet (%d/%d)\n", sequence_ctr, j);
	  
	  if(flush_buffer_write(ipipe->fd_out, buffer, packet_size) != packet_size) {
	    perror("(" __FILE__ ") write program stream packet");
	    exit(1);
	  } 
	  
	  //reset
	  flag_force=0;
	}
	
	break;
	
      case TC_DEMUX_SEQ_ADJUST:
      case TC_DEMUX_SEQ_ADJUST2:
	
	if((flag_flush && !flag_skip && (payload_id & select)) || flag_force) {
	  
	  if(p_write(ipipe->fd_out, buffer, packet_size) != packet_size) {
	    perror("(" __FILE__ ") write program stream packet");
	    exit(1);
	  } 
	  
	  //reset
	  flag_force=0;
	  
	  if(ipipe->verbose & TC_STATS) fprintf(stderr, "writing packet %d\n", j);
	  
	} else {
	 
	  ++i;
	  if(ipipe->verbose & TC_STATS) fprintf(stderr, "skipping packet %d\n", j);
	}
	
	break;
	
	
      case TC_DEMUX_SEQ_LIST:

	//count packs
	++packet_ctr;
	 
	// nothing to do
	break;
	
      case TC_DEMUX_OFF:
	
	if(p_write(ipipe->fd_out, buffer, packet_size) != packet_size) {
	  perror("(" __FILE__ ") write program stream packet");
	  exit(1);
	} 
	
	if(ipipe->verbose & TC_STATS) fprintf(stderr, "writing packet %d\n", j);
	
	break;
      }
      
      //aborting?
      if(flag_eos) break;

      //total packs (2k each) counter
      ++j;
      
    } // process next packet/block
    
    if(ipipe->verbose & TC_SYNC) fprintf(stderr, "EOS - flushing packet buffer\n");

    //post processing

    if(demux_mode == TC_DEMUX_SEQ_FSYNC || demux_mode == TC_DEMUX_SEQ_FSYNC2) {
      seq_close();
      flush_buffer_close();
    }
    
    if(demux_mode == TC_DEMUX_SEQ_LIST) {
   
      ptr = seq_register(sequence_ctr);
      
      zz=(flag_field_encoded==3)?(seq_picture_ctr-pack_picture_ctr):
	  (seq_picture_ctr-pack_picture_ctr)/2;

      if(ptr!=NULL && ptr->id)  seq_list(ptr->prev, ref_pts, zz, packet_ctr, flag_sync_active);

      fflush(stdout);

      //summary
      seq_list_frames();

      seq_close();
      flush_buffer_close();

    }

    //summary
    if(ipipe->verbose & TC_DEBUG) fprintf(stderr, "[%s] (pid=%d) %d/%d packets discarded\n", __FILE__, getpid(), i, j);
    
    if(buffer!=NULL) free(buffer);
    
    return;
}

