/*
 *  demux_pass.c
 *
 *  Copyright (C) Thomas Oestreich - May 2002
 *
 *  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 "libtc/libtc.h"
#include "tcinfo.h"

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


int gop, gop_pts, gop_cnt;

/* if you listen carefully, then you can hear the desesperate
 * whisper of this code calling for a rewrite. Or for a redesign.
 * Or both. --  FR
 */

/* ------------------------------------------------------------
 *
 * demuxer pass_through mode
 *
 * ------------------------------------------------------------*/

void tcdemux_pass_through(info_t *ipipe, int *pass, int npass)
{
    int bytes, p, id = 0;
    int j = 0; // skipped packets counter
    char *buffer = NULL;
    int payload_id = 0, select = PACKAGE_ALL;
    int unit, unit_seek = 0, track = 0, is_track = 0;
    int resync_seq1       = 0;
    int resync_seq2       = INT_MAX;
    int seq_dump          = 0;
    int seq_seek          = 0;
    int keep_seq          = 0;
    int has_pts_dts       = 0;
    int flag_flush        = 0;
    // will be switched on as soon start of sequences to flush is reached
    int flag_eos          = 0;
    int flag_notify       = 1;
    int flag_skip         = 0;
    int flag_sync_reset   = 0;
    int flag_sync_active  = 0;
    const char *logfile;
    unsigned int pkt_size = VOB_PACKET_SIZE;
    double fps;

    // allocate space
    buffer = tc_zalloc(pkt_size);
    if (!buffer) {
        tc_log_perror(__FILE__, "out of memory");
        exit(1);
    }

    // copy info parameter to local variables
    verbose     = ipipe->verbose;
    unit_seek   = ipipe->ps_unit;
    resync_seq1 = ipipe->ps_seq1;
    resync_seq2 = ipipe->ps_seq2;
    track       = ipipe->track;
    select      = ipipe->select;
    fps         = ipipe->fps;
    logfile     = ipipe->name;
    keep_seq    = ipipe->keep_seq;

    if (keep_seq)
        flag_sync_active=1;

    unit = unit_seek;

    seq_seek = resync_seq1;
    seq_dump = resync_seq2 - resync_seq1;

    ++unit_seek;
    ++seq_seek;

    for (p = 0; p < npass; p++)
        tc_log_msg(__FILE__, "pass[%i]=0x%x", p, pass[p]);

    for (;;) {
        /* ------------------------------------------------------------
         *
         * (I) read a 2048k block
         *
         * ------------------------------------------------------------*/

        bytes = tc_pread(ipipe->fd_in, buffer, pkt_size);
        if (bytes != pkt_size) {
            // program end code?
            if (bytes == 4) {
                if (scan_pack_header(buffer, MPEG_PROGRAM_END_CODE)) {
                    if (verbose & TC_DEBUG)
                        tc_log_msg(__FILE__,
                                   "(pid=%i) program stream end code found",
                                   getpid());
                    break;
                }
            }

            if (bytes)
                tc_log_warn(__FILE__,
                            "invalid program stream packet size (%i/%i)",
                            bytes, pkt_size);
            break;
        }

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

        if (!scan_pack_header(buffer, TC_MAGIC_VOB)) {
            if (flag_notify && (verbose & TC_DEBUG))
                tc_log_warn(__FILE__,
                            "(pid=%i) invalid packet header detected",
                            getpid());

            // something else?
            if (scan_pack_header(buffer, MPEG_VIDEO)
              | scan_pack_header(buffer, MPEG_AUDIO)) {
              /* CAUTION: bitwise OR */

                if (verbose & TC_STATS)
                    tc_log_msg(__FILE__,
                               "(pid=%i) MPEG system stream detected",
                               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; /* UGHly */
            } else {
                tc_log_warn(__FILE__,
                            "(pid=%i) '0x%02x%02x%02x%02x' not yet supported",
                            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 (verbose & TC_STATS)
                    tc_log_msg(__FILE__,
                               "(pid=%i) MPEG-1 video stream detected",
                               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 (verbose & TC_STATS) {
                //display info only once
                tc_log_msg(__FILE__,
                           "(pid=%i) MPEG-2 video stream detected",
                           getpid());
            }
        } else {
            // MPEG1
            if ((buffer[4] & 0xf0) == 0x20) {
                payload_id = PACKAGE_MPEG1;

                if (verbose & TC_STATS) {
                    // display info only once
                    tc_log_msg(__FILE__,
                               "(pid=%i) MPEG-1 video stream detected",
                               getpid());
                }
            } else {
                payload_id = PACKAGE_PASS;

                if (verbose & TC_DEBUG)
                    tc_log_warn(__FILE__,
                                "(pid=%i) unknown stream packet id detected",
                                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;

        // check for subid here:

        is_track = id;

        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);
        }

        /* ------------------------------------------------------------
         *
         * (VII) evaluate scan results - flush packet
         *
         * ------------------------------------------------------------*/

flush_packet:
        for (p = 0; p < npass; p++) {
            if (is_track == pass[p]) {
                if (tc_pwrite(ipipe->fd_out, buffer, pkt_size) != pkt_size) {
                    tc_log_perror(__FILE__, "write program stream packet");
                    exit(1);
                }
                break;
            }
        }
        
        // aborting?
        if (flag_eos)
            break;

        //total packs (2k each) counter
        ++j;

    } // process next packet/block

    if (buffer != NULL)
        tc_free(buffer);

    return;
}


