/* Network-Integrated Multimedia Middleware (NMM)
 * http://www.motama.com/nmm.html
 *  
 * Copyright (C) 2006-2007
 * Motama GmbH, Saarbruecken, Germany
 * http://www.motama.com
 *  
 * This file is part of NMM. 
 * 
 * NMM 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; only version 2 of the License.      
 * 
 * NMM 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 NMM; if not, write to the Free Software Foundation,    
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

#ifndef NMM_MPEGDEMUXNODE_HPP
#define NMM_MPEGDEMUXNODE_HPP NMM_MPEGDEMUXNODE_HPP

#include "nmm/nmm_prefix.hpp"

#include "nmm/plugins/interfaces/IStreamJumpImpl.hpp"
#include "nmm/multimedia/ISyncResetImpl.hpp"
#include "nmm/plugins/av/mpeg/IMPEGDemuxImpl.hpp"
#include "nmm/multimedia/GenericDemultiplexerNode.hpp"

#include <inttypes.h>
#include "visibility.hpp"

/* timestamping */
#define FLOAT_0x10000 (double)((uint32_t)1 << 16)
#define STD_SYSTEM_CLOCK_FREQ 90000L

#define MAX_MPEG_VIDEO 16
#define MAX_SPU        32
#define MAX_MPEG_AUDIO 32
#define MAX_AC3_AUDIO  8

namespace NMM {

  class MPEGDemuxNode :
    public GenericDemultiplexerNode,
    public IStreamJumpImpl,
    public ISyncResetImpl,
    public IMPEGDemuxImpl
  {
  private:
    int analysed_buffers;

    /* Buffer with demuxed data( audio, video, ... ) */
    Buffer* out;

    bool skip_audio;
    bool send_event;

    int  temp;

    /* size of our extra buffer space */
    int             fragment_size;

    /* bytes that could not be demultiplexed because data was needed */
    int             rest_length;

    /* current length of incoming buffer */
    int             cur_buffer_length;

    /* pointer to current position of stream data */
    uint8_t*        buf;

    /* pointer to end of stream data */
    uint8_t*        end;

    /* pointer to begin of stream data */
    uint8_t*        tmpbuffer;

    /* size of tmpbuffer */
    int             inbuffer_size;

    bool first_pts;
    struct timeval start_time;
    struct timeval now;
    Timestamp timestamp;

    string mpeg_video_string[MAX_MPEG_VIDEO];
    string mpeg_audio_string[MAX_MPEG_AUDIO];
    string ac3_audio_string[MAX_AC3_AUDIO];
    string spu_string[MAX_SPU];

    long int mpeg_video_count[MAX_MPEG_VIDEO];
    long int mpeg_audio_count[MAX_MPEG_AUDIO];
    long int ac3_audio_count[MAX_AC3_AUDIO];
    long int spu_count[MAX_SPU];

    struct timeval readTimecode(uint8_t *pointer);

    /* indicates if demuxed data should be send to all output or if output 0 can be switched */
    bool dvd_mode;

    /* the streams with this number are output to mpeg_video0, mpeg_audio0, ac3_audio0, spu0 */
    unsigned int video_stream_number;
    unsigned int audio_stream_number;
    unsigned int spu_stream_number;

    unsigned short mpeg_version;

    /**
    * demuxes stream that begins at <buf> and ends at <end>
    * \returns true  if enough data was there to demux a packet (this packet is saved in Buffer <out>)
    *                <buf> points to the next packet
    *                Output Jack is set correctly
    * \returns false if there was not enough data to demux a packet, then <buf> points to the
    *                begin of the last (undemuxed) packet and <out> is a NULL pointer
    */
    bool demux();

    /**
     * small helper method for demux()
     * packs a MPEG buffer (as char*) from <data_start> to
     * <packet_end> into a NMM buffer, sets the Node's output
     * stream to <jack_tag> and the buffer's timestamp.stream_counter
     * to <counter>
     * \param data_start pointer to the beginning of the char array to prepare
     * \param packet_end pointer to the end of the char array to prepare
     * \param jack_tag the jack tag of the Node's output stream, to select
     * \param counter the timestamp.stream_counter to set for the NMM buffer's timestamp
     * \returns the NMM buffer to prepare to send
     */
    inline Buffer* sendMPEGPacket(const uint8_t* data_start, const uint8_t* packet_end, const string jack_tag, const long int counter);

  protected:
    OutputJack* getMPEGOutputStream(const string& jack_tag = "default");

    virtual Message *processBuffer (Buffer *);
    
    /** Reset workingflag and some variables */
    virtual Result doFlush();
    
    /** Initialize properties. */
    virtual Result doInit();
    virtual Result doInitOutput();
    virtual Result doDeinitOutput();
    
    /** IStreamJump interface. */
    virtual Result fastForward( const nmm_int32& value );
    virtual Result streamJump( const nmm_int32& value );
    virtual Result sendIFramesOnly( const nmm_int32& count );
    
    /** ISyncReset interface. */
    virtual Result syncReset();
    
    /** IMPEGDemux interface. */
    virtual void setVideoStream( const nmm_int32& stream );
    virtual void setAudioStream( const nmm_int32& stream );
    virtual void setSPUStream( const nmm_int32& stream );
    virtual void setDVDMode( const nmm_bool& stream );
    
  public:
    
    MPEGDemuxNode(const char *name = "MPEGDemuxNode", StreamQueue::Mode = StreamQueue::MODE_SUSPEND, int = 20);
    ~MPEGDemuxNode();
    
    long int getMpegVideoPackets( unsigned int stream ) { return( mpeg_video_count[stream] ); };
    long int getMpegAudioPackets( unsigned int stream ) { return( mpeg_audio_count[stream] ); };
    long int getAc3AudioPackets( unsigned int stream ) { return( ac3_audio_count[stream] ); };
    long int getSpuPackets( unsigned int stream ) { return( spu_count[stream] ); };
  };

}

#include "nmm/nmm_suffix.hpp"

#endif //NMM_MPEGDEMUXNODE_HPP



