[MCIAVI32] Sync with Wine Staging 3.3. CORE-14434
[reactos.git] / dll / win32 / mciavi32 / mciavi.c
index fd51e81..b32accb 100644 (file)
  *        audio is played... still should be stopped ASAP
  */
 
-//#include <string.h>
+#include <string.h>
 #include "private_mciavi.h"
-#include <wine/debug.h>
-#include <wine/unicode.h>
+#include "wine/debug.h"
+#include "wine/unicode.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(mciavi);
 
@@ -87,7 +87,6 @@ static        DWORD   MCIAVI_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
 
     InitializeCriticalSection(&wma->cs);
     wma->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": WINE_MCIAVI.cs");
-    wma->ack_event = CreateEventW(NULL, FALSE, FALSE, NULL);
     wma->hStopEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
     wma->wDevID = modp->wDeviceID;
     wma->wCommandTable = mciLoadCommandResource(MCIAVI_hInstance, mciAviWStr, 0);
@@ -121,7 +120,6 @@ static      DWORD   MCIAVI_drvClose(DWORD dwDevID)
        mciSetDriverData(dwDevID, 0);
        mciFreeCommandResource(wma->wCommandTable);
 
-        CloseHandle(wma->ack_event);
         CloseHandle(wma->hStopEvent);
 
         LeaveCriticalSection(&wma->cs);
@@ -336,13 +334,164 @@ DWORD MCIAVI_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
     return dwRet;
 }
 
