/*
 *  LAME MP3 encoder for DirectShow
 *  DirectShow filter implementation
 *
 *  Copyright (c) 2000-2005 Marie Orlova, Peter Gubanov, Vitaly Ivanov, Elecard Ltd.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library 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.
 */

#include <mmreg.h>
#include "Encoder.h"

#define KEY_LAME_ENCODER            "SOFTWARE\\GNU\\LAME MPEG Layer III Audio Encoder Filter"

#define VALUE_BITRATE               "Bitrate"
#define VALUE_VARIABLE              "Variable"
#define VALUE_VARIABLEMIN           "VariableMin"
#define VALUE_VARIABLEMAX           "VariableMax"
#define VALUE_QUALITY               "Quality"
#define VALUE_VBR_QUALITY           "VBR Quality"
#define VALUE_SAMPLE_RATE           "Sample Rate"

#define VALUE_STEREO_MODE           "Stereo Mode"
#define VALUE_FORCE_MS              "Force MS"

#define VALUE_LAYER                 "Layer"
#define VALUE_ORIGINAL              "Original"
#define VALUE_COPYRIGHT             "Copyright"
#define VALUE_CRC                   "CRC"
#define VALUE_FORCE_MONO            "Force Mono"
#define VALUE_SET_DURATION          "Set Duration"
#define VALUE_SAMPLE_OVERLAP        "Allow sample overlap"
#define VALUE_PES                   "PES"

#define VALUE_ENFORCE_MIN           "EnforceVBRmin"
#define VALUE_VOICE                 "Voice Mode"
#define VALUE_KEEP_ALL_FREQ         "Keep All Frequencies"
#define VALUE_STRICT_ISO            "Strict ISO"
#define VALUE_DISABLE_SHORT_BLOCK   "No Short Block"
#define VALUE_XING_TAG              "Xing Tag"
#define VALUE_MODE_FIXED            "Mode Fixed"


typedef struct 
{
    DWORD      nSampleRate;
    DWORD      nBitRate;
    MPEG_mode  ChMode;                       //Channel coding mode
} current_output_format_t;

typedef struct 
{
    DWORD   nSampleRate;
    DWORD   nBitRate;
} output_caps_t;

typedef struct
{
    LONGLONG        sample;
    REFERENCE_TIME  delta;

    BOOL            applied;
} resync_point_t;

#define RESYNC_COUNT    4

// The maximum number of capabilities that we can expose in our IAMStreamConfig
// implementation is currently set to 100. This number is larger than we
// should ever realistically need. However, a cleaner implementation might
// be to use a dynamically sized array like std::vector or CAtlArray to 
// hold this data.
#define MAX_IAMSTREAMCONFIG_CAPS 100

