static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb, DWORD *statusp) { HRESULT hr; hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp); if (FAILED (hr)) { dsound_logerr (hr, "Could not get capture buffer status\n"); return -1; } return 0; }
/* return number of readed bytes */ static guint gst_directsound_src_read (GstAudioSrc * asrc, gpointer data, guint length, GstClockTime * timestamp) { GstDirectSoundSrc *dsoundsrc; HRESULT hRes; /* Result for windows functions */ DWORD dwCurrentCaptureCursor = 0; DWORD dwBufferSize = 0; LPVOID pLockedBuffer1 = NULL; LPVOID pLockedBuffer2 = NULL; DWORD dwSizeBuffer1 = 0; DWORD dwSizeBuffer2 = 0; DWORD dwStatus = 0; GST_DEBUG_OBJECT (asrc, "reading directsoundsrc"); dsoundsrc = GST_DIRECTSOUND_SRC (asrc); GST_DSOUND_LOCK (dsoundsrc); /* Get current buffer status */ hRes = IDirectSoundCaptureBuffer_GetStatus (dsoundsrc->pDSBSecondary, &dwStatus); /* Starting capturing if not already */ if (!(dwStatus & DSCBSTATUS_CAPTURING)) { hRes = IDirectSoundCaptureBuffer_Start (dsoundsrc->pDSBSecondary, DSCBSTART_LOOPING); // Sleep (dsoundsrc->latency_time/1000); GST_DEBUG_OBJECT (asrc, "capture started"); } // calculate_buffersize: while (length > dwBufferSize) { Sleep (dsoundsrc->latency_time / 1000); hRes = IDirectSoundCaptureBuffer_GetCurrentPosition (dsoundsrc->pDSBSecondary, &dwCurrentCaptureCursor, NULL); /* calculate the buffer */ if (dwCurrentCaptureCursor < dsoundsrc->current_circular_offset) { dwBufferSize = dsoundsrc->buffer_size - (dsoundsrc->current_circular_offset - dwCurrentCaptureCursor); } else { dwBufferSize = dwCurrentCaptureCursor - dsoundsrc->current_circular_offset; } } // while (... /* Lock the buffer */ hRes = IDirectSoundCaptureBuffer_Lock (dsoundsrc->pDSBSecondary, dsoundsrc->current_circular_offset, length, &pLockedBuffer1, &dwSizeBuffer1, &pLockedBuffer2, &dwSizeBuffer2, 0L); /* Copy buffer data to another buffer */ if (hRes == DS_OK) { memcpy (data, pLockedBuffer1, dwSizeBuffer1); } /* ...and if something is in another buffer */ if (pLockedBuffer2 != NULL) { memcpy (((guchar *) data + dwSizeBuffer1), pLockedBuffer2, dwSizeBuffer2); } dsoundsrc->current_circular_offset += dwSizeBuffer1 + dwSizeBuffer2; dsoundsrc->current_circular_offset %= dsoundsrc->buffer_size; IDirectSoundCaptureBuffer_Unlock (dsoundsrc->pDSBSecondary, pLockedBuffer1, dwSizeBuffer1, pLockedBuffer2, dwSizeBuffer2); GST_DSOUND_UNLOCK (dsoundsrc); /* return length (readed data size in bytes) */ return length; }
static void test_capture_buffer(LPDIRECTSOUNDCAPTURE dsco, LPDIRECTSOUNDCAPTUREBUFFER dscbo, int record) { HRESULT rc; DSCBCAPS dscbcaps; WAVEFORMATEX wfx; DWORD size,status; capture_state_t state; int i, ref; /* Private dsound.dll: Error: Invalid caps pointer */ rc=IDirectSoundCaptureBuffer_GetCaps(dscbo,0); ok(rc==DSERR_INVALIDPARAM,"IDirectSoundCaptureBuffer_GetCaps() should " "have returned DSERR_INVALIDPARAM, returned: %08x\n", rc); /* Private dsound.dll: Error: Invalid caps pointer */ dscbcaps.dwSize=0; rc=IDirectSoundCaptureBuffer_GetCaps(dscbo,&dscbcaps); ok(rc==DSERR_INVALIDPARAM,"IDirectSoundCaptureBuffer_GetCaps() should " "have returned DSERR_INVALIDPARAM, returned: %08x\n", rc); dscbcaps.dwSize=sizeof(dscbcaps); rc=IDirectSoundCaptureBuffer_GetCaps(dscbo,&dscbcaps); ok(rc==DS_OK,"IDirectSoundCaptureBuffer_GetCaps() failed: %08x\n", rc); if (rc==DS_OK && winetest_debug > 1) { trace(" Caps: size = %d flags=0x%08x buffer size=%d\n", dscbcaps.dwSize,dscbcaps.dwFlags,dscbcaps.dwBufferBytes); } /* Query the format size. Note that it may not match sizeof(wfx) */ /* Private dsound.dll: Error: Either pwfxFormat or pdwSizeWritten must * be non-NULL */ rc=IDirectSoundCaptureBuffer_GetFormat(dscbo,NULL,0,NULL); ok(rc==DSERR_INVALIDPARAM,"IDirectSoundCaptureBuffer_GetFormat() should " "have returned DSERR_INVALIDPARAM, returned: %08x\n", rc); size=0; rc=IDirectSoundCaptureBuffer_GetFormat(dscbo,NULL,0,&size); ok(rc==DS_OK && size!=0,"IDirectSoundCaptureBuffer_GetFormat() should " "have returned the needed size: rc=%08x, size=%d\n", rc,size); rc=IDirectSoundCaptureBuffer_GetFormat(dscbo,&wfx,sizeof(wfx),NULL); ok(rc==DS_OK,"IDirectSoundCaptureBuffer_GetFormat() failed: %08x\n", rc); if (rc==DS_OK && winetest_debug > 1) { trace(" Format: tag=0x%04x %dx%dx%d avg.B/s=%d align=%d\n", wfx.wFormatTag,wfx.nSamplesPerSec,wfx.wBitsPerSample, wfx.nChannels,wfx.nAvgBytesPerSec,wfx.nBlockAlign); } /* Private dsound.dll: Error: Invalid status pointer */ rc=IDirectSoundCaptureBuffer_GetStatus(dscbo,0); ok(rc==DSERR_INVALIDPARAM,"IDirectSoundCaptureBuffer_GetStatus() should " "have returned DSERR_INVALIDPARAM, returned: %08x\n", rc); rc=IDirectSoundCaptureBuffer_GetStatus(dscbo,&status); ok(rc==DS_OK,"IDirectSoundCaptureBuffer_GetStatus() failed: %08x\n", rc); if (rc==DS_OK && winetest_debug > 1) { trace(" Status=0x%04x\n",status); } ZeroMemory(&state, sizeof(state)); state.dscbo=dscbo; state.wfx=&wfx; state.buffer_size = dscbcaps.dwBufferBytes; for (i = 0; i < NOTIFICATIONS; i++) state.event[i] = CreateEvent( NULL, FALSE, FALSE, NULL ); state.size = dscbcaps.dwBufferBytes / NOTIFICATIONS; rc=IDirectSoundCaptureBuffer_QueryInterface(dscbo,&IID_IDirectSoundNotify, (void **)&(state.notify)); ok((rc==DS_OK)&&(state.notify!=NULL), "IDirectSoundCaptureBuffer_QueryInterface() failed: %08x\n", rc); for (i = 0; i < NOTIFICATIONS; i++) { state.posnotify[i].dwOffset = (i * state.size) + state.size - 1; state.posnotify[i].hEventNotify = state.event[i]; } rc=IDirectSoundNotify_SetNotificationPositions(state.notify,NOTIFICATIONS, state.posnotify); ok(rc==DS_OK,"IDirectSoundNotify_SetNotificationPositions() failed: %08x\n", rc); ref=IDirectSoundNotify_Release(state.notify); ok(ref==0,"IDirectSoundNotify_Release(): has %d references, should have " "0\n",ref); if (record) { rc=IDirectSoundCaptureBuffer_Start(dscbo,DSCBSTART_LOOPING); ok(rc==DS_OK,"IDirectSoundCaptureBuffer_Start() failed: %08x\n", rc); rc=IDirectSoundCaptureBuffer_GetStatus(dscbo,&status); ok(rc==DS_OK,"IDirectSoundCaptureBuffer_GetStatus() failed: %08x\n", rc); ok(status==(DSCBSTATUS_CAPTURING|DSCBSTATUS_LOOPING), "GetStatus: bad status: %x\n",status); /* wait for the notifications */ for (i = 0; i < (NOTIFICATIONS * 2); i++) { rc=WaitForMultipleObjects(NOTIFICATIONS,state.event,FALSE,3000); ok(rc==(WAIT_OBJECT_0+(i%NOTIFICATIONS)), "WaitForMultipleObjects failed: 0x%x\n",rc); if (rc!=(WAIT_OBJECT_0+(i%NOTIFICATIONS))) { ok((rc==WAIT_TIMEOUT)||(rc==WAIT_FAILED), "Wrong notification: should be %d, got %d\n", i%NOTIFICATIONS,rc-WAIT_OBJECT_0); } if (!capture_buffer_service(&state)) break; } rc=IDirectSoundCaptureBuffer_Stop(dscbo); ok(rc==DS_OK,"IDirectSoundCaptureBuffer_Stop() failed: %08x\n", rc); } }
/* return number of readed bytes */ static guint gst_directsound_src_read (GstAudioSrc * asrc, gpointer data, guint length, GstClockTime * timestamp) { GstDirectSoundSrc *dsoundsrc; guint64 sleep_time_ms, sleep_until; GstClockID clock_id; HRESULT hRes; /* Result for windows functions */ DWORD dwCurrentCaptureCursor = 0; DWORD dwBufferSize = 0; LPVOID pLockedBuffer1 = NULL; LPVOID pLockedBuffer2 = NULL; DWORD dwSizeBuffer1 = 0; DWORD dwSizeBuffer2 = 0; DWORD dwStatus = 0; GST_DEBUG_OBJECT (asrc, "reading directsoundsrc"); dsoundsrc = GST_DIRECTSOUND_SRC (asrc); GST_DSOUND_LOCK (dsoundsrc); /* Get current buffer status */ hRes = IDirectSoundCaptureBuffer_GetStatus (dsoundsrc->pDSBSecondary, &dwStatus); if (FAILED (hRes)) { GST_DSOUND_UNLOCK (dsoundsrc); return -1; } /* Starting capturing if not already */ if (!(dwStatus & DSCBSTATUS_CAPTURING)) { hRes = IDirectSoundCaptureBuffer_Start (dsoundsrc->pDSBSecondary, DSCBSTART_LOOPING); GST_INFO_OBJECT (asrc, "capture started"); } /* Loop till the source has produced bytes equal to or greater than @length. * * DirectSound has a notification-based API that uses Windows CreateEvent() * + WaitForSingleObject(), but it is completely useless for live streams. * * 1. You must schedule all events before starting capture * 2. The events are all fired exactly once * 3. You cannot schedule new events while a capture is running * 4. You cannot stop/schedule/start either * * This means you cannot use the API while doing live looped capture and we * must resort to this. * * However, this is almost as efficient as event-based capture since it's ok * to consistently overwait by a fixed amount; the extra bytes will just end * up being used in the next call, and the extra latency will be constant. */ while (TRUE) { hRes = IDirectSoundCaptureBuffer_GetCurrentPosition (dsoundsrc->pDSBSecondary, &dwCurrentCaptureCursor, NULL); if (FAILED (hRes)) { GST_DSOUND_UNLOCK (dsoundsrc); return -1; } /* calculate the size of the buffer that's been captured while accounting * for wrap-arounds */ if (dwCurrentCaptureCursor < dsoundsrc->current_circular_offset) { dwBufferSize = dsoundsrc->buffer_size - (dsoundsrc->current_circular_offset - dwCurrentCaptureCursor); } else { dwBufferSize = dwCurrentCaptureCursor - dsoundsrc->current_circular_offset; } if (dwBufferSize >= length) { /* Yay, we got all the data we need */ break; } else { GST_DEBUG_OBJECT (asrc, "not enough data, got %lu (want at least %u)", dwBufferSize, length); /* If we didn't get enough data, sleep for a proportionate time */ sleep_time_ms = gst_util_uint64_scale (dsoundsrc->latency_time, length - dwBufferSize, length * 1000); /* Make sure we don't run in a tight loop unnecessarily */ sleep_time_ms = MAX (sleep_time_ms, 10); /* Sleep using gst_clock_id_wait() so that we can be interrupted */ sleep_until = gst_clock_get_time (dsoundsrc->system_clock) + sleep_time_ms * GST_MSECOND; /* Setup the clock id wait */ if (G_UNLIKELY (dsoundsrc->read_wait_clock_id == NULL || gst_clock_single_shot_id_reinit (dsoundsrc->system_clock, dsoundsrc->read_wait_clock_id, sleep_until) == FALSE)) { if (dsoundsrc->read_wait_clock_id != NULL) gst_clock_id_unref (dsoundsrc->read_wait_clock_id); dsoundsrc->read_wait_clock_id = gst_clock_new_single_shot_id (dsoundsrc->system_clock, sleep_until); } clock_id = dsoundsrc->read_wait_clock_id; dsoundsrc->reset_while_sleeping = FALSE; GST_DEBUG_OBJECT (asrc, "waiting %" G_GUINT64_FORMAT "ms for more data", sleep_time_ms); GST_DSOUND_UNLOCK (dsoundsrc); gst_clock_id_wait (clock_id, NULL); GST_DSOUND_LOCK (dsoundsrc); if (dsoundsrc->reset_while_sleeping == TRUE) { GST_DEBUG_OBJECT (asrc, "reset while sleeping, cancelled read"); GST_DSOUND_UNLOCK (dsoundsrc); return -1; } } } GST_DEBUG_OBJECT (asrc, "Got enough data: %lu bytes (wanted at least %u)", dwBufferSize, length); /* Lock the buffer and read only the first @length bytes. Keep the rest in * the capture buffer for the next read. */ hRes = IDirectSoundCaptureBuffer_Lock (dsoundsrc->pDSBSecondary, dsoundsrc->current_circular_offset, length, &pLockedBuffer1, &dwSizeBuffer1, &pLockedBuffer2, &dwSizeBuffer2, 0L); /* NOTE: We now assume that dwSizeBuffer1 + dwSizeBuffer2 == length since the * API is supposed to guarantee that */ /* Copy buffer data to another buffer */ if (hRes == DS_OK) { memcpy (data, pLockedBuffer1, dwSizeBuffer1); } /* ...and if something is in another buffer */ if (pLockedBuffer2 != NULL) { memcpy (((guchar *) data + dwSizeBuffer1), pLockedBuffer2, dwSizeBuffer2); } dsoundsrc->current_circular_offset += dwSizeBuffer1 + dwSizeBuffer2; dsoundsrc->current_circular_offset %= dsoundsrc->buffer_size; IDirectSoundCaptureBuffer_Unlock (dsoundsrc->pDSBSecondary, pLockedBuffer1, dwSizeBuffer1, pLockedBuffer2, dwSizeBuffer2); GST_DSOUND_UNLOCK (dsoundsrc); /* We always read exactly @length data */ return length; }