-static DWORD MCIAVI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms);
+static double currenttime_us(void)
+{
+    LARGE_INTEGER lc, lf;
+    QueryPerformanceCounter(&lc);
+    QueryPerformanceFrequency(&lf);
+    return (lc.QuadPart * 1000000) / lf.QuadPart;
+}
+
+/***************************************************************************
+ *                             MCIAVI_player                   [internal]
+ */
+static DWORD   MCIAVI_player(WINE_MCIAVI *wma, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
+{
+    DWORD              dwRet;
+    LPWAVEHDR          waveHdr = NULL;
+    unsigned           i, nHdr = 0;
+    DWORD              numEvents = 1;
+    HANDLE             events[2];
+    double next_frame_us;
+    BOOL wait_audio = TRUE;
+
+    EnterCriticalSection(&wma->cs);
+
+    if (wma->dwToVideoFrame <= wma->dwCurrVideoFrame)
+    {
+        dwRet = 0;
+        goto mci_play_done;
+    }
+
+    events[0] = wma->hStopEvent;
+    if (wma->lpWaveFormat) {
+       if (MCIAVI_OpenAudio(wma, &nHdr, &waveHdr) != 0)
+        {
+            /* can't play audio */
+            HeapFree(GetProcessHeap(), 0, wma->lpWaveFormat);
+            wma->lpWaveFormat = NULL;
+        }
+       else
+       {
+            /* fill the queue with as many wave headers as possible */
+            MCIAVI_PlayAudioBlocks(wma, nHdr, waveHdr);
+            events[1] = wma->hEvent;
+            numEvents = 2;
+       }
+    }
+
+    next_frame_us = currenttime_us();
+    while (wma->dwStatus == MCI_MODE_PLAY)
+    {
+        HDC hDC;
+        double tc, delta;
+        DWORD ret;
+
+        tc = currenttime_us();
+
+        hDC = wma->hWndPaint ? GetDC(wma->hWndPaint) : 0;
+        if (hDC)
+        {
+            while(next_frame_us <= tc && wma->dwCurrVideoFrame < wma->dwToVideoFrame){
+                double dur;
+                dur = MCIAVI_PaintFrame(wma, hDC);
+                ++wma->dwCurrVideoFrame;
+                if(!dur)
+                    break;
+                next_frame_us += dur;
+                TRACE("next_frame: %f\n", next_frame_us);
+            }
+            ReleaseDC(wma->hWndPaint, hDC);
+        }
+        if (wma->dwCurrVideoFrame >= wma->dwToVideoFrame)
+        {
+            if (!(dwFlags & MCI_DGV_PLAY_REPEAT))
+                break;
+            TRACE("repeat media as requested\n");
+            wma->dwCurrVideoFrame = wma->dwCurrAudioBlock = 0;
+        }
+
+        if (wma->lpWaveFormat)
+            MCIAVI_PlayAudioBlocks(wma, nHdr, waveHdr);
+
+        tc = currenttime_us();
+        if (tc < next_frame_us)
+            delta = next_frame_us - tc;
+        else
+            delta = 0;
+
+        /* check if the playback was cancelled */
+        if ((wma->mci_break.flags & MCI_BREAK_KEY) &&
+            (GetAsyncKeyState(wma->mci_break.parms.nVirtKey) & 0x8000))
+        {
+            if (!(wma->mci_break.flags & MCI_BREAK_HWND) ||
+                GetForegroundWindow() == wma->mci_break.parms.hwndBreak)
+            {
+                /* we queue audio blocks ahead so ignore them otherwise the audio
+                 * will keep playing until the buffer is empty */
+                wait_audio = FALSE;
+
+                TRACE("playback cancelled using break key\n");
+                break;
+            }
+        }
+
+        LeaveCriticalSection(&wma->cs);
+        ret = WaitForMultipleObjects(numEvents, events, FALSE, delta / 1000);
+        EnterCriticalSection(&wma->cs);
+        if (ret == WAIT_OBJECT_0 || wma->dwStatus != MCI_MODE_PLAY) break;
+    }
+
+    if (wma->lpWaveFormat)
+    {
+        if (wait_audio)
+            while (wma->dwEventCount != nHdr - 1)
+            {
+                LeaveCriticalSection(&wma->cs);
+                Sleep(100);
+                EnterCriticalSection(&wma->cs);
+            }
+
+       /* just to get rid of some race conditions between play, stop and pause */
+       LeaveCriticalSection(&wma->cs);
+       waveOutReset(wma->hWave);
+       EnterCriticalSection(&wma->cs);
+
+       for (i = 0; i < nHdr; i++)
+           waveOutUnprepareHeader(wma->hWave, &waveHdr[i], sizeof(WAVEHDR));
+    }
+
+    dwRet = 0;
+
+    if (wma->lpWaveFormat) {
+       HeapFree(GetProcessHeap(), 0, waveHdr);
+
+       if (wma->hWave) {
+           LeaveCriticalSection(&wma->cs);
+           waveOutClose(wma->hWave);
+           EnterCriticalSection(&wma->cs);
+           wma->hWave = 0;
+       }
+       CloseHandle(wma->hEvent);
+    }
+
+mci_play_done:
+    wma->dwStatus = MCI_MODE_STOP;
+
+    if (dwFlags & MCI_NOTIFY) {
+       TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
+       mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
+                       wma->wDevID, MCI_NOTIFY_SUCCESSFUL);
+    }
+    LeaveCriticalSection(&wma->cs);
+    return dwRet;
+}
 
 struct MCIAVI_play_data
 {
-    MCIDEVICEID wDevID;
+    WINE_MCIAVI *wma;
     DWORD flags;
-    MCI_PLAY_PARMS params;
+    MCI_PLAY_PARMS params; /* FIXME: notify via wma->hCallback like the other MCI drivers */
 };
 
 /*
@@ -356,9 +505,9 @@ static DWORD WINAPI MCIAVI_mciPlay_thread(LPVOID arg)
     struct MCIAVI_play_data *data = (struct MCIAVI_play_data *)arg;
     DWORD ret;
 
-    TRACE("In thread before async play command (id %08x, flags %08x)\n", data->wDevID, data->flags);
-    ret = MCIAVI_mciPlay(data->wDevID, data->flags | MCI_WAIT, &data->params);
-    TRACE("In thread after async play command (id %08x, flags %08x)\n", data->wDevID, data->flags);
+    TRACE("In thread before async play command (id %u, flags %08x)\n", data->wma->wDevID, data->flags);
+    ret = MCIAVI_player(data->wma, data->flags, &data->params);
+    TRACE("In thread after async play command (id %u, flags %08x)\n", data->wma->wDevID, data->flags);
 
     HeapFree(GetProcessHeap(), 0, data);
     return ret;
@@ -369,14 +518,15 @@ static DWORD WINAPI MCIAVI_mciPlay_thread(LPVOID arg)
  */
 static DWORD MCIAVI_mciPlay_async(WINE_MCIAVI *wma, DWORD dwFlags, LPMCI_PLAY_PARMS lpParams)
 {
-    HANDLE handle, ack_event = wma->ack_event;
+    HANDLE handle;
     struct MCIAVI_play_data *data = HeapAlloc(GetProcessHeap(), 0, sizeof(struct MCIAVI_play_data));
 
     if (!data) return MCIERR_OUT_OF_MEMORY;
 
-    data->wDevID = wma->wDevID;
+    data->wma = wma;
     data->flags = dwFlags;
-    data->params = *lpParams;
+    if (dwFlags & MCI_NOTIFY)
+        data->params.dwCallback = lpParams->dwCallback;
 
     if (!(handle = CreateThread(NULL, 0, MCIAVI_mciPlay_thread, data, 0, NULL)))
     {
@@ -385,9 +535,6 @@ static DWORD MCIAVI_mciPlay_async(WINE_MCIAVI *wma, DWORD dwFlags, LPMCI_PLAY_PA
     }
     SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL);
     CloseHandle(handle);
-    /* wait until the thread starts up, so the app could see a changed status */
-    WaitForSingleObject(ack_event, INFINITE);
-    TRACE("Async play has started\n");
     return 0;
 }
 
