/*
 * Java port of ffmpeg MPEG demultiplexer.
 * Copyright (c) 2003 Jonathan Hueber.
 *
 * Copyright (c) 2000, 2001 Fabrice Bellard.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * See Credits file and Readme for details
 * 1a39e335700bec46ae31a38e2156a898
 */
package net.sourceforge.jffmpeg.demux.mpg;

import javax.media.Demultiplexer;
import javax.media.protocol.Positionable;
import javax.media.protocol.Seekable;
import javax.media.Time;
import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.FileTypeDescriptor;
import javax.media.Track;
import javax.media.protocol.DataSource;
import javax.media.protocol.PullDataSource;
import javax.media.protocol.PullSourceStream;
import javax.media.MediaLocator;
import javax.media.BadHeaderException;
import javax.media.Buffer;
import javax.media.Format;
import javax.media.format.AudioFormat;
import javax.media.format.VideoFormat;
import javax.media.TrackListener;

import java.io.IOException;
import java.util.HashMap;
import java.util.Enumeration;

import java.awt.Dimension;

/**
 * Mpeg Track implementation.  Reads an MPEG stream
 */
public class MpegVideoTrack extends MpegTrack {
    /**
     * Constructor - pass Track Identifier
     */
    public MpegVideoTrack( MpegDemux demux, int trackId ) {
        super( demux, trackId );
    }

    /**
     * Return Audio format
     */
    public Format getFormat() {
        return format;
    }

    /**
     * Header information
     */
    private boolean parsedHeader = false;
    private Buffer header = new Buffer();

    /**
     * Mpeg Frame rate conversion
     */
    private float[] frameRateTable = new float[] {
        0, (float)24000/1001, 24, 25,
        (float)30000/1001, 30, 50, (float)60000/1001,
        60, 15, 5, 10, 12, 15, 0
    };

    /**
     * Read Header information
     */
    public synchronized void parseHeader() throws IOException {
        long firstMpeg2Packet = 0;
        int width = 0;
        int height = 0;
        float frameRate = 0.0f;
 
        /**
         * Parse Header packets
         */
        while ( !parsedHeader ) {
            /**
             * Read Packet Identifier
             */
            int packetID = demux.peekPacket( pos );

            /**
             * Check this is a header packet 
             */
            if ( packetID < 0x1b0 ) {
                parsedHeader = true;
                break;
            }

            /**
             * Embedded Stream -- MPEG2
             */
            if ( ((packetID >= 0x1c0 && packetID <= 0x1df) ||
                  (packetID >= 0x1e0 && packetID <= 0x1ef) ||
                  (packetID == 0x1bd))) {
                /**
                 * Enter video stream
                 */
                firstMpeg2Packet = pos;
                pos = demux.readDTSHeader( header, pos );
                continue;
            }

            /**
             * Read Header Packet 
             */
            int pointer = header.getLength() + 4;

            /**
             * Sequence Start code
             */
            if ( packetID == MpegDemux.SEQUENCE_START_CODE ) {
                /**
                 * Get Width/Hight/Frame rate 
                 */
                pos = demux.readPacket( header, trackId, pos );

                byte[] packet = (byte[])header.getData();
                width = ((packet[ pointer ] & 0xff) << 4)
                      | ((packet[ pointer + 1 ] >>4) &0xf);
                height = ((packet[ pointer + 1 ] & 0xf) << 8)
                      | ((packet[ pointer + 2 ]) & 0xff);
                int frame_rate_index = packet[ pointer + 3 ] & 0xf;
                frameRate = frameRateTable[ frame_rate_index ];

                format = new VideoFormat( "mpeg", new Dimension(width,height), 
                                          10000, (new byte[1]).getClass(), 
                                          frameRate );
            } else if ( packetID == MpegDemux.EXT_START_CODE ) {
                /**
                 * MPEG 2 Extension 
                 */
                pos = demux.readPacket( header, trackId, pos );

                byte[] packet = (byte[])header.getData();
                int extensionCode = (packet[pointer]>>4) & 0xf;

                /* Sequence Extension */
                if ( extensionCode == 1 ) {
                    width  |= ((packet[pointer+1]&0x1)<<12)
                             |((packet[pointer+2]&0x80)<<3);
                    height |= ((packet[pointer+2]&0xa0)<<5);

                format = new VideoFormat( "mpeg", new Dimension(width,height), 
                                          10000, (new byte[1]).getClass(), 
                                          frameRate );
                }
            } else {
                /* Not a recognised header packet */
                int p = header.getLength();
                pos = demux.readPacket( header, trackId, pos );
                header.setLength(p);
            }
        }

        /**
         * Exit Mpeg 2 packet
         */
        if ( firstMpeg2Packet != 0 ) {
            pos = firstMpeg2Packet;
        }
    }

    /**
     * Return a buffer containing audio data
     */
    public void readFrame(Buffer buffer) {
//        if ( trackId == MpegDemux.AUDIO ) return;
        try {
            /** Allocate buffer */
            byte[] buf = (byte[])buffer.getData();
            if ( buf == null || buf.length < 100000 ) {
                buf = new byte[ 1000000 ];
                buffer.setData( buf );
            }

            buffer.setLength(0);
            buffer.setOffset( 0 );

            /** Loop until we have a "trackId" packet */
            boolean gotPacket = false;
            while (!gotPacket) {
                /* Look at the next packet */
                int startcode = demux.peekPacket( pos );
//		System.out.println( trackId + " " + Long.toHexString(pos) + ": " +Integer.toHexString( startcode ) );

                /* Is this a DTS/PTS type packet (Mpeg2)? */
                if ( ((startcode >= 0x1c0 && startcode <= 0x1df) ||
                      (startcode >= 0x1e0 && startcode <= 0x1ef) ||
                      (startcode == 0x1bd))) {

                    /** Is this the right kind of DTS/PTS packet? */
                    if ( startcode != trackId )
                    {
                        pos = demux.skipDTSPacket( pos );
                        continue;
                    }
 
                    pos = demux.readDTSHeader( buffer, pos );
                    continue;
                }
                    

                /* This is an MPEG1 type packet */
                if ( startcode == MpegDemux.SEQUENCE_END_CODE ) {
                    /* End of file */
                    buffer.setEOM( true );
                }

                if (   startcode == MpegDemux.SEQUENCE_END_CODE
                    || startcode == MpegDemux.GOP_START_CODE ) {
                    /* End of Group Of Pictures */
                    gotPacket = true;
                    pos = demux.skipPacket( pos );
                    break;
                }

                if ( startcode == MpegDemux.PACK_START_CODE ) {
                    /* Audio packet */
                    if ( trackId == startcode ) {
                        pos = demux.readPacket( buffer, trackId, pos + 4 );
                        gotPacket = true;
                    } else {
                        pos = demux.skipPacket( pos );
                    }
                } else {
                    /* Video Packet */
                    /* MPEGI video tracks all start with a header */
                    if ( buffer.getLength() == 0 ) {
                        byte[] buff = (byte[])buffer.getData();
                        buffer.setLength( header.getLength() );
                        buffer.setOffset( 0 );
                        System.arraycopy( header.getData(), 0, buff, 0, header.getLength() );
                    }
                    pos = demux.readPacket( buffer, trackId, pos );
                }
            }
//	    System.out.println( "Got packet" );
            /* Set buffer information */
	    //            buffer.setFlags( Buffer.FLAG_NO_WAIT );
        } catch( Exception e ) {
            e.printStackTrace();
        }
    }
}
