//---------------------------------------------------------------------------
//	DVCapture
//  2002 - 2005 Stanislav Sumec <sumec@fit.vutbr.cz>
//
//	DSTools.cpp
//  DirectShow auxiliary functions
//---------------------------------------------------------------------------
#include "dshow_hack.h"
#pragma hdrstop
#include "DSTools.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
//---------------------------------------------------------------------------

HRESULT InitCaptureGraphBuilder(IGraphBuilder **ppGraph, ICaptureGraphBuilder2 **ppBuild, IMediaControl **ppMC) {
    if (!ppGraph || !ppBuild)
    {
        return E_POINTER;
    }
    IGraphBuilder *pGraph = NULL;
    ICaptureGraphBuilder2 *pBuild = NULL;

    // Create the Capture Graph Builder.
    HRESULT hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL,
        CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void**)&pBuild);
    if (SUCCEEDED(hr))
    {
        // Create the Filter Graph Manager.
        hr = CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER,
            IID_IGraphBuilder, (void**)&pGraph);
        if (SUCCEEDED(hr))
        {
            // Initialize the Capture Graph Builder.
            pBuild->SetFiltergraph(pGraph);

            // Return both interface pointers to the caller.
            *ppBuild = pBuild;
            *ppGraph = pGraph; // The caller must release both interfaces.

            // Obtain interfaces for media control and Video Window
    				hr = pGraph->QueryInterface(IID_IMediaControl,(LPVOID *) ppMC);
    				if (FAILED(hr)) {
            	pGraph->Release();
            	pBuild->Release();
            	return hr;
            }
            return S_OK;
        }
        else
        {
            pBuild->Release();
        }
    }
    return hr; // Failed
}
//---------------------------------------------------------------------------

HRESULT AddGraphToRot(IUnknown *pUnkGraph, DWORD *pdwRegister) {
    IMoniker * pMoniker;
    IRunningObjectTable *pROT;
    WCHAR wsz[128];
    HRESULT hr;

    if (!pUnkGraph || !pdwRegister)
        return E_POINTER;

    if (FAILED(GetRunningObjectTable(0, &pROT)))
        return E_FAIL;

    wsprintfW(wsz, L"FilterGraph %08x pid %08x\0", (DWORD_PTR)pUnkGraph,
              GetCurrentProcessId());

    hr = CreateItemMoniker(L"!", wsz, &pMoniker);
    if (SUCCEEDED(hr))
    {
        // Use the ROTFLAGS_REGISTRATIONKEEPSALIVE to ensure a strong reference
        // to the object.  Using this flag will cause the object to remain
        // registered until it is explicitly revoked with the Revoke() method.
        //
        // Not using this flag means that if GraphEdit remotely connects
        // to this graph and then GraphEdit exits, this object registration
        // will be deleted, causing future attempts by GraphEdit to fail until
        // this application is restarted or until the graph is registered again.
        hr = pROT->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, pUnkGraph,
                            pMoniker, pdwRegister);
        pMoniker->Release();
    }

    pROT->Release();
    return hr;
}
//---------------------------------------------------------------------------

void RemoveGraphFromRot(DWORD pdwRegister) {
    IRunningObjectTable *pROT;

    if (SUCCEEDED(GetRunningObjectTable(0, &pROT)))
    {
        pROT->Revoke(pdwRegister);
        pROT->Release();
    }
}
//---------------------------------------------------------------------------

TIBaseFilters GetFilters(IGraphBuilder *pGraph) {
	TIBaseFilters Filters;
  // Enumerate the filters in the graph.
  IEnumFilters *pEnum = NULL;
  HRESULT hr = pGraph->EnumFilters(&pEnum);
  if (SUCCEEDED(hr)) {
    IBaseFilter *pFilter = NULL;
    while (S_OK == pEnum->Next(1, &pFilter, NULL))
    	Filters.push_back(pFilter);
    pEnum->Release();
  }
  return Filters;
}
//---------------------------------------------------------------------------

void ReleaseFilters(TIBaseFilters &Filters) {
	TIBaseFilters::iterator i = Filters.begin();
  while (i != Filters.end()) {
  	(*i)->Release();
  	i++;
  }
  Filters.clear();
}
//---------------------------------------------------------------------------

void RemoveFilters(IGraphBuilder *pGraph, TIBaseFilters &Filters) {
	TIBaseFilters::iterator i = Filters.begin();
  while (i != Filters.end()) {
  	SAFE_REMOVE(pGraph, (*i));
  	i++;
  }
  Filters.clear();
}
//---------------------------------------------------------------------------

TIBaseFilters GetNewFilters(IGraphBuilder *pGraph, TIBaseFilters OldFilters) {
  TIBaseFilters NewFilters = GetFilters(pGraph), DiffFilters;
  TIBaseFilters::iterator i = NewFilters.begin();
  while (i != NewFilters.end()) {
    TIBaseFilters::iterator j = OldFilters.begin();
    while (j != OldFilters.end() && (*i) != (*j))
      j++;
    if (j == OldFilters.end())
      DiffFilters.push_back(*i);
    else
      (*i)->Release();
    i++;
  }
	return DiffFilters;
}
//---------------------------------------------------------------------------

Pins GetPins(IBaseFilter *f, PIN_DIRECTION dir) {
  Pins pins;

  IEnumPins  *pEnum;
  IPin       *pPin;
  int SelectedPin = -1;

  HRESULT hr = f->EnumPins(&pEnum);
  if (SUCCEEDED(hr)) {
    while(pEnum->Next(1, &pPin, 0) == S_OK) {
      PIN_INFO pi;
      hr = pPin->QueryPinInfo(&pi);
      if (SUCCEEDED(hr)) {
        if (pi.dir == dir) {
          pins.push_back(pPin);
        } else
          pPin->Release();
        pi.pFilter->Release();
      } else
        pPin->Release();
    }
    pEnum->Release();
  }

  return pins;
}
//---------------------------------------------------------------------------

void ReleasePins(Pins &pins) {
  for (int i = 0; i < pins.size(); i++)
    pins[i]->Release();
  pins.clear();
}
//---------------------------------------------------------------------------

void GetPinParams(IPin *p, double &level, double &pan, bool &enabled) {
  level = 0;
  pan = 0;
  enabled = false;

  IAMAudioInputMixer *pPinMixer;

  int hr = p->QueryInterface(IID_IAMAudioInputMixer, (void **)&pPinMixer);
  if (SUCCEEDED(hr)) {
    int e;
    hr = pPinMixer->get_Enable(&e);
    if (SUCCEEDED(hr))
      enabled = e;
  /*
    hr = pPinMixer->get_Bass(&dblBass);
    hr = pPinMixer->get_BassRange(&dblBassRange);
    hr = pPinMixer->get_Loudness(&bLoudness);
  */
    hr = pPinMixer->get_MixLevel(&level);
/*
    hr = pPinMixer->get_Mono(&bMono);
*/
    hr = pPinMixer->get_Pan(&pan);
/*
    hr = pPinMixer->get_Treble(&dblTreble);
    hr = pPinMixer->get_TrebleRange(&dblTrebleRange);
*/
    pPinMixer->Release();
  }
}
//---------------------------------------------------------------------------

int FindSelectedPin(Pins &pins) {
  for (int i = 0; i < pins.size(); i++) {
    double level, pan;
    bool enabled;
    GetPinParams(pins[i], level, pan, enabled);
    if (enabled)
      return i;
  }
  return -1;
}
//---------------------------------------------------------------------------

AnsiString GetPinName(IPin *p) {
  PIN_INFO pi;
  int hr = p->QueryPinInfo(&pi);
  if (SUCCEEDED(hr)) {
    pi.pFilter->Release();
    return pi.achName + 1;
  } else
    return "unknown";
}
//---------------------------------------------------------------------------

int FindPin(Pins &pins, AnsiString name) {
  for (int i = 0; i < pins.size(); i++) {
    if (GetPinName(pins[i]) == name)
      return i;
  }
  return -1;
}
//---------------------------------------------------------------------------

void SetPinParams(IPin *p, double level, double pan, bool enabled) {
  IAMAudioInputMixer *pPinMixer;
  int hr = p->QueryInterface(IID_IAMAudioInputMixer, (void **)&pPinMixer);
  if (SUCCEEDED(hr)) {
    pPinMixer->put_Enable(enabled);
    pPinMixer->put_MixLevel(level);
    pPinMixer->put_Pan(pan);

    pPinMixer->put_Mono(false);
    pPinMixer->put_Loudness(0);
    pPinMixer->put_Treble(0);
    pPinMixer->put_Bass(0);

    pPinMixer->Release();
  }
}
//---------------------------------------------------------------------------

IPin *GetPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, int Number) {
    BOOL       bFound = FALSE;
    IEnumPins  *pEnum;
    IPin       *pPin;
    int Count = 0;

    HRESULT hr = pFilter->EnumPins(&pEnum);
    if (FAILED(hr))
    {
        return NULL;
    }
    while(pEnum->Next(1, &pPin, 0) == S_OK)
    {
        PIN_DIRECTION PinDirThis;
        pPin->QueryDirection(&PinDirThis);

        if (PinDir == PinDirThis) {
          if (Count == Number) {
            bFound = TRUE;
            break;
          } else
            Count++;
        }

        pPin->Release();
    }
    pEnum->Release();
    return (bFound ? pPin : NULL);
}
//---------------------------------------------------------------------------

