home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 275 / DPCS0111DVD.ISO / Toolkit / Audio-Visual / VirtualDub / Source / VirtualDub-1.9.10-src.7z / src / Riza / source / cap_vfw.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2009-09-14  |  34.5 KB  |  1,238 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    A/V interface library
  3. //    Copyright (C) 1998-2004 Avery Lee
  4. //
  5. //    This program is free software; you can redistribute it and/or modify
  6. //    it under the terms of the GNU General Public License as published by
  7. //    the Free Software Foundation; either version 2 of the License, or
  8. //    (at your option) any later version.
  9. //
  10. //    This program is distributed in the hope that it will be useful,
  11. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. //    GNU General Public License for more details.
  14. //
  15. //    You should have received a copy of the GNU General Public License
  16. //    along with this program; if not, write to the Free Software
  17. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  19. #ifdef _MSC_VER
  20.     #pragma warning(disable: 4786)        // STFU
  21. #endif
  22.  
  23. #include <vd2/Riza/capdriver.h>
  24. #include <vd2/system/vdstring.h>
  25. #include <vd2/system/error.h>
  26. #include <vd2/system/time.h>
  27. #include <windows.h>
  28. #include <vfw.h>
  29. #include <vector>
  30.  
  31. using namespace nsVDCapture;
  32.  
  33. extern HINSTANCE g_hInst;
  34.  
  35. static const CAPTUREPARMS g_defaultCaptureParms={
  36.     1000000/15,        // 15fps
  37.     FALSE,            // fMakeUserHitOKForCapture
  38.     100,            // wPercentDropForError
  39.     TRUE,            // callbacks won't work if Yield is TRUE
  40.     324000,            // we like index entries
  41.     4,                // wChunkGranularity
  42.     FALSE,            // fUsingDOSMemory
  43.     10,                // wNumVideoRequested
  44.     TRUE,            // fCaptureAudio
  45.     0,                // wNumAudioRequested
  46.     0,                // vKeyAbort
  47.     FALSE,            // fAbortLeftMouse
  48.     FALSE,            // fAbortRightMouse
  49.     FALSE,            // fLimitEnabled
  50.     0,                // wTimeLimit
  51.     FALSE,            // fMCIControl
  52.     FALSE,            // fStepMCIDevice
  53.     0,0,            // dwMCIStartTime, dwMCIStopTime
  54.     FALSE,            // fStepCaptureAt2x
  55.     10,                // wStepCaptureAverageFrames
  56.     0,                // dwAudioBufferSize
  57.     FALSE,            // fDisableWriteCache
  58.     AVSTREAMMASTER_NONE,    //    AVStreamMaster
  59. };
  60.  
  61. class VDCaptureDriverVFW : public IVDCaptureDriver {
  62.     VDCaptureDriverVFW(const VDCaptureDriverVFW&);
  63.     VDCaptureDriverVFW& operator=(const VDCaptureDriverVFW&);
  64. public:
  65.     VDCaptureDriverVFW(HMODULE hmodAVICap, int driverIndex);
  66.     ~VDCaptureDriverVFW();
  67.  
  68.     void    *AsInterface(uint32 id) { return NULL; }
  69.  
  70.     bool    Init(VDGUIHandle hParent);
  71.     void    Shutdown();
  72.  
  73.     void    SetCallback(IVDCaptureDriverCallback *pCB);
  74.  
  75.     void    LockUpdates() {}
  76.     void    UnlockUpdates() {}
  77.  
  78.     bool    IsHardwareDisplayAvailable();
  79.  
  80.     void    SetDisplayMode(nsVDCapture::DisplayMode m);
  81.     nsVDCapture::DisplayMode        GetDisplayMode();
  82.  
  83.     void    SetDisplayRect(const vdrect32& r);
  84.     vdrect32    GetDisplayRectAbsolute();
  85.     void    SetDisplayVisibility(bool vis);
  86.  
  87.     void    SetFramePeriod(sint32 ms);
  88.     sint32    GetFramePeriod();
  89.  
  90.     uint32    GetPreviewFrameCount();
  91.  
  92.     bool    GetVideoFormat(vdstructex<BITMAPINFOHEADER>& vformat);
  93.     bool    SetVideoFormat(const BITMAPINFOHEADER *pbih, uint32 size);
  94.  
  95.     bool    SetTunerChannel(int channel) { return false; }
  96.     int        GetTunerChannel() { return -1; }
  97.     bool    GetTunerChannelRange(int& minChannel, int& maxChannel) { return false; }
  98.     uint32    GetTunerFrequencyPrecision() { return 0; }
  99.     uint32    GetTunerExactFrequency() { return 0; }
  100.     bool    SetTunerExactFrequency(uint32 freq) { return false; }
  101.     nsVDCapture::TunerInputMode    GetTunerInputMode() { return kTunerInputUnknown; }
  102.     void    SetTunerInputMode(nsVDCapture::TunerInputMode tunerMode) {}
  103.  
  104.     int        GetAudioDeviceCount();
  105.     const wchar_t *GetAudioDeviceName(int idx);
  106.     bool    SetAudioDevice(int idx);
  107.     int        GetAudioDeviceIndex();
  108.     bool    IsAudioDeviceIntegrated(int idx) { return false; }
  109.  
  110.     int        GetVideoSourceCount();
  111.     const wchar_t *GetVideoSourceName(int idx);
  112.     bool    SetVideoSource(int idx);
  113.     int        GetVideoSourceIndex();
  114.  
  115.     int        GetAudioSourceCount();
  116.     const wchar_t *GetAudioSourceName(int idx);
  117.     bool    SetAudioSource(int idx);
  118.     int        GetAudioSourceIndex();
  119.  
  120.     int        GetAudioInputCount();
  121.     const wchar_t *GetAudioInputName(int idx);
  122.     bool    SetAudioInput(int idx);
  123.     int        GetAudioInputIndex();
  124.  
  125.     int        GetAudioSourceForVideoSource(int idx) { return -2; }
  126.  
  127.     bool    IsAudioCapturePossible();
  128.     bool    IsAudioCaptureEnabled();
  129.     bool    IsAudioPlaybackPossible() { return false; }
  130.     bool    IsAudioPlaybackEnabled() { return false; }
  131.     void    SetAudioCaptureEnabled(bool b);
  132.     void    SetAudioAnalysisEnabled(bool b);
  133.     void    SetAudioPlaybackEnabled(bool b) {}
  134.  
  135.     void    GetAvailableAudioFormats(std::list<vdstructex<WAVEFORMATEX> >& aformats);
  136.  
  137.     bool    GetAudioFormat(vdstructex<WAVEFORMATEX>& aformat);
  138.     bool    SetAudioFormat(const WAVEFORMATEX *pwfex, uint32 size);
  139.  
  140.     bool    IsDriverDialogSupported(nsVDCapture::DriverDialog dlg);
  141.     void    DisplayDriverDialog(nsVDCapture::DriverDialog dlg);
  142.  
  143.     bool    IsPropertySupported(uint32 id) { return false; }
  144.     sint32    GetPropertyInt(uint32 id, bool *pAutomatic) { if (pAutomatic) *pAutomatic = true; return 0; }
  145.     void    SetPropertyInt(uint32 id, sint32 value, bool automatic) {}
  146.     void    GetPropertyInfoInt(uint32 id, sint32& minVal, sint32& maxVal, sint32& step, sint32& defaultVal, bool& automatic, bool& manual) {}
  147.  
  148.     bool    CaptureStart();
  149.     void    CaptureStop();
  150.     void    CaptureAbort();
  151.  
  152. protected:
  153.     void    SyncCaptureStop();
  154.     void    SyncCaptureAbort();
  155.     void    InitMixerSupport();
  156.     void    ShutdownMixerSupport();
  157.     bool    InitWaveAnalysis();
  158.     void    ShutdownWaveAnalysis();
  159.     sint64    ComputeGlobalTime();
  160.  
  161.     static LRESULT CALLBACK ErrorCallback(HWND hWnd, int nID, LPCSTR lpsz);
  162.     static LRESULT CALLBACK StatusCallback(HWND hWnd, int nID, LPCSTR lpsz);
  163.     static LRESULT CALLBACK ControlCallback(HWND hwnd, int nState);
  164.     static LRESULT CALLBACK PreviewCallback(HWND hWnd, VIDEOHDR *lpVHdr);
  165.     static LRESULT CALLBACK VideoCallback(HWND hWnd, LPVIDEOHDR lpVHdr);
  166.     static LRESULT CALLBACK WaveCallback(HWND hWnd, LPWAVEHDR lpWHdr);
  167.     static LRESULT CALLBACK StaticMessageSinkWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  168.  
  169.     enum { kPreviewTimerID = 100 };
  170.  
  171.     HWND    mhwnd;
  172.     HWND    mhwndParent;
  173.     HWND    mhwndEventSink;
  174.     HMODULE    mhmodAVICap;
  175.  
  176.     bool    mbCapturing;
  177.     bool    mbVisible;
  178.     bool    mbBlockVideoFrames;            ///< Prevents video frames from being sent the callback. This is used to barrier frames while we sent video format change events.
  179.     bool    mbAudioHardwarePresent;
  180.     bool    mbAudioHardwareEnabled;
  181.     bool    mbAudioCaptureEnabled;
  182.     bool    mbAudioAnalysisEnabled;
  183.     bool    mbAudioAnalysisActive;
  184.  
  185.     IVDCaptureDriverCallback    *mpCB;
  186.     DisplayMode            mDisplayMode;
  187.     uint32    mGlobalTimeBase;
  188.  
  189.     int        mDriverIndex;
  190.     UINT    mPreviewFrameTimer;
  191.     VDAtomicInt    mPreviewFrameCount;
  192.  
  193.     HMIXER    mhMixer;
  194.     int        mMixerInput;
  195.     MIXERCONTROL    mMixerInputControl;
  196.     typedef std::vector<VDStringW>    MixerInputs;
  197.     MixerInputs    mMixerInputs;
  198.  
  199.     HWAVEIN mhWaveIn;
  200.     WAVEHDR    mWaveBufHdrs[2];
  201.     vdblock<char>    mWaveBuffer;
  202.  
  203.     CAPDRIVERCAPS        mCaps;
  204.  
  205.     MyError    mCaptureError;
  206.  
  207.     static ATOM sMsgSinkClass;
  208. };
  209.  
  210. ATOM VDCaptureDriverVFW::sMsgSinkClass;
  211.  
  212. VDCaptureDriverVFW::VDCaptureDriverVFW(HMODULE hmodAVICap, int driverIndex)
  213.     : mhwnd(NULL)
  214.     , mhwndParent(NULL)
  215.     , mhwndEventSink(NULL)
  216.     , mhmodAVICap(hmodAVICap)
  217.     , mpCB(NULL)
  218.     , mbCapturing(false)
  219.     , mbVisible(false)
  220.     , mbBlockVideoFrames(false)
  221.     , mbAudioAnalysisEnabled(false)
  222.     , mbAudioAnalysisActive(false)
  223.     , mDisplayMode(kDisplayNone)
  224.     , mDriverIndex(driverIndex)
  225.     , mPreviewFrameTimer(0)
  226.     , mhMixer(NULL)
  227.     , mhWaveIn(NULL)
  228. {
  229.     memset(mWaveBufHdrs, 0, sizeof mWaveBufHdrs);
  230. }
  231.  
  232. VDCaptureDriverVFW::~VDCaptureDriverVFW() {
  233.     Shutdown();
  234. }
  235.  
  236. void VDCaptureDriverVFW::SetCallback(IVDCaptureDriverCallback *pCB) {
  237.     mpCB = pCB;
  238. }
  239.  
  240. bool VDCaptureDriverVFW::Init(VDGUIHandle hParent) {
  241.     mhwndParent = (HWND)hParent;
  242.  
  243.     if (!sMsgSinkClass) {
  244.         WNDCLASS wc = { 0, StaticMessageSinkWndProc, 0, sizeof(VDCaptureDriverVFW *), g_hInst, NULL, NULL, NULL, NULL, "Riza VFW event sink" };
  245.  
  246.         sMsgSinkClass = RegisterClass(&wc);
  247.  
  248.         if (!sMsgSinkClass)
  249.             return false;
  250.     }
  251.  
  252.     // Attempt to open mixer device. It is OK for this to fail. Note that we
  253.     // have a bit of a problem in that (a) the mixer API doesn't take
  254.     // WAVE_MAPPER, and (b) we can't get access to the handle that the
  255.     // capture window creates. For now, we sort of fake it.
  256.     InitMixerSupport();
  257.  
  258.     // Create message sink.
  259.     if (!(mhwndEventSink = CreateWindow((LPCTSTR)sMsgSinkClass, "", WS_POPUP, 0, 0, 0, 0, mhwndParent, NULL, g_hInst, this))) {
  260.         Shutdown();
  261.         return false;
  262.     }
  263.  
  264.     typedef HWND (VFWAPI *tpcapCreateCaptureWindow)(LPCSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hwnd, int nID);
  265.     const tpcapCreateCaptureWindow pcapCreateCaptureWindow = (tpcapCreateCaptureWindow)GetProcAddress(mhmodAVICap, "capCreateCaptureWindowA");
  266.     if (!VDINLINEASSERT(pcapCreateCaptureWindow)) {
  267.         Shutdown();
  268.         return false;
  269.     }
  270.  
  271.     mhwnd = pcapCreateCaptureWindow("", WS_CHILD, 0, 0, 0, 0, mhwndParent, 0);
  272.     if (!mhwnd) {
  273.         Shutdown();
  274.         return false;
  275.     }
  276.  
  277.     if (!capDriverConnect(mhwnd, mDriverIndex)) {
  278.         Shutdown();
  279.         return false;
  280.     }
  281.  
  282.     VDVERIFY(capDriverGetCaps(mhwnd, &mCaps, sizeof mCaps));
  283.  
  284.     capCaptureSetSetup(mhwnd, &g_defaultCaptureParms, sizeof g_defaultCaptureParms);
  285.  
  286.     mPreviewFrameCount = 0;
  287.  
  288.     capSetUserData(mhwnd, this);
  289.     capSetCallbackOnError(mhwnd, ErrorCallback);
  290.     capSetCallbackOnStatus(mhwnd, StatusCallback);
  291.     capSetCallbackOnCapControl(mhwnd, ControlCallback);
  292.     capSetCallbackOnVideoStream(mhwnd, VideoCallback);
  293.     capSetCallbackOnWaveStream(mhwnd, WaveCallback);
  294.  
  295.     capPreviewRate(mhwnd, 1000 / 15);
  296.  
  297.     CAPSTATUS cs;
  298.  
  299.     mbAudioHardwarePresent = false;
  300.     if (VDINLINEASSERT(capGetStatus(mhwnd, &cs, sizeof(CAPSTATUS)))) {
  301.         mbAudioHardwarePresent = (cs.fAudioHardware != 0);
  302.     }
  303.     mbAudioHardwareEnabled = mbAudioHardwarePresent;
  304.     mbAudioCaptureEnabled = true;
  305.  
  306.     return true;
  307. }
  308.  
  309. void VDCaptureDriverVFW::Shutdown() {
  310.     ShutdownWaveAnalysis();
  311.  
  312.     if (mPreviewFrameTimer) {
  313.         KillTimer(mhwndEventSink, mPreviewFrameTimer);
  314.         mPreviewFrameTimer = 0;
  315.     }
  316.  
  317.     if (mhwndEventSink) {
  318.         DestroyWindow(mhwndEventSink);
  319.         mhwndEventSink = NULL;
  320.     }
  321.  
  322.     if (mhwnd) {
  323.         capDriverDisconnect(mhwnd);
  324.         DestroyWindow(mhwnd);
  325.         mhwnd = NULL;
  326.     }
  327.  
  328.     if (mhMixer) {
  329.         mixerClose(mhMixer);
  330.         mhMixer = NULL;
  331.     }
  332. }
  333.  
  334. bool VDCaptureDriverVFW::IsHardwareDisplayAvailable() {
  335.     return 0 != mCaps.fHasOverlay;
  336. }
  337.  
  338. void VDCaptureDriverVFW::SetDisplayMode(DisplayMode mode) {
  339.     if (mode == mDisplayMode)
  340.         return;
  341.  
  342.     if (mDisplayMode == kDisplayAnalyze) {
  343.         if (mPreviewFrameTimer) {
  344.             KillTimer(mhwndEventSink, mPreviewFrameTimer);
  345.             mPreviewFrameTimer = 0;
  346.         }
  347.     }
  348.  
  349.     mDisplayMode = mode;
  350.  
  351.     switch(mode) {
  352.     case kDisplayNone:
  353.         capSetCallbackOnFrame(mhwnd, NULL);
  354.         capPreview(mhwnd, FALSE);
  355.         capOverlay(mhwnd, FALSE);
  356.         ShowWindow(mhwnd, SW_HIDE);
  357.         break;
  358.     case kDisplayHardware:
  359.         // we have to kill this callback or dual-field overlay may not work
  360.         capSetCallbackOnFrame(mhwnd, NULL);
  361.         capPreview(mhwnd, FALSE);
  362.         capOverlay(mhwnd, TRUE);
  363.         if (mbVisible)
  364.             ShowWindow(mhwnd, SW_SHOWNA);
  365.         break;
  366.     case kDisplaySoftware:
  367.         capSetCallbackOnFrame(mhwnd, PreviewCallback);
  368.         capOverlay(mhwnd, FALSE);
  369.         capPreview(mhwnd, TRUE);
  370.         if (mbVisible)
  371.             ShowWindow(mhwnd, SW_SHOWNA);
  372.         break;
  373.     case kDisplayAnalyze:
  374.         capSetCallbackOnFrame(mhwnd, PreviewCallback);
  375.         capOverlay(mhwnd, FALSE);
  376.         capPreview(mhwnd, FALSE);
  377.         if (!mPreviewFrameTimer)
  378.             mPreviewFrameTimer = SetTimer(mhwndEventSink, kPreviewTimerID, 66, NULL);
  379.         if (mbVisible)
  380.             ShowWindow(mhwnd, SW_HIDE);
  381.         break;
  382.     }
  383. }
  384.  
  385. DisplayMode VDCaptureDriverVFW::GetDisplayMode() {
  386.     return mDisplayMode;
  387. }
  388.  
  389. void VDCaptureDriverVFW::SetDisplayRect(const vdrect32& r) {
  390.     SetWindowPos(mhwnd, NULL, r.left, r.top, r.width(), r.height(), SWP_NOZORDER|SWP_NOACTIVATE);
  391. }
  392.  
  393. vdrect32 VDCaptureDriverVFW::GetDisplayRectAbsolute() {
  394.     RECT r;
  395.     GetWindowRect(mhwnd, &r);
  396.     MapWindowPoints(GetParent(mhwnd), NULL, (LPPOINT)&r, 2);
  397.     return vdrect32(r.left, r.top, r.right, r.bottom);
  398. }
  399.  
  400. void VDCaptureDriverVFW::SetDisplayVisibility(bool vis) {
  401.     if (vis == mbVisible)
  402.         return;
  403.  
  404.     mbVisible = vis;
  405.     ShowWindow(mhwnd, vis && mDisplayMode != kDisplayAnalyze ? SW_SHOWNA : SW_HIDE);
  406. }
  407.  
  408. void VDCaptureDriverVFW::SetFramePeriod(sint32 framePeriod100nsUnits) {
  409.     CAPTUREPARMS cp;
  410.  
  411.     if (capCaptureGetSetup(mhwnd, &cp, sizeof(CAPTUREPARMS))) {
  412.         cp.dwRequestMicroSecPerFrame = (framePeriod100nsUnits + 5) / 10;
  413.  
  414.         capCaptureSetSetup(mhwnd, &cp, sizeof(CAPTUREPARMS));
  415.  
  416.         if (mpCB)
  417.             mpCB->CapEvent(kEventVideoFrameRateChanged, 0);
  418.     }
  419. }
  420.  
  421. sint32 VDCaptureDriverVFW::GetFramePeriod() {
  422.     CAPTUREPARMS cp;
  423.  
  424.     if (VDINLINEASSERT(capCaptureGetSetup(mhwnd, &cp, sizeof(CAPTUREPARMS))))
  425.         return cp.dwRequestMicroSecPerFrame*10;
  426.  
  427.     return 10000000 / 15;
  428. }
  429.  
  430. uint32 VDCaptureDriverVFW::GetPreviewFrameCount() {
  431.     if (mDisplayMode == kDisplaySoftware || mDisplayMode == kDisplayAnalyze)
  432.         return mPreviewFrameCount;
  433.  
  434.     return 0;
  435. }
  436.  
  437. bool VDCaptureDriverVFW::GetVideoFormat(vdstructex<BITMAPINFOHEADER>& vformat) {
  438.     DWORD dwSize = capGetVideoFormatSize(mhwnd);
  439.  
  440.     vformat.resize(dwSize);
  441.  
  442.     VDVERIFY(capGetVideoFormat(mhwnd, vformat.data(), vformat.size()));
  443.     return true;
  444. }
  445.  
  446. bool VDCaptureDriverVFW::SetVideoFormat(const BITMAPINFOHEADER *pbih, uint32 size) {
  447.     bool success = false;
  448.  
  449.     mbBlockVideoFrames = true;
  450.     if (capSetVideoFormat(mhwnd, (BITMAPINFOHEADER *)pbih, size)) {
  451.         if (mpCB)
  452.             mpCB->CapEvent(kEventVideoFormatChanged, 0);
  453.         success = true;
  454.     }
  455.     mbBlockVideoFrames = false;
  456.     return success;
  457. }
  458.  
  459. int VDCaptureDriverVFW::GetAudioDeviceCount() {
  460.     return mbAudioHardwarePresent ? 1 : 0;
  461. }
  462.  
  463. const wchar_t *VDCaptureDriverVFW::GetAudioDeviceName(int idx) {
  464.     if (idx || !mbAudioHardwarePresent)
  465.         return NULL;
  466.  
  467.     return L"Wave Mapper";
  468. }
  469.  
  470. bool VDCaptureDriverVFW::SetAudioDevice(int idx) {
  471.     if (idx < -1 || idx >= 1)
  472.         return false;
  473.     
  474.     if (!idx && !mbAudioHardwarePresent)
  475.         return false;
  476.  
  477.     bool enable = !idx;
  478.  
  479.     if (enable == mbAudioHardwareEnabled)
  480.         return true;
  481.  
  482.     ShutdownWaveAnalysis();
  483.     mbAudioHardwareEnabled = enable;
  484.     InitWaveAnalysis();
  485.  
  486.     return true;
  487. }
  488.  
  489. int VDCaptureDriverVFW::GetAudioDeviceIndex() {
  490.     return mbAudioHardwareEnabled ? 0 : -1;
  491. }
  492.  
  493. int VDCaptureDriverVFW::GetVideoSourceCount() {
  494.     return 0;
  495. }
  496.  
  497. const wchar_t *VDCaptureDriverVFW::GetVideoSourceName(int idx) {
  498.     return NULL;
  499. }
  500.  
  501. bool VDCaptureDriverVFW::SetVideoSource(int idx) {
  502.     return idx == -1;
  503. }
  504.  
  505. int VDCaptureDriverVFW::GetVideoSourceIndex() {
  506.     return -1;
  507. }
  508.  
  509. int VDCaptureDriverVFW::GetAudioSourceCount() {
  510.     return 0;
  511. }
  512.  
  513. const wchar_t *VDCaptureDriverVFW::GetAudioSourceName(int idx) {
  514.     return NULL;
  515. }
  516.  
  517. bool VDCaptureDriverVFW::SetAudioSource(int idx) {
  518.     return idx == -1;
  519. }
  520.  
  521. int VDCaptureDriverVFW::GetAudioSourceIndex() {
  522.     return -1;
  523. }
  524.  
  525. int VDCaptureDriverVFW::GetAudioInputCount() {
  526.     return mbAudioHardwareEnabled ? mMixerInputs.size() : 0;
  527. }
  528.  
  529. const wchar_t *VDCaptureDriverVFW::GetAudioInputName(int idx) {
  530.     if (!mbAudioHardwareEnabled || (unsigned)idx >= mMixerInputs.size())
  531.         return NULL;
  532.  
  533.     MixerInputs::const_iterator it(mMixerInputs.begin());
  534.  
  535.     std::advance(it, idx);
  536.  
  537.     return (*it).c_str();
  538. }
  539.  
  540. bool VDCaptureDriverVFW::SetAudioInput(int idx) {
  541.     if (!mbAudioHardwareEnabled || !mhMixer)
  542.         return idx == -1;
  543.  
  544.     VDASSERT(mMixerInputs.size() == mMixerInputControl.cMultipleItems);
  545.  
  546.     if (idx != -1 && (unsigned)idx >= mMixerInputControl.cMultipleItems)
  547.         return false;
  548.  
  549.     // attempt to set the appropriate mixer input
  550.  
  551.     vdblock<MIXERCONTROLDETAILS_BOOLEAN> vals(mMixerInputControl.cMultipleItems);
  552.  
  553.     for(unsigned i=0; i<mMixerInputControl.cMultipleItems; ++i)
  554.         vals[i].fValue = (i == idx);
  555.  
  556.     MIXERCONTROLDETAILS details = {sizeof(MIXERCONTROLDETAILS)};
  557.  
  558.     details.dwControlID        = mMixerInputControl.dwControlID;
  559.     details.cChannels        = 1;
  560.     details.cMultipleItems    = mMixerInputControl.cMultipleItems;
  561.     details.cbDetails        = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
  562.     details.paDetails        = vals.data();
  563.  
  564.     if (MMSYSERR_NOERROR != mixerSetControlDetails((HMIXEROBJ)mhMixer, &details, MIXER_SETCONTROLDETAILSF_VALUE))
  565.         return false;
  566.  
  567.     mMixerInput = idx;
  568.  
  569.     return true;
  570. }
  571.  
  572. int VDCaptureDriverVFW::GetAudioInputIndex() {
  573.     return mbAudioHardwareEnabled ? mMixerInput : -1;
  574. }
  575.  
  576. bool VDCaptureDriverVFW::IsAudioCapturePossible() {
  577.     return mbAudioHardwareEnabled;
  578. }
  579.  
  580. bool VDCaptureDriverVFW::IsAudioCaptureEnabled() {
  581.     return mbAudioHardwareEnabled && mbAudioCaptureEnabled;
  582. }
  583.  
  584. void VDCaptureDriverVFW::SetAudioCaptureEnabled(bool b) {
  585.     mbAudioCaptureEnabled = b;
  586. }
  587.  
  588. void VDCaptureDriverVFW::SetAudioAnalysisEnabled(bool b) {
  589.     if (mbAudioAnalysisEnabled == b)
  590.         return;
  591.  
  592.     mbAudioAnalysisEnabled = b;
  593.  
  594.     if (mbAudioAnalysisEnabled)
  595.         InitWaveAnalysis();
  596.     else
  597.         ShutdownWaveAnalysis();
  598. }
  599.  
  600. void VDCaptureDriverVFW::GetAvailableAudioFormats(std::list<vdstructex<WAVEFORMATEX> >& aformats) {
  601.     static const int kSamplingRates[]={
  602.         8000,
  603.         11025,
  604.         12000,
  605.         16000,
  606.         22050,
  607.         24000,
  608.         32000,
  609.         44100,
  610.         48000,
  611.         96000,
  612.         192000
  613.     };
  614.  
  615.     static const int kChannelCounts[]={
  616.         1,
  617.         2
  618.     };
  619.  
  620.     static const int kSampleDepths[]={
  621.         8,
  622.         16
  623.     };
  624.  
  625.     for(int sridx=0; sridx < sizeof kSamplingRates / sizeof kSamplingRates[0]; ++sridx)
  626.         for(int chidx=0; chidx < sizeof kChannelCounts / sizeof kChannelCounts[0]; ++chidx)
  627.             for(int sdidx=0; sdidx < sizeof kSampleDepths / sizeof kSampleDepths[0]; ++sdidx) {
  628.                 WAVEFORMATEX wfex={
  629.                     WAVE_FORMAT_PCM,
  630.                     kChannelCounts[chidx],
  631.                     kSamplingRates[sridx],
  632.                     0,
  633.                     0,
  634.                     kSampleDepths[sdidx],
  635.                     0
  636.                 };
  637.  
  638.                 wfex.nBlockAlign = wfex.nChannels * (wfex.wBitsPerSample >> 3);
  639.                 wfex.nAvgBytesPerSec = wfex.nBlockAlign * wfex.nSamplesPerSec;
  640.  
  641.                 if (MMSYSERR_NOERROR ==waveInOpen(NULL, WAVE_MAPPER, &wfex, 0, 0, WAVE_FORMAT_QUERY | WAVE_FORMAT_DIRECT)) {
  642.                     aformats.push_back(vdstructex<WAVEFORMATEX>(&wfex, sizeof wfex));
  643.                 }
  644.             }
  645. }
  646.  
  647. bool VDCaptureDriverVFW::GetAudioFormat(vdstructex<WAVEFORMATEX>& aformat) {
  648.     DWORD dwSize = capGetAudioFormatSize(mhwnd);
  649.  
  650.     if (!dwSize)
  651.         return false;
  652.  
  653.     aformat.resize(dwSize);
  654.  
  655.     if (dwSize < sizeof(PCMWAVEFORMAT))
  656.         return false;
  657.  
  658.     if (!capGetAudioFormat(mhwnd, aformat.data(), aformat.size()))
  659.         return false;
  660.  
  661.     // adjust PCMWAVEFORMAT to WAVEFORMATEX
  662.     if (dwSize < sizeof(WAVEFORMATEX)) {
  663.         aformat.resize(sizeof(WAVEFORMATEX));
  664.  
  665.         aformat->cbSize = 0;
  666.     }
  667.  
  668.     return true;
  669. }
  670.  
  671. bool VDCaptureDriverVFW::SetAudioFormat(const WAVEFORMATEX *pwfex, uint32 size) {
  672.     if (!mbAudioHardwareEnabled)
  673.         return false;
  674.  
  675.     ShutdownWaveAnalysis();
  676.     bool success = 0 != capSetAudioFormat(mhwnd, (WAVEFORMATEX *)pwfex, size);
  677.     if (mbAudioAnalysisEnabled)
  678.         InitWaveAnalysis();
  679.     return success;
  680. }
  681.  
  682. bool VDCaptureDriverVFW::IsDriverDialogSupported(DriverDialog dlg) {
  683.     switch(dlg) {
  684.     case kDialogVideoFormat:
  685.         return mCaps.fHasDlgVideoFormat != 0;
  686.     case kDialogVideoSource:
  687.         return mCaps.fHasDlgVideoSource != 0;
  688.     case kDialogVideoDisplay:
  689.         return mCaps.fHasDlgVideoDisplay != 0;
  690.     }
  691.  
  692.     return false;
  693. }
  694.  
  695. void VDCaptureDriverVFW::DisplayDriverDialog(DriverDialog dlg) {
  696.     VDASSERT(IsDriverDialogSupported(dlg));
  697.  
  698.     switch(dlg) {
  699.     case kDialogVideoFormat:
  700.         // The format dialog is special (i.e. nasty) in that it can change the
  701.         // video format, so we block outgoing frames while it is up and check
  702.         // for a format change.
  703.         {
  704.             vdstructex<BITMAPINFOHEADER> oldFormat, newFormat;
  705.  
  706.             mbBlockVideoFrames = true;
  707.             GetVideoFormat(oldFormat);
  708.             capDlgVideoFormat(mhwnd);
  709.             GetVideoFormat(newFormat);
  710.             if (oldFormat != newFormat && mpCB)
  711.                 mpCB->CapEvent(kEventVideoFormatChanged, 0);
  712.             mbBlockVideoFrames = false;
  713.         }
  714.         break;
  715.     case kDialogVideoSource:
  716.         capDlgVideoSource(mhwnd);
  717.         break;
  718.     case kDialogVideoDisplay:
  719.         capDlgVideoDisplay(mhwnd);
  720.         break;
  721.     }
  722. }
  723.  
  724. bool VDCaptureDriverVFW::CaptureStart() {
  725.     ShutdownWaveAnalysis();
  726.  
  727.     // Aim for 0.1s audio buffers.
  728.     CAPTUREPARMS cp;
  729.     if (capCaptureGetSetup(mhwnd, &cp, sizeof(CAPTUREPARMS))) {
  730.         cp.fCaptureAudio = mbAudioHardwareEnabled && mbAudioCaptureEnabled;
  731.  
  732.         if (cp.fCaptureAudio) {
  733.             vdstructex<WAVEFORMATEX> wfex;
  734.             if (GetAudioFormat(wfex)) {
  735.                 cp.wNumAudioRequested = 10;
  736.                 cp.dwAudioBufferSize = (wfex->nAvgBytesPerSec / 10 + wfex->nBlockAlign - 1);
  737.                 cp.dwAudioBufferSize -= cp.dwAudioBufferSize % wfex->nBlockAlign;
  738.             }
  739.         }
  740.  
  741.         capCaptureSetSetup(mhwnd, &cp, sizeof(CAPTUREPARMS));
  742.     }
  743.  
  744.     if (!VDINLINEASSERTFALSE(mbCapturing)) {
  745.         mGlobalTimeBase = VDGetAccurateTick();
  746.         mbCapturing = !!capCaptureSequenceNoFile(mhwnd);
  747.  
  748.         if (!mbCapturing && mbAudioAnalysisEnabled)
  749.             InitWaveAnalysis();
  750.     }
  751.  
  752.     return mbCapturing;
  753. }
  754.  
  755. void VDCaptureDriverVFW::CaptureStop() {
  756.     SendMessage(mhwndEventSink, WM_APP+16, 0, 0);
  757. }
  758.  
  759. void VDCaptureDriverVFW::CaptureAbort() {
  760.     SendMessage(mhwndEventSink, WM_APP+17, 0, 0);
  761. }
  762.  
  763. void VDCaptureDriverVFW::SyncCaptureStop() {
  764.     if (VDINLINEASSERT(mbCapturing)) {
  765.         capCaptureStop(mhwnd);
  766.         mbCapturing = false;
  767.  
  768.         if (mbAudioAnalysisEnabled)
  769.             InitWaveAnalysis();
  770.     }
  771. }
  772.  
  773. void VDCaptureDriverVFW::SyncCaptureAbort() {
  774.     if (mbCapturing) {
  775.         capCaptureAbort(mhwnd);
  776.         mbCapturing = false;
  777.  
  778.         if (mbAudioAnalysisEnabled)
  779.             InitWaveAnalysis();
  780.     }
  781. }
  782.  
  783. void VDCaptureDriverVFW::InitMixerSupport() {
  784.     WAVEINCAPS wcaps={0};
  785.     if (MMSYSERR_NOERROR == waveInGetDevCaps(WAVE_MAPPER, &wcaps, sizeof wcaps) && wcaps.dwFormats) {
  786.         WAVEFORMATEX wfex;
  787.  
  788.         // create lowest-common denominator format for device
  789.         wfex.wFormatTag            = WAVE_FORMAT_PCM;
  790.  
  791.         if (wcaps.dwFormats & (WAVE_FORMAT_4M08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4S16))
  792.             wfex.nSamplesPerSec = 11025;
  793.         else if (wcaps.dwFormats & (WAVE_FORMAT_2M08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2S16))
  794.             wfex.nSamplesPerSec = 22050;
  795.         else
  796.             wfex.nSamplesPerSec = 44100;
  797.  
  798.         if (wcaps.dwFormats & (WAVE_FORMAT_1M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_2M08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_4M08 | WAVE_FORMAT_4M16))
  799.             wfex.nChannels = 1;
  800.         else
  801.             wfex.nChannels = 2;
  802.  
  803.         if (wcaps.dwFormats & (WAVE_FORMAT_1M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_4S08))
  804.             wfex.wBitsPerSample = 8;
  805.         else
  806.             wfex.wBitsPerSample = 16;
  807.  
  808.         wfex.nBlockAlign        = wfex.wBitsPerSample >> 3;
  809.         wfex.nAvgBytesPerSec    = wfex.nSamplesPerSec * wfex.nBlockAlign;
  810.         wfex.cbSize                = 0;
  811.  
  812.         // create the device
  813.         HWAVEIN hwi;
  814.         if (MMSYSERR_NOERROR == waveInOpen(&hwi, WAVE_MAPPER, &wfex, 0, 0, CALLBACK_NULL)) {
  815.             // create mixer based on device
  816.  
  817.             if (MMSYSERR_NOERROR == mixerOpen(&mhMixer, (UINT)hwi, 0, 0, MIXER_OBJECTF_HWAVEIN)) {
  818.                 MIXERLINE mixerLine = {sizeof(MIXERLINE)};
  819.  
  820.                 mixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
  821.  
  822.                 if (MMSYSERR_NOERROR == mixerGetLineInfo((HMIXEROBJ)mhMixer, &mixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE)) {
  823.  
  824.                     // Try to find a MIXER or MUX control
  825.                     MIXERLINECONTROLS lineControls = {sizeof(MIXERLINECONTROLS)};
  826.  
  827.                     mMixerInputControl.cbStruct = sizeof(MIXERCONTROL);
  828.                     mMixerInputControl.dwControlType = 0;
  829.  
  830.                     lineControls.dwLineID = mixerLine.dwLineID;
  831.                     lineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX;
  832.                     lineControls.cControls = 1;
  833.                     lineControls.pamxctrl = &mMixerInputControl;
  834.                     lineControls.cbmxctrl = sizeof(MIXERCONTROL);
  835.  
  836.                     MMRESULT res;
  837.  
  838.                     res = mixerGetLineControls((HMIXEROBJ)mhMixer, &lineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE);
  839.  
  840.                     if (MMSYSERR_NOERROR != res) {
  841.                         lineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_MIXER;
  842.  
  843.                         res = mixerGetLineControls((HMIXEROBJ)mhMixer, &lineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE);
  844.                     }
  845.  
  846.                     // The mux/mixer control must be of MULTIPLE type; otherwise, we reject it.
  847.                     if (!(mMixerInputControl.fdwControl & MIXERCONTROL_CONTROLF_MULTIPLE))
  848.                         res = MMSYSERR_ERROR;
  849.  
  850.                     // If we were successful, then enumerate all source lines and push them into the map.
  851.                     if (MMSYSERR_NOERROR != res) {
  852.                         mixerClose(mhMixer);
  853.                         mhMixer = NULL;
  854.                     } else {
  855.                         // Enumerate control inputs and populate the name array
  856.                         vdblock<MIXERCONTROLDETAILS_LISTTEXT> names(mMixerInputControl.cMultipleItems);
  857.  
  858.                         MIXERCONTROLDETAILS details = {sizeof(MIXERCONTROLDETAILS)};
  859.  
  860.                         details.dwControlID        = mMixerInputControl.dwControlID;
  861.                         details.cChannels        = 1;
  862.                         details.cMultipleItems    = mMixerInputControl.cMultipleItems;
  863.                         details.cbDetails        = sizeof(MIXERCONTROLDETAILS_LISTTEXT);
  864.                         details.paDetails        = names.data();
  865.  
  866.                         mMixerInput = -1;
  867.  
  868.                         if (MMSYSERR_NOERROR == mixerGetControlDetails((HMIXEROBJ)mhMixer, &details, MIXER_GETCONTROLDETAILSF_LISTTEXT)) {
  869.                             mMixerInputs.reserve(details.cMultipleItems);
  870.  
  871.                             for(unsigned i=0; i<details.cMultipleItems; ++i)
  872.                                 mMixerInputs.push_back(MixerInputs::value_type(VDTextAToW(names[i].szName)));
  873.  
  874.                             vdblock<MIXERCONTROLDETAILS_BOOLEAN> vals(mMixerInputControl.cMultipleItems);
  875.  
  876.                             details.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
  877.                             details.paDetails = vals.data();
  878.  
  879.                             if (MMSYSERR_NOERROR == mixerGetControlDetails((HMIXEROBJ)mhMixer, &details, MIXER_GETCONTROLDETAILSF_VALUE)) {
  880.                                 // Attempt to find a mixer input that is set. Note that for
  881.                                 // a multiple-select type (MIXER) this will pick the first
  882.                                 // enabled input.
  883.                                 for(unsigned i=0; i<details.cMultipleItems; ++i)
  884.                                     if (vals[i].fValue) {
  885.                                         mMixerInput = i;
  886.                                         break;
  887.                                     }
  888.                             }
  889.                         }
  890.                     }
  891.                 }
  892.  
  893.                 // We don't close the mixer here; it is left open while we have the
  894.                 // capture device opened.
  895.             }
  896.  
  897.             waveInClose(hwi);
  898.         }
  899.     }
  900. }
  901.  
  902. void VDCaptureDriverVFW::ShutdownMixerSupport() {
  903.     if (mhMixer) {
  904.         mixerClose(mhMixer);
  905.         mhMixer = NULL;
  906.     }
  907. }
  908.  
  909. bool VDCaptureDriverVFW::InitWaveAnalysis() {
  910.     if (!mbAudioHardwareEnabled)
  911.         return false;
  912.  
  913.     vdstructex<WAVEFORMATEX> aformat;
  914.  
  915.     if (!GetAudioFormat(aformat))
  916.         return false;
  917.  
  918.     uint32    blockSize = (aformat->nAvgBytesPerSec + 9) / 10 + aformat->nBlockAlign - 1;
  919.     blockSize -= blockSize % aformat->nBlockAlign;
  920.  
  921.     mWaveBuffer.resize(blockSize*2);
  922.  
  923.     if (MMSYSERR_NOERROR != waveInOpen(&mhWaveIn, WAVE_MAPPER, aformat.data(), (DWORD_PTR)mhwndEventSink, 0, CALLBACK_WINDOW | WAVE_FORMAT_DIRECT))
  924.         return false;
  925.  
  926.     mbAudioAnalysisActive = true;
  927.     for(int i=0; i<2; ++i) {
  928.         WAVEHDR& hdr = mWaveBufHdrs[i];
  929.  
  930.         hdr.lpData            = &mWaveBuffer[blockSize*i];
  931.         hdr.dwBufferLength    = blockSize;
  932.         hdr.dwBytesRecorded    = 0;
  933.         hdr.dwFlags            = 0;
  934.         hdr.dwLoops            = 0;
  935.         if (MMSYSERR_NOERROR != waveInPrepareHeader(mhWaveIn, &hdr, sizeof(WAVEHDR))) {
  936.             ShutdownWaveAnalysis();
  937.             return false;
  938.         }
  939.  
  940.         if (MMSYSERR_NOERROR != waveInAddBuffer(mhWaveIn, &hdr, sizeof(WAVEHDR))) {
  941.             ShutdownWaveAnalysis();
  942.             return false;
  943.         }
  944.     }
  945.  
  946.     if (MMSYSERR_NOERROR != waveInStart(mhWaveIn)) {
  947.         ShutdownWaveAnalysis();
  948.         return false;
  949.     }
  950.  
  951.     return true;
  952. }
  953.  
  954. void VDCaptureDriverVFW::ShutdownWaveAnalysis() {
  955.     if (mhWaveIn) {
  956.         mbAudioAnalysisActive = false;
  957.         waveInReset(mhWaveIn);
  958.  
  959.         for(int i=0; i<2; ++i) {
  960.             if (mWaveBufHdrs[i].dwFlags & WHDR_PREPARED)
  961.                 waveInUnprepareHeader(mhWaveIn, &mWaveBufHdrs[i], sizeof(WAVEHDR));
  962.         }
  963.  
  964.         waveInClose(mhWaveIn);
  965.         mhWaveIn = NULL;
  966.     }
  967.  
  968.     mWaveBuffer.clear();
  969. }
  970.  
  971. sint64 VDCaptureDriverVFW::ComputeGlobalTime() {
  972.     // AVICap internally seems to use timeGetTime() when queried via capGetStatus(), but
  973.     // this isn't guaranteed anywhere.
  974.     return (sint64)((uint64)(VDGetAccurateTick() - mGlobalTimeBase) * 1000);
  975. }
  976.  
  977. #pragma vdpragma_TODO("Need to find some way to propagate these errors back nicely")
  978.  
  979. LRESULT CALLBACK VDCaptureDriverVFW::ErrorCallback(HWND hwnd, int nID, LPCSTR lpsz) {
  980. #if 0
  981.     static const struct {
  982.         int id;
  983.         const char *szError;
  984.     } g_betterCaptureErrors[]={
  985.         { 434,    "Warning: No frames captured.\n"
  986.                 "\n"
  987.                 "Make sure your capture card is functioning correctly and that a valid video source "
  988.                 "is connected.  You might also try turning off overlay, reducing the image size, or "
  989.                 "reducing the image depth to 24 or 16-bit." },
  990.         { 439,    "Error: Cannot find a driver to draw this non-RGB image format.  Preview and histogram functions will be unavailable." },
  991.     };
  992.  
  993.     char buf[1024];
  994.     int i;
  995.  
  996.     if (!nID) return 0;
  997.  
  998.     for(i=0; i<sizeof g_betterCaptureErrors/sizeof g_betterCaptureErrors[0]; i++)
  999.         if (g_betterCaptureErrors[i].id == nID) {
  1000.             MessageBox(GetParent(hWnd), g_betterCaptureErrors[i].szError, "VirtualDub capture error", MB_OK);
  1001.             return 0;
  1002.         }
  1003.  
  1004.     buf[0] = 0;
  1005.     _snprintf(buf, sizeof buf / sizeof buf[0], "Error %d: %s", nID, lpsz);
  1006.     MessageBox(GetParent(hWnd), buf, "VirtualDub capture error", MB_OK);
  1007.     VDDEBUG("%s\n",buf);
  1008. #endif
  1009.  
  1010.     return 0;
  1011. }
  1012.  
  1013. LRESULT CALLBACK VDCaptureDriverVFW::StatusCallback(HWND hwnd, int nID, LPCSTR lpsz) {
  1014.     VDCaptureDriverVFW *pThis = (VDCaptureDriverVFW *)capGetUserData(hwnd);
  1015.  
  1016.     if (nID == IDS_CAP_BEGIN) {
  1017.         CAPSTATUS capStatus;
  1018.         capGetStatus(hwnd, (LPARAM)&capStatus, sizeof(CAPSTATUS));
  1019.  
  1020.         if (pThis->mpCB) {
  1021.             try {
  1022.                 pThis->mpCB->CapBegin((sint64)capStatus.dwCurrentTimeElapsedMS * 1000);
  1023.             } catch(MyError& e) {
  1024.                 pThis->mCaptureError.TransferFrom(e);
  1025.                 capCaptureAbort(hwnd);
  1026.                 pThis->mbCapturing = false;
  1027.             }
  1028.         }
  1029.     } else if (nID == IDS_CAP_END) {
  1030.         if (pThis->mpCB) {
  1031.             pThis->mpCB->CapEnd(pThis->mCaptureError.gets() ? &pThis->mCaptureError : NULL);
  1032.             pThis->mbCapturing = false;
  1033.         }
  1034.  
  1035.         pThis->mCaptureError.discard();
  1036.     }
  1037.  
  1038. #if 0
  1039.     char buf[1024];
  1040.  
  1041.     // Intercept nID=510 (per frame info)
  1042.  
  1043.     if (nID == 510)
  1044.         return 0;
  1045.  
  1046.     if (nID) {
  1047.         buf[0] = 0;
  1048.         _snprintf(buf, sizeof buf / sizeof buf[0], "Status %d: %s", nID, lpsz);
  1049.         SendMessage(GetDlgItem(GetParent(hWnd), IDC_STATUS_WINDOW), SB_SETTEXT, 0, (LPARAM)buf);
  1050.         VDDEBUG("%s\n",buf);
  1051.     } else {
  1052.         SendMessage(GetDlgItem(GetParent(hWnd), IDC_STATUS_WINDOW), SB_SETTEXT, 0, (LPARAM)"");
  1053.     }
  1054.  
  1055.     return 0;
  1056. #endif
  1057.  
  1058.     return 0;
  1059. }
  1060.  
  1061. LRESULT CALLBACK VDCaptureDriverVFW::ControlCallback(HWND hwnd, int nState) {
  1062.     VDCaptureDriverVFW *pThis = (VDCaptureDriverVFW *)capGetUserData(hwnd);
  1063.  
  1064.     if (pThis->mpCB) {
  1065.         switch(nState) {
  1066.         case CONTROLCALLBACK_PREROLL:
  1067.             return pThis->mpCB->CapEvent(kEventPreroll, 0);
  1068.         case CONTROLCALLBACK_CAPTURING:
  1069.             return pThis->mpCB->CapEvent(kEventCapturing, 0);
  1070.         }
  1071.     }
  1072.  
  1073.     return TRUE;
  1074. }
  1075.  
  1076. LRESULT CALLBACK VDCaptureDriverVFW::PreviewCallback(HWND hwnd, VIDEOHDR *lpVHdr) {
  1077.     VDCaptureDriverVFW *pThis = (VDCaptureDriverVFW *)capGetUserData(hwnd);
  1078.     if (pThis->mpCB && pThis->mDisplayMode == kDisplayAnalyze && !pThis->mbBlockVideoFrames) {
  1079.         try {
  1080.             pThis->mpCB->CapProcessData(-1, lpVHdr->lpData, lpVHdr->dwBytesUsed, (sint64)lpVHdr->dwTimeCaptured * 1000, 0 != (lpVHdr->dwFlags & VHDR_KEYFRAME), 0);
  1081.         } catch(MyError&) {
  1082.             // Eat preview errors.
  1083.         }
  1084.     }
  1085.     ++pThis->mPreviewFrameCount;
  1086.     return 0;
  1087. }
  1088.  
  1089. LRESULT CALLBACK VDCaptureDriverVFW::VideoCallback(HWND hwnd, LPVIDEOHDR lpVHdr) {
  1090.     VDCaptureDriverVFW *pThis = (VDCaptureDriverVFW *)capGetUserData(hwnd);
  1091.  
  1092.     if (pThis->mpCB && !pThis->mbBlockVideoFrames) {
  1093.         try {
  1094.             pThis->mpCB->CapProcessData(0, lpVHdr->lpData, lpVHdr->dwBytesUsed, (sint64)lpVHdr->dwTimeCaptured * 1000, 0 != (lpVHdr->dwFlags & VHDR_KEYFRAME), pThis->ComputeGlobalTime());
  1095.         } catch(MyError& e) {
  1096.             pThis->mCaptureError.TransferFrom(e);
  1097.             capCaptureAbort(hwnd);
  1098.             pThis->mbCapturing = false;
  1099.         }
  1100.     }
  1101.     return 0;
  1102. }
  1103.  
  1104. LRESULT CALLBACK VDCaptureDriverVFW::WaveCallback(HWND hwnd, LPWAVEHDR lpWHdr) {
  1105.     VDCaptureDriverVFW *pThis = (VDCaptureDriverVFW *)capGetUserData(hwnd);
  1106.  
  1107.     if (pThis->mpCB) {
  1108.         try {
  1109.             pThis->mpCB->CapProcessData(1, lpWHdr->lpData, lpWHdr->dwBytesRecorded, -1, false, pThis->ComputeGlobalTime());
  1110.         } catch(MyError& e) {
  1111.             pThis->mCaptureError.TransferFrom(e);
  1112.             capCaptureAbort(hwnd);
  1113.             pThis->mbCapturing = false;
  1114.         }
  1115.     }
  1116.     return 0;
  1117. }
  1118.  
  1119. LRESULT CALLBACK VDCaptureDriverVFW::StaticMessageSinkWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
  1120.     switch(msg) {
  1121.         case WM_NCCREATE:
  1122.             SetWindowLongPtr(hwnd, 0, (LONG_PTR)((LPCREATESTRUCT)lParam)->lpCreateParams);
  1123.             break;
  1124.         case MM_WIM_DATA:
  1125.             {
  1126.                 VDCaptureDriverVFW *pThis = (VDCaptureDriverVFW *)GetWindowLongPtr(hwnd, 0);
  1127.  
  1128.                 if (pThis->mpCB) {
  1129.                     WAVEHDR& hdr = *(WAVEHDR *)lParam;
  1130.  
  1131.                     if (pThis->mbAudioAnalysisActive) {
  1132.                         // For some reason this is sometimes called after reset. Don't know why yet.
  1133.                         if (hdr.dwBytesRecorded) {
  1134.                             try {
  1135.                                 pThis->mpCB->CapProcessData(-2, hdr.lpData, hdr.dwBytesRecorded, -1, false, 0);
  1136.                             } catch(const MyError&) {
  1137.                                 // eat the error
  1138.                             }
  1139.                         }
  1140.  
  1141.                         waveInAddBuffer(pThis->mhWaveIn, &hdr, sizeof(WAVEHDR));
  1142.                     }
  1143.                 }
  1144.             }
  1145.             return 0;
  1146.         case WM_APP+16:
  1147.             {
  1148.                 VDCaptureDriverVFW *pThis = (VDCaptureDriverVFW *)GetWindowLongPtr(hwnd, 0);
  1149.                 pThis->SyncCaptureStop();
  1150.             }
  1151.             return 0;
  1152.         case WM_APP+17:
  1153.             {
  1154.                 VDCaptureDriverVFW *pThis = (VDCaptureDriverVFW *)GetWindowLongPtr(hwnd, 0);
  1155.                 pThis->SyncCaptureAbort();
  1156.             }
  1157.             return 0;
  1158.         case WM_TIMER:
  1159.             {
  1160.                 VDCaptureDriverVFW *pThis = (VDCaptureDriverVFW *)GetWindowLongPtr(hwnd, 0);
  1161.                 if (pThis->mDisplayMode == kDisplayAnalyze && !pThis->mbCapturing)
  1162.                     capGrabFrameNoStop(pThis->mhwnd);
  1163.             }
  1164.             return 0;
  1165.     }
  1166.  
  1167.     return DefWindowProc(hwnd, msg, wParam, lParam);
  1168. }
  1169.  
  1170. ///////////////////////////////////////////////////////////////////////////
  1171.  
  1172. class VDCaptureSystemVFW : public IVDCaptureSystem {
  1173. public:
  1174.     VDCaptureSystemVFW();
  1175.     ~VDCaptureSystemVFW();
  1176.  
  1177.     void EnumerateDrivers();
  1178.  
  1179.     int GetDeviceCount();
  1180.     const wchar_t *GetDeviceName(int index);
  1181.  
  1182.     IVDCaptureDriver *CreateDriver(int deviceIndex);
  1183.  
  1184. protected:
  1185.     HMODULE    mhmodAVICap;
  1186.     int mDriverCount;
  1187.     VDStringW mDrivers[10];
  1188. };
  1189.  
  1190. IVDCaptureSystem *VDCreateCaptureSystemVFW() {
  1191.     return new VDCaptureSystemVFW;
  1192. }
  1193.  
  1194. VDCaptureSystemVFW::VDCaptureSystemVFW()
  1195.     : mhmodAVICap(LoadLibrary("avicap32"))
  1196.     , mDriverCount(0)
  1197. {
  1198. }
  1199.  
  1200. VDCaptureSystemVFW::~VDCaptureSystemVFW() {
  1201.     if (mhmodAVICap)
  1202.         FreeLibrary(mhmodAVICap);
  1203. }
  1204.  
  1205. void VDCaptureSystemVFW::EnumerateDrivers() {
  1206.     if (!mhmodAVICap)
  1207.         return;
  1208.  
  1209.     typedef BOOL (VFWAPI *tpcapGetDriverDescriptionA)(UINT wDriverIndex, LPSTR lpszName, INT cbName, LPSTR lpszVer, INT cbVer);
  1210.  
  1211.     const tpcapGetDriverDescriptionA pcapGetDriverDescriptionA = (tpcapGetDriverDescriptionA)GetProcAddress(mhmodAVICap, "capGetDriverDescriptionA");
  1212.  
  1213.     if (pcapGetDriverDescriptionA) {
  1214.         char buf[256], ver[256];
  1215.  
  1216.         mDriverCount = 0;
  1217.         while(mDriverCount < 10 && pcapGetDriverDescriptionA(mDriverCount, buf, sizeof buf, ver, sizeof ver)) {
  1218.             mDrivers[mDriverCount] = VDTextAToW(buf) + L" (VFW)";
  1219.             ++mDriverCount;
  1220.         }
  1221.     }
  1222. }
  1223.  
  1224. int VDCaptureSystemVFW::GetDeviceCount() {
  1225.     return mDriverCount;
  1226. }
  1227.  
  1228. const wchar_t *VDCaptureSystemVFW::GetDeviceName(int index) {
  1229.     return (unsigned)index < (unsigned)mDriverCount ? mDrivers[index].c_str() : NULL;
  1230. }
  1231.  
  1232. IVDCaptureDriver *VDCaptureSystemVFW::CreateDriver(int index) {
  1233.     if ((unsigned)index >= (unsigned)mDriverCount)
  1234.         return NULL;
  1235.  
  1236.     return new VDCaptureDriverVFW(mhmodAVICap, index);
  1237. }
  1238.