///////////////////////////////////////////////////////////////////
// CMpegAudEnc class - implementation for ITransformFilter interface
///////////////////////////////////////////////////////////////////
class CMpegAudEncOutPin;
class CMpegAudEncPropertyPage;
class CMpegAudEnc : public CTransformFilter,
                    public ISpecifyPropertyPages,
                    public IAudioEncoderProperties,
                    public CPersistStream
{
public:
    DECLARE_IUNKNOWN

    static CUnknown *CreateInstance(LPUNKNOWN lpunk, HRESULT *phr);

    LPAMOVIESETUP_FILTER GetSetupData();

    HRESULT Reconnect();

    HRESULT Receive(IMediaSample *pSample);

    HRESULT CheckInputType(const CMediaType* mtIn);
    HRESULT CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut);
    HRESULT DecideBufferSize(IMemAllocator * pAllocator, ALLOCATOR_PROPERTIES *pProperties);

    HRESULT GetMediaType  (int iPosition, CMediaType *pMediaType);
    HRESULT SetMediaType  (PIN_DIRECTION direction,const CMediaType *pmt);


    //
    HRESULT StartStreaming();
    HRESULT StopStreaming();
    HRESULT EndOfStream();
    HRESULT BeginFlush();

    ~CMpegAudEnc(void);

    // ISpecifyPropertyPages
    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
    STDMETHODIMP GetPages(CAUUID *pcauuid);

    // IAudioEncoderProperties
    STDMETHODIMP get_PESOutputEnabled(DWORD *dwEnabled);    // PES header. Obsolete
    STDMETHODIMP set_PESOutputEnabled(DWORD dwEnabled);     // PES header. Obsolete

    STDMETHODIMP get_MPEGLayer(DWORD *dwLayer);
    STDMETHODIMP set_MPEGLayer(DWORD dwLayer);

    STDMETHODIMP get_Bitrate(DWORD *dwBitrate);
    STDMETHODIMP set_Bitrate(DWORD dwBitrate);   
    STDMETHODIMP get_Variable(DWORD *dwVariable);
    STDMETHODIMP set_Variable(DWORD dwVariable);
    STDMETHODIMP get_VariableMin(DWORD *dwMin);
    STDMETHODIMP set_VariableMin(DWORD dwMin);
    STDMETHODIMP get_VariableMax(DWORD *dwMax);
    STDMETHODIMP set_VariableMax(DWORD dwMax);
    STDMETHODIMP get_Quality(DWORD *dwQuality);
    STDMETHODIMP set_Quality(DWORD dwQuality);
    STDMETHODIMP get_VariableQ(DWORD *dwVBRq);
    STDMETHODIMP set_VariableQ(DWORD dwVBRq);
    STDMETHODIMP get_SourceSampleRate(DWORD *dwSampleRate);
    STDMETHODIMP get_SourceChannels(DWORD *dwChannels);
    STDMETHODIMP get_SampleRate(DWORD *dwSampleRate);
    STDMETHODIMP set_SampleRate(DWORD dwSampleRate);

    STDMETHODIMP get_ChannelMode(DWORD *dwChannelMode);
    STDMETHODIMP set_ChannelMode(DWORD dwChannelMode);
    STDMETHODIMP get_ForceMS(DWORD *dwFlag);
    STDMETHODIMP set_ForceMS(DWORD dwFlag);
    STDMETHODIMP get_EnforceVBRmin(DWORD *dwFlag);
    STDMETHODIMP set_EnforceVBRmin(DWORD dwFlag);
    STDMETHODIMP get_VoiceMode(DWORD *dwFlag);
    STDMETHODIMP set_VoiceMode(DWORD dwFlag);
    STDMETHODIMP get_KeepAllFreq(DWORD *dwFlag);
    STDMETHODIMP set_KeepAllFreq(DWORD dwFlag);
    STDMETHODIMP get_StrictISO(DWORD *dwFlag);
    STDMETHODIMP set_StrictISO(DWORD dwFlag);
    STDMETHODIMP get_NoShortBlock(DWORD *dwNoShortBlock);
    STDMETHODIMP set_NoShortBlock(DWORD dwNoShortBlock);
    STDMETHODIMP get_XingTag(DWORD *dwXingTag);
    STDMETHODIMP set_XingTag(DWORD dwXingTag);
    STDMETHODIMP get_ModeFixed(DWORD *dwModeFixed);
    STDMETHODIMP set_ModeFixed(DWORD dwModeFixed);


    STDMETHODIMP get_CRCFlag(DWORD *dwFlag);
    STDMETHODIMP set_CRCFlag(DWORD dwFlag);
    STDMETHODIMP get_ForceMono(DWORD *dwFlag);
    STDMETHODIMP set_ForceMono(DWORD dwFlag);
    STDMETHODIMP get_SetDuration(DWORD *dwFlag);
    STDMETHODIMP set_SetDuration(DWORD dwFlag);
    STDMETHODIMP get_OriginalFlag(DWORD *dwFlag);
    STDMETHODIMP set_OriginalFlag(DWORD dwFlag);
    STDMETHODIMP get_CopyrightFlag(DWORD *dwFlag);
    STDMETHODIMP set_CopyrightFlag(DWORD dwFlag);
    STDMETHODIMP get_SampleOverlap(DWORD *dwFlag);
    STDMETHODIMP set_SampleOverlap(DWORD dwFlag);

    STDMETHODIMP get_ParameterBlockSize(BYTE *pcBlock, DWORD *pdwSize);
    STDMETHODIMP set_ParameterBlockSize(BYTE *pcBlock, DWORD dwSize);

    STDMETHODIMP DefaultAudioEncoderProperties();
    STDMETHODIMP LoadAudioEncoderPropertiesFromRegistry();
    STDMETHODIMP SaveAudioEncoderPropertiesToRegistry();
    STDMETHODIMP InputTypeDefined();

    STDMETHODIMP ApplyChanges();

    // CPersistStream
    HRESULT WriteToStream(IStream *pStream);
    HRESULT ReadFromStream(IStream *pStream);

    int SizeMax();
    STDMETHODIMP GetClassID(CLSID *pClsid);