@@ -397,13 +544,8 @@ static DWORD MCIAVI_mciPlay_async(WINE_MCIAVI *wma, DWORD dwFlags, LPMCI_PLAY_PA
 static DWORD   MCIAVI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
 {
     WINE_MCIAVI *wma;
-    DWORD              frameTime;
     DWORD              dwRet;
-    LPWAVEHDR          waveHdr = NULL;
-    unsigned           i, nHdr = 0;
     DWORD              dwFromFrame, dwToFrame;
-    DWORD              numEvents = 1;
-    HANDLE             events[2];
 
     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
 
@@ -414,6 +556,9 @@ static      DWORD   MCIAVI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms
     if (dwFlags & MCI_DGV_PLAY_REVERSE) return MCIERR_UNSUPPORTED_FUNCTION;
     if (dwFlags & MCI_TEST)    return 0;
 
+    if (dwFlags & (MCI_MCIAVI_PLAY_WINDOW|MCI_MCIAVI_PLAY_FULLSCREEN|MCI_MCIAVI_PLAY_FULLBY2))
+       FIXME("Unsupported flag %08x\n", dwFlags);
+
     EnterCriticalSection(&wma->cs);
 
     if (!wma->hFile)
@@ -427,16 +572,6 @@ static     DWORD   MCIAVI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms
         return MCIERR_NO_WINDOW;
     }
 
-    LeaveCriticalSection(&wma->cs);
-
-    if (!(dwFlags & MCI_WAIT))
-        return MCIAVI_mciPlay_async(wma, dwFlags, lpParms);
-
-    if (!(GetWindowLongW(wma->hWndPaint, GWL_STYLE) & WS_VISIBLE))
-        ShowWindow(wma->hWndPaint, SW_SHOWNA);
-
-    EnterCriticalSection(&wma->cs);
-
     dwFromFrame = wma->dwCurrVideoFrame;
     dwToFrame = wma->dwPlayableVideoFrames - 1;
 
@@ -454,123 +589,34 @@ static   DWORD   MCIAVI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms
     wma->dwCurrVideoFrame = dwFromFrame;
     wma->dwToVideoFrame = dwToFrame;
 
+    LeaveCriticalSection(&wma->cs);
+
+    if (!(GetWindowLongW(wma->hWndPaint, GWL_STYLE) & WS_VISIBLE))
+        ShowWindow(wma->hWndPaint, SW_SHOWNA);
+
+    EnterCriticalSection(&wma->cs);
+
     /* if already playing exit */
     if (wma->dwStatus == MCI_MODE_PLAY)
     {
         LeaveCriticalSection(&wma->cs);
-        SetEvent(wma->ack_event);
         return 0;
     }
 
-    if (wma->dwToVideoFrame <= wma->dwCurrVideoFrame)
-    {
-        dwRet = 0;
-        SetEvent(wma->ack_event);
-        goto mci_play_done;
-    }
-
     wma->dwStatus = MCI_MODE_PLAY;
-    /* signal the state change */
-    SetEvent(wma->ack_event);
 
-    if (dwFlags & (MCI_DGV_PLAY_REPEAT|MCI_MCIAVI_PLAY_WINDOW|MCI_MCIAVI_PLAY_FULLSCREEN))
-       FIXME("Unsupported flag %08x\n", dwFlags);
-
-    /* time is in microseconds, we should convert it to milliseconds */
-    frameTime = (wma->mah.dwMicroSecPerFrame + 500) / 1000;
-
-    events[0] = wma->hStopEvent;
-    if (wma->lpWaveFormat) {
-       if (MCIAVI_OpenAudio(wma, &nHdr, &waveHdr) != 0)
-        {
-            /* can't play audio */
-            HeapFree(GetProcessHeap(), 0, wma->lpWaveFormat);
-            wma->lpWaveFormat = NULL;
-        }
-       else
-       {
-            /* fill the queue with as many wave headers as possible */
-            MCIAVI_PlayAudioBlocks(wma, nHdr, waveHdr);
-            events[1] = wma->hEvent;
-            numEvents = 2;
-       }
-    }
-
-    while (wma->dwStatus == MCI_MODE_PLAY)
-    {
-        HDC hDC;
-        DWORD tc, delta;
-        DWORD ret;
-
-       tc = GetTickCount();
-
-        hDC = wma->hWndPaint ? GetDC(wma->hWndPaint) : 0;
-        if (hDC)
-        {
-            MCIAVI_PaintFrame(wma, hDC);
-            ReleaseDC(wma->hWndPaint, hDC);
-        }
+    LeaveCriticalSection(&wma->cs);
 
-        if (wma->lpWaveFormat)
-           MCIAVI_PlayAudioBlocks(wma, nHdr, waveHdr);
+    if (dwFlags & MCI_WAIT)
+        return MCIAVI_player(wma, dwFlags, lpParms);
 
-       delta = GetTickCount() - tc;
-       if (delta < frameTime)
-            delta = frameTime - delta;
-        else
-            delta = 0;
+    dwRet = MCIAVI_mciPlay_async(wma, dwFlags, lpParms);
 
-        LeaveCriticalSection(&wma->cs);
-        ret = WaitForMultipleObjects(numEvents, events, FALSE, delta);
+    if (dwRet) {
         EnterCriticalSection(&wma->cs);
-        if (ret == WAIT_OBJECT_0 || wma->dwStatus != MCI_MODE_PLAY) break;
-
-       if (wma->dwCurrVideoFrame < dwToFrame)
-           wma->dwCurrVideoFrame++;
-        else
-            break;
-    }
-
-    if (wma->lpWaveFormat) {
-       while (wma->dwEventCount != nHdr - 1)
-        {
-            LeaveCriticalSection(&wma->cs);
-            Sleep(100);
-            EnterCriticalSection(&wma->cs);
-        }
-
-       /* just to get rid of some race conditions between play, stop and pause */
-       LeaveCriticalSection(&wma->cs);
-       waveOutReset(wma->hWave);
-       EnterCriticalSection(&wma->cs);
-
-       for (i = 0; i < nHdr; i++)
-           waveOutUnprepareHeader(wma->hWave, &waveHdr[i], sizeof(WAVEHDR));
-    }
-
-    dwRet = 0;
-
-    if (wma->lpWaveFormat) {
-       HeapFree(GetProcessHeap(), 0, waveHdr);
-
-       if (wma->hWave) {
-           LeaveCriticalSection(&wma->cs);
-           waveOutClose(wma->hWave);
-           EnterCriticalSection(&wma->cs);
-           wma->hWave = 0;
-       }
-       CloseHandle(wma->hEvent);
-    }
-
-mci_play_done:
-    wma->dwStatus = MCI_MODE_STOP;
-
-    if (dwFlags & MCI_NOTIFY) {
-       TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
-       mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
-                       wDevID, MCI_NOTIFY_SUCCESSFUL);
+        wma->dwStatus = MCI_MODE_STOP;
+        LeaveCriticalSection(&wma->cs);
     }
-    LeaveCriticalSection(&wma->cs);
     return dwRet;
 }
 
