1
/* $Id: bb10_dev.c 4151 2012-06-01 04:49:57Z ming $ */
3
* Copyright (C) 2008-2012 Teluu Inc. (http://www.teluu.com)
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
* This is the implementation of BlackBerry 10 (BB10) audio device.
22
* Original code was kindly donated by Truphone Ltd. (http://www.truphone.com)
23
* The key methods here are bb10_capture_open, bb10_play_open together
24
* with the capture and play threads ca_thread_func and pb_thread_func
27
#include <pjmedia_audiodev.h>
28
#include <pj/assert.h>
32
#include <pjmedia/errno.h>
34
#if defined(PJMEDIA_AUDIO_DEV_HAS_BB10) && PJMEDIA_AUDIO_DEV_HAS_BB10 != 0
37
#include <sys/types.h>
39
#include <sys/select.h>
42
#include <sys/asoundlib.h>
45
#define THIS_FILE "bb10_dev.c"
46
#define BB10_DEVICE_NAME "plughw:%d,%d"
47
/* Double these for 16khz sampling */
48
#define PREFERRED_FRAME_SIZE 320
49
#define VOIP_SAMPLE_RATE 8000
51
/* Set to 1 to enable tracing */
53
# define TRACE_(expr) PJ_LOG(4,expr)
61
static pj_status_t bb10_factory_init(pjmedia_aud_dev_factory *f);
62
static pj_status_t bb10_factory_destroy(pjmedia_aud_dev_factory *f);
63
static pj_status_t bb10_factory_refresh(pjmedia_aud_dev_factory *f);
64
static unsigned bb10_factory_get_dev_count(pjmedia_aud_dev_factory *f);
65
static pj_status_t bb10_factory_get_dev_info(pjmedia_aud_dev_factory *f,
67
pjmedia_aud_dev_info *info);
68
static pj_status_t bb10_factory_default_param(pjmedia_aud_dev_factory *f,
70
pjmedia_aud_param *param);
71
static pj_status_t bb10_factory_create_stream(pjmedia_aud_dev_factory *f,
72
const pjmedia_aud_param *param,
73
pjmedia_aud_rec_cb rec_cb,
74
pjmedia_aud_play_cb play_cb,
76
pjmedia_aud_stream **p_strm);
81
static pj_status_t bb10_stream_get_param(pjmedia_aud_stream *strm,
82
pjmedia_aud_param *param);
83
static pj_status_t bb10_stream_get_cap(pjmedia_aud_stream *strm,
84
pjmedia_aud_dev_cap cap,
86
static pj_status_t bb10_stream_set_cap(pjmedia_aud_stream *strm,
87
pjmedia_aud_dev_cap cap,
89
static pj_status_t bb10_stream_start(pjmedia_aud_stream *strm);
90
static pj_status_t bb10_stream_stop(pjmedia_aud_stream *strm);
91
static pj_status_t bb10_stream_destroy(pjmedia_aud_stream *strm);
96
pjmedia_aud_dev_factory base;
101
pjmedia_aud_dev_info devs[1];
106
pjmedia_aud_stream base;
110
struct bb10_factory *af;
112
pjmedia_aud_param param; /* Running parameter */
113
int rec_id; /* Capture device id */
118
snd_mixer_t *pb_mixer;
119
unsigned long pb_frames; /* samples_per_frame */
120
pjmedia_aud_play_cb pb_cb;
121
unsigned pb_buf_size;
123
pj_thread_t *pb_thread;
127
snd_mixer_t *ca_mixer;
128
unsigned long ca_frames; /* samples_per_frame */
129
pjmedia_aud_rec_cb ca_cb;
130
unsigned ca_buf_size;
132
pj_thread_t *ca_thread;
135
static pjmedia_aud_dev_factory_op bb10_factory_op =
138
&bb10_factory_destroy,
139
&bb10_factory_get_dev_count,
140
&bb10_factory_get_dev_info,
141
&bb10_factory_default_param,
142
&bb10_factory_create_stream,
143
&bb10_factory_refresh
146
static pjmedia_aud_stream_op bb10_stream_op =
148
&bb10_stream_get_param,
149
&bb10_stream_get_cap,
150
&bb10_stream_set_cap,
157
* BB10 - tests loads the audio units and sets up the driver structure
159
static pj_status_t bb10_add_dev (struct bb10_factory *af)
161
pjmedia_aud_dev_info *adi;
162
int pb_result, ca_result;
165
snd_pcm_t *pcm_handle;
167
if (af->dev_cnt >= PJ_ARRAY_SIZE(af->devs))
170
adi = &af->devs[af->dev_cnt];
172
TRACE_((THIS_FILE, "bb10_add_dev Enter"));
174
if ((pb_result = snd_pcm_open_preferred (&pcm_handle, &card, &dev,
175
SND_PCM_OPEN_PLAYBACK)) >= 0)
177
TRACE_((THIS_FILE, "Try to open the device for playback - success"));
178
snd_pcm_close (pcm_handle);
180
TRACE_((THIS_FILE, "Try to open the device for playback - failure"));
183
if ((ca_result = snd_pcm_open_preferred (&pcm_handle, &card, &dev,
184
SND_PCM_OPEN_CAPTURE)) >=0)
186
TRACE_((THIS_FILE, "Try to open the device for capture - success"));
187
snd_pcm_close (pcm_handle);
189
TRACE_((THIS_FILE, "Try to open the device for capture - failure"));
192
if (pb_result < 0 && ca_result < 0) {
193
TRACE_((THIS_FILE, "Unable to open sound device", "preferred"));
194
return PJMEDIA_EAUD_NODEV;
197
/* Reset device info */
198
pj_bzero(adi, sizeof(*adi));
200
/* Set device name */
201
strcpy(adi->name, "preferred");
203
/* Check the number of playback channels */
204
adi->output_count = (pb_result >= 0) ? 1 : 0;
206
/* Check the number of capture channels */
207
adi->input_count = (ca_result >= 0) ? 1 : 0;
209
/* Set the default sample rate */
210
adi->default_samples_per_sec = 8000;
213
strcpy(adi->driver, "BB10");
217
PJ_LOG (4,(THIS_FILE, "Added sound device %s", adi->name));
222
/* Create BB10 audio driver. */
223
pjmedia_aud_dev_factory* pjmedia_bb10_factory(pj_pool_factory *pf)
225
struct bb10_factory *af;
228
pool = pj_pool_create(pf, "bb10_aud_base", 256, 256, NULL);
229
af = PJ_POOL_ZALLOC_T(pool, struct bb10_factory);
231
af->base_pool = pool;
232
af->base.op = &bb10_factory_op;
238
/* API: init factory */
239
static pj_status_t bb10_factory_init(pjmedia_aud_dev_factory *f)
243
status = bb10_factory_refresh(f);
244
if (status != PJ_SUCCESS)
247
PJ_LOG(4,(THIS_FILE, "BB10 initialized"));
252
/* API: destroy factory */
253
static pj_status_t bb10_factory_destroy(pjmedia_aud_dev_factory *f)
255
struct bb10_factory *af = (struct bb10_factory*)f;
258
TRACE_((THIS_FILE, "bb10_factory_destroy() - 1"));
259
pj_pool_release(af->pool);
263
pj_pool_t *pool = af->base_pool;
264
af->base_pool = NULL;
265
TRACE_((THIS_FILE, "bb10_factory_destroy() - 2"));
266
pj_pool_release(pool);
273
/* API: refresh the device list */
274
static pj_status_t bb10_factory_refresh(pjmedia_aud_dev_factory *f)
276
struct bb10_factory *af = (struct bb10_factory*)f;
279
TRACE_((THIS_FILE, "bb10_factory_refresh()"));
281
if (af->pool != NULL) {
282
pj_pool_release(af->pool);
286
af->pool = pj_pool_create(af->pf, "bb10_aud", 256, 256, NULL);
289
err = bb10_add_dev(af);
291
PJ_LOG(4,(THIS_FILE, "BB10 driver found %d devices", af->dev_cnt));
297
/* API: get device count */
298
static unsigned bb10_factory_get_dev_count(pjmedia_aud_dev_factory *f)
300
struct bb10_factory *af = (struct bb10_factory*)f;
305
/* API: get device info */
306
static pj_status_t bb10_factory_get_dev_info(pjmedia_aud_dev_factory *f,
308
pjmedia_aud_dev_info *info)
310
struct bb10_factory *af = (struct bb10_factory*)f;
312
PJ_ASSERT_RETURN(index>=0 && index<af->dev_cnt, PJ_EINVAL);
314
pj_memcpy(info, &af->devs[index], sizeof(*info));
315
info->caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
316
PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
321
/* API: create default parameter */
322
static pj_status_t bb10_factory_default_param(pjmedia_aud_dev_factory *f,
324
pjmedia_aud_param *param)
326
struct bb10_factory *af = (struct bb10_factory*)f;
327
pjmedia_aud_dev_info *adi;
329
PJ_ASSERT_RETURN(index>=0 && index<af->dev_cnt, PJ_EINVAL);
331
adi = &af->devs[index];
333
pj_bzero(param, sizeof(*param));
334
if (adi->input_count && adi->output_count) {
335
param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
336
param->rec_id = index;
337
param->play_id = index;
338
} else if (adi->input_count) {
339
param->dir = PJMEDIA_DIR_CAPTURE;
340
param->rec_id = index;
341
param->play_id = PJMEDIA_AUD_INVALID_DEV;
342
} else if (adi->output_count) {
343
param->dir = PJMEDIA_DIR_PLAYBACK;
344
param->play_id = index;
345
param->rec_id = PJMEDIA_AUD_INVALID_DEV;
347
return PJMEDIA_EAUD_INVDEV;
350
param->clock_rate = adi->default_samples_per_sec;
351
param->channel_count = 1;
352
param->samples_per_frame = adi->default_samples_per_sec * 20 / 1000;
353
param->bits_per_sample = 16;
354
param->flags = adi->caps;
355
param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
356
param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
358
TRACE_((THIS_FILE, "bb10_factory_default_param clock = %d flags = %d"
359
" spf = %d", param->clock_rate, param->flags,
360
param->samples_per_frame));
366
static void close_play_pcm(struct bb10_stream *stream)
368
if (stream != NULL && stream->pb_pcm != NULL) {
369
snd_pcm_close(stream->pb_pcm);
370
stream->pb_pcm = NULL;
374
static void close_play_mixer(struct bb10_stream *stream)
376
if (stream != NULL && stream->pb_mixer != NULL) {
377
snd_mixer_close(stream->pb_mixer);
378
stream->pb_mixer = NULL;
382
static void flush_play(struct bb10_stream *stream)
384
if (stream != NULL && stream->pb_pcm != NULL) {
385
snd_pcm_plugin_flush (stream->pb_pcm, SND_PCM_CHANNEL_PLAYBACK);
389
static void close_capture_pcm(struct bb10_stream *stream)
391
if (stream != NULL && stream->ca_pcm != NULL) {
392
snd_pcm_close(stream->ca_pcm);
393
stream->ca_pcm = NULL;
397
static void close_capture_mixer(struct bb10_stream *stream)
399
if (stream != NULL && stream->ca_mixer != NULL) {
400
snd_mixer_close(stream->ca_mixer);
401
stream->ca_mixer = NULL;
405
static void flush_capture(struct bb10_stream *stream)
407
if (stream != NULL && stream->ca_pcm != NULL) {
408
snd_pcm_plugin_flush (stream->ca_pcm, SND_PCM_CHANNEL_CAPTURE);
414
* Play audio received from PJMEDIA
416
static int pb_thread_func (void *arg)
418
struct bb10_stream* stream = (struct bb10_stream *) arg;
419
/* Handle from bb10_open_playback */
421
int size = stream->pb_buf_size;
422
/* 160 frames for 20ms */
423
unsigned long nframes = stream->pb_frames;
424
void *user_data = stream->user_data;
425
char *buf = stream->pb_buf;
429
pj_bzero (buf, size);
432
TRACE_((THIS_FILE, "pb_thread_func: size = %d ", size));
434
/* Do the final initialization now the thread has started. */
435
if ((result = snd_pcm_plugin_prepare(stream->pb_pcm,
436
SND_PCM_CHANNEL_PLAYBACK)) < 0)
438
close_play_mixer(stream);
439
close_play_pcm(stream);
440
TRACE_((THIS_FILE, "pb_thread_func failed prepare = %d", result));
444
while (!stream->quit) {
447
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
448
/* pointer to buffer filled by PJMEDIA */
451
frame.timestamp.u64 = tstamp.u64;
454
result = stream->pb_cb (user_data, &frame);
455
if (result != PJ_SUCCESS || stream->quit)
458
if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
459
pj_bzero (buf, size);
461
/* Write 640 to play unit */
462
result = snd_pcm_plugin_write(stream->pb_pcm,buf,size);
463
if (result != size) {
464
TRACE_((THIS_FILE, "pb_thread_func failed write = %d", result));
467
tstamp.u64 += nframes;
471
close_play_mixer(stream);
472
close_play_pcm(stream);
473
TRACE_((THIS_FILE, "pb_thread_func: Stopped"));
480
static int ca_thread_func (void *arg)
482
struct bb10_stream* stream = (struct bb10_stream *) arg;
483
int size = stream->ca_buf_size;
484
unsigned long nframes = stream->ca_frames;
485
void *user_data = stream->user_data;
486
/* Buffer to fill for PJMEDIA */
487
char *buf = stream->ca_buf;
490
struct sched_param param;
493
TRACE_((THIS_FILE, "ca_thread_func: size = %d ", size));
495
thid = (pthread_t*) pj_thread_get_os_handle (pj_thread_this());
496
param.sched_priority = sched_get_priority_max (SCHED_RR);
498
result = pthread_setschedparam (*thid, SCHED_RR, ¶m);
500
if (result == EPERM) {
501
PJ_LOG (4,(THIS_FILE, "Unable to increase thread priority, "
502
"root access needed."));
504
PJ_LOG (4,(THIS_FILE, "Unable to increase thread priority, "
505
"error: %d", result));
509
pj_bzero (buf, size);
512
/* Final init now the thread has started */
513
if ((result = snd_pcm_plugin_prepare (stream->ca_pcm,
514
SND_PCM_CHANNEL_CAPTURE)) < 0)
516
close_capture_mixer(stream);
517
close_capture_pcm(stream);
518
TRACE_((THIS_FILE, "ca_thread_func failed prepare = %d", result));
522
while (!stream->quit) {
525
pj_bzero (buf, size);
527
result = snd_pcm_plugin_read(stream->ca_pcm, buf,size);
528
if (result == -EPIPE) {
529
PJ_LOG (4,(THIS_FILE, "ca_thread_func: overrun!"));
530
snd_pcm_plugin_prepare (stream->ca_pcm, SND_PCM_CHANNEL_CAPTURE);
532
} else if (result < 0) {
533
PJ_LOG (4,(THIS_FILE, "ca_thread_func: error reading data!"));
539
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
540
frame.buf = (void *) buf;
542
frame.timestamp.u64 = tstamp.u64;
545
result = stream->ca_cb (user_data, &frame);
546
if (result != PJ_SUCCESS || stream->quit)
549
tstamp.u64 += nframes;
552
flush_capture(stream);
553
close_capture_mixer(stream);
554
close_capture_pcm(stream);
555
TRACE_((THIS_FILE, "ca_thread_func: Stopped"));
561
static pj_status_t bb10_open_playback (struct bb10_stream *stream,
562
const pjmedia_aud_param *param)
567
snd_pcm_channel_info_t pi;
568
snd_pcm_channel_setup_t setup;
569
snd_mixer_group_t group;
570
snd_pcm_channel_params_t pp;
572
unsigned long tmp_buf_size;
574
if (param->play_id < 0 || param->play_id >= stream->af->dev_cnt) {
575
return PJMEDIA_EAUD_INVDEV;
578
if ((ret = snd_pcm_open_preferred (&stream->pb_pcm, &card, &dev,
579
SND_PCM_OPEN_PLAYBACK)) < 0)
581
TRACE_((THIS_FILE, "snd_pcm_open_preferred ret = %d", ret));
582
return PJMEDIA_EAUD_SYSERR;
586
memset (&pi, 0, sizeof (pi));
587
pi.channel = SND_PCM_CHANNEL_PLAYBACK;
588
if ((ret = snd_pcm_plugin_info (stream->pb_pcm, &pi)) < 0) {
589
TRACE_((THIS_FILE, "snd_pcm_plugin_info ret = %d", ret));
590
return PJMEDIA_EAUD_SYSERR;
593
memset (&pp, 0, sizeof (pp));
595
/* Request VoIP compatible capabilities
596
* On simulator frag_size is always negotiated to 170
598
pp.mode = SND_PCM_MODE_BLOCK;
599
pp.channel = SND_PCM_CHANNEL_PLAYBACK;
600
pp.start_mode = SND_PCM_START_DATA;
601
pp.stop_mode = SND_PCM_STOP_ROLLOVER;
602
/* HARD CODE for the time being PJMEDIA expects 640 for 16khz */
603
pp.buf.block.frag_size = PREFERRED_FRAME_SIZE*2;
604
/* Increasing this internal buffer count delays write failure in the loop */
605
pp.buf.block.frags_max = 4;
606
pp.buf.block.frags_min = 1;
607
pp.format.interleave = 1;
608
/* HARD CODE for the time being PJMEDIA expects 16khz */
609
PJ_TODO(REMOVE_SAMPLE_RATE_HARD_CODE);
610
pj_assert(param->clock_rate == VOIP_SAMPLE_RATE * 2);
611
pp.format.rate = VOIP_SAMPLE_RATE*2;
612
pp.format.voices = 1;
613
pp.format.format = SND_PCM_SFMT_S16_LE;
615
/* Make the calls as per the wave sample */
616
if ((ret = snd_pcm_plugin_params (stream->pb_pcm, &pp)) < 0) {
617
TRACE_((THIS_FILE, "snd_pcm_plugin_params ret = %d", ret));
618
return PJMEDIA_EAUD_SYSERR;
621
memset (&setup, 0, sizeof (setup));
622
memset (&group, 0, sizeof (group));
623
setup.channel = SND_PCM_CHANNEL_PLAYBACK;
624
setup.mixer_gid = &group.gid;
626
if ((ret = snd_pcm_plugin_setup (stream->pb_pcm, &setup)) < 0) {
627
TRACE_((THIS_FILE, "snd_pcm_plugin_setup ret = %d", ret));
628
return PJMEDIA_EAUD_SYSERR;
631
if (group.gid.name[0] == 0) {
632
return PJMEDIA_EAUD_SYSERR;
635
if ((ret = snd_mixer_open (&stream->pb_mixer, card,
636
setup.mixer_device)) < 0)
638
TRACE_((THIS_FILE, "snd_mixer_open ret = %d", ret));
639
return PJMEDIA_EAUD_SYSERR;
643
rate = param->clock_rate;
644
/* Set the sound device buffer size and latency */
645
if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY) {
646
tmp_buf_size = (rate / 1000) * param->output_latency_ms;
648
tmp_buf_size = (rate / 1000) * PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
650
/* Set period size to samples_per_frame frames. */
651
stream->pb_frames = param->samples_per_frame;
652
stream->param.output_latency_ms = tmp_buf_size / (rate / 1000);
655
stream->pb_buf_size = stream->pb_frames * param->channel_count *
656
(param->bits_per_sample/8);
657
stream->pb_buf = (char *) pj_pool_alloc(stream->pool, stream->pb_buf_size);
659
TRACE_((THIS_FILE, "bb10_open_playback: pb_frames = %d clock = %d",
660
stream->pb_frames, param->clock_rate));
665
static pj_status_t bb10_open_capture (struct bb10_stream *stream,
666
const pjmedia_aud_param *param)
670
unsigned long tmp_buf_size;
674
snd_pcm_channel_info_t pi;
675
snd_mixer_group_t group;
676
snd_pcm_channel_params_t pp;
677
snd_pcm_channel_setup_t setup;
679
if (param->rec_id < 0 || param->rec_id >= stream->af->dev_cnt)
680
return PJMEDIA_EAUD_INVDEV;
682
/* BB10 Audio init here (not prepare) */
683
if ((ret = snd_pcm_open_preferred (&stream->ca_pcm, &card, &dev,
684
SND_PCM_OPEN_CAPTURE)) < 0)
686
TRACE_((THIS_FILE, "snd_pcm_open_preferred ret = %d", ret));
687
return PJMEDIA_EAUD_SYSERR;
690
/* sample reads the capabilities of the capture */
691
memset (&pi, 0, sizeof (pi));
692
pi.channel = SND_PCM_CHANNEL_CAPTURE;
693
if ((ret = snd_pcm_plugin_info (stream->ca_pcm, &pi)) < 0) {
694
TRACE_((THIS_FILE, "snd_pcm_plugin_info ret = %d", ret));
695
return PJMEDIA_EAUD_SYSERR;
698
/* Request the VoIP parameters
699
* These parameters are different to waverec sample
701
memset (&pp, 0, sizeof (pp));
703
pp.mode = SND_PCM_MODE_BLOCK;
704
pp.channel = SND_PCM_CHANNEL_CAPTURE;
705
pp.start_mode = SND_PCM_START_DATA;
706
/* Auto-recover from errors */
707
pp.stop_mode = SND_PCM_STOP_ROLLOVER;
708
/* HARD CODE for the time being PJMEDIA expects 640 for 16khz */
709
pp.buf.block.frag_size = PREFERRED_FRAME_SIZE*2;
710
/* Not applicable for capture hence -1 */
711
pp.buf.block.frags_max = -1;
712
pp.buf.block.frags_min = 1;
713
pp.format.interleave = 1;
714
/* HARD CODE for the time being PJMEDIA expects 16khz */
715
PJ_TODO(REMOVE_SAMPLE_RATE_HARD_CODE);
716
pj_assert(param->clock_rate == VOIP_SAMPLE_RATE * 2);
717
pp.format.rate = VOIP_SAMPLE_RATE*2;
718
pp.format.voices = 1;
719
pp.format.format = SND_PCM_SFMT_S16_LE;
721
/* make the request */
722
if ((ret = snd_pcm_plugin_params (stream->ca_pcm, &pp)) < 0) {
723
TRACE_((THIS_FILE, "snd_pcm_plugin_params ret = %d", ret));
724
return PJMEDIA_EAUD_SYSERR;
727
/* Again based on the sample */
728
memset (&setup, 0, sizeof (setup));
729
memset (&group, 0, sizeof (group));
730
setup.channel = SND_PCM_CHANNEL_CAPTURE;
731
setup.mixer_gid = &group.gid;
732
if ((ret = snd_pcm_plugin_setup (stream->ca_pcm, &setup)) < 0) {
733
TRACE_((THIS_FILE, "snd_pcm_plugin_setup ret = %d", ret));
734
return PJMEDIA_EAUD_SYSERR;
737
frame_size = setup.buf.block.frag_size;
739
if (group.gid.name[0] == 0) {
743
if ((ret = snd_mixer_open (&stream->ca_mixer, card,
744
setup.mixer_device)) < 0)
746
TRACE_((THIS_FILE,"snd_mixer_open ret = %d",ret));
747
return PJMEDIA_EAUD_SYSERR;
750
/* frag_size should be 160 */
751
frame_size = setup.buf.block.frag_size;
756
rate = param->clock_rate;
757
stream->ca_frames = (unsigned long) param->samples_per_frame /
758
param->channel_count;
760
/* Set the sound device buffer size and latency */
761
if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY) {
762
tmp_buf_size = (rate / 1000) * param->input_latency_ms;
764
tmp_buf_size = (rate / 1000) * PJMEDIA_SND_DEFAULT_REC_LATENCY;
767
stream->param.input_latency_ms = tmp_buf_size / (rate / 1000);
770
stream->ca_buf_size = stream->ca_frames * param->channel_count *
771
(param->bits_per_sample/8);
772
stream->ca_buf = (char *)pj_pool_alloc (stream->pool, stream->ca_buf_size);
774
TRACE_((THIS_FILE, "bb10_open_capture: ca_frames = %d clock = %d",
775
stream->ca_frames, param->clock_rate));
781
/* API: create stream */
782
static pj_status_t bb10_factory_create_stream(pjmedia_aud_dev_factory *f,
783
const pjmedia_aud_param *param,
784
pjmedia_aud_rec_cb rec_cb,
785
pjmedia_aud_play_cb play_cb,
787
pjmedia_aud_stream **p_strm)
789
struct bb10_factory *af = (struct bb10_factory*)f;
792
struct bb10_stream* stream;
794
pool = pj_pool_create (af->pf, "bb10%p", 1024, 1024, NULL);
798
/* Allocate and initialize comon stream data */
799
stream = PJ_POOL_ZALLOC_T (pool, struct bb10_stream);
800
stream->base.op = &bb10_stream_op;
803
stream->user_data = user_data;
804
stream->pb_cb = play_cb;
805
stream->ca_cb = rec_cb;
807
pj_memcpy(&stream->param, param, sizeof(*param));
810
if (param->dir & PJMEDIA_DIR_PLAYBACK) {
811
status = bb10_open_playback (stream, param);
812
if (status != PJ_SUCCESS) {
813
pj_pool_release (pool);
819
if (param->dir & PJMEDIA_DIR_CAPTURE) {
820
status = bb10_open_capture (stream, param);
821
if (status != PJ_SUCCESS) {
822
if (param->dir & PJMEDIA_DIR_PLAYBACK) {
823
close_play_mixer(stream);
824
close_play_pcm(stream);
826
pj_pool_release (pool);
831
*p_strm = &stream->base;
836
/* API: get running parameter */
837
static pj_status_t bb10_stream_get_param(pjmedia_aud_stream *s,
838
pjmedia_aud_param *pi)
840
struct bb10_stream *stream = (struct bb10_stream*)s;
842
PJ_ASSERT_RETURN(s && pi, PJ_EINVAL);
844
pj_memcpy(pi, &stream->param, sizeof(*pi));
850
/* API: get capability */
851
static pj_status_t bb10_stream_get_cap(pjmedia_aud_stream *s,
852
pjmedia_aud_dev_cap cap,
855
struct bb10_stream *stream = (struct bb10_stream*)s;
857
PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
859
if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
860
(stream->param.dir & PJMEDIA_DIR_CAPTURE))
862
/* Recording latency */
863
*(unsigned*)pval = stream->param.input_latency_ms;
865
} else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
866
(stream->param.dir & PJMEDIA_DIR_PLAYBACK))
868
/* Playback latency */
869
*(unsigned*)pval = stream->param.output_latency_ms;
872
return PJMEDIA_EAUD_INVCAP;
877
/* API: set capability */
878
static pj_status_t bb10_stream_set_cap(pjmedia_aud_stream *strm,
879
pjmedia_aud_dev_cap cap,
884
PJ_UNUSED_ARG(value);
886
return PJMEDIA_EAUD_INVCAP;
890
/* API: start stream */
891
static pj_status_t bb10_stream_start (pjmedia_aud_stream *s)
893
struct bb10_stream *stream = (struct bb10_stream*)s;
894
pj_status_t status = PJ_SUCCESS;
897
if (stream->param.dir & PJMEDIA_DIR_PLAYBACK) {
898
status = pj_thread_create (stream->pool,
899
"bb10sound_playback",
905
if (status != PJ_SUCCESS)
909
if (stream->param.dir & PJMEDIA_DIR_CAPTURE) {
910
status = pj_thread_create (stream->pool,
911
"bb10sound_playback",
917
if (status != PJ_SUCCESS) {
918
stream->quit = PJ_TRUE;
919
pj_thread_join(stream->pb_thread);
920
pj_thread_destroy(stream->pb_thread);
921
stream->pb_thread = NULL;
929
/* API: stop stream */
930
static pj_status_t bb10_stream_stop (pjmedia_aud_stream *s)
932
struct bb10_stream *stream = (struct bb10_stream*)s;
935
TRACE_((THIS_FILE,"bb10_stream_stop()"));
937
if (stream->pb_thread) {
938
pj_thread_join (stream->pb_thread);
939
pj_thread_destroy(stream->pb_thread);
940
stream->pb_thread = NULL;
943
if (stream->ca_thread) {
944
pj_thread_join (stream->ca_thread);
945
pj_thread_destroy(stream->ca_thread);
946
stream->ca_thread = NULL;
952
static pj_status_t bb10_stream_destroy (pjmedia_aud_stream *s)
954
struct bb10_stream *stream = (struct bb10_stream*)s;
956
TRACE_((THIS_FILE,"bb10_stream_destroy()"));
958
bb10_stream_stop (s);
960
pj_pool_release (stream->pool);
965
#endif /* PJMEDIA_AUDIO_DEV_HAS_BB10 */