* 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);
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);
mciSetDriverData(dwDevID, 0);
mciFreeCommandResource(wma->wCommandTable);
- CloseHandle(wma->ack_event);
CloseHandle(wma->hStopEvent);
LeaveCriticalSection(&wma->cs);
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 */
};
/*
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;
*/
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)))
{
}
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;
}
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);
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)
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;
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;
}
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]
*/
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);