void GetAudioParams(ICaptureGraphBuilder2 *pBuild, IBaseFilter *f, int &rate, int &bits, int &chn, AUDIO_STREAM_CONFIG_CAPS &scc) {
  rate = 44100;
  bits = 16;
  chn = 2;
  scc.MinimumChannels = chn;
  scc.MaximumChannels = chn;
  scc.ChannelsGranularity = 1;
  scc.MinimumBitsPerSample = bits;
  scc.MaximumBitsPerSample = bits;
  scc.BitsPerSampleGranularity = 1;
  scc.MinimumSampleFrequency = rate;
  scc.MaximumSampleFrequency = rate;
  scc.SampleFrequencyGranularity = 1;

  IAMStreamConfig *conf = NULL;
  int hr = pBuild->FindInterface(NULL, &MEDIATYPE_Audio, f, IID_IAMStreamConfig, (void**)&conf);
  if (SUCCEEDED(hr)) {
    int iCount, iSize;
    hr = conf->GetNumberOfCapabilities(&iCount, &iSize);
    if (SUCCEEDED(hr)) {
      AM_MEDIA_TYPE *pmt;
      if (sizeof(scc) == iSize) {
        hr = conf->GetStreamCaps(0, &pmt, (BYTE*)(&scc));
        if (SUCCEEDED(hr) /*&& pmt->majortype == MEDIATYPE_Audio*/) {
          if (pmt->formattype == FORMAT_WaveFormatEx && pmt->cbFormat >= sizeof(WAVEFORMAT)) {
            WAVEFORMAT *wf = (WAVEFORMAT*)pmt->pbFormat;
            chn = wf->nChannels;
            bits = 8 * wf->nAvgBytesPerSec / wf->nSamplesPerSec / wf->nChannels;
            rate = wf->nSamplesPerSec;
          }
          DeleteMediaType(pmt);
        }
      }
    }
    SAFE_RELEASE(conf);
  }
}
//---------------------------------------------------------------------------

void GetAudioParams(ICaptureGraphBuilder2 *pBuild, IBaseFilter *f, int &rate, int &bits, int &chn) {
  AUDIO_STREAM_CONFIG_CAPS scc;
  GetAudioParams(pBuild, f, rate, bits, chn, scc);
}
//---------------------------------------------------------------------------

void SetAudioParams(ICaptureGraphBuilder2 *pBuild, IBaseFilter *f, int rate, int bits, int chn) {
  IAMStreamConfig *conf = NULL;
  int hr = pBuild->FindInterface(NULL, &MEDIATYPE_Audio, f, IID_IAMStreamConfig, (void**)&conf);
  if (SUCCEEDED(hr)) {
    int iCount, iSize;
    hr = conf->GetNumberOfCapabilities(&iCount, &iSize);
    if (SUCCEEDED(hr)) {
      AUDIO_STREAM_CONFIG_CAPS scc;
      AM_MEDIA_TYPE *pmt;
      if (sizeof(scc) == iSize) {
        hr = conf->GetStreamCaps(0, &pmt, (char*)(&scc));
        if (SUCCEEDED(hr)) {
          if (pmt->formattype == FORMAT_WaveFormatEx && pmt->cbFormat >= sizeof(WAVEFORMAT)) {
            WAVEFORMAT *wf = (WAVEFORMAT*)pmt->pbFormat;
            wf->nChannels = chn;
            wf->nSamplesPerSec = rate;
            wf->nAvgBytesPerSec = (bits / 8) * wf->nSamplesPerSec * wf->nChannels;
            pmt->lSampleSize = (bits / 8) * wf->nChannels;
            conf->SetFormat(pmt);
          }
          DeleteMediaType(pmt);
        }
      }
    }
    SAFE_RELEASE(conf);
  }
}
//---------------------------------------------------------------------------

