/* 
 * $Id: FrameGrabThread.cpp 4483 2008-01-02 09:19:06Z soarchin $
 * 
 * this file is part of eMule
 * Copyright (C)2003 Merkur ( devs@emule-project.net / http://www.emule-project.net )
 * 
 * This program 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 of the License, or (at your option) any later version.
 * 
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "stdafx.h"
#include "emule.h"
#include "FrameGrabThread.h"
#include "../CxImage/xImage.h"
#include "OtherFunctions.h"
#include "quantize.h"
#if _MSC_VER<1400
// DirectShow MediaDet
#include <strmif.h>
//#include <uuids.h>
#define _DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
        EXTERN_C const GUID DECLSPEC_SELECTANY name \
                = { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }
_DEFINE_GUID(MEDIATYPE_Video, 0x73646976, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
_DEFINE_GUID(MEDIATYPE_Audio, 0x73647561, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
_DEFINE_GUID(FORMAT_VideoInfo,0x05589f80, 0xc356, 0x11ce, 0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a);
_DEFINE_GUID(FORMAT_WaveFormatEx,0x05589f81, 0xc356, 0x11ce, 0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a);
//#define MMNODRV		// mmsystem: Installable driver support
#define MMNOSOUND		// mmsystem: Sound support
//#define MMNOWAVE		// mmsystem: Waveform support
#define MMNOMIDI		// mmsystem: MIDI support
#define MMNOAUX			// mmsystem: Auxiliary audio support
#define MMNOMIXER		// mmsystem: Mixer support
#define MMNOTIMER		// mmsystem: Timer support
#define MMNOJOY			// mmsystem: Joystick support
#define MMNOMCI			// mmsystem: MCI support
//#define MMNOMMIO		// mmsystem: Multimedia file I/O support
#define MMNOMMSYSTEM	// mmsystem: General MMSYSTEM functions
#include <qedit.h>
typedef struct tagVIDEOINFOHEADER
{
    RECT            rcSource;          // The bit we really want to use
    RECT            rcTarget;          // Where the video should go
    DWORD           dwBitRate;         // Approximate bit data rate
    DWORD           dwBitErrorRate;    // Bit error rate for this stream
    REFERENCE_TIME  AvgTimePerFrame;   // Average time per frame (100ns units)
    BITMAPINFOHEADER bmiHeader;
}
VIDEOINFOHEADER;
#endif
#include "emuledlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


IMPLEMENT_DYNCREATE(CFrameGrabThread, CWinThread)

CFrameGrabThread::CFrameGrabThread()
{

}

CFrameGrabThread::~CFrameGrabThread()
{

}

BOOL CFrameGrabThread::InitInstance()
{
    DbgSetThreadName("FrameGrabThread");
    InitThreadLocale();
    return TRUE;
}

BOOL CFrameGrabThread::Run()
{
    imgResults = new CxImage*[nFramesToGrab];
    FrameGrabResult_Struct* result = new FrameGrabResult_Struct;
    result->nImagesGrabbed = (uint8)GrabFrames();
    result->imgResults = imgResults;
    result->pSender = pSender;
    VERIFY( PostMessage(theApp.emuledlg->m_hWnd,TM_FRAMEGRABFINISHED, (WPARAM)pOwner,(LPARAM)result) );
    return 0;
}

UINT CFrameGrabThread::GrabFrames()
{
#define TIMEBETWEENFRAMES	50.0 // could be a param later, if needed
    for (int i = 0; i!= nFramesToGrab; i++)
        imgResults[i] = NULL;
#if _MSC_VER<1400
    try
    {
        HRESULT hr;
        CComPtr<IMediaDet> pDet;
        CoInitialize(NULL);
        hr = pDet.CoCreateInstance(__uuidof(MediaDet));
        if (!SUCCEEDED(hr))
            return 0;

        // Convert the file name to a BSTR.
        CComBSTR bstrFilename(strFileName);
        hr = pDet->put_Filename(bstrFilename);

        long lStreams;
        bool bFound = false;
        hr = pDet->get_OutputStreams(&lStreams);
        for (long i = 0; i < lStreams; i++)
        {
            GUID major_type;
            hr = pDet->put_CurrentStream(i);
            hr = pDet->get_StreamType(&major_type);
            if (major_type == MEDIATYPE_Video)
            {
                bFound = true;
                break;
            }
        }

        if (!bFound)
            return 0;

        double dLength = 0;
        pDet->get_StreamLength(&dLength);
        if (dStartTime > dLength)
            dStartTime = 0;

        long width = 0, height = 0;
        AM_MEDIA_TYPE mt;
        hr = pDet->get_StreamMediaType(&mt);
        if (mt.formattype == FORMAT_VideoInfo)
        {
            VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)(mt.pbFormat);
            width = pVih->bmiHeader.biWidth;
            height = pVih->bmiHeader.biHeight;

            // We want the absolute height, don't care about orientation.
            if (height < 0) height *= -1;
        }
        else
        {
            return 0; // Should not happen, in theory.
        }

        /*FreeMediaType(mt); = */
        if (mt.cbFormat != 0)
        {
            CoTaskMemFree((PVOID)mt.pbFormat);
            mt.cbFormat = 0;
            mt.pbFormat = NULL;
        }
        if (mt.pUnk != NULL)
        {
            mt.pUnk->Release();
            mt.pUnk = NULL;
        }
        /**/

        long size;
        uint32 nFramesGrabbed;
        for (nFramesGrabbed = 0; nFramesGrabbed != nFramesToGrab; nFramesGrabbed++)
        {
            hr = pDet->GetBitmapBits(dStartTime + (nFramesGrabbed*TIMEBETWEENFRAMES), &size, NULL, width, height);
            if (SUCCEEDED(hr))
            {
                // we could also directly create a Bitmap in memory, however this caused problems/failed with *some* movie files
                // when I tried it for the MMPreview, while this method works always - so I'll continue to use this one
                long nFullBufferLen = sizeof( BITMAPFILEHEADER ) + size;
                char* buffer = new char[nFullBufferLen];

                BITMAPFILEHEADER bfh;
                memset( &bfh, 0, sizeof( bfh ) );
                bfh.bfType = 'MB';
                bfh.bfSize = nFullBufferLen;
                bfh.bfOffBits = sizeof( BITMAPINFOHEADER ) + sizeof( BITMAPFILEHEADER );
                memcpy(buffer,&bfh,sizeof( bfh ) );

                try
                {
                    hr = pDet->GetBitmapBits(dStartTime+ (nFramesGrabbed*TIMEBETWEENFRAMES), NULL, buffer + sizeof( bfh ), width, height);
                }
                catch (...)
                {
                    ASSERT(0);
                    hr = E_FAIL;
                }
                if (SUCCEEDED(hr))
                {
                    // decode
                    CxImage* imgResult = new CxImage();
                    imgResult->Decode((BYTE*)buffer, nFullBufferLen, CXIMAGE_FORMAT_BMP);
                    delete[] buffer;
                    if (!imgResult->IsValid())
                    {
                        delete imgResult;
                        break;
                    }

                    // resize if needed
                    if (nMaxWidth > 0 && nMaxWidth < width)
                    {
                        float scale = (float)nMaxWidth / imgResult->GetWidth();
                        int nMaxHeigth = (int)(imgResult->GetHeight() * scale);
                        imgResult->Resample(nMaxWidth, nMaxHeigth, 0);
                    }

                    // decrease bpp if needed
                    if (bReduceColor)
                    {
                        CQuantizer q(256,8);
                        q.ProcessImage(imgResult->GetDIB());
                        RGBQUAD* ppal=(RGBQUAD*)malloc(256*sizeof(RGBQUAD));
                        q.SetColorTable(ppal);
                        imgResult->DecreaseBpp(8, true, ppal);
                        free(ppal);
                    }

                    //CString TestName;
                    //TestName.Format("G:\\testframe%i.png",nFramesGrabbed);
                    //imgResult->Save(TestName,CXIMAGE_FORMAT_PNG);
                    // done
                    imgResults[nFramesGrabbed] = imgResult;
                }
                else
                {
                    delete[] buffer;
                    break;
                }
            }
        }
        return nFramesGrabbed;
    }
    catch (...)
    {
        ASSERT(0);
        return 0;
    }
#else
    return 0;
#endif
}

void CFrameGrabThread::SetValues(const CKnownFile* in_pOwner, CString in_strFileName,uint8 in_nFramesToGrab, double in_dStartTime, bool in_bReduceColor, uint16 in_nMaxWidth, void* in_pSender)
{
    strFileName =in_strFileName;
    nFramesToGrab = in_nFramesToGrab;
    dStartTime = in_dStartTime;
    bReduceColor = in_bReduceColor;
    nMaxWidth = in_nMaxWidth;
    pOwner = in_pOwner;
    pSender = in_pSender;
}

BEGIN_MESSAGE_MAP(CFrameGrabThread, CWinThread)
END_MESSAGE_MAP()
