1
/* $Id: pa_dev.c 3553 2011-05-05 06:14:19Z nanang $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
#include <pjmedia-audiodev/audiodev_imp.h>
21
#include <pj/assert.h>
24
#include <pj/string.h>
26
#if PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO
28
#include <portaudio.h>
30
#define THIS_FILE "pa_dev.c"
31
#define DRIVER_NAME "PA"
33
/* Enable call to PaUtil_SetDebugPrintFunction, but this is not always
34
* available across all PortAudio versions (?)
36
/*#define USE_PA_DEBUG_PRINT */
40
pjmedia_aud_dev_factory base;
47
* Sound stream descriptor.
48
* This struct may be used for both unidirectional or bidirectional sound
53
pjmedia_aud_stream base;
61
pj_uint32_t samples_per_sec;
62
unsigned samples_per_frame;
69
pjmedia_aud_rec_cb rec_cb;
70
pjmedia_aud_play_cb play_cb;
72
pj_timestamp play_timestamp;
73
pj_timestamp rec_timestamp;
74
pj_uint32_t underflow;
79
pj_bool_t rec_thread_exited;
80
pj_bool_t rec_thread_initialized;
81
pj_thread_desc rec_thread_desc;
82
pj_thread_t *rec_thread;
84
pj_bool_t play_thread_exited;
85
pj_bool_t play_thread_initialized;
86
pj_thread_desc play_thread_desc;
87
pj_thread_t *play_thread;
89
/* Sometime the record callback does not return framesize as configured
90
* (e.g: in OSS), while this module must guarantee returning framesize
91
* as configured in the creation settings. In this case, we need a buffer
92
* for the recorded samples.
95
unsigned rec_buf_count;
97
/* Sometime the player callback does not request framesize as configured
98
* (e.g: in Linux OSS) while sound device will always get samples from
99
* the other component as many as configured samples_per_frame.
101
pj_int16_t *play_buf;
102
unsigned play_buf_count;
106
/* Factory prototypes */
107
static pj_status_t pa_init(pjmedia_aud_dev_factory *f);
108
static pj_status_t pa_destroy(pjmedia_aud_dev_factory *f);
109
static pj_status_t pa_refresh(pjmedia_aud_dev_factory *f);
110
static unsigned pa_get_dev_count(pjmedia_aud_dev_factory *f);
111
static pj_status_t pa_get_dev_info(pjmedia_aud_dev_factory *f,
113
pjmedia_aud_dev_info *info);
114
static pj_status_t pa_default_param(pjmedia_aud_dev_factory *f,
116
pjmedia_aud_param *param);
117
static pj_status_t pa_create_stream(pjmedia_aud_dev_factory *f,
118
const pjmedia_aud_param *param,
119
pjmedia_aud_rec_cb rec_cb,
120
pjmedia_aud_play_cb play_cb,
122
pjmedia_aud_stream **p_aud_strm);
124
/* Stream prototypes */
125
static pj_status_t strm_get_param(pjmedia_aud_stream *strm,
126
pjmedia_aud_param *param);
127
static pj_status_t strm_get_cap(pjmedia_aud_stream *strm,
128
pjmedia_aud_dev_cap cap,
130
static pj_status_t strm_set_cap(pjmedia_aud_stream *strm,
131
pjmedia_aud_dev_cap cap,
133
static pj_status_t strm_start(pjmedia_aud_stream *strm);
134
static pj_status_t strm_stop(pjmedia_aud_stream *strm);
135
static pj_status_t strm_destroy(pjmedia_aud_stream *strm);
138
static pjmedia_aud_dev_factory_op pa_op =
149
static pjmedia_aud_stream_op pa_strm_op =
161
static int PaRecorderCallback(const void *input,
163
unsigned long frameCount,
164
const PaStreamCallbackTimeInfo* timeInfo,
165
PaStreamCallbackFlags statusFlags,
168
struct pa_aud_stream *stream = (struct pa_aud_stream*) userData;
169
pj_status_t status = 0;
172
PJ_UNUSED_ARG(output);
173
PJ_UNUSED_ARG(timeInfo);
175
if (stream->quit_flag)
181
/* Known cases of callback's thread:
182
* - The thread may be changed in the middle of a session, e.g: in MacOS
183
* it happens when plugging/unplugging headphone.
184
* - The same thread may be reused in consecutive sessions. The first
185
* session will leave TLS set, but release the TLS data address,
186
* so the second session must re-register the callback's thread.
188
if (stream->rec_thread_initialized == 0 || !pj_thread_is_registered())
190
pj_bzero(stream->rec_thread_desc, sizeof(pj_thread_desc));
191
status = pj_thread_register("pa_rec", stream->rec_thread_desc,
192
&stream->rec_thread);
193
stream->rec_thread_initialized = 1;
194
PJ_LOG(5,(THIS_FILE, "Recorder thread started"));
197
if (statusFlags & paInputUnderflow)
199
if (statusFlags & paInputOverflow)
202
/* Calculate number of samples we've got */
203
nsamples = frameCount * stream->channel_count + stream->rec_buf_count;
205
if (nsamples >= stream->samples_per_frame)
207
/* If buffer is not empty, combine the buffer with the just incoming
208
* samples, then call put_frame.
210
if (stream->rec_buf_count) {
211
unsigned chunk_count = 0;
214
chunk_count = stream->samples_per_frame - stream->rec_buf_count;
215
pjmedia_copy_samples(stream->rec_buf + stream->rec_buf_count,
216
(pj_int16_t*)input, chunk_count);
218
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
219
frame.buf = (void*) stream->rec_buf;
220
frame.size = stream->samples_per_frame * stream->bytes_per_sample;
221
frame.timestamp.u64 = stream->rec_timestamp.u64;
224
status = (*stream->rec_cb)(stream->user_data, &frame);
226
input = (pj_int16_t*) input + chunk_count;
227
nsamples -= stream->samples_per_frame;
228
stream->rec_buf_count = 0;
229
stream->rec_timestamp.u64 += stream->samples_per_frame /
230
stream->channel_count;
233
/* Give all frames we have */
234
while (nsamples >= stream->samples_per_frame && status == 0) {
237
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
238
frame.buf = (void*) input;
239
frame.size = stream->samples_per_frame * stream->bytes_per_sample;
240
frame.timestamp.u64 = stream->rec_timestamp.u64;
243
status = (*stream->rec_cb)(stream->user_data, &frame);
245
input = (pj_int16_t*) input + stream->samples_per_frame;
246
nsamples -= stream->samples_per_frame;
247
stream->rec_timestamp.u64 += stream->samples_per_frame /
248
stream->channel_count;
251
/* Store the remaining samples into the buffer */
252
if (nsamples && status == 0) {
253
stream->rec_buf_count = nsamples;
254
pjmedia_copy_samples(stream->rec_buf, (pj_int16_t*)input,
259
/* Not enough samples, let's just store them in the buffer */
260
pjmedia_copy_samples(stream->rec_buf + stream->rec_buf_count,
262
frameCount * stream->channel_count);
263
stream->rec_buf_count += frameCount * stream->channel_count;
270
stream->rec_thread_exited = 1;
274
static int PaPlayerCallback( const void *input,
276
unsigned long frameCount,
277
const PaStreamCallbackTimeInfo* timeInfo,
278
PaStreamCallbackFlags statusFlags,
281
struct pa_aud_stream *stream = (struct pa_aud_stream*) userData;
282
pj_status_t status = 0;
283
unsigned nsamples_req = frameCount * stream->channel_count;
285
PJ_UNUSED_ARG(input);
286
PJ_UNUSED_ARG(timeInfo);
288
if (stream->quit_flag)
294
/* Known cases of callback's thread:
295
* - The thread may be changed in the middle of a session, e.g: in MacOS
296
* it happens when plugging/unplugging headphone.
297
* - The same thread may be reused in consecutive sessions. The first
298
* session will leave TLS set, but release the TLS data address,
299
* so the second session must re-register the callback's thread.
301
if (stream->play_thread_initialized == 0 || !pj_thread_is_registered())
303
pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc));
304
status = pj_thread_register("portaudio", stream->play_thread_desc,
305
&stream->play_thread);
306
stream->play_thread_initialized = 1;
307
PJ_LOG(5,(THIS_FILE, "Player thread started"));
310
if (statusFlags & paOutputUnderflow)
312
if (statusFlags & paOutputOverflow)
316
/* Check if any buffered samples */
317
if (stream->play_buf_count) {
318
/* samples buffered >= requested by sound device */
319
if (stream->play_buf_count >= nsamples_req) {
320
pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,
322
stream->play_buf_count -= nsamples_req;
323
pjmedia_move_samples(stream->play_buf,
324
stream->play_buf + nsamples_req,
325
stream->play_buf_count);
331
/* samples buffered < requested by sound device */
332
pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,
333
stream->play_buf_count);
334
nsamples_req -= stream->play_buf_count;
335
output = (pj_int16_t*)output + stream->play_buf_count;
336
stream->play_buf_count = 0;
339
/* Fill output buffer as requested */
340
while (nsamples_req && status == 0) {
341
if (nsamples_req >= stream->samples_per_frame) {
344
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
346
frame.size = stream->samples_per_frame * stream->bytes_per_sample;
347
frame.timestamp.u64 = stream->play_timestamp.u64;
350
status = (*stream->play_cb)(stream->user_data, &frame);
351
if (status != PJ_SUCCESS)
354
if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
355
pj_bzero(frame.buf, frame.size);
357
nsamples_req -= stream->samples_per_frame;
358
output = (pj_int16_t*)output + stream->samples_per_frame;
362
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
363
frame.buf = stream->play_buf;
364
frame.size = stream->samples_per_frame * stream->bytes_per_sample;
365
frame.timestamp.u64 = stream->play_timestamp.u64;
368
status = (*stream->play_cb)(stream->user_data, &frame);
369
if (status != PJ_SUCCESS)
372
if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
373
pj_bzero(frame.buf, frame.size);
375
pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,
377
stream->play_buf_count = stream->samples_per_frame - nsamples_req;
378
pjmedia_move_samples(stream->play_buf,
379
stream->play_buf+nsamples_req,
380
stream->play_buf_count);
384
stream->play_timestamp.u64 += stream->samples_per_frame /
385
stream->channel_count;
392
stream->play_thread_exited = 1;
397
static int PaRecorderPlayerCallback( const void *input,
399
unsigned long frameCount,
400
const PaStreamCallbackTimeInfo* timeInfo,
401
PaStreamCallbackFlags statusFlags,
406
rc = PaRecorderCallback(input, output, frameCount, timeInfo,
407
statusFlags, userData);
408
if (rc != paContinue)
411
rc = PaPlayerCallback(input, output, frameCount, timeInfo,
412
statusFlags, userData);
416
#ifdef USE_PA_DEBUG_PRINT
417
/* Logging callback from PA */
418
static void pa_log_cb(const char *log)
420
PJ_LOG(5,(THIS_FILE, "PA message: %s", log));
423
/* We should include pa_debugprint.h for this, but the header
424
* is not available publicly. :(
426
typedef void (*PaUtilLogCallback ) (const char *log);
427
void PaUtil_SetDebugPrintFunction(PaUtilLogCallback cb);
432
* Init PortAudio audio driver.
434
pjmedia_aud_dev_factory* pjmedia_pa_factory(pj_pool_factory *pf)
436
struct pa_aud_factory *f;
439
pool = pj_pool_create(pf, "portaudio", 64, 64, NULL);
440
f = PJ_POOL_ZALLOC_T(pool, struct pa_aud_factory);
449
/* API: Init factory */
450
static pj_status_t pa_init(pjmedia_aud_dev_factory *f)
456
#ifdef USE_PA_DEBUG_PRINT
457
PaUtil_SetDebugPrintFunction(&pa_log_cb);
460
err = Pa_Initialize();
463
"PortAudio sound library initialized, status=%d", err));
464
PJ_LOG(4,(THIS_FILE, "PortAudio host api count=%d",
465
Pa_GetHostApiCount()));
466
PJ_LOG(4,(THIS_FILE, "Sound device count=%d",
467
pa_get_dev_count(f)));
469
return err ? PJMEDIA_AUDIODEV_ERRNO_FROM_PORTAUDIO(err) : PJ_SUCCESS;
473
/* API: Destroy factory */
474
static pj_status_t pa_destroy(pjmedia_aud_dev_factory *f)
476
struct pa_aud_factory *pa = (struct pa_aud_factory*)f;
480
PJ_LOG(4,(THIS_FILE, "PortAudio sound library shutting down.."));
482
err = Pa_Terminate();
486
pj_pool_release(pool);
488
return err ? PJMEDIA_AUDIODEV_ERRNO_FROM_PORTAUDIO(err) : PJ_SUCCESS;
492
/* API: Refresh the device list. */
493
static pj_status_t pa_refresh(pjmedia_aud_dev_factory *f)
500
/* API: Get device count. */
501
static unsigned pa_get_dev_count(pjmedia_aud_dev_factory *f)
503
int count = Pa_GetDeviceCount();
505
return count < 0 ? 0 : count;
509
/* API: Get device info. */
510
static pj_status_t pa_get_dev_info(pjmedia_aud_dev_factory *f,
512
pjmedia_aud_dev_info *info)
514
const PaDeviceInfo *pa_info;
518
pa_info = Pa_GetDeviceInfo(index);
520
return PJMEDIA_EAUD_INVDEV;
522
pj_bzero(info, sizeof(*info));
523
strncpy(info->name, pa_info->name, sizeof(info->name));
524
info->name[sizeof(info->name)-1] = '\0';
525
info->input_count = pa_info->maxInputChannels;
526
info->output_count = pa_info->maxOutputChannels;
527
info->default_samples_per_sec = (unsigned)pa_info->defaultSampleRate;
528
strncpy(info->driver, DRIVER_NAME, sizeof(info->driver));
529
info->driver[sizeof(info->driver)-1] = '\0';
530
info->caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
531
PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
537
/* API: fill in with default parameter. */
538
static pj_status_t pa_default_param(pjmedia_aud_dev_factory *f,
540
pjmedia_aud_param *param)
542
pjmedia_aud_dev_info adi;
547
status = pa_get_dev_info(f, index, &adi);
548
if (status != PJ_SUCCESS)
551
pj_bzero(param, sizeof(*param));
552
if (adi.input_count && adi.output_count) {
553
param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
554
param->rec_id = index;
555
param->play_id = index;
556
} else if (adi.input_count) {
557
param->dir = PJMEDIA_DIR_CAPTURE;
558
param->rec_id = index;
559
param->play_id = PJMEDIA_AUD_INVALID_DEV;
560
} else if (adi.output_count) {
561
param->dir = PJMEDIA_DIR_PLAYBACK;
562
param->play_id = index;
563
param->rec_id = PJMEDIA_AUD_INVALID_DEV;
565
return PJMEDIA_EAUD_INVDEV;
568
param->clock_rate = adi.default_samples_per_sec;
569
param->channel_count = 1;
570
param->samples_per_frame = adi.default_samples_per_sec * 20 / 1000;
571
param->bits_per_sample = 16;
572
param->flags = adi.caps;
573
param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
574
param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
580
/* Internal: Get PortAudio default input device ID */
581
static int pa_get_default_input_dev(int channel_count)
585
/* Special for Windows - try to use the DirectSound implementation
586
* first since it provides better latency.
588
#if PJMEDIA_PREFER_DIRECT_SOUND
589
if (Pa_HostApiTypeIdToHostApiIndex(paDirectSound) >= 0) {
590
const PaHostApiInfo *pHI;
591
int index = Pa_HostApiTypeIdToHostApiIndex(paDirectSound);
592
pHI = Pa_GetHostApiInfo(index);
594
const PaDeviceInfo *paDevInfo = NULL;
595
paDevInfo = Pa_GetDeviceInfo(pHI->defaultInputDevice);
596
if (paDevInfo && paDevInfo->maxInputChannels >= channel_count)
597
return pHI->defaultInputDevice;
602
/* Enumerate the host api's for the default devices, and return
603
* the device with suitable channels.
605
count = Pa_GetHostApiCount();
606
for (i=0; i < count; ++i) {
607
const PaHostApiInfo *pHAInfo;
609
pHAInfo = Pa_GetHostApiInfo(i);
613
if (pHAInfo->defaultInputDevice >= 0) {
614
const PaDeviceInfo *paDevInfo;
616
paDevInfo = Pa_GetDeviceInfo(pHAInfo->defaultInputDevice);
618
if (paDevInfo->maxInputChannels >= channel_count)
619
return pHAInfo->defaultInputDevice;
623
/* If still no device is found, enumerate all devices */
624
count = Pa_GetDeviceCount();
625
for (i=0; i<count; ++i) {
626
const PaDeviceInfo *paDevInfo;
628
paDevInfo = Pa_GetDeviceInfo(i);
629
if (paDevInfo->maxInputChannels >= channel_count)
636
/* Internal: Get PortAudio default output device ID */
637
static int pa_get_default_output_dev(int channel_count)
641
/* Special for Windows - try to use the DirectSound implementation
642
* first since it provides better latency.
644
#if PJMEDIA_PREFER_DIRECT_SOUND
645
if (Pa_HostApiTypeIdToHostApiIndex(paDirectSound) >= 0) {
646
const PaHostApiInfo *pHI;
647
int index = Pa_HostApiTypeIdToHostApiIndex(paDirectSound);
648
pHI = Pa_GetHostApiInfo(index);
650
const PaDeviceInfo *paDevInfo = NULL;
651
paDevInfo = Pa_GetDeviceInfo(pHI->defaultOutputDevice);
652
if (paDevInfo && paDevInfo->maxOutputChannels >= channel_count)
653
return pHI->defaultOutputDevice;
658
/* Enumerate the host api's for the default devices, and return
659
* the device with suitable channels.
661
count = Pa_GetHostApiCount();
662
for (i=0; i < count; ++i) {
663
const PaHostApiInfo *pHAInfo;
665
pHAInfo = Pa_GetHostApiInfo(i);
669
if (pHAInfo->defaultOutputDevice >= 0) {
670
const PaDeviceInfo *paDevInfo;
672
paDevInfo = Pa_GetDeviceInfo(pHAInfo->defaultOutputDevice);
674
if (paDevInfo->maxOutputChannels >= channel_count)
675
return pHAInfo->defaultOutputDevice;
679
/* If still no device is found, enumerate all devices */
680
count = Pa_GetDeviceCount();
681
for (i=0; i<count; ++i) {
682
const PaDeviceInfo *paDevInfo;
684
paDevInfo = Pa_GetDeviceInfo(i);
685
if (paDevInfo->maxOutputChannels >= channel_count)
693
/* Internal: create capture/recorder stream */
694
static pj_status_t create_rec_stream( struct pa_aud_factory *pa,
695
const pjmedia_aud_param *param,
696
pjmedia_aud_rec_cb rec_cb,
698
pjmedia_aud_stream **p_snd_strm)
701
pjmedia_aud_dev_index rec_id;
702
struct pa_aud_stream *stream;
703
PaStreamParameters inputParam;
705
const PaDeviceInfo *paDevInfo = NULL;
706
const PaHostApiInfo *paHostApiInfo = NULL;
707
unsigned paFrames, paRate, paLatency;
708
const PaStreamInfo *paSI;
711
PJ_ASSERT_RETURN(rec_cb && p_snd_strm, PJ_EINVAL);
713
rec_id = param->rec_id;
715
rec_id = pa_get_default_input_dev(param->channel_count);
717
/* No such device. */
718
return PJMEDIA_EAUD_NODEFDEV;
722
paDevInfo = Pa_GetDeviceInfo(rec_id);
724
/* Assumed it is "No such device" error. */
725
return PJMEDIA_EAUD_INVDEV;
728
if (param->bits_per_sample == 8)
729
sampleFormat = paUInt8;
730
else if (param->bits_per_sample == 16)
731
sampleFormat = paInt16;
732
else if (param->bits_per_sample == 32)
733
sampleFormat = paInt32;
735
return PJMEDIA_EAUD_SAMPFORMAT;
737
pool = pj_pool_create(pa->pf, "recstrm", 1024, 1024, NULL);
741
stream = PJ_POOL_ZALLOC_T(pool, struct pa_aud_stream);
743
pj_strdup2_with_null(pool, &stream->name, paDevInfo->name);
744
stream->dir = PJMEDIA_DIR_CAPTURE;
745
stream->rec_id = rec_id;
746
stream->play_id = -1;
747
stream->user_data = user_data;
748
stream->samples_per_sec = param->clock_rate;
749
stream->samples_per_frame = param->samples_per_frame;
750
stream->bytes_per_sample = param->bits_per_sample / 8;
751
stream->channel_count = param->channel_count;
752
stream->rec_cb = rec_cb;
754
stream->rec_buf = (pj_int16_t*)pj_pool_alloc(pool,
755
stream->samples_per_frame * stream->bytes_per_sample);
756
stream->rec_buf_count = 0;
758
pj_bzero(&inputParam, sizeof(inputParam));
759
inputParam.device = rec_id;
760
inputParam.channelCount = param->channel_count;
761
inputParam.hostApiSpecificStreamInfo = NULL;
762
inputParam.sampleFormat = sampleFormat;
763
if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY)
764
inputParam.suggestedLatency = param->input_latency_ms / 1000.0;
766
inputParam.suggestedLatency = PJMEDIA_SND_DEFAULT_REC_LATENCY / 1000.0;
768
paHostApiInfo = Pa_GetHostApiInfo(paDevInfo->hostApi);
770
/* Frames in PortAudio is number of samples in a single channel */
771
paFrames = param->samples_per_frame / param->channel_count;
773
err = Pa_OpenStream( &stream->rec_strm, &inputParam, NULL,
774
param->clock_rate, paFrames,
775
paClipOff, &PaRecorderCallback, stream );
776
if (err != paNoError) {
777
pj_pool_release(pool);
778
return PJMEDIA_AUDIODEV_ERRNO_FROM_PORTAUDIO(err);
781
paSI = Pa_GetStreamInfo(stream->rec_strm);
782
paRate = (unsigned)paSI->sampleRate;
783
paLatency = (unsigned)(paSI->inputLatency * 1000);
785
PJ_LOG(5,(THIS_FILE, "Opened device %s (%s) for recording, sample "
787
"bits=%d, %d samples per frame, latency=%d ms",
788
paDevInfo->name, paHostApiInfo->name,
789
paRate, param->channel_count,
790
param->bits_per_sample, param->samples_per_frame,
793
*p_snd_strm = &stream->base;
798
/* Internal: create playback stream */
799
static pj_status_t create_play_stream(struct pa_aud_factory *pa,
800
const pjmedia_aud_param *param,
801
pjmedia_aud_play_cb play_cb,
803
pjmedia_aud_stream **p_snd_strm)
806
pjmedia_aud_dev_index play_id;
807
struct pa_aud_stream *stream;
808
PaStreamParameters outputParam;
810
const PaDeviceInfo *paDevInfo = NULL;
811
const PaHostApiInfo *paHostApiInfo = NULL;
812
const PaStreamInfo *paSI;
813
unsigned paFrames, paRate, paLatency;
816
PJ_ASSERT_RETURN(play_cb && p_snd_strm, PJ_EINVAL);
818
play_id = param->play_id;
820
play_id = pa_get_default_output_dev(param->channel_count);
822
/* No such device. */
823
return PJMEDIA_EAUD_NODEFDEV;
827
paDevInfo = Pa_GetDeviceInfo(play_id);
829
/* Assumed it is "No such device" error. */
830
return PJMEDIA_EAUD_INVDEV;
833
if (param->bits_per_sample == 8)
834
sampleFormat = paUInt8;
835
else if (param->bits_per_sample == 16)
836
sampleFormat = paInt16;
837
else if (param->bits_per_sample == 32)
838
sampleFormat = paInt32;
840
return PJMEDIA_EAUD_SAMPFORMAT;
842
pool = pj_pool_create(pa->pf, "playstrm", 1024, 1024, NULL);
846
stream = PJ_POOL_ZALLOC_T(pool, struct pa_aud_stream);
848
pj_strdup2_with_null(pool, &stream->name, paDevInfo->name);
849
stream->dir = PJMEDIA_DIR_PLAYBACK;
850
stream->play_id = play_id;
852
stream->user_data = user_data;
853
stream->samples_per_sec = param->clock_rate;
854
stream->samples_per_frame = param->samples_per_frame;
855
stream->bytes_per_sample = param->bits_per_sample / 8;
856
stream->channel_count = param->channel_count;
857
stream->play_cb = play_cb;
859
stream->play_buf = (pj_int16_t*)pj_pool_alloc(pool,
860
stream->samples_per_frame *
861
stream->bytes_per_sample);
862
stream->play_buf_count = 0;
864
pj_bzero(&outputParam, sizeof(outputParam));
865
outputParam.device = play_id;
866
outputParam.channelCount = param->channel_count;
867
outputParam.hostApiSpecificStreamInfo = NULL;
868
outputParam.sampleFormat = sampleFormat;
869
if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY)
870
outputParam.suggestedLatency=param->output_latency_ms / 1000.0;
872
outputParam.suggestedLatency=PJMEDIA_SND_DEFAULT_PLAY_LATENCY/1000.0;
874
paHostApiInfo = Pa_GetHostApiInfo(paDevInfo->hostApi);
876
/* Frames in PortAudio is number of samples in a single channel */
877
paFrames = param->samples_per_frame / param->channel_count;
879
err = Pa_OpenStream( &stream->play_strm, NULL, &outputParam,
880
param->clock_rate, paFrames,
881
paClipOff, &PaPlayerCallback, stream );
882
if (err != paNoError) {
883
pj_pool_release(pool);
884
return PJMEDIA_AUDIODEV_ERRNO_FROM_PORTAUDIO(err);
887
paSI = Pa_GetStreamInfo(stream->play_strm);
888
paRate = (unsigned)(paSI->sampleRate);
889
paLatency = (unsigned)(paSI->outputLatency * 1000);
891
PJ_LOG(5,(THIS_FILE, "Opened device %d: %s(%s) for playing, sample rate=%d"
893
"bits=%d, %d samples per frame, latency=%d ms",
894
play_id, paDevInfo->name, paHostApiInfo->name,
895
paRate, param->channel_count,
896
param->bits_per_sample, param->samples_per_frame,
899
*p_snd_strm = &stream->base;
905
/* Internal: Create both player and recorder stream */
906
static pj_status_t create_bidir_stream(struct pa_aud_factory *pa,
907
const pjmedia_aud_param *param,
908
pjmedia_aud_rec_cb rec_cb,
909
pjmedia_aud_play_cb play_cb,
911
pjmedia_aud_stream **p_snd_strm)
914
pjmedia_aud_dev_index rec_id, play_id;
915
struct pa_aud_stream *stream;
916
PaStream *paStream = NULL;
917
PaStreamParameters inputParam;
918
PaStreamParameters outputParam;
920
const PaDeviceInfo *paRecDevInfo = NULL;
921
const PaDeviceInfo *paPlayDevInfo = NULL;
922
const PaHostApiInfo *paRecHostApiInfo = NULL;
923
const PaHostApiInfo *paPlayHostApiInfo = NULL;
924
const PaStreamInfo *paSI;
925
unsigned paFrames, paRate, paInputLatency, paOutputLatency;
928
PJ_ASSERT_RETURN(play_cb && rec_cb && p_snd_strm, PJ_EINVAL);
930
rec_id = param->rec_id;
932
rec_id = pa_get_default_input_dev(param->channel_count);
934
/* No such device. */
935
return PJMEDIA_EAUD_NODEFDEV;
939
paRecDevInfo = Pa_GetDeviceInfo(rec_id);
941
/* Assumed it is "No such device" error. */
942
return PJMEDIA_EAUD_INVDEV;
945
play_id = param->play_id;
947
play_id = pa_get_default_output_dev(param->channel_count);
949
/* No such device. */
950
return PJMEDIA_EAUD_NODEFDEV;
954
paPlayDevInfo = Pa_GetDeviceInfo(play_id);
955
if (!paPlayDevInfo) {
956
/* Assumed it is "No such device" error. */
957
return PJMEDIA_EAUD_INVDEV;
961
if (param->bits_per_sample == 8)
962
sampleFormat = paUInt8;
963
else if (param->bits_per_sample == 16)
964
sampleFormat = paInt16;
965
else if (param->bits_per_sample == 32)
966
sampleFormat = paInt32;
968
return PJMEDIA_EAUD_SAMPFORMAT;
970
pool = pj_pool_create(pa->pf, "sndstream", 1024, 1024, NULL);
974
stream = PJ_POOL_ZALLOC_T(pool, struct pa_aud_stream);
976
pj_strdup2_with_null(pool, &stream->name, paRecDevInfo->name);
977
stream->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
978
stream->play_id = play_id;
979
stream->rec_id = rec_id;
980
stream->user_data = user_data;
981
stream->samples_per_sec = param->clock_rate;
982
stream->samples_per_frame = param->samples_per_frame;
983
stream->bytes_per_sample = param->bits_per_sample / 8;
984
stream->channel_count = param->channel_count;
985
stream->rec_cb = rec_cb;
986
stream->play_cb = play_cb;
988
stream->rec_buf = (pj_int16_t*)pj_pool_alloc(pool,
989
stream->samples_per_frame * stream->bytes_per_sample);
990
stream->rec_buf_count = 0;
992
stream->play_buf = (pj_int16_t*)pj_pool_alloc(pool,
993
stream->samples_per_frame * stream->bytes_per_sample);
994
stream->play_buf_count = 0;
996
pj_bzero(&inputParam, sizeof(inputParam));
997
inputParam.device = rec_id;
998
inputParam.channelCount = param->channel_count;
999
inputParam.hostApiSpecificStreamInfo = NULL;
1000
inputParam.sampleFormat = sampleFormat;
1001
if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY)
1002
inputParam.suggestedLatency = param->input_latency_ms / 1000.0;
1004
inputParam.suggestedLatency = PJMEDIA_SND_DEFAULT_REC_LATENCY / 1000.0;
1006
paRecHostApiInfo = Pa_GetHostApiInfo(paRecDevInfo->hostApi);
1008
pj_bzero(&outputParam, sizeof(outputParam));
1009
outputParam.device = play_id;
1010
outputParam.channelCount = param->channel_count;
1011
outputParam.hostApiSpecificStreamInfo = NULL;
1012
outputParam.sampleFormat = sampleFormat;
1013
if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY)
1014
outputParam.suggestedLatency=param->output_latency_ms / 1000.0;
1016
outputParam.suggestedLatency=PJMEDIA_SND_DEFAULT_PLAY_LATENCY/1000.0;
1018
paPlayHostApiInfo = Pa_GetHostApiInfo(paPlayDevInfo->hostApi);
1020
/* Frames in PortAudio is number of samples in a single channel */
1021
paFrames = param->samples_per_frame / param->channel_count;
1023
/* If both input and output are on the same device, open a single stream
1024
* for both input and output.
1026
if (rec_id == play_id) {
1027
err = Pa_OpenStream( &paStream, &inputParam, &outputParam,
1028
param->clock_rate, paFrames,
1029
paClipOff, &PaRecorderPlayerCallback, stream );
1030
if (err == paNoError) {
1031
/* Set play stream and record stream to the same stream */
1032
stream->play_strm = stream->rec_strm = paStream;
1038
/* .. otherwise if input and output are on the same device, OR if we're
1039
* unable to open a bidirectional stream, then open two separate
1040
* input and output stream.
1042
if (paStream == NULL) {
1043
/* Open input stream */
1044
err = Pa_OpenStream( &stream->rec_strm, &inputParam, NULL,
1045
param->clock_rate, paFrames,
1046
paClipOff, &PaRecorderCallback, stream );
1047
if (err == paNoError) {
1048
/* Open output stream */
1049
err = Pa_OpenStream( &stream->play_strm, NULL, &outputParam,
1050
param->clock_rate, paFrames,
1051
paClipOff, &PaPlayerCallback, stream );
1052
if (err != paNoError)
1053
Pa_CloseStream(stream->rec_strm);
1057
if (err != paNoError) {
1058
pj_pool_release(pool);
1059
return PJMEDIA_AUDIODEV_ERRNO_FROM_PORTAUDIO(err);
1062
paSI = Pa_GetStreamInfo(stream->rec_strm);
1063
paRate = (unsigned)(paSI->sampleRate);
1064
paInputLatency = (unsigned)(paSI->inputLatency * 1000);
1065
paSI = Pa_GetStreamInfo(stream->play_strm);
1066
paOutputLatency = (unsigned)(paSI->outputLatency * 1000);
1068
PJ_LOG(5,(THIS_FILE, "Opened device %s(%s)/%s(%s) for recording and "
1069
"playback, sample rate=%d, ch=%d, "
1070
"bits=%d, %d samples per frame, input latency=%d ms, "
1071
"output latency=%d ms",
1072
paRecDevInfo->name, paRecHostApiInfo->name,
1073
paPlayDevInfo->name, paPlayHostApiInfo->name,
1074
paRate, param->channel_count,
1075
param->bits_per_sample, param->samples_per_frame,
1076
paInputLatency, paOutputLatency));
1078
*p_snd_strm = &stream->base;
1084
/* API: create stream */
1085
static pj_status_t pa_create_stream(pjmedia_aud_dev_factory *f,
1086
const pjmedia_aud_param *param,
1087
pjmedia_aud_rec_cb rec_cb,
1088
pjmedia_aud_play_cb play_cb,
1090
pjmedia_aud_stream **p_aud_strm)
1092
struct pa_aud_factory *pa = (struct pa_aud_factory*)f;
1095
if (param->dir == PJMEDIA_DIR_CAPTURE) {
1096
status = create_rec_stream(pa, param, rec_cb, user_data, p_aud_strm);
1097
} else if (param->dir == PJMEDIA_DIR_PLAYBACK) {
1098
status = create_play_stream(pa, param, play_cb, user_data, p_aud_strm);
1099
} else if (param->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK) {
1100
status = create_bidir_stream(pa, param, rec_cb, play_cb, user_data,
1106
if (status != PJ_SUCCESS)
1109
(*p_aud_strm)->op = &pa_strm_op;
1115
/* API: Get stream parameters */
1116
static pj_status_t strm_get_param(pjmedia_aud_stream *s,
1117
pjmedia_aud_param *pi)
1119
struct pa_aud_stream *strm = (struct pa_aud_stream*)s;
1120
const PaStreamInfo *paPlaySI = NULL, *paRecSI = NULL;
1122
PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1123
PJ_ASSERT_RETURN(strm->play_strm || strm->rec_strm, PJ_EINVALIDOP);
1125
if (strm->play_strm) {
1126
paPlaySI = Pa_GetStreamInfo(strm->play_strm);
1128
if (strm->rec_strm) {
1129
paRecSI = Pa_GetStreamInfo(strm->rec_strm);
1132
pj_bzero(pi, sizeof(*pi));
1133
pi->dir = strm->dir;
1134
pi->play_id = strm->play_id;
1135
pi->rec_id = strm->rec_id;
1136
pi->clock_rate = (unsigned)(paPlaySI ? paPlaySI->sampleRate :
1137
paRecSI->sampleRate);
1138
pi->channel_count = strm->channel_count;
1139
pi->samples_per_frame = strm->samples_per_frame;
1140
pi->bits_per_sample = strm->bytes_per_sample * 8;
1142
pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
1143
pi->input_latency_ms = (unsigned)(paRecSI ? paRecSI->inputLatency *
1147
pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
1148
pi->output_latency_ms = (unsigned)(paPlaySI? paPlaySI->outputLatency *
1156
/* API: get capability */
1157
static pj_status_t strm_get_cap(pjmedia_aud_stream *s,
1158
pjmedia_aud_dev_cap cap,
1161
struct pa_aud_stream *strm = (struct pa_aud_stream*)s;
1163
PJ_ASSERT_RETURN(strm && pval, PJ_EINVAL);
1165
if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY && strm->rec_strm) {
1166
const PaStreamInfo *si = Pa_GetStreamInfo(strm->rec_strm);
1168
return PJMEDIA_EAUD_SYSERR;
1170
*(unsigned*)pval = (unsigned)(si->inputLatency * 1000);
1172
} else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY && strm->play_strm) {
1173
const PaStreamInfo *si = Pa_GetStreamInfo(strm->play_strm);
1175
return PJMEDIA_EAUD_SYSERR;
1177
*(unsigned*)pval = (unsigned)(si->outputLatency * 1000);
1180
return PJMEDIA_EAUD_INVCAP;
1185
/* API: set capability */
1186
static pj_status_t strm_set_cap(pjmedia_aud_stream *strm,
1187
pjmedia_aud_dev_cap cap,
1190
PJ_UNUSED_ARG(strm);
1192
PJ_UNUSED_ARG(value);
1194
/* Nothing is supported */
1195
return PJMEDIA_EAUD_INVCAP;
1199
/* API: start stream. */
1200
static pj_status_t strm_start(pjmedia_aud_stream *s)
1202
struct pa_aud_stream *stream = (struct pa_aud_stream*)s;
1205
PJ_LOG(5,(THIS_FILE, "Starting %s stream..", stream->name.ptr));
1207
if (stream->play_strm)
1208
err = Pa_StartStream(stream->play_strm);
1210
if (err==0 && stream->rec_strm && stream->rec_strm != stream->play_strm) {
1211
err = Pa_StartStream(stream->rec_strm);
1213
Pa_StopStream(stream->play_strm);
1216
PJ_LOG(5,(THIS_FILE, "Done, status=%d", err));
1218
return err ? PJMEDIA_AUDIODEV_ERRNO_FROM_PORTAUDIO(err) : PJ_SUCCESS;
1222
/* API: stop stream. */
1223
static pj_status_t strm_stop(pjmedia_aud_stream *s)
1225
struct pa_aud_stream *stream = (struct pa_aud_stream*)s;
1228
stream->quit_flag = 1;
1229
for (i=0; !stream->rec_thread_exited && i<100; ++i)
1230
pj_thread_sleep(10);
1231
for (i=0; !stream->play_thread_exited && i<100; ++i)
1232
pj_thread_sleep(10);
1236
PJ_LOG(5,(THIS_FILE, "Stopping stream.."));
1238
if (stream->play_strm)
1239
err = Pa_StopStream(stream->play_strm);
1241
if (stream->rec_strm && stream->rec_strm != stream->play_strm)
1242
err = Pa_StopStream(stream->rec_strm);
1244
stream->play_thread_initialized = 0;
1245
stream->rec_thread_initialized = 0;
1247
PJ_LOG(5,(THIS_FILE, "Done, status=%d", err));
1249
return err ? PJMEDIA_AUDIODEV_ERRNO_FROM_PORTAUDIO(err) : PJ_SUCCESS;
1253
/* API: destroy stream. */
1254
static pj_status_t strm_destroy(pjmedia_aud_stream *s)
1256
struct pa_aud_stream *stream = (struct pa_aud_stream*)s;
1259
stream->quit_flag = 1;
1260
for (i=0; !stream->rec_thread_exited && i<100; ++i) {
1263
for (i=0; !stream->play_thread_exited && i<100; ++i) {
1267
PJ_LOG(5,(THIS_FILE, "Closing %.*s: %lu underflow, %lu overflow",
1268
(int)stream->name.slen,
1270
stream->underflow, stream->overflow));
1272
if (stream->play_strm)
1273
err = Pa_CloseStream(stream->play_strm);
1275
if (stream->rec_strm && stream->rec_strm != stream->play_strm)
1276
err = Pa_CloseStream(stream->rec_strm);
1278
pj_pool_release(stream->pool);
1280
return err ? PJMEDIA_AUDIODEV_ERRNO_FROM_PORTAUDIO(err) : PJ_SUCCESS;
1283
#endif /* PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO */