/* 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_MPEGVIDEODECODENODE_HPP
#define NMM_MPEGVIDEODECODENODE_HPP NMM_MPEGVIDEODECODENODE_HPP

#include "nmm/nmm_prefix.hpp"

//#include "nmm/utils/utils.hpp"
#include "nmm/utils/time/Rational.hpp"
#include "nmm/multimedia/GenericProcessorNode.hpp"

#include "nmm/multimedia/ISyncResetImpl.hpp"
#include "nmm/plugins/interfaces/ITrackSizeImpl.hpp"

#include "nmm/plugins/interfaces/IStreamJumpImpl.hpp"
#include "nmm/plugins/interfaces/IStillFrameImpl.hpp"
#include "nmm/plugins/interfaces/IMPEGVideoDecoderImpl.hpp"

//#include <stdio.h>
//#include <stdlib.h>
//#include <assert.h>
//#include <fcntl.h>
//#include <unistd.h>
//#include <stdio.h>
//#include <stdlib.h>
//#include <errno.h>
#include <inttypes.h>

extern "C" {
#include "mpeg2dec/mpeg2.h"
}

#include "visibility.hpp"

namespace NMM {

  class MPEGVideoDecodeNode :
    public GenericProcessorNode,
    protected ISyncResetImpl,
    protected IStreamJumpImpl,
    protected IMPEGVideoDecoderImpl,
    public ITrackSizeImpl,
    protected IStillFrameImpl
  {
  private:
    /* temporary buffer for fastmemcpy */
    char tbuf[2048];

    bool in_buffer_timestamp_taken;  //!< true if a valid timestamp has been found in
                                     //!< an incoming buffer. Is set to false again when
                                     //!< the next buffer is output.
    Time in_buffer_sync_time;        //!< The sync_time of the incoming buffer (if its
                                     //!< timestamp was valid.
    int frame_type;                  //!< The type of the last decoded frame (I-, P- or B-Type)
    Timestamp timestamp;             //!< The timestamp to put in the next outgoing buffer.

    /* number of decoded frames (for benchmarking) */
    unsigned int decoded_frames;

    /* this is where we keep the state of the mpeg decoder */
    mpeg2dec_t* mpeg2dec;

    /* output buffer with decoded yv12 frame */
    Buffer* out;

    /* number of bytes for one yv12 frame */
    unsigned int buffer_size;

    /* save input buffer pointer */
    Buffer* old_in_buffer;

    /* >BEGIN: variables that holds information about the current stream properties */
    unsigned int image_height;
    unsigned int image_width;

    Rational aspect_ratio;

    float framerate;
    /* >END */

    /* indicates if format has been changed */
    bool format_changed;

    int  i_frames_only;  //!< if > 0 the decoder sends only every <i_frames_only> I-frame
    bool mpeg_resync;    //!< indicates that only partially decodable frames will be dropped
    bool drop_b_frames;  //!< drop B-frames between first I- and P-frame

    int  frames_to_drop;
    int  iframes_to_drop;

    ThreadMutex process_buffer_mutex;
    
    u_int64_t track_size;
    bool send_track_duration;

    const mpeg2_picture_t* first_iframe;
		
    /**
     * allocates memory to hold 3 frames
     * deallocates previously allocated memory
     * gives frames to mpeg decoder
     * \param size number of bytes for each frame to allocate
     */
    void allocateFrameBuffers( unsigned int size );

    /**
     * allocates memory for 1 frame
     * gives frame to mpeg decoder
     */
    void allocateNewFrameBuffer();

    /**
     * deallocates frame buffers memory
     */
    void deAllocateFrameBuffers();

    /**
     * decodes mpeg data
     * \returns true iff a frame could be decoded, otherwise there was
     * not enougth data to decode a frame
     */
    bool decodeMPEG2();

    /**
     * fast memcopy
     * fastmemcpy can only copy buffers with a size mutible of 2048
     * this function uses fastmemcopy for 2048 blocks and the normal memcpy for the rest
     */
    void copyBytes(char* src, char* dest, unsigned int nbytes);

    /**
     * reset workingflag
     */
    Result doFlush();

    void setWOPInternal();

  protected:
    virtual Result doInit();
    virtual Result doDeinit();
    virtual Result doInitOutput();
    
    virtual Message* processBuffer( Buffer* in_buffer );
    
    /** Resets the parameters of timestamp computation.
     * The stream_counter and the sync_time are set to 0.
     * MPEGVideoDecode puts timestamps in every outgoing buffer, so is_valid
     * is initialized to true.
     * That means if the incoming buffers don't have a valid timestamp,
     * the next outgoing buffer has stream_counter and sync_time 0.
     * Also an 'resolution_changed'-event is sendt.
     */
    virtual Result syncReset();

    // IStreamJump interface
    /**
     * Methods from IStreamJump interface.
     * Upstream events for seeking in MPEG streams.
     */
    virtual Result fastForward( const nmm_int32& value );
    virtual Result streamJump( const nmm_int32& value );
    virtual Result sendIFramesOnly(const nmm_int32& n);

    // IMPEGVideoDecoder interface
    /**
     * Resync MPEG decoder.
     * drop frames that are incomplete due to temporal dependencies.
     */
    virtual Result resyncMPEG();

    // IStillFrame interface
    /**
     * Handle still frames.
     * if we receive a still frame, we must immediately send the last decoded frame.
     */
    virtual Result setStillFrame(const nmm_bool& value);

    // ITrackSizeImpl interface
    Result trackSize(const nmm_uint64& bytes);
    
  public:
    MPEGVideoDecodeNode( const char* = "MPEGVideoDecodeNode", StreamQueue::Mode = StreamQueue::MODE_SUSPEND, int = 20);
    ~MPEGVideoDecodeNode();
    
    /* set number of Decoded Frames to zero */
    void resetDecodedFrameCounter() { decoded_frames = 0; };

    /* get number of decoded frames */
    unsigned int getDecodedFrameCounter() { return( decoded_frames ); };
  };

}

#include "nmm/nmm_suffix.hpp"

#endif //NMM_MPEGVIDEODECODENODE_HPP