@@ -843,6 +889,30 @@ static     DWORD   MCIAVI_mciCue(UINT wDevID, DWORD dwFlags, LPMCI_DGV_CUE_PARMS lpPar
     return 0;
 }
 
+/******************************************************************************
+ *                             MCIAVI_mciBreak                 [internal]
+ */
+static DWORD   MCIAVI_mciBreak(UINT wDevID, DWORD dwFlags, LPMCI_BREAK_PARMS lpParms)
+{
+    WINE_MCIAVI *wma;
+
+    TRACE("(%04x, %08x, %p)\n", wDevID, dwFlags, lpParms);
+
+    if (lpParms == NULL)       return MCIERR_NULL_PARAMETER_BLOCK;
+
+    wma = MCIAVI_mciGetOpenDev(wDevID);
+    if (wma == NULL)           return MCIERR_INVALID_DEVICE_ID;
+
+    EnterCriticalSection(&wma->cs);
+
+    wma->mci_break.flags = dwFlags;
+    wma->mci_break.parms = *lpParms;
+
+    LeaveCriticalSection(&wma->cs);
+
+    return 0;
+}
+
 /******************************************************************************
  *                             MCIAVI_mciSetAudio                      [internal]
  */
@@ -961,6 +1031,7 @@ LRESULT CALLBACK MCIAVI_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
     case MCI_WHERE:            return MCIAVI_mciWhere     (dwDevID, dwParam1, (LPMCI_DGV_RECT_PARMS)      dwParam2);
     case MCI_STEP:             return MCIAVI_mciStep      (dwDevID, dwParam1, (LPMCI_DGV_STEP_PARMS)      dwParam2);
     case MCI_CUE:              return MCIAVI_mciCue       (dwDevID, dwParam1, (LPMCI_DGV_CUE_PARMS)       dwParam2);
+    case MCI_BREAK:            return MCIAVI_mciBreak     (dwDevID, dwParam1, (LPMCI_BREAK_PARMS)         dwParam2);
        /* Digital Video specific */
     case MCI_SETAUDIO:         return MCIAVI_mciSetAudio  (dwDevID, dwParam1, (LPMCI_DGV_SETAUDIO_PARMSW) dwParam2);
     case MCI_SIGNAL:           return MCIAVI_mciSignal    (dwDevID, dwParam1, (LPMCI_DGV_SIGNAL_PARMS)    dwParam2);