void GetVideoParams(ICaptureGraphBuilder2 *pBuild, IBaseFilter *f, int &width, int &height, int &frames, VIDEO_STREAM_CONFIG_CAPS &scc) {
  width = 720;
  height = 576;
  frames = 25;

  scc.MinCroppingSize.cx = width;
  scc.MaxCroppingSize.cx = width;
  scc.MinCroppingSize.cy = height;
  scc.MaxCroppingSize.cy = height;
  scc.CropGranularityX = 1;
  scc.CropGranularityY = 1;
#if 0
  IAMStreamConfig *conf = NULL;
  int hr = pBuild->FindInterface(NULL, &MEDIATYPE_Video, f, IID_IAMStreamConfig, (void**)&conf);
  if (SUCCEEDED(hr)) {
    int iCount, iSize;
    hr = conf->GetNumberOfCapabilities(&iCount, &iSize);
    if (SUCCEEDED(hr)) {
      AM_MEDIA_TYPE *pmt;
      VIDEO_STREAM_CONFIG_CAPS nscc;

      if (sizeof(nscc) == iSize) {
/*        for (int i = 0; i < iCount; i++) */{
        hr = conf->GetStreamCaps(1, &pmt, (BYTE*)(&nscc));
        if (SUCCEEDED(hr)) {
          if (pmt->formattype == FORMAT_VideoInfo /*&& pmt->majortype == MEDIATYPE_Interleaved*/) {
            scc = nscc;

            VIDEOINFOHEADER *vh = (VIDEOINFOHEADER*)pmt->pbFormat;
            width = vh->bmiHeader.biWidth;
            height = vh->bmiHeader.biHeight;
          }
          DeleteMediaType(pmt);
        }
        }
      }
    }
    SAFE_RELEASE(conf);
  }
#endif
}
//---------------------------------------------------------------------------

void SetVideoParams(ICaptureGraphBuilder2 *pBuild, IBaseFilter *f, int width, int height, int frames) {
#if 0
  IAMStreamConfig *conf = NULL;
  int hr = pBuild->FindInterface(NULL, &MEDIATYPE_Video, f, IID_IAMStreamConfig, (void**)&conf);
  if (SUCCEEDED(hr)) {
    int iCount, iSize;
    hr = conf->GetNumberOfCapabilities(&iCount, &iSize);
    if (SUCCEEDED(hr)) {
      AM_MEDIA_TYPE *pmt;
      VIDEO_STREAM_CONFIG_CAPS nscc;

      if (sizeof(nscc) == iSize) {
/*        for (int i = 0; i < iCount; i++) */{
        hr = conf->GetStreamCaps(1, &pmt, (BYTE*)(&nscc));
        if (SUCCEEDED(hr) /*&& pmt->majortype == MEDIATYPE_Audio*/) {
          if (pmt->formattype == FORMAT_VideoInfo /*&& pmt->majortype == MEDIATYPE_Interleaved*/) {
            VIDEOINFOHEADER *vh = (VIDEOINFOHEADER*)pmt->pbFormat;
            vh->rcTarget.right = width;
            vh->rcTarget.bottom = height;
            vh->rcSource.right = width;
            vh->rcSource.bottom = height;


            vh->bmiHeader.biWidth = width;
            vh->bmiHeader.biHeight = height;

            conf->SetFormat(pmt);
/*
            VIDEOINFOHEADER *vh = (VIDEOINFOHEADER*)pmt->pbFormat;
            width = vh->bmiHeader.biWidth;
            height = vh->bmiHeader.biHeight;
*/
          }
          DeleteMediaType(pmt);
        }
        }
      }
    }
    SAFE_RELEASE(conf);
  }
#endif
}
//---------------------------------------------------------------------------

int Round(double v) {
  if (v < 0)
    return (int(v) - v < 0.5) ? int(v) : int(v) - 1;
  else
    return (v - int(v) < 0.5) ? int(v) : int(v) + 1;
}
//---------------------------------------------------------------------------

void ShowFilterProperties(HWND Handle, IBaseFilter *pFilter) {
    ISpecifyPropertyPages *pProp;
    HRESULT hr = pFilter->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pProp);
    if (SUCCEEDED(hr)) {
      // Get the filter's name and IUnknown pointer.
      FILTER_INFO FilterInfo;
      hr = pFilter->QueryFilterInfo(&FilterInfo);
      IUnknown *pFilterUnk;
      pFilter->QueryInterface(IID_IUnknown, (void **)&pFilterUnk);

      // Show the page.
      CAUUID caGUID;
      pProp->GetPages(&caGUID);
      pProp->Release();

      OleCreatePropertyFrame(
          Handle,                 // Parent window
          0, 0,                   // Reserved
          FilterInfo.achName,     // Caption for the dialog box
          1,                      // Number of objects (just the filter)
          &pFilterUnk,            // Array of object pointers.
          caGUID.cElems,          // Number of property pages
          caGUID.pElems,          // Array of property page CLSIDs
          0,                      // Locale identifier
          0, NULL                 // Reserved
      );
      // Clean up.
      pFilterUnk->Release();
      FilterInfo.pGraph->Release();
      CoTaskMemFree(caGUID.pElems);
    }
}
//---------------------------------------------------------------------------
