📄 dsound.c
字号:
if (rc == WAIT_OBJECT_0) {
if (events[0] == strm->play_strm.hEvent)
signalled_dir = PJMEDIA_DIR_PLAYBACK;
else
signalled_dir = PJMEDIA_DIR_CAPTURE;
} else {
if (events[1] == strm->play_strm.hEvent)
signalled_dir = PJMEDIA_DIR_PLAYBACK;
else
signalled_dir = PJMEDIA_DIR_CAPTURE;
}
if (signalled_dir == PJMEDIA_DIR_PLAYBACK) {
struct dsound_stream *dsound_strm;
/*
* DirectSound has requested us to feed some frames to
* playback buffer.
*/
dsound_strm = &strm->play_strm;
status = PJ_SUCCESS;
/* Get frame from application. */
status = (*strm->play_cb)(strm->user_data,
dsound_strm->timestamp.u32.lo,
strm->buffer,
bytes_per_frame);
if (status != PJ_SUCCESS)
break;
/* Write to DirectSound buffer. */
AppWriteDataToBuffer( dsound_strm->ds.play.lpDsBuffer,
dsound_strm->dwBytePos,
(LPBYTE)strm->buffer,
bytes_per_frame);
/* Increment position. */
dsound_strm->dwBytePos += bytes_per_frame;
if (dsound_strm->dwBytePos >= dsound_strm->dwDsBufferSize)
dsound_strm->dwBytePos -= dsound_strm->dwDsBufferSize;
dsound_strm->timestamp.u64 += strm->samples_per_frame;
} else {
/*
* DirectSound has indicated that it has some frames ready
* in the capture buffer. Get as much frames as possible to
* prevent overflows.
*/
struct dsound_stream *dsound_strm;
BOOL rc;
dsound_strm = &strm->rec_strm;
do {
/* Capture from DirectSound buffer. */
rc = AppReadDataFromBuffer(dsound_strm->ds.capture.lpDsBuffer,
dsound_strm->dwBytePos,
(LPBYTE)strm->buffer,
bytes_per_frame);
if (!rc) {
pj_bzero(strm->buffer, bytes_per_frame);
}
/* Call callback */
status = (*strm->rec_cb)(strm->user_data,
dsound_strm->timestamp.u32.lo,
strm->buffer,
bytes_per_frame);
/* Quit thread on error. */
if (status != PJ_SUCCESS)
goto on_error;
/* Increment position. */
dsound_strm->dwBytePos += bytes_per_frame;
if (dsound_strm->dwBytePos >= dsound_strm->dwDsBufferSize)
dsound_strm->dwBytePos -= dsound_strm->dwDsBufferSize;
dsound_strm->timestamp.u64 += strm->samples_per_frame;
/* Fetch while we have more than 1 frame */
} while (dsound_captured_size(dsound_strm) > bytes_per_frame);
}
}
on_error:
PJ_LOG(5,(THIS_FILE, "DirectSound: thread stopping.."));
return 0;
}
/* DirectSound enum device callback */
static BOOL CALLBACK DSEnumCallback( LPGUID lpGuid, LPCSTR lpcstrDescription,
LPCSTR lpcstrModule, LPVOID lpContext)
{
unsigned index, max = sizeof(dev_info[index].info.name);
pj_bool_t is_capture_device = (lpContext != NULL);
PJ_UNUSED_ARG(lpcstrModule);
/* Put the capture and playback of the same devices to the same
* dev_info item, by looking at the GUID.
*/
for (index=0; index<dev_count; ++index) {
if (dev_info[index].lpGuid == lpGuid)
break;
}
if (index == dev_count)
++dev_count;
else if (dev_count >= MAX_HARDWARE) {
pj_assert(!"Too many DirectSound hardware found");
PJ_LOG(4,(THIS_FILE, "Too many hardware found, some devices will "
"not be listed"));
return FALSE;
}
strncpy(dev_info[index].info.name, lpcstrDescription, max);
dev_info[index].info.name[max-1] = '\0';
dev_info[index].lpGuid = lpGuid;
dev_info[index].info.default_samples_per_sec = 44100;
/* Just assumed that device supports stereo capture/playback */
if (is_capture_device)
dev_info[index].info.input_count+=2;
else
dev_info[index].info.output_count+=2;
return TRUE;
}
/*
* Init sound library.
*/
PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory)
{
HRESULT hr;
unsigned i;
if (++snd_init_count != 1)
return PJ_SUCCESS;
pool_factory = factory;
/* Enumerate sound playback devices */
hr = DirectSoundEnumerate(&DSEnumCallback, NULL);
if (FAILED(hr))
return PJ_RETURN_OS_ERROR(hr);
/* Enumerate sound capture devices */
hr = DirectSoundCaptureEnumerate(&DSEnumCallback, (void*)1);
if (FAILED(hr))
return PJ_RETURN_OS_ERROR(hr);
PJ_LOG(4,(THIS_FILE, "DirectSound initialized, found %d devices:",
dev_count));
for (i=0; i<dev_count; ++i) {
PJ_LOG(4,(THIS_FILE, " dev_id %d: %s (in=%d, out=%d)",
i, dev_info[i].info.name,
dev_info[i].info.input_count,
dev_info[i].info.output_count));
}
return PJ_SUCCESS;
}
/*
* Deinitialize sound library.
*/
PJ_DEF(pj_status_t) pjmedia_snd_deinit(void)
{
--snd_init_count;
return PJ_SUCCESS;
}
/*
* Get device count.
*/
PJ_DEF(int) pjmedia_snd_get_dev_count(void)
{
return dev_count;
}
/*
* Get device info.
*/
PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index)
{
if (index == (unsigned)-1)
index = 0;
PJ_ASSERT_RETURN(index < dev_count, NULL);
return &dev_info[index].info;
}
/*
* Open stream.
*/
static pj_status_t open_stream( pjmedia_dir dir,
int rec_id,
int play_id,
unsigned clock_rate,
unsigned channel_count,
unsigned samples_per_frame,
unsigned bits_per_sample,
pjmedia_snd_rec_cb rec_cb,
pjmedia_snd_play_cb play_cb,
void *user_data,
pjmedia_snd_stream **p_snd_strm)
{
pj_pool_t *pool;
pjmedia_snd_stream *strm;
pj_status_t status;
/* Make sure sound subsystem has been initialized with
* pjmedia_snd_init()
*/
PJ_ASSERT_RETURN( pool_factory != NULL, PJ_EINVALIDOP );
/* Can only support 16bits per sample */
PJ_ASSERT_RETURN(bits_per_sample == BITS_PER_SAMPLE, PJ_EINVAL);
/* Create and Initialize stream descriptor */
pool = pj_pool_create(pool_factory, "dsound-dev", 1000, 1000, NULL);
PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
strm = pj_pool_zalloc(pool, sizeof(pjmedia_snd_stream));
strm->dir = dir;
strm->play_id = play_id;
strm->rec_id = rec_id;
strm->pool = pool;
strm->rec_cb = rec_cb;
strm->play_cb = play_cb;
strm->user_data = user_data;
strm->clock_rate = clock_rate;
strm->samples_per_frame = samples_per_frame;
strm->bits_per_sample = bits_per_sample;
strm->channel_count = channel_count;
strm->buffer = pj_pool_alloc(pool, samples_per_frame * BYTES_PER_SAMPLE);
if (!strm->buffer) {
pj_pool_release(pool);
return PJ_ENOMEM;
}
/* Create player stream */
if (dir & PJMEDIA_DIR_PLAYBACK) {
status = init_player_stream( &strm->play_strm, play_id, clock_rate,
channel_count, samples_per_frame,
DEFAULT_BUFFER_COUNT );
if (status != PJ_SUCCESS) {
pjmedia_snd_stream_close(strm);
return status;
}
}
/* Create capture stream */
if (dir & PJMEDIA_DIR_CAPTURE) {
status = init_capture_stream( &strm->rec_strm, rec_id, clock_rate,
channel_count, samples_per_frame,
DEFAULT_BUFFER_COUNT);
if (status != PJ_SUCCESS) {
pjmedia_snd_stream_close(strm);
return status;
}
}
/* Create and start the thread */
status = pj_thread_create(pool, "dsound", &dsound_dev_thread, strm,
0, 0, &strm->thread);
if (status != PJ_SUCCESS) {
pjmedia_snd_stream_close(strm);
return status;
}
*p_snd_strm = strm;
return PJ_SUCCESS;
}
/*
* Open stream.
*/
PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index,
unsigned clock_rate,
unsigned channel_count,
unsigned samples_per_frame,
unsigned bits_per_sample,
pjmedia_snd_rec_cb rec_cb,
void *user_data,
pjmedia_snd_stream **p_snd_strm)
{
PJ_ASSERT_RETURN(rec_cb && p_snd_strm, PJ_EINVAL);
return open_stream( PJMEDIA_DIR_CAPTURE, index, -1,
clock_rate, channel_count, samples_per_frame,
bits_per_sample, rec_cb, NULL, user_data,
p_snd_strm);
}
PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index,
unsigned clock_rate,
unsigned channel_count,
unsigned samples_per_frame,
unsigned bits_per_sample,
pjmedia_snd_play_cb play_cb,
void *user_data,
pjmedia_snd_stream **p_snd_strm)
{
PJ_ASSERT_RETURN(play_cb && p_snd_strm, PJ_EINVAL);
return open_stream( PJMEDIA_DIR_PLAYBACK, -1, index,
clock_rate, channel_count, samples_per_frame,
bits_per_sample, NULL, play_cb, user_data,
p_snd_strm);
}
/*
* Open both player and recorder.
*/
PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id,
int play_id,
unsigned clock_rate,
unsigned channel_count,
unsigned samples_per_frame,
unsigned bits_per_sample,
pjmedia_snd_rec_cb rec_cb,
pjmedia_snd_play_cb play_cb,
void *user_data,
pjmedia_snd_stream **p_snd_strm)
{
PJ_ASSERT_RETURN(rec_cb && play_cb && p_snd_strm, PJ_EINVAL);
return open_stream( PJMEDIA_DIR_CAPTURE_PLAYBACK, rec_id, play_id,
clock_rate, channel_count, samples_per_frame,
bits_per_sample, rec_cb, play_cb, user_data,
p_snd_strm );
}
/*
* Get stream info.
*/
PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm,
pjmedia_snd_stream_info *pi)
{
PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
pj_bzero(pi, sizeof(*pi));
pi->dir = strm->dir;
pi->play_id = strm->play_id;
pi->rec_id = strm->rec_id;
pi->clock_rate = strm->clock_rate;
pi->channel_count = strm->channel_count;
pi->samples_per_frame = strm->samples_per_frame;
pi->bits_per_sample = strm->bits_per_sample;
pi->rec_latency = 0;
pi->play_latency = 0;
return PJ_SUCCESS;
}
/*
* Start stream.
*/
PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream)
{
HRESULT hr;
PJ_UNUSED_ARG(stream);
if (stream->play_strm.ds.play.lpDsBuffer) {
hr = IDirectSoundBuffer_Play(stream->play_strm.ds.play.lpDsBuffer,
0, 0, DSBPLAY_LOOPING);
if (FAILED(hr))
return PJ_RETURN_OS_ERROR(hr);
PJ_LOG(5,(THIS_FILE, "DirectSound playback stream started"));
}
if (stream->rec_strm.ds.capture.lpDsBuffer) {
hr = IDirectSoundCaptureBuffer_Start(stream->rec_strm.ds.capture.lpDsBuffer,
DSCBSTART_LOOPING );
if (FAILED(hr))
return PJ_RETURN_OS_ERROR(hr);
PJ_LOG(5,(THIS_FILE, "DirectSound capture stream started"));
}
return PJ_SUCCESS;
}
/*
* Stop stream.
*/
PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream)
{
PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
if (stream->play_strm.ds.play.lpDsBuffer) {
PJ_LOG(5,(THIS_FILE, "Stopping DirectSound playback stream"));
IDirectSoundBuffer_Stop( stream->play_strm.ds.play.lpDsBuffer );
}
if (stream->rec_strm.ds.capture.lpDsBuffer) {
PJ_LOG(5,(THIS_FILE, "Stopping DirectSound capture stream"));
IDirectSoundCaptureBuffer_Stop(stream->rec_strm.ds.capture.lpDsBuffer);
}
return PJ_SUCCESS;
}
/*
* Destroy stream.
*/
PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream)
{
PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
pjmedia_snd_stream_stop(stream);
if (stream->thread) {
stream->thread_quit_flag = 1;
pj_thread_join(stream->thread);
pj_thread_destroy(stream->thread);
stream->thread = NULL;
}
if (stream->play_strm.lpDsNotify) {
IDirectSoundNotify_Release( stream->play_strm.lpDsNotify );
stream->play_strm.lpDsNotify = NULL;
}
if (stream->play_strm.hEvent) {
CloseHandle(stream->play_strm.hEvent);
stream->play_strm.hEvent = NULL;
}
if (stream->play_strm.ds.play.lpDsBuffer) {
IDirectSoundBuffer_Release( stream->play_strm.ds.play.lpDsBuffer );
stream->play_strm.ds.play.lpDsBuffer = NULL;
}
if (stream->play_strm.ds.play.lpDs) {
IDirectSound_Release( stream->play_strm.ds.play.lpDs );
stream->play_strm.ds.play.lpDs = NULL;
}
if (stream->rec_strm.lpDsNotify) {
IDirectSoundNotify_Release( stream->rec_strm.lpDsNotify );
stream->rec_strm.lpDsNotify = NULL;
}
if (stream->rec_strm.hEvent) {
CloseHandle(stream->rec_strm.hEvent);
stream->rec_strm.hEvent = NULL;
}
if (stream->rec_strm.ds.capture.lpDsBuffer) {
IDirectSoundCaptureBuffer_Release( stream->rec_strm.ds.capture.lpDsBuffer );
stream->rec_strm.ds.capture.lpDsBuffer = NULL;
}
if (stream->rec_strm.ds.capture.lpDs) {
IDirectSoundCapture_Release( stream->rec_strm.ds.capture.lpDs );
stream->rec_strm.ds.capture.lpDs = NULL;
}
pj_pool_release(stream->pool);
return PJ_SUCCESS;
}
#endif /* PJMEDIA_SOUND_IMPLEMENTATION */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -