#include <Windows.h>
//#include <InitGuid.h>
#include <uuids.h>
#include <stdio.h>
#include <vector>
#include <mferror.h>
#include <process.h>

#include "kbmf_decoder.h"
#include "kbdshow_config.h"
#include "../../kbwma/source/kpifile_stream.h"
#include "kpi_player.h"
#include "kbdshow_config.h"

#pragma comment(lib, "mfplat.lib")
#pragma comment(lib, "mfreadwrite.lib")
#pragma comment(lib, "evr.lib")
#pragma comment(lib, "mf.lib")
#pragma comment(lib, "mfuuid.lib")

#if 0
// {922BA95F-BCE0-494B-B836-A2199BCB0CBA}
static const GUID guid_KbPresentationTimeSource =
{ 0x922ba95f, 0xbce0, 0x494b, { 0xb8, 0x36, 0xa2, 0x19, 0x9b, 0xcb, 0xc, 0xba } };

class KbPresentationTimeSource : public KbKpiUnknownImpl<IMFPresentationTimeSource>
{
    KbDShowDecoderBase::KMP_STATUS* m_pStatus;
public:
    KbPresentationTimeSource(KbDShowDecoderBase::KMP_STATUS *pStatus){
        m_pStatus = pStatus;
    }
    HRESULT STDMETHODCALLTYPE GetUnderlyingClock(IMFClock** ppClock){
        *ppClock = this;
        AddRef();
        return S_OK;
    }
    HRESULT STDMETHODCALLTYPE GetClockCharacteristics(DWORD* pdwCharacteristics){
        if(!pdwCharacteristics)return E_INVALIDARG;
        *pdwCharacteristics = 0;//MFCLOCK_CHARACTERISTICS_FLAG_FREQUENCY_10MHZ| /*MFCLOCK_CHARACTERISTICS_FLAG_IS_SYSTEM_CLOCK|*/ MFCLOCK_CHARACTERISTICS_FLAG_ALWAYS_RUNNING;
        return S_OK;
    }
    HRESULT STDMETHODCALLTYPE GetCorrelatedTime(DWORD dwReserved,LONGLONG* pllClockTime, MFTIME* phnsSystemTime);
    HRESULT STDMETHODCALLTYPE GetContinuityKey(DWORD* pdwContinuityKey)
    {
        if (!pdwContinuityKey)return E_INVALIDARG;
        *pdwContinuityKey = 0;
        return S_OK;
    }
    HRESULT STDMETHODCALLTYPE GetState(DWORD dwReserved, MFCLOCK_STATE* peClockState)
    {
        if (!peClockState)return E_INVALIDARG;
        *peClockState = MFCLOCK_STATE_RUNNING;
        return S_OK;
    }
    HRESULT STDMETHODCALLTYPE GetProperties(MFCLOCK_PROPERTIES* pClockProperties);
};
HRESULT STDMETHODCALLTYPE KbPresentationTimeSource::GetCorrelatedTime(DWORD dwReserved, LONGLONG* pllClockTime, MFTIME* phnsSystemTime)
{
    UINT64 qwPos = m_pStatus->dwPosition;
    if(pllClockTime){
        *pllClockTime = qwPos;
    }
    if(phnsSystemTime){
        *phnsSystemTime = qwPos;
    }
    return S_OK;
}
HRESULT STDMETHODCALLTYPE KbPresentationTimeSource::GetProperties(MFCLOCK_PROPERTIES* pClockProperties)
{
    if (!pClockProperties)return E_INVALIDARG;
    ZeroMemory(pClockProperties, sizeof(*pClockProperties));
    pClockProperties->guidClockId = guid_KbPresentationTimeSource;
    pClockProperties->qwClockFrequency = MFCLOCK_FREQUENCY_HNS;
    pClockProperties->dwClockTolerance = MFCLOCK_TOLERANCE_UNKNOWN;
    pClockProperties->dwClockJitter = 0;//MFCLOCK_JITTER_ISR
    return S_OK;
}
#endif
//
//Windows XP で動作するようにするため MF.dll/MFPlat.dll/MFReadWrite.dll を遅延ロードする
//(「リンカー」->「入力」->「DLL の遅延読み込み」)
//使用する API が増えると遅延ロードすべき DLL が増える可能性あり
//
bool KbMFDecoder::s_bInitialized = false;
/////////////////////////////////////////////////////////////////////////
bool __fastcall KbMFDecoder::Initialize(void)
{//XP で Media Foundation の API を呼ばないように OS チェック
    if (!s_bInitialized) {
        if (CheckVistaOrLater()) {
            MFStartup(MF_VERSION);
            s_bInitialized = true;
        }
    }
    return s_bInitialized;
}
/////////////////////////////////////////////////////////////////////////
void __fastcall KbMFDecoder::Uninitialize(void)
{   
    if(s_bInitialized){
        MFShutdown();
        s_bInitialized = false;
    }
}
/////////////////////////////////////////////////////////////////////////
KbMFDecoder::KbMFDecoder(void)
    :KbDShowDecoderBase()
{
    m_pByteStream = NULL;
    m_pSourceReader = NULL;
    m_pSample = NULL;
    m_pMediaBuffer = NULL;
    m_pMediaSession = NULL;
    m_pMediaSource = NULL;
    m_pTopology = NULL;
    m_pVideoDisplayControl = NULL;
    m_pPresentationClock = NULL;
    m_hMFEvent = NULL;
    m_dwBlockAlign = 0;
    m_dwPointer = 0;
}
/////////////////////////////////////////////////////////////////////////
KbMFDecoder::~KbMFDecoder(void)
{
    if(m_hThread){
        SendCommand(COMMAND_QUIT, 0, 0);
        WaitForSingleObject(m_hThread, INFINITE);
        CloseHandle(m_hThread);
        m_hThread = NULL;
    }
    if(m_hMFEvent)CloseHandle(m_hMFEvent);
    if(m_pMediaBuffer) {
        m_pMediaBuffer->Release();
    }
    if(m_pSample) {
        m_pSample->Release();
    }
    if(m_pSourceReader) {
        m_pSourceReader->Release();
    }
    if(m_pByteStream){
        m_pByteStream->Release();
    }
    //Uninitialize();//プラグインアンロード時に呼ぶ
}
/////////////////////////////////////////////////////////////////////////
BOOL __fastcall KbMFDecoder::Open(const KPI_MEDIAINFO *cpRequest,
                                  IKpiConfig *pConfig,
                                  IKpiFile   *pFile, 
                                  IKpiFolder *pFolder)
{
    if(!Initialize()){//初期化失敗(OS が XP 以前)
        return 0;
    }
    KbDShow_Settings settings;
    kbdshow_GetSettings(pConfig, &settings);
    m_rx = settings.rx;
    m_ry = settings.ry;
    IMFSourceReader *pReader = NULL;
    IMFByteStream* pByteStream = NULL;
#if 0
    const wchar_t *cszFileName;
    if(!pFile->GetRealFileW(&cszFileName)){
        return 0;
    }
    HRESULT hr = MFCreateSourceReaderFromURL(cszFileName, NULL, &pReader);
    if (FAILED(hr)) {
        return 0;
    }
#else
    KbKpiFileStream *pStream = new KbKpiFileStream(pFile);
    HRESULT hr = MFCreateMFByteStreamOnStream(pStream, &pByteStream);
    pStream->Release();
    if(FAILED(hr)){
        return 0;
    }
    hr = MFCreateSourceReaderFromByteStream(pByteStream, NULL, &pReader);
    if (FAILED(hr)) {
        pByteStream->Release();
        return 0;
    }
    m_pByteStream = pByteStream;
#endif
    pReader->SetStreamSelection(MF_SOURCE_READER_FIRST_AUDIO_STREAM, TRUE);
    IMFMediaType *pMediaType = NULL;
    pReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 
                                &pMediaType);
    if(!pMediaType){
        pReader->Release();
        return 0;
    }
    WAVEFORMATEXTENSIBLE *pWfx = NULL;
    UINT32 cbSize = 0;
    UINT32 Flags = 0;
    MFCreateWaveFormatExFromMFMediaType(pMediaType, (WAVEFORMATEX**)&pWfx, &cbSize, 0);
    if(!pWfx){
        pMediaType->Release();
        pReader->Release();
        return 0;
    }
    int nSampleRate = pWfx->Format.nSamplesPerSec;
    int nChannels = pWfx->Format.nChannels;
    int nBps = pWfx->Format.wBitsPerSample;
    int nBlockAlign = (nBps / 8) * nChannels;
    if(pWfx->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT){//32bit float/64bit float の wav で確認
        nBps *= -1;                                       //KbWavDecoder で処理するので wav で呼ばれることはないが
    }
    else if(pWfx->Format.wFormatTag != WAVE_FORMAT_PCM){
        //なるべく高いビット数で処理する
        int bps_table[] = {-64, -32, 32, 24, 16, 0};//
        int i = 0;;
        if(nBps != 0){//元のビット数のまま
            for(i = 0; bps_table[i]; i++){
                if(nBps == bps_table[i])break;
            }
        }
        while(1){
            if(!bps_table[i]){//諦める
                CoTaskMemFree(pWfx);
                pMediaType->Release();
                pReader->Release();
                return 0;
            }
            nBps = bps_table[i];
            nBlockAlign = nBps / 8 * nChannels;
            pMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
            if(nBps < 0){
                nBlockAlign *= -1;
                pMediaType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_Float);
            }
            else{
                pMediaType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
            }
            pMediaType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, nChannels);
            pMediaType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, nSampleRate);
            pMediaType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, abs(nBps));
            pMediaType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, nBlockAlign);
            pMediaType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, nBlockAlign * nSampleRate);
            pMediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
            hr = pReader->SetCurrentMediaType(
                (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM,
                NULL,
                pMediaType);
            if (!FAILED(hr)) {
                break;
            }
            i++;
        }
    }
    CoTaskMemFree(pWfx);
    UINT64 qwDuration = 0;
    PROPVARIANT var = {0};
    pReader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &var);
    qwDuration = *(UINT64*)&var.hVal;
    PropVariantClear(&var);
    pMediaType->Release();
    pMediaType = NULL;
    
    if(settings.bVideo){//動画の映像を再生
        pReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, &pMediaType);
        if (pMediaType) {
            MFVIDEOFORMAT* pVideoFormat = NULL;
            cbSize = 0;
            MFCreateMFVideoFormatFromMFMediaType(pMediaType, &pVideoFormat, &cbSize);
            if(pVideoFormat){
                m_MediaInfo.dwVideoWidth = pVideoFormat->videoInfo.dwWidth;
                m_MediaInfo.dwVideoHeight = pVideoFormat->videoInfo.dwHeight;
                CoTaskMemFree(pVideoFormat);
                if (m_MediaInfo.dwVideoWidth > 0 && m_MediaInfo.dwVideoHeight > 0) {
                    CreateKmpStatus();//本体から再生位置情報を得るための構造体へのポインタ取得
                }
            }
            pMediaType->Release();
        }
    }
    m_dwBlockAlign  = nBlockAlign;
    m_MediaInfo.dwSampleRate = nSampleRate;
    m_MediaInfo.dwChannels = nChannels;
    m_MediaInfo.qwLength = qwDuration;
    m_MediaInfo.nBitsPerSample = nBps;
    m_MediaInfo.dwSeekableFlags = KPI_MEDIAINFO::SEEK_FLAGS_SAMPLE;
    m_MediaInfo.dwNumber = 1;
    m_MediaInfo.dwCount = 1;
    m_pSourceReader = pReader;
    m_pFile = pFile;
    pFile->AddRef();
    m_pConfig = pConfig;
    pConfig->AddRef();
    return m_MediaInfo.dwCount;
}
/////////////////////////////////////////////////////////////////////////
DWORD  WINAPI KbMFDecoder::Select(DWORD dwNumber, 
                                     const KPI_MEDIAINFO **ppMediaInfo,
                                     IKpiTagInfo *pTagInfo,
                                     DWORD dwTagGetFlags)
{
    if(ppMediaInfo){
        *ppMediaInfo = NULL;
    }
    if(dwNumber > m_MediaInfo.dwCount){
        return 0;
    }
    if(!ppMediaInfo){
        return 1;
    }
    if(m_MediaInfo.dwSampleRate){
        m_MediaInfo.dwNumber = 1;
        *ppMediaInfo = &m_MediaInfo;
        return 1;
    }
    return 0;
}
/////////////////////////////////////////////////////////////////////////
DWORD  WINAPI KbMFDecoder::Render(BYTE *pBuffer, DWORD dwSizeSample)
{
    DWORD dwBlockAlign = m_dwBlockAlign;
    DWORD dwSize = dwSizeSample * dwBlockAlign;
    DWORD dwRet = 0;
    LONGLONG llTimeStamp = 0;
    while(dwRet < dwSize){
        if (!m_pSample){
            DWORD dwActualStreamIndex = 0;
            DWORD dwStreamFlags = 0;
            m_pSourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM,
                0,//MF_SOURCE_READER_CONTROLF_DRAIN,
                &dwActualStreamIndex,
                &dwStreamFlags,
                &llTimeStamp,
                &m_pSample);
            //if(dwStreamFlags & MF_SOURCE_READERF_ENDOFSTREAM){
                //int t = 0;
            //}
            if (m_pSample) {
                m_pSample->ConvertToContiguousBuffer(&m_pMediaBuffer);
                if (!m_pMediaBuffer) {
                    break;
                }
            }
            else {
                //m_pSourceReader->Flush(MF_SOURCE_READER_ALL_STREAMS);
                break;
            }
        }
        BYTE *buf = NULL;
        DWORD cur = 0;
        m_pMediaBuffer->Lock(&buf, NULL, &cur);
        if(!buf){
            return dwRet/dwBlockAlign;
        }
        DWORD copy = cur - m_dwPointer;
        if(dwRet + copy > dwSize){
            copy = dwSize - dwRet;
        }
        if (copy == 0) {
            m_pMediaBuffer->Unlock();
            m_pMediaBuffer->Release();m_pMediaBuffer = NULL;
            m_pSample->Release();m_pSample = NULL;
            m_dwPointer = 0;
            continue;
        }
        memcpy(pBuffer + dwRet, buf + m_dwPointer, copy);
        m_pMediaBuffer->Unlock();
        if(dwRet + copy < dwSize){
            dwRet += copy;
        }
        else{
            dwRet = dwSize;
        }
        m_dwPointer += copy;
    }
    DWORD dwRetSample = dwRet / dwBlockAlign;
    m_qwCurSample += dwRetSample;
    return dwRetSample;
}
/////////////////////////////////////////////////////////////////////////
UINT64 WINAPI KbMFDecoder::Seek(UINT64 qwPosSample, DWORD dwFlag)
{
    if(m_qwCurSample == (UINT64)-1){
        CreateVideoWindow();
    }
    if(m_qwCurSample == qwPosSample){
        return qwPosSample;
    }
    if(m_pMediaBuffer){m_pMediaBuffer->Release();m_pMediaBuffer = NULL;}
    if(m_pSample){m_pSample->Release(); m_pSample = NULL;}
    m_dwPointer = 0;
    UINT64 qwPos100ns = kpi_SampleTo100ns(qwPosSample, m_MediaInfo.dwSampleRate);
    PROPVARIANT var = {0};
    var.vt = VT_I8;
    var.uhVal.QuadPart = qwPos100ns;

    m_pSourceReader->SetCurrentPosition(GUID_NULL, var);
    SendCommand(COMMAND_RUN, (WPARAM)&qwPos100ns, 0);
    m_qwCurSample = qwPosSample;
    return qwPosSample;
}
/////////////////////////////////////////////////////////////////////////
DWORD  WINAPI KbMFDecoder::UpdateConfig(void* pvReserved)
{
    if (!m_hWnd)return 0;
    if (m_MediaInfo.dwVideoHeight == 0 ||
        m_MediaInfo.dwVideoWidth == 0) {
        return 0;
    }
    if (m_dwThreadId == GetCurrentThreadId()) {
        UpdateWindowPosition();
    }
    else {
        SendCommand(COMMAND_SETVIDEOPOS, 0, 0);
    }
    return 0;
}
/////////////////////////////////////////////////////////////////////////
void  __stdcall KbMFDecoder::ThreadProc(void)
{
    m_hMFEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    do {
        IMFSourceResolver* pSourceResolver = NULL;
        IKpiFile* pFileV = NULL;
        m_pFile->CreateClone(&pFileV);
        if (!pFileV)break;
        MFCreateMediaSession(NULL, &m_pMediaSession);
        if (!m_pMediaSession)break;
        MFCreateSourceResolver(&pSourceResolver);
        if (!pSourceResolver){
            break;
        }
        MF_OBJECT_TYPE object_type = MF_OBJECT_INVALID;

        IUnknown* pObj = NULL;
        if (1) {
            KbKpiFileStream* pStream = new KbKpiFileStream(pFileV);
            pFileV->Release();
            IMFByteStream* pByteStream = NULL;
            MFCreateMFByteStreamOnStream(pStream, &pByteStream);
            pStream->Release();
            if (!pByteStream) {
                pSourceResolver->Release();
                break;
            }
            wchar_t szFileName[MAX_PATH];
            m_pFile->GetFileName(szFileName, sizeof(szFileName));
            //CreateObjectFromByteStream の第2引数にファイル名を渡す(拡張子を渡す)
            pSourceResolver->CreateObjectFromByteStream(pByteStream, szFileName, MF_RESOLUTION_MEDIASOURCE,
                NULL,
                &object_type,
                &pObj);
            pByteStream->Release();
        }
        /*else{
            const wchar_t *cszFileName = NULL;
            pFile->GetRealFileW(&cszFileName);
            hr = pSourceResolver->CreateObjectFromURL(
                cszFileName,
                MF_RESOLUTION_MEDIASOURCE,
                NULL,
                &object_type,
                &pObj);
        }*/
        if (!pObj) {
            pSourceResolver->Release();
            break;
        }
        pObj->QueryInterface(IID_IMFMediaSource, (void**)&m_pMediaSource);
        pObj->Release();
        pSourceResolver->Release();
        if (!m_pMediaSource) {
            break;
        }
        MFCreateTopology(&m_pTopology);
        if (!m_pTopology)break;
        IMFPresentationDescriptor* pPresentationDescriptor = NULL;
        m_pMediaSource->CreatePresentationDescriptor(&pPresentationDescriptor);
        if (!pPresentationDescriptor)break;
        {
            IMFClock* pClock = NULL;
            m_pMediaSession->GetClock(&pClock);
            if (pClock) {
                pClock->QueryInterface(IID_IMFPresentationClock, (void**)&m_pPresentationClock);
                pClock->Release();
            }
        }
        DWORD dwStreamCount = 0;
        pPresentationDescriptor->GetStreamDescriptorCount(&dwStreamCount);
        for (DWORD i = 0; i < dwStreamCount; i++) {
            IMFStreamDescriptor* pStreamDescriptor = NULL;
            BOOL bSelected = FALSE;
            pPresentationDescriptor->GetStreamDescriptorByIndex(i, &bSelected, &pStreamDescriptor);
            if (bSelected) {
                IMFActivate* pSinkActivate = NULL;
                HRESULT hr = CreateMediaSinkActivate(pStreamDescriptor, &pSinkActivate);
                if (SUCCEEDED(hr)) {
                    IMFTopologyNode* pSourceNode = NULL;
                    IMFTopologyNode* pOutputNode = NULL;
                    AddSourceNode(pPresentationDescriptor, pStreamDescriptor, &pSourceNode);
                    AddOutputNode(pSinkActivate, &pOutputNode);
                    if (pSourceNode && pOutputNode)pSourceNode->ConnectOutput(0, pOutputNode, 0);
                    if (pOutputNode)pOutputNode->Release();
                    if (pSourceNode)pSourceNode->Release();
                }
                if (pSinkActivate)pSinkActivate->Release();
            }
            if (pStreamDescriptor)pStreamDescriptor->Release();
        }
        ResetEvent(m_hMFEvent);
        HRESULT hr = m_pMediaSession->BeginGetEvent(this, NULL);
        hr = m_pMediaSession->SetTopology(0, m_pTopology);
        if(SUCCEEDED(hr)){
            WaitForSingleObject(m_hMFEvent, INFINITE);
        }
        else{
            m_pMediaSession->Stop();
            m_pMediaSession->Close();
            m_pMediaSource->Shutdown();
            m_pMediaSession->Shutdown();
            m_pMediaSource->Release();m_pMediaSource = NULL;
            m_pMediaSession->Release();m_pMediaSession = NULL;
        }
    }while (0);

    MessageLoop();
    if (m_pPresentationClock) {
        m_pPresentationClock->Release(); m_pPresentationClock = NULL;
    }
    if (m_pVideoDisplayControl) {
        m_pVideoDisplayControl->Release(); m_pVideoDisplayControl = NULL;
    }
    if (m_pTopology) {
        m_pTopology->Release(); m_pTopology = NULL;
    }
    if (m_pMediaSource){
        m_pMediaSource->Release();m_pMediaSource = NULL;
    }
    if (m_pMediaSession) {
        m_pMediaSession->Release(); m_pMediaSession = NULL;
    }
    if (m_hWndDraw) {
        DestroyWindow(m_hWndDraw); m_hWndDraw = NULL;
    }
    if (m_hWnd) {
        DestroyWindow(m_hWnd); m_hWnd = NULL;
    }
}
/////////////////////////////////////////////////////////////////////////
void __fastcall KbMFDecoder::MessageLoop(void)
{
    if(!m_pMediaSession)return;
    HANDLE hHandles[] = { m_hEvent };
    DWORD dwWait;
    MSG msg;
    PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
    SetEvent(m_hEventDone);
    DWORD dwWaitTime = INFINITE;
    while (1) {
        if (m_hWnd) {
            dwWait = MsgWaitForMultipleObjects(1, hHandles, FALSE, dwWaitTime/*INFINITE*/, QS_ALLINPUT);
            if (dwWait == WAIT_OBJECT_0 + 1) {
                if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
                continue;
            }
            else if (dwWait == WAIT_TIMEOUT) {
                if (!m_pStatus){
                    dwWaitTime = INFINITE;
                }
                else if (m_pStatus->dwStatus == KPI_STATUS_STOP) {
                    if(m_nStatus != KPI_STATUS_STOP){
                        m_nStatus = KPI_STATUS_STOP;
                        m_pMediaSession->Stop();
                        if (m_nStopMode != 0) {
                            if (m_hWnd){
                                SendMessage(m_hWnd, WM_CLOSE, 0, 0);
                            }
                        }
                    }
                    dwWaitTime = INFINITE;
                }
                else if(m_pStatus->dwStatus == KPI_STATUS_PAUSE){
                    if(m_nStatus != KPI_STATUS_PAUSE){
                        m_nStatus = KPI_STATUS_PAUSE;
                        m_pMediaSession->Pause();
                    }
                }
                else if(m_pStatus->dwStatus == KPI_STATUS_PLAYING){
                    if (m_pPresentationClock) {
                        MFTIME mftime = 0;
                        m_pPresentationClock->GetTime(&mftime);
                        DWORD dwPos = mftime / 10000;
                        int diff = m_pStatus->dwPosition - dwPos;
                        if(abs(diff) > 100){
#ifdef _DEBUG
                            wchar_t sz[128];
                            _snwprintf_s(sz, _TRUNCATE, L"diff=%d\n", diff);
                            OutputDebugString(sz);
#endif
                                
                            PROPVARIANT varStart = { 0 };
                            varStart.vt = VT_I8;
                            varStart.hVal.QuadPart = (UINT64)m_pStatus->dwPosition * 10000ui64;
                            ResetEvent(m_hMFEvent);
                            m_pMediaSession->Start(NULL, &varStart);
                            m_pMediaSession->BeginGetEvent(this, NULL);
                            WaitForSingleObject(m_hMFEvent, INFINITE);
                            m_nStatus = KPI_STATUS_PLAYING;
                        }
                    }
                }
                continue;
            }
        }
        else {
            dwWait = WaitForSingleObject(m_hEvent, INFINITE);
        }
        switch (m_Command.nCommand) {
        case COMMAND_RUN: {
            //KbPresentationTimeSource *pTimeSource = new KbPresentationTimeSource(m_pStatus);
            //m_pPresentationClock->SetTimeSource(pTimeSource);
            //pTimeSource->Release();
            UINT64 qwStartPos = *(UINT64*)m_Command.wParam;
            PROPVARIANT varStart = { 0 };
            varStart.vt = VT_I8;
            varStart.hVal.QuadPart = qwStartPos;
            if (!m_pVideoDisplayControl) {
                MFGetService(m_pMediaSession,
                    MR_VIDEO_RENDER_SERVICE,
                    __uuidof(IMFVideoDisplayControl),
                    (void**)&m_pVideoDisplayControl);
                if (m_pVideoDisplayControl) {
                    m_pVideoDisplayControl->SetVideoWindow(m_hWndDraw);
                    m_pVideoDisplayControl->SetAspectRatioMode(MFVideoARMode_PreservePicture);
                }
            }
            ResetEvent(m_hMFEvent);
            m_pMediaSession->Start(NULL, &varStart);
            m_pMediaSession->BeginGetEvent(this, NULL);
            WaitForSingleObject(m_hMFEvent, INFINITE);
            m_nStatus = KPI_STATUS_PLAYING;
            dwWaitTime = 100;//m_nStop != 0 ? 1000 : INFINITE;
            UpdateWindowPosition();
            SetEvent(m_hEventDone);
            break;
        }//run
        case COMMAND_QUIT: {
            if (m_pMediaSession) {
                ResetEvent(m_hMFEvent);
                if (!FAILED(m_pMediaSession->Close())) {
                    WaitForSingleObject(m_hMFEvent, INFINITE);
                }
                m_pMediaSource->Shutdown();
                m_pMediaSession->Shutdown();
            }
            if (m_hWnd) {
                SendMessage(m_hWnd, WM_CLOSE, 0, 0);
                if (m_pVideoDisplayControl) {
                    //HRESULT hr = m_pVideoDisplayControl->SetVideoWindow(NULL);
                    m_pVideoDisplayControl->Release();
                    m_pVideoDisplayControl = NULL;
                }
                DestroyWindow(m_hWndDraw); m_hWndDraw = NULL;
                DestroyWindow(m_hWnd); m_hWnd = NULL;
            }
            return;
            //PostQuitMessage(0);
            break;
        }//quit
        case COMMAND_SETVIDEOPOS: {
            UpdateWindowPosition();
            SetEvent(m_hEventDone);
            break;
        };
        default:
            break;
        }
    }
}
/////////////////////////////////////////////////////////////////////////
void __fastcall KbMFDecoder::CreateVideoWindow(void)
{
    if (m_hWnd || m_hThread) {
        return;
    }
    if (m_MediaInfo.dwVideoWidth == 0 || m_MediaInfo.dwVideoHeight == 0) {
        return;
    }
    if (!m_pStatus || !m_pStatus->dwAccepted) {//本体から再生位置情報を取得出来ない
        return;
    }
    CreateThread();
}
/////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK KbMFDecoder::VideoWindowProcInternal(HWND hWnd,
    UINT message, WPARAM wParam, LPARAM lParam)
{
    if(hWnd == m_hWndDraw){
        if(message != WM_PAINT){
            return KbDShowDecoderBase::VideoWindowProcInternal(hWnd, message, wParam, lParam);
        }
    }
    switch (message) {
    case WM_CLOSE: {
        if (m_pVideoDisplayControl) {
            ShowWindow(hWnd, SW_HIDE);
            if (m_nStopMode == 2) {
                //m_bStop = true;
            }
        }
        break;
    }
    case WM_SIZE: {
        if (m_pVideoDisplayControl) {
            double ratio = (double)m_MediaInfo.dwVideoWidth / (double)m_MediaInfo.dwVideoHeight;
            int cx = LOWORD(lParam);
            int cy = HIWORD(lParam);
            int new_cx = cx;
            int new_cy = (double)cx / ratio;
            if (new_cy > cy) {
                new_cy = cy;
                new_cx = (double)cy * ratio;
            }
            RECT rc;
            rc.left = (cx - new_cx) / 2;
            rc.top = (cy - new_cy) / 2;
            rc.right = rc.left + new_cx;
            rc.bottom = rc.top + new_cy;
            MoveWindow(m_hWndDraw, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, false/*TRUE*/);
            rc.left = rc.top = 0;
            rc.right = new_cx;
            rc.bottom = new_cy;
            m_pVideoDisplayControl->SetVideoPosition(NULL, &rc);
        }
        break;
    }
    case WM_PAINT: {
        if (m_pVideoDisplayControl) {
            m_pVideoDisplayControl->RepaintVideo();
        }
        break;
    }
    default:
        break;
    }
    return KbDShowDecoderBase::VideoWindowProcInternal(hWnd, message, wParam, lParam);
}
/////////////////////////////////////////////////////////////////////////
HRESULT KbMFDecoder::CreateMediaSinkActivate(IMFStreamDescriptor* stream_desc,
    IMFActivate** activate)
{
    IMFMediaTypeHandler* handler = NULL;
    GUID guidMajorType = GUID_NULL;
    HRESULT hr;
    hr = stream_desc->GetMediaTypeHandler(&handler);
    if (!handler) {
        return hr;
    }
    handler->GetMajorType(&guidMajorType);
    if (guidMajorType == MFMediaType_Audio) {
        //hr = MFCreateAudioRendererActivate(activate);
        hr = MF_E_INVALIDMEDIATYPE;
    }
    else if (guidMajorType == MFMediaType_Video) {
        //KbDShow_Settings settings;
        //kbdshow_GetSettings(m_pConfig, &settings);
        //if (settings.bVideo) {
            if (!m_hWnd) {
                int nLeft = 0, nTop = 0, nWidth = 0, nHeight = 0;
                kbdshow_GetVideoPosition(m_pConfig, &nLeft, &nTop, &nWidth, &nHeight);
                if (nWidth == 0 || nHeight == 0) {
                    nWidth = m_MediaInfo.dwVideoWidth;
                    nHeight = m_MediaInfo.dwVideoHeight;
                }
                nWidth += GetSystemMetrics(SM_CXFRAME) * 2;
                nHeight += ( + GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME) * 2);
                wchar_t szCaption[MAX_PATH];
                m_pFile->GetFileName(szCaption, sizeof(szCaption));
                wcsncat_s(szCaption, L"(MF)", _TRUNCATE);
                KbDShowDecoderBase::CreateVideoWindow(nLeft, nTop, nWidth, nHeight);
                SetWindowText(m_hWnd, szCaption);
            }
            hr = MFCreateVideoRendererActivate(m_hWndDraw, activate);
        //}
        //else {
        //    hr = MF_E_INVALIDMEDIATYPE;
        //}
    }
    else {
        hr = MF_E_INVALIDMEDIATYPE;
    }
    handler->Release();
    return hr;
}
/////////////////////////////////////////////////////////////////////////
void KbMFDecoder::AddSourceNode(IMFPresentationDescriptor* pres_desc,
    IMFStreamDescriptor* stream_desc, IMFTopologyNode** ppNode)
{
    IMFTopologyNode* pNode = NULL;
    MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &pNode);
    if (pNode) {
        pNode->SetUnknown(MF_TOPONODE_SOURCE, m_pMediaSource);
        pNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pres_desc);
        pNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, stream_desc);
        m_pTopology->AddNode(pNode);
    }
    *ppNode = pNode;
}
/////////////////////////////////////////////////////////////////////////
void KbMFDecoder::AddOutputNode(IMFActivate* activate, IMFTopologyNode** ppNode)
{
    IMFTopologyNode* pNode = NULL;
    MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pNode);
    if (pNode) {
        pNode->SetObject(activate);
        pNode->SetUINT32(MF_TOPONODE_STREAMID, 0);
        m_pTopology->AddNode(pNode);
    }
    *ppNode = pNode;
}
/////////////////////////////////////////////////////////////////////////
HRESULT STDMETHODCALLTYPE  KbMFDecoder::Invoke(IMFAsyncResult* pAsyncResult)
{
    HRESULT hr;
    MediaEventType me_type = MEUnknown;
    IMFMediaEvent* media_event = NULL;
    hr = m_pMediaSession->EndGetEvent(pAsyncResult, &media_event);
    if (!media_event) {
        if (WaitForSingleObject(m_hMFEvent, 0) == WAIT_TIMEOUT) {
            SetEvent(m_hMFEvent);
        }
        return hr;
    }
    hr = media_event->GetType(&me_type);
    if (me_type == MESessionClosed) {
        SetEvent(m_hMFEvent);
        media_event->Release();
        return hr;
    }
    media_event->GetStatus(&hr);
    if (FAILED(hr)) {
        if (WaitForSingleObject(m_hMFEvent, 0) == WAIT_TIMEOUT) {
            SetEvent(m_hMFEvent);
        }
        //return hr;
    }
    else if (me_type == MESessionStarted) {
        SetEvent(m_hMFEvent);
    }
    else if (me_type == MESessionPaused) {
        //SetEvent(m_hMFEvent);
    }
    else if (me_type == MESessionStopped) {
        SetEvent(m_hMFEvent);
    }
    else if (me_type == MESessionEnded) {
    }
    else if (me_type == MESessionTopologyStatus) {
        MF_TOPOSTATUS topo_status = MF_TOPOSTATUS_INVALID;
        media_event->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, (UINT32*)&topo_status);
        if (topo_status == MF_TOPOSTATUS_READY) {
            SetEvent(m_hMFEvent);
        }
        //if (topo_status == MF_TOPOSTATUS_STARTED_SOURCE) {//MESessionStarted より先に呼ばれる
        //  int t = 0;
        //}
    }
    media_event->Release();
    hr = m_pMediaSession->BeginGetEvent(this, NULL);
    return hr;
}
/////////////////////////////////////////////////////////////////////////