private:
    CMpegAudEnc(LPUNKNOWN lpunk, HRESULT *phr);

    HRESULT FlushEncodedSamples();

    void ReadPresetSettings(MPEG_ENCODER_CONFIG *pmec);

    void LoadOutputCapabilities(DWORD sample_rate);

    // Encoder object
    CEncoder                    m_Encoder;

    REFERENCE_TIME              m_rtStreamTime;
    REFERENCE_TIME              m_rtFrameTime;
    REFERENCE_TIME              m_rtEstimated;

    // Synchronization data
    LONGLONG                    m_samplesIn;
    LONGLONG                    m_samplesOut;
    int                         m_samplesPerFrame;
    int                         m_bytesPerSample;
    float                       m_bytesToDuration;

    resync_point_t              m_sync[RESYNC_COUNT];
    int                         m_sync_in_idx;
    int                         m_sync_out_idx;

    BOOL                        m_hasFinished;

    CCritSec                    m_cs;

    DWORD                       m_setDuration;
    DWORD                       m_allowOverlap;

	REFERENCE_TIME m_rtBytePos;

	BOOL						m_bStreamOutput;      // Binary stream output
	long						m_cbStreamAlignment;  // Stream block size
    int                         m_CapsNum;
	int                         m_currentMediaTypeIndex;
    output_caps_t               OutputCaps[MAX_IAMSTREAMCONFIG_CAPS];

protected:
    friend class CMpegAudEncOutPin;
    friend class CMpegAudEncPropertyPage;
};


class CMpegAudEncOutPin : public CTransformOutputPin, public IAMStreamConfig
{
public:

    //////////////////////////////////////////////////////////////////////////
    //  IUnknown
    //////////////////////////////////////////////////////////////////////////
    DECLARE_IUNKNOWN
    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);

    //////////////////////////////////////////////////////////////////////////
    //  IAMStreamConfig
    //////////////////////////////////////////////////////////////////////////
    HRESULT STDMETHODCALLTYPE SetFormat(AM_MEDIA_TYPE *pmt);
    HRESULT STDMETHODCALLTYPE GetFormat(AM_MEDIA_TYPE **ppmt);
    HRESULT STDMETHODCALLTYPE GetNumberOfCapabilities(int *piCount, int *piSize);
    HRESULT STDMETHODCALLTYPE GetStreamCaps(int iIndex, AM_MEDIA_TYPE **pmt, BYTE *pSCC);

    //////////////////////////////////////////////////////////////////////////
    //  CTransformOutputPin
    //////////////////////////////////////////////////////////////////////////
    CMpegAudEncOutPin( CMpegAudEnc * pFilter, HRESULT * pHr );
    ~CMpegAudEncOutPin();

    HRESULT CheckMediaType(const CMediaType *pmtOut);
    HRESULT GetMediaType(int iPosition, CMediaType *pmt);
    HRESULT SetMediaType(const CMediaType *pmt);
    
private:
    BOOL        m_SetFormat;
    CMpegAudEnc *m_pFilter;

    current_output_format_t  m_CurrentOutputFormat;

protected:
    friend class CMpegAudEnc;

};
