blob: 7a588bb3226e3e4e15a8c93656d71c4a4bfda586 [file] [log] [blame]
Sauw Ming152532f2012-06-01 04:29:56 +00001/* $Id$ */
2/*
3 * Copyright (C) 2008-2012 Teluu Inc. (http://www.teluu.com)
4 *
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.
9 *
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.
14 *
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
18 */
19
20/*
21 * This is the implementation of BlackBerry 10 (BB10) audio device.
Sauw Mingca298802012-06-01 04:49:57 +000022 * Original code was kindly donated by Truphone Ltd. (http://www.truphone.com)
Sauw Ming152532f2012-06-01 04:29:56 +000023 * 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
25 */
26
27#include <pjmedia_audiodev.h>
28#include <pj/assert.h>
29#include <pj/log.h>
30#include <pj/os.h>
31#include <pj/pool.h>
32#include <pjmedia/errno.h>
33
34#if defined(PJMEDIA_AUDIO_DEV_HAS_BB10) && PJMEDIA_AUDIO_DEV_HAS_BB10 != 0
35
Benny Prijonocc61b742012-08-14 08:39:59 +000036#ifndef PJ_BBSDK_VER
37 /* Format: 0xMMNNRR: MM: major, NN: minor, RR: revision */
38# define PJ_BBSDK_VER 0x100006
39#endif
40
Sauw Ming152532f2012-06-01 04:29:56 +000041#include <sys/time.h>
42#include <sys/types.h>
43#include <unistd.h>
44#include <sys/select.h>
45#include <pthread.h>
46#include <errno.h>
47#include <sys/asoundlib.h>
Benny Prijonocc61b742012-08-14 08:39:59 +000048#if PJ_BBSDK_VER >= 0x100006
49#include <audio/audio_manager_routing.h>
50#endif
Sauw Ming152532f2012-06-01 04:29:56 +000051
52
53#define THIS_FILE "bb10_dev.c"
54#define BB10_DEVICE_NAME "plughw:%d,%d"
55/* Double these for 16khz sampling */
56#define PREFERRED_FRAME_SIZE 320
57#define VOIP_SAMPLE_RATE 8000
58
59/* Set to 1 to enable tracing */
60#if 1
61# define TRACE_(expr) PJ_LOG(4,expr)
62#else
63# define TRACE_(expr)
64#endif
65
66/*
67 * Factory prototypes
68 */
69static pj_status_t bb10_factory_init(pjmedia_aud_dev_factory *f);
70static pj_status_t bb10_factory_destroy(pjmedia_aud_dev_factory *f);
71static pj_status_t bb10_factory_refresh(pjmedia_aud_dev_factory *f);
72static unsigned bb10_factory_get_dev_count(pjmedia_aud_dev_factory *f);
73static pj_status_t bb10_factory_get_dev_info(pjmedia_aud_dev_factory *f,
74 unsigned index,
75 pjmedia_aud_dev_info *info);
76static pj_status_t bb10_factory_default_param(pjmedia_aud_dev_factory *f,
77 unsigned index,
78 pjmedia_aud_param *param);
79static pj_status_t bb10_factory_create_stream(pjmedia_aud_dev_factory *f,
80 const pjmedia_aud_param *param,
81 pjmedia_aud_rec_cb rec_cb,
82 pjmedia_aud_play_cb play_cb,
83 void *user_data,
84 pjmedia_aud_stream **p_strm);
85
86/*
87 * Stream prototypes
88 */
89static pj_status_t bb10_stream_get_param(pjmedia_aud_stream *strm,
90 pjmedia_aud_param *param);
91static pj_status_t bb10_stream_get_cap(pjmedia_aud_stream *strm,
92 pjmedia_aud_dev_cap cap,
93 void *value);
94static pj_status_t bb10_stream_set_cap(pjmedia_aud_stream *strm,
95 pjmedia_aud_dev_cap cap,
96 const void *value);
97static pj_status_t bb10_stream_start(pjmedia_aud_stream *strm);
98static pj_status_t bb10_stream_stop(pjmedia_aud_stream *strm);
99static pj_status_t bb10_stream_destroy(pjmedia_aud_stream *strm);
100
101
102struct bb10_factory
103{
104 pjmedia_aud_dev_factory base;
105 pj_pool_factory *pf;
106 pj_pool_t *pool;
107 pj_pool_t *base_pool;
108 unsigned dev_cnt;
109 pjmedia_aud_dev_info devs[1];
110};
111
112struct bb10_stream
113{
114 pjmedia_aud_stream base;
115
116 /* Common */
117 pj_pool_t *pool;
118 struct bb10_factory *af;
119 void *user_data;
120 pjmedia_aud_param param; /* Running parameter */
121 int rec_id; /* Capture device id */
122 int quit;
123
124 /* Playback */
Benny Prijono2ce87f12013-02-05 05:15:01 +0000125 unsigned int pb_ctrl_audio_manager_handle;
Sauw Ming152532f2012-06-01 04:29:56 +0000126 snd_pcm_t *pb_pcm;
Benny Prijonod7636bd2013-01-14 10:08:20 +0000127 unsigned int pb_audio_manager_handle;
Sauw Ming152532f2012-06-01 04:29:56 +0000128 unsigned long pb_frames; /* samples_per_frame */
129 pjmedia_aud_play_cb pb_cb;
130 unsigned pb_buf_size;
131 char *pb_buf;
132 pj_thread_t *pb_thread;
133
134 /* Capture */
135 snd_pcm_t *ca_pcm;
Benny Prijonod7636bd2013-01-14 10:08:20 +0000136 unsigned int ca_audio_manager_handle;
Sauw Ming152532f2012-06-01 04:29:56 +0000137 unsigned long ca_frames; /* samples_per_frame */
138 pjmedia_aud_rec_cb ca_cb;
139 unsigned ca_buf_size;
140 char *ca_buf;
141 pj_thread_t *ca_thread;
142};
143
144static pjmedia_aud_dev_factory_op bb10_factory_op =
145{
146 &bb10_factory_init,
147 &bb10_factory_destroy,
148 &bb10_factory_get_dev_count,
149 &bb10_factory_get_dev_info,
150 &bb10_factory_default_param,
151 &bb10_factory_create_stream,
152 &bb10_factory_refresh
153};
154
155static pjmedia_aud_stream_op bb10_stream_op =
156{
157 &bb10_stream_get_param,
158 &bb10_stream_get_cap,
159 &bb10_stream_set_cap,
160 &bb10_stream_start,
161 &bb10_stream_stop,
162 &bb10_stream_destroy
163};
164
165/*
166 * BB10 - tests loads the audio units and sets up the driver structure
167 */
168static pj_status_t bb10_add_dev (struct bb10_factory *af)
169{
170 pjmedia_aud_dev_info *adi;
171 int pb_result, ca_result;
Benny Prijonocc61b742012-08-14 08:39:59 +0000172 unsigned int handle;
Sauw Ming152532f2012-06-01 04:29:56 +0000173 snd_pcm_t *pcm_handle;
174
175 if (af->dev_cnt >= PJ_ARRAY_SIZE(af->devs))
176 return PJ_ETOOMANY;
177
178 adi = &af->devs[af->dev_cnt];
179
180 TRACE_((THIS_FILE, "bb10_add_dev Enter"));
181
Benny Prijonod7636bd2013-01-14 10:08:20 +0000182 if ((pb_result = audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT,
Benny Prijonocc61b742012-08-14 08:39:59 +0000183 &pcm_handle,
184 &handle,
Benny Prijono2ce87f12013-02-05 05:15:01 +0000185 (char*)"voice",
Benny Prijonocc61b742012-08-14 08:39:59 +0000186 SND_PCM_OPEN_PLAYBACK))
187 >= 0)
Sauw Ming152532f2012-06-01 04:29:56 +0000188 {
Benny Prijono2ce87f12013-02-05 05:15:01 +0000189 snd_pcm_close (pcm_handle);
190 audio_manager_free_handle(handle);
Sauw Ming152532f2012-06-01 04:29:56 +0000191 } else {
192 TRACE_((THIS_FILE, "Try to open the device for playback - failure"));
193 }
194
Benny Prijonod7636bd2013-01-14 10:08:20 +0000195 if ((ca_result = audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT,
Benny Prijonocc61b742012-08-14 08:39:59 +0000196 &pcm_handle,
197 &handle,
Benny Prijono2ce87f12013-02-05 05:15:01 +0000198 (char*)"voice",
Benny Prijonocc61b742012-08-14 08:39:59 +0000199 SND_PCM_OPEN_CAPTURE))
200 >= 0)
Sauw Ming152532f2012-06-01 04:29:56 +0000201 {
Benny Prijono2ce87f12013-02-05 05:15:01 +0000202 snd_pcm_close (pcm_handle);
203 audio_manager_free_handle(handle);
Benny Prijonod7636bd2013-01-14 10:08:20 +0000204
Sauw Ming152532f2012-06-01 04:29:56 +0000205 } else {
206 TRACE_((THIS_FILE, "Try to open the device for capture - failure"));
207 }
208
209 if (pb_result < 0 && ca_result < 0) {
210 TRACE_((THIS_FILE, "Unable to open sound device", "preferred"));
211 return PJMEDIA_EAUD_NODEV;
212 }
213
214 /* Reset device info */
215 pj_bzero(adi, sizeof(*adi));
216
217 /* Set device name */
218 strcpy(adi->name, "preferred");
219
220 /* Check the number of playback channels */
221 adi->output_count = (pb_result >= 0) ? 1 : 0;
222
223 /* Check the number of capture channels */
224 adi->input_count = (ca_result >= 0) ? 1 : 0;
225
226 /* Set the default sample rate */
227 adi->default_samples_per_sec = 8000;
228
229 /* Driver name */
230 strcpy(adi->driver, "BB10");
231
232 ++af->dev_cnt;
233
234 PJ_LOG (4,(THIS_FILE, "Added sound device %s", adi->name));
235
236 return PJ_SUCCESS;
237}
238
239/* Create BB10 audio driver. */
240pjmedia_aud_dev_factory* pjmedia_bb10_factory(pj_pool_factory *pf)
241{
242 struct bb10_factory *af;
243 pj_pool_t *pool;
244
245 pool = pj_pool_create(pf, "bb10_aud_base", 256, 256, NULL);
246 af = PJ_POOL_ZALLOC_T(pool, struct bb10_factory);
247 af->pf = pf;
248 af->base_pool = pool;
249 af->base.op = &bb10_factory_op;
250
251 return &af->base;
252}
253
254
255/* API: init factory */
256static pj_status_t bb10_factory_init(pjmedia_aud_dev_factory *f)
257{
258 pj_status_t status;
Benny Prijonocc61b742012-08-14 08:39:59 +0000259
Sauw Ming152532f2012-06-01 04:29:56 +0000260 status = bb10_factory_refresh(f);
261 if (status != PJ_SUCCESS)
262 return status;
263
264 PJ_LOG(4,(THIS_FILE, "BB10 initialized"));
265 return PJ_SUCCESS;
266}
267
268
269/* API: destroy factory */
270static pj_status_t bb10_factory_destroy(pjmedia_aud_dev_factory *f)
271{
272 struct bb10_factory *af = (struct bb10_factory*)f;
273
274 if (af->pool) {
275 TRACE_((THIS_FILE, "bb10_factory_destroy() - 1"));
276 pj_pool_release(af->pool);
277 }
278
279 if (af->base_pool) {
280 pj_pool_t *pool = af->base_pool;
281 af->base_pool = NULL;
282 TRACE_((THIS_FILE, "bb10_factory_destroy() - 2"));
283 pj_pool_release(pool);
284 }
285
286 return PJ_SUCCESS;
287}
288
289
290/* API: refresh the device list */
291static pj_status_t bb10_factory_refresh(pjmedia_aud_dev_factory *f)
292{
293 struct bb10_factory *af = (struct bb10_factory*)f;
294 int err;
295
296 TRACE_((THIS_FILE, "bb10_factory_refresh()"));
297
298 if (af->pool != NULL) {
299 pj_pool_release(af->pool);
300 af->pool = NULL;
301 }
302
303 af->pool = pj_pool_create(af->pf, "bb10_aud", 256, 256, NULL);
304 af->dev_cnt = 0;
305
306 err = bb10_add_dev(af);
307
308 PJ_LOG(4,(THIS_FILE, "BB10 driver found %d devices", af->dev_cnt));
309
310 return err;
311}
312
313
314/* API: get device count */
315static unsigned bb10_factory_get_dev_count(pjmedia_aud_dev_factory *f)
316{
317 struct bb10_factory *af = (struct bb10_factory*)f;
318 return af->dev_cnt;
319}
320
321
322/* API: get device info */
323static pj_status_t bb10_factory_get_dev_info(pjmedia_aud_dev_factory *f,
324 unsigned index,
325 pjmedia_aud_dev_info *info)
326{
327 struct bb10_factory *af = (struct bb10_factory*)f;
328
329 PJ_ASSERT_RETURN(index>=0 && index<af->dev_cnt, PJ_EINVAL);
330
331 pj_memcpy(info, &af->devs[index], sizeof(*info));
332 info->caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
Benny Prijono9ea1bcd2013-04-15 10:26:58 +0000333 PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY |
334 PJMEDIA_AUD_DEV_CAP_EC;
Benny Prijonocc61b742012-08-14 08:39:59 +0000335
Sauw Ming152532f2012-06-01 04:29:56 +0000336 return PJ_SUCCESS;
337}
338
339/* API: create default parameter */
340static pj_status_t bb10_factory_default_param(pjmedia_aud_dev_factory *f,
341 unsigned index,
342 pjmedia_aud_param *param)
343{
344 struct bb10_factory *af = (struct bb10_factory*)f;
345 pjmedia_aud_dev_info *adi;
346
347 PJ_ASSERT_RETURN(index>=0 && index<af->dev_cnt, PJ_EINVAL);
348
349 adi = &af->devs[index];
350
351 pj_bzero(param, sizeof(*param));
352 if (adi->input_count && adi->output_count) {
353 param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
354 param->rec_id = index;
355 param->play_id = index;
356 } else if (adi->input_count) {
357 param->dir = PJMEDIA_DIR_CAPTURE;
358 param->rec_id = index;
359 param->play_id = PJMEDIA_AUD_INVALID_DEV;
360 } else if (adi->output_count) {
361 param->dir = PJMEDIA_DIR_PLAYBACK;
362 param->play_id = index;
363 param->rec_id = PJMEDIA_AUD_INVALID_DEV;
364 } else {
365 return PJMEDIA_EAUD_INVDEV;
366 }
367
368 param->clock_rate = adi->default_samples_per_sec;
369 param->channel_count = 1;
370 param->samples_per_frame = adi->default_samples_per_sec * 20 / 1000;
371 param->bits_per_sample = 16;
372 param->flags = adi->caps;
373 param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
374 param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
375
376 TRACE_((THIS_FILE, "bb10_factory_default_param clock = %d flags = %d"
377 " spf = %d", param->clock_rate, param->flags,
378 param->samples_per_frame));
Benny Prijonocc61b742012-08-14 08:39:59 +0000379
Sauw Ming152532f2012-06-01 04:29:56 +0000380 return PJ_SUCCESS;
381}
382
383
384static void close_play_pcm(struct bb10_stream *stream)
385{
386 if (stream != NULL && stream->pb_pcm != NULL) {
387 snd_pcm_close(stream->pb_pcm);
388 stream->pb_pcm = NULL;
Benny Prijono2ce87f12013-02-05 05:15:01 +0000389
390 if (stream->pb_audio_manager_handle != 0) {
Benny Prijonod7636bd2013-01-14 10:08:20 +0000391 audio_manager_free_handle(stream->pb_audio_manager_handle);
392 stream->pb_audio_manager_handle = 0;
393 }
Benny Prijono2ce87f12013-02-05 05:15:01 +0000394
395 if (stream->pb_ctrl_audio_manager_handle != 0) {
396 audio_manager_free_handle(stream->pb_ctrl_audio_manager_handle);
397 stream->pb_ctrl_audio_manager_handle = 0;
398 }
Sauw Ming152532f2012-06-01 04:29:56 +0000399 }
400}
401
Sauw Ming152532f2012-06-01 04:29:56 +0000402static void flush_play(struct bb10_stream *stream)
403{
404 if (stream != NULL && stream->pb_pcm != NULL) {
405 snd_pcm_plugin_flush (stream->pb_pcm, SND_PCM_CHANNEL_PLAYBACK);
406 }
407}
408
409static void close_capture_pcm(struct bb10_stream *stream)
410{
411 if (stream != NULL && stream->ca_pcm != NULL) {
412 snd_pcm_close(stream->ca_pcm);
413 stream->ca_pcm = NULL;
Benny Prijono2ce87f12013-02-05 05:15:01 +0000414
415 if (stream->ca_audio_manager_handle != 0) {
Benny Prijonod7636bd2013-01-14 10:08:20 +0000416 audio_manager_free_handle(stream->ca_audio_manager_handle);
417 stream->ca_audio_manager_handle = 0;
418 }
Sauw Ming152532f2012-06-01 04:29:56 +0000419 }
420}
421
Sauw Ming152532f2012-06-01 04:29:56 +0000422static void flush_capture(struct bb10_stream *stream)
423{
424 if (stream != NULL && stream->ca_pcm != NULL) {
425 snd_pcm_plugin_flush (stream->ca_pcm, SND_PCM_CHANNEL_CAPTURE);
426 }
427}
428
429
430/**
431 * Play audio received from PJMEDIA
432 */
433static int pb_thread_func (void *arg)
434{
435 struct bb10_stream* stream = (struct bb10_stream *) arg;
436 /* Handle from bb10_open_playback */
437 /* Will be 640 */
438 int size = stream->pb_buf_size;
439 /* 160 frames for 20ms */
440 unsigned long nframes = stream->pb_frames;
441 void *user_data = stream->user_data;
442 char *buf = stream->pb_buf;
443 pj_timestamp tstamp;
444 int result = 0;
445
446 pj_bzero (buf, size);
447 tstamp.u64 = 0;
448
449 TRACE_((THIS_FILE, "pb_thread_func: size = %d ", size));
450
451 /* Do the final initialization now the thread has started. */
452 if ((result = snd_pcm_plugin_prepare(stream->pb_pcm,
453 SND_PCM_CHANNEL_PLAYBACK)) < 0)
454 {
Sauw Ming152532f2012-06-01 04:29:56 +0000455 close_play_pcm(stream);
456 TRACE_((THIS_FILE, "pb_thread_func failed prepare = %d", result));
Benny Prijono2ce87f12013-02-05 05:15:01 +0000457 return PJ_SUCCESS;
Sauw Ming152532f2012-06-01 04:29:56 +0000458 }
459
460 while (!stream->quit) {
461 pjmedia_frame frame;
462
463 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
464 /* pointer to buffer filled by PJMEDIA */
465 frame.buf = buf;
466 frame.size = size;
467 frame.timestamp.u64 = tstamp.u64;
468 frame.bit_info = 0;
469
Benny Prijono2ce87f12013-02-05 05:15:01 +0000470 /* Read the audio from pjmedia */
Sauw Ming152532f2012-06-01 04:29:56 +0000471 result = stream->pb_cb (user_data, &frame);
472 if (result != PJ_SUCCESS || stream->quit)
473 break;
474
475 if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
476 pj_bzero (buf, size);
477
478 /* Write 640 to play unit */
479 result = snd_pcm_plugin_write(stream->pb_pcm,buf,size);
Benny Prijonocc61b742012-08-14 08:39:59 +0000480 if (result != size || result < 0) {
Benny Prijono2ce87f12013-02-05 05:15:01 +0000481 /* either the write to output device has failed or not the
482 * full amount of bytes have been written. This usually happens
483 * when audio routing is being changed by another thread
484 * Use a status variable for reading the error
485 */
486 snd_pcm_channel_status_t status;
487 status.channel = SND_PCM_CHANNEL_PLAYBACK;
Benny Prijonocc61b742012-08-14 08:39:59 +0000488 if (snd_pcm_plugin_status (stream->pb_pcm, &status) < 0) {
Benny Prijono2ce87f12013-02-05 05:15:01 +0000489 /* Call has failed nothing we can do except log and
490 * continue */
491 PJ_LOG(4,(THIS_FILE,
Benny Prijonocc61b742012-08-14 08:39:59 +0000492 "underrun: playback channel status error"));
Benny Prijono2ce87f12013-02-05 05:15:01 +0000493 } else {
494 /* The status of the error has been read
495 * RIM say these are expected so we can "re-prepare" the stream
496 */
497 PJ_LOG(4,(THIS_FILE,"PLAY thread ERROR status = %d",
498 status.status));
499 if (status.status == SND_PCM_STATUS_READY ||
500 status.status == SND_PCM_STATUS_UNDERRUN ||
501 status.status == SND_PCM_STATUS_ERROR )
502 {
503 if (snd_pcm_plugin_prepare (stream->pb_pcm,
504 SND_PCM_CHANNEL_PLAYBACK) < 0)
505 {
506 PJ_LOG(4,(THIS_FILE,
507 "underrun: playback channel prepare error"));
508 }
509 }
510 }
Sauw Ming152532f2012-06-01 04:29:56 +0000511 }
Sauw Ming152532f2012-06-01 04:29:56 +0000512 tstamp.u64 += nframes;
513 }
514
515 flush_play(stream);
Sauw Ming152532f2012-06-01 04:29:56 +0000516 close_play_pcm(stream);
517 TRACE_((THIS_FILE, "pb_thread_func: Stopped"));
Benny Prijonocc61b742012-08-14 08:39:59 +0000518
Sauw Ming152532f2012-06-01 04:29:56 +0000519 return PJ_SUCCESS;
520}
521
522
523
524static int ca_thread_func (void *arg)
525{
526 struct bb10_stream* stream = (struct bb10_stream *) arg;
527 int size = stream->ca_buf_size;
528 unsigned long nframes = stream->ca_frames;
529 void *user_data = stream->user_data;
530 /* Buffer to fill for PJMEDIA */
531 char *buf = stream->ca_buf;
532 pj_timestamp tstamp;
533 int result;
534 struct sched_param param;
535 pthread_t *thid;
536
537 TRACE_((THIS_FILE, "ca_thread_func: size = %d ", size));
538
539 thid = (pthread_t*) pj_thread_get_os_handle (pj_thread_this());
540 param.sched_priority = sched_get_priority_max (SCHED_RR);
541
542 result = pthread_setschedparam (*thid, SCHED_RR, &param);
543 if (result) {
544 if (result == EPERM) {
545 PJ_LOG (4,(THIS_FILE, "Unable to increase thread priority, "
546 "root access needed."));
547 } else {
548 PJ_LOG (4,(THIS_FILE, "Unable to increase thread priority, "
549 "error: %d", result));
550 }
551 }
552
553 pj_bzero (buf, size);
554 tstamp.u64 = 0;
555
556 /* Final init now the thread has started */
557 if ((result = snd_pcm_plugin_prepare (stream->ca_pcm,
558 SND_PCM_CHANNEL_CAPTURE)) < 0)
559 {
Sauw Ming152532f2012-06-01 04:29:56 +0000560 close_capture_pcm(stream);
561 TRACE_((THIS_FILE, "ca_thread_func failed prepare = %d", result));
Benny Prijono2ce87f12013-02-05 05:15:01 +0000562 return PJ_SUCCESS;
Sauw Ming152532f2012-06-01 04:29:56 +0000563 }
564
565 while (!stream->quit) {
566 pjmedia_frame frame;
567
568 pj_bzero (buf, size);
Benny Prijonocc61b742012-08-14 08:39:59 +0000569
Benny Prijono2ce87f12013-02-05 05:15:01 +0000570 /* read the input device */
Sauw Ming152532f2012-06-01 04:29:56 +0000571 result = snd_pcm_plugin_read(stream->ca_pcm, buf,size);
Benny Prijonocc61b742012-08-14 08:39:59 +0000572 if(result <0 || result != size) {
Benny Prijono2ce87f12013-02-05 05:15:01 +0000573 /* We expect result to be size (640)
574 * It's not so we have to read the status error and "prepare"
575 * the channel. This usually happens when output audio routing
576 * has been changed by another thread.
577 * We won't "continue", instead just do what we can and leave
578 * the end of the loop to write what's in the buffer. Not entirely
579 * correct but saves a potential underrun in PJMEDIA
580 */
581 PJ_LOG (4,(THIS_FILE,
582 "snd_pcm_plugin_read ERROR read = %d required = %d",
583 result,size));
Benny Prijonocc61b742012-08-14 08:39:59 +0000584 snd_pcm_channel_status_t status;
Benny Prijono2ce87f12013-02-05 05:15:01 +0000585 status.channel = SND_PCM_CHANNEL_CAPTURE;
586 if ((result = snd_pcm_plugin_status (stream->ca_pcm, &status)) < 0)
587 {
588 /* Should not fail but all we can do is continue */
589 PJ_LOG(4,(THIS_FILE, "capture: snd_pcm_plugin_status ret = %d",
590 result));
591 } else {
592 /* RIM say these are the errors that we should "prepare"
593 * after */
594 if (status.status == SND_PCM_STATUS_READY ||
595 status.status == SND_PCM_STATUS_OVERRUN ||
596 status.status == SND_PCM_STATUS_ERROR)
597 {
598 if (snd_pcm_plugin_prepare (stream->ca_pcm,
599 SND_PCM_CHANNEL_CAPTURE) < 0)
600 {
601 PJ_LOG (4,(THIS_FILE,
602 "overrun: capture channel prepare error"));
603 }
604 }
Benny Prijonocc61b742012-08-14 08:39:59 +0000605 }
Sauw Ming152532f2012-06-01 04:29:56 +0000606 }
607
608 if (stream->quit)
609 break;
610
Benny Prijono2ce87f12013-02-05 05:15:01 +0000611 /* Write the capture audio data to PJMEDIA */
Sauw Ming152532f2012-06-01 04:29:56 +0000612 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
613 frame.buf = (void *) buf;
614 frame.size = size;
615 frame.timestamp.u64 = tstamp.u64;
616 frame.bit_info = 0;
617
618 result = stream->ca_cb (user_data, &frame);
619 if (result != PJ_SUCCESS || stream->quit)
620 break;
621
622 tstamp.u64 += nframes;
623 }
624
625 flush_capture(stream);
Sauw Ming152532f2012-06-01 04:29:56 +0000626 close_capture_pcm(stream);
627 TRACE_((THIS_FILE, "ca_thread_func: Stopped"));
628
629 return PJ_SUCCESS;
630}
631
Benny Prijono2ce87f12013-02-05 05:15:01 +0000632/* Audio routing, speaker/headset */
633static pj_status_t bb10_initialize_playback_ctrl(struct bb10_stream *stream,
634 bool speaker)
635{
636 /* Although the play and capture have audio manager handles, audio routing
637 * requires a separate handle
638 */
639 int ret = PJ_SUCCESS;
640
641 if (stream->pb_ctrl_audio_manager_handle == 0) {
642 /* lazy init an audio manager handle */
643 ret = audio_manager_get_handle(AUDIO_TYPE_VIDEO_CHAT, 0, false,
644 &stream->pb_ctrl_audio_manager_handle);
645 if (ret != 0) {
646 TRACE_((THIS_FILE, "audio_manager_get_handle ret = %d",ret));
647 return PJMEDIA_EAUD_SYSERR;
648 }
649 }
650
651 /* Set for either speaker or earpiece */
652 if (speaker) {
653 ret = audio_manager_set_handle_type(
654 stream->pb_ctrl_audio_manager_handle,
655 AUDIO_TYPE_VIDEO_CHAT,
656 AUDIO_DEVICE_SPEAKER,
657 AUDIO_DEVICE_DEFAULT);
658 } else {
659 ret = audio_manager_set_handle_type(
660 stream->pb_ctrl_audio_manager_handle,
661 AUDIO_TYPE_VIDEO_CHAT,
662 AUDIO_DEVICE_HANDSET,
663 AUDIO_DEVICE_DEFAULT);
664 }
665
666 if (ret == 0) {
667 /* RIM recommend this call */
668 ret = audio_manager_set_handle_routing_conditions(
669 stream->pb_ctrl_audio_manager_handle,
670 SETTINGS_RESET_ON_DEVICE_CONNECTION);
671 if (ret != 0) {
672 TRACE_((THIS_FILE,
673 "audio_manager_set_handle_routing_conditions ret = %d",
674 ret));
675 return PJMEDIA_EAUD_SYSERR;
676 }
677 } else {
678 TRACE_((THIS_FILE, "audio_manager_set_handle_type ret = %d", ret));
679 return PJMEDIA_EAUD_SYSERR;
680 }
681
682 return PJ_SUCCESS;
683}
Sauw Ming152532f2012-06-01 04:29:56 +0000684
685static pj_status_t bb10_open_playback (struct bb10_stream *stream,
686 const pjmedia_aud_param *param)
687{
Sauw Ming152532f2012-06-01 04:29:56 +0000688 int ret = 0;
689 snd_pcm_channel_info_t pi;
690 snd_pcm_channel_setup_t setup;
691 snd_mixer_group_t group;
Sauw Mingca298802012-06-01 04:49:57 +0000692 snd_pcm_channel_params_t pp;
Sauw Ming152532f2012-06-01 04:29:56 +0000693 unsigned int rate;
694 unsigned long tmp_buf_size;
695
696 if (param->play_id < 0 || param->play_id >= stream->af->dev_cnt) {
697 return PJMEDIA_EAUD_INVDEV;
698 }
699
Benny Prijono2ce87f12013-02-05 05:15:01 +0000700 /* Use the bb10 audio manager API to open as opposed to QNX core audio
701 * Echo cancellation built in
702 */
703 if ((ret = audio_manager_snd_pcm_open_name(
704 AUDIO_TYPE_VIDEO_CHAT,
705 &stream->pb_pcm, &stream->pb_audio_manager_handle,
706 (char*)"voice",
707 SND_PCM_OPEN_PLAYBACK)) < 0)
Benny Prijonocc61b742012-08-14 08:39:59 +0000708 {
709 TRACE_((THIS_FILE, "audio_manager_snd_pcm_open_name ret = %d", ret));
710 return PJMEDIA_EAUD_SYSERR;
711 }
Benny Prijono2ce87f12013-02-05 05:15:01 +0000712
713 /* Required call from January 2013 gold OS release */
714 if ((ret = snd_pcm_plugin_set_disable(stream->pb_pcm,
715 PLUGIN_DISABLE_MMAP)) < 0)
716 {
717 TRACE_((THIS_FILE, "snd_pcm_plugin_set_disable ret = %d", ret));
718 return PJMEDIA_EAUD_SYSERR;
Sauw Ming152532f2012-06-01 04:29:56 +0000719 }
720
Benny Prijono2ce87f12013-02-05 05:15:01 +0000721 /* Required call from January 2013 gold OS release */
722 if ((ret = snd_pcm_plugin_set_enable(stream->pb_pcm,
723 PLUGIN_ROUTING)) < 0)
724 {
725 TRACE_((THIS_FILE, "snd_pcm_plugin_set_enable ret = %d", ret));
726 return PJMEDIA_EAUD_SYSERR;
Benny Prijonod7636bd2013-01-14 10:08:20 +0000727 }
Benny Prijono2ce87f12013-02-05 05:15:01 +0000728
Sauw Ming152532f2012-06-01 04:29:56 +0000729 /* TODO PJ_ZERO */
730 memset (&pi, 0, sizeof (pi));
731 pi.channel = SND_PCM_CHANNEL_PLAYBACK;
732 if ((ret = snd_pcm_plugin_info (stream->pb_pcm, &pi)) < 0) {
Benny Prijono2ce87f12013-02-05 05:15:01 +0000733 TRACE_((THIS_FILE, "snd_pcm_plugin_info ret = %d", ret));
734 return PJMEDIA_EAUD_SYSERR;
Sauw Ming152532f2012-06-01 04:29:56 +0000735 }
736
Sauw Ming152532f2012-06-01 04:29:56 +0000737 memset (&pp, 0, sizeof (pp));
738
Benny Prijono2ce87f12013-02-05 05:15:01 +0000739 /* Request VoIP compatible capabilities */
Sauw Ming152532f2012-06-01 04:29:56 +0000740 pp.mode = SND_PCM_MODE_BLOCK;
741 pp.channel = SND_PCM_CHANNEL_PLAYBACK;
742 pp.start_mode = SND_PCM_START_DATA;
743 pp.stop_mode = SND_PCM_STOP_ROLLOVER;
744 /* HARD CODE for the time being PJMEDIA expects 640 for 16khz */
745 pp.buf.block.frag_size = PREFERRED_FRAME_SIZE*2;
Benny Prijono2ce87f12013-02-05 05:15:01 +0000746 /* RIM recommends maximum of 3 */
747 pp.buf.block.frags_max = 3;
Sauw Ming152532f2012-06-01 04:29:56 +0000748 pp.buf.block.frags_min = 1;
749 pp.format.interleave = 1;
750 /* HARD CODE for the time being PJMEDIA expects 16khz */
Sauw Mingca298802012-06-01 04:49:57 +0000751 PJ_TODO(REMOVE_SAMPLE_RATE_HARD_CODE);
752 pj_assert(param->clock_rate == VOIP_SAMPLE_RATE * 2);
Sauw Ming152532f2012-06-01 04:29:56 +0000753 pp.format.rate = VOIP_SAMPLE_RATE*2;
754 pp.format.voices = 1;
755 pp.format.format = SND_PCM_SFMT_S16_LE;
756
757 /* Make the calls as per the wave sample */
758 if ((ret = snd_pcm_plugin_params (stream->pb_pcm, &pp)) < 0) {
759 TRACE_((THIS_FILE, "snd_pcm_plugin_params ret = %d", ret));
760 return PJMEDIA_EAUD_SYSERR;
761 }
762
763 memset (&setup, 0, sizeof (setup));
764 memset (&group, 0, sizeof (group));
765 setup.channel = SND_PCM_CHANNEL_PLAYBACK;
766 setup.mixer_gid = &group.gid;
Benny Prijonocc61b742012-08-14 08:39:59 +0000767
Sauw Ming152532f2012-06-01 04:29:56 +0000768 if ((ret = snd_pcm_plugin_setup (stream->pb_pcm, &setup)) < 0) {
769 TRACE_((THIS_FILE, "snd_pcm_plugin_setup ret = %d", ret));
770 return PJMEDIA_EAUD_SYSERR;
771 }
772
773 if (group.gid.name[0] == 0) {
774 return PJMEDIA_EAUD_SYSERR;
775 }
Sauw Ming152532f2012-06-01 04:29:56 +0000776
777 rate = param->clock_rate;
778 /* Set the sound device buffer size and latency */
779 if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY) {
780 tmp_buf_size = (rate / 1000) * param->output_latency_ms;
781 } else {
782 tmp_buf_size = (rate / 1000) * PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
783 }
784 /* Set period size to samples_per_frame frames. */
785 stream->pb_frames = param->samples_per_frame;
786 stream->param.output_latency_ms = tmp_buf_size / (rate / 1000);
787
788 /* Set our buffer */
789 stream->pb_buf_size = stream->pb_frames * param->channel_count *
790 (param->bits_per_sample/8);
791 stream->pb_buf = (char *) pj_pool_alloc(stream->pool, stream->pb_buf_size);
792
793 TRACE_((THIS_FILE, "bb10_open_playback: pb_frames = %d clock = %d",
794 stream->pb_frames, param->clock_rate));
Benny Prijono2ce87f12013-02-05 05:15:01 +0000795
Sauw Ming152532f2012-06-01 04:29:56 +0000796 return PJ_SUCCESS;
797}
798
799static pj_status_t bb10_open_capture (struct bb10_stream *stream,
800 const pjmedia_aud_param *param)
801{
802 int ret = 0;
803 unsigned int rate;
804 unsigned long tmp_buf_size;
Sauw Ming152532f2012-06-01 04:29:56 +0000805 int frame_size;
806 snd_pcm_channel_info_t pi;
807 snd_mixer_group_t group;
808 snd_pcm_channel_params_t pp;
809 snd_pcm_channel_setup_t setup;
810
811 if (param->rec_id < 0 || param->rec_id >= stream->af->dev_cnt)
812 return PJMEDIA_EAUD_INVDEV;
813
Benny Prijono2ce87f12013-02-05 05:15:01 +0000814 if ((ret=audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT,
815 &stream->ca_pcm,
816 &stream->ca_audio_manager_handle,
817 (char*)"voice",
818 SND_PCM_OPEN_CAPTURE)) < 0)
Benny Prijonocc61b742012-08-14 08:39:59 +0000819 {
820 TRACE_((THIS_FILE, "audio_manager_snd_pcm_open_name ret = %d", ret));
821 return PJMEDIA_EAUD_SYSERR;
822 }
Benny Prijono2ce87f12013-02-05 05:15:01 +0000823 /* Required call from January 2013 gold OS release */
824 if ((ret = snd_pcm_plugin_set_disable (stream->ca_pcm,
825 PLUGIN_DISABLE_MMAP)) < 0)
826 {
Benny Prijono9ea1bcd2013-04-15 10:26:58 +0000827 TRACE_((THIS_FILE, "snd_pcm_plugin_set_disable failed: %d",ret));
Sauw Ming152532f2012-06-01 04:29:56 +0000828 return PJMEDIA_EAUD_SYSERR;
829 }
Benny Prijono2ce87f12013-02-05 05:15:01 +0000830 /* Required call from January 2013 gold OS release */
831 if ((ret = snd_pcm_plugin_set_enable(stream->ca_pcm,
832 PLUGIN_ROUTING)) < 0)
833 {
Benny Prijono9ea1bcd2013-04-15 10:26:58 +0000834 TRACE_((THIS_FILE, "snd_pcm_plugin_set_enable failed: %d",ret));
Benny Prijono2ce87f12013-02-05 05:15:01 +0000835 return PJMEDIA_EAUD_SYSERR;
836 }
Sauw Ming152532f2012-06-01 04:29:56 +0000837
838 /* sample reads the capabilities of the capture */
839 memset (&pi, 0, sizeof (pi));
840 pi.channel = SND_PCM_CHANNEL_CAPTURE;
841 if ((ret = snd_pcm_plugin_info (stream->ca_pcm, &pi)) < 0) {
842 TRACE_((THIS_FILE, "snd_pcm_plugin_info ret = %d", ret));
843 return PJMEDIA_EAUD_SYSERR;
844 }
845
846 /* Request the VoIP parameters
847 * These parameters are different to waverec sample
848 */
849 memset (&pp, 0, sizeof (pp));
850 /* Blocking read */
851 pp.mode = SND_PCM_MODE_BLOCK;
852 pp.channel = SND_PCM_CHANNEL_CAPTURE;
853 pp.start_mode = SND_PCM_START_DATA;
854 /* Auto-recover from errors */
855 pp.stop_mode = SND_PCM_STOP_ROLLOVER;
856 /* HARD CODE for the time being PJMEDIA expects 640 for 16khz */
857 pp.buf.block.frag_size = PREFERRED_FRAME_SIZE*2;
Benny Prijono2ce87f12013-02-05 05:15:01 +0000858 /* From January 2013 gold OS release. RIM recommend these for capture */
859 pp.buf.block.frags_max = 1;
Sauw Ming152532f2012-06-01 04:29:56 +0000860 pp.buf.block.frags_min = 1;
861 pp.format.interleave = 1;
862 /* HARD CODE for the time being PJMEDIA expects 16khz */
863 PJ_TODO(REMOVE_SAMPLE_RATE_HARD_CODE);
Sauw Mingca298802012-06-01 04:49:57 +0000864 pj_assert(param->clock_rate == VOIP_SAMPLE_RATE * 2);
Sauw Ming152532f2012-06-01 04:29:56 +0000865 pp.format.rate = VOIP_SAMPLE_RATE*2;
866 pp.format.voices = 1;
867 pp.format.format = SND_PCM_SFMT_S16_LE;
868
869 /* make the request */
870 if ((ret = snd_pcm_plugin_params (stream->ca_pcm, &pp)) < 0) {
871 TRACE_((THIS_FILE, "snd_pcm_plugin_params ret = %d", ret));
872 return PJMEDIA_EAUD_SYSERR;
873 }
874
875 /* Again based on the sample */
876 memset (&setup, 0, sizeof (setup));
877 memset (&group, 0, sizeof (group));
878 setup.channel = SND_PCM_CHANNEL_CAPTURE;
879 setup.mixer_gid = &group.gid;
880 if ((ret = snd_pcm_plugin_setup (stream->ca_pcm, &setup)) < 0) {
881 TRACE_((THIS_FILE, "snd_pcm_plugin_setup ret = %d", ret));
882 return PJMEDIA_EAUD_SYSERR;
883 }
884
885 frame_size = setup.buf.block.frag_size;
886
887 if (group.gid.name[0] == 0) {
888 } else {
889 }
890
Sauw Ming152532f2012-06-01 04:29:56 +0000891 frame_size = setup.buf.block.frag_size;
892
Sauw Ming152532f2012-06-01 04:29:56 +0000893 /* Set clock rate */
894 rate = param->clock_rate;
895 stream->ca_frames = (unsigned long) param->samples_per_frame /
896 param->channel_count;
897
898 /* Set the sound device buffer size and latency */
899 if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY) {
900 tmp_buf_size = (rate / 1000) * param->input_latency_ms;
901 } else {
902 tmp_buf_size = (rate / 1000) * PJMEDIA_SND_DEFAULT_REC_LATENCY;
903 }
904
905 stream->param.input_latency_ms = tmp_buf_size / (rate / 1000);
906
907 /* Set our buffer */
908 stream->ca_buf_size = stream->ca_frames * param->channel_count *
909 (param->bits_per_sample/8);
910 stream->ca_buf = (char *)pj_pool_alloc (stream->pool, stream->ca_buf_size);
911
912 TRACE_((THIS_FILE, "bb10_open_capture: ca_frames = %d clock = %d",
913 stream->ca_frames, param->clock_rate));
914
915 return PJ_SUCCESS;
916}
917
918
919/* API: create stream */
920static pj_status_t bb10_factory_create_stream(pjmedia_aud_dev_factory *f,
921 const pjmedia_aud_param *param,
922 pjmedia_aud_rec_cb rec_cb,
923 pjmedia_aud_play_cb play_cb,
924 void *user_data,
925 pjmedia_aud_stream **p_strm)
926{
927 struct bb10_factory *af = (struct bb10_factory*)f;
928 pj_status_t status;
929 pj_pool_t* pool;
930 struct bb10_stream* stream;
931
932 pool = pj_pool_create (af->pf, "bb10%p", 1024, 1024, NULL);
933 if (!pool)
934 return PJ_ENOMEM;
935
936 /* Allocate and initialize comon stream data */
937 stream = PJ_POOL_ZALLOC_T (pool, struct bb10_stream);
938 stream->base.op = &bb10_stream_op;
939 stream->pool = pool;
940 stream->af = af;
941 stream->user_data = user_data;
942 stream->pb_cb = play_cb;
943 stream->ca_cb = rec_cb;
944 stream->quit = 0;
945 pj_memcpy(&stream->param, param, sizeof(*param));
946
947 /* Init playback */
948 if (param->dir & PJMEDIA_DIR_PLAYBACK) {
949 status = bb10_open_playback (stream, param);
950 if (status != PJ_SUCCESS) {
951 pj_pool_release (pool);
952 return status;
953 }
954 }
955
956 /* Init capture */
957 if (param->dir & PJMEDIA_DIR_CAPTURE) {
958 status = bb10_open_capture (stream, param);
959 if (status != PJ_SUCCESS) {
960 if (param->dir & PJMEDIA_DIR_PLAYBACK) {
Sauw Ming152532f2012-06-01 04:29:56 +0000961 close_play_pcm(stream);
962 }
963 pj_pool_release (pool);
964 return status;
965 }
966 }
967
Benny Prijono2ce87f12013-02-05 05:15:01 +0000968 /* Part of the play functionality but the RIM/Truphone loopback sample
969 * initialializes after the play and capture
970 * "false" is default/earpiece for output
971 */
972 status = bb10_initialize_playback_ctrl(stream,false);
973 if (status != PJ_SUCCESS) {
974 return PJMEDIA_EAUD_SYSERR;
975 }
976
Sauw Ming152532f2012-06-01 04:29:56 +0000977 *p_strm = &stream->base;
978 return PJ_SUCCESS;
979}
980
981
Benny Prijono2ce87f12013-02-05 05:15:01 +0000982/*
983 * API: get running parameter
984 * based on ALSA template
985 */
Sauw Ming152532f2012-06-01 04:29:56 +0000986static pj_status_t bb10_stream_get_param(pjmedia_aud_stream *s,
987 pjmedia_aud_param *pi)
988{
989 struct bb10_stream *stream = (struct bb10_stream*)s;
990
991 PJ_ASSERT_RETURN(s && pi, PJ_EINVAL);
992
993 pj_memcpy(pi, &stream->param, sizeof(*pi));
994
995 return PJ_SUCCESS;
996}
997
998
Benny Prijono2ce87f12013-02-05 05:15:01 +0000999/*
1000 * API: get capability
1001 * based on ALSA template
1002*/
Sauw Ming152532f2012-06-01 04:29:56 +00001003static pj_status_t bb10_stream_get_cap(pjmedia_aud_stream *s,
1004 pjmedia_aud_dev_cap cap,
1005 void *pval)
1006{
1007 struct bb10_stream *stream = (struct bb10_stream*)s;
1008
1009 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1010
1011 if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
1012 (stream->param.dir & PJMEDIA_DIR_CAPTURE))
1013 {
1014 /* Recording latency */
1015 *(unsigned*)pval = stream->param.input_latency_ms;
1016 return PJ_SUCCESS;
Benny Prijono2ce87f12013-02-05 05:15:01 +00001017
Sauw Ming152532f2012-06-01 04:29:56 +00001018 } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
1019 (stream->param.dir & PJMEDIA_DIR_PLAYBACK))
1020 {
1021 /* Playback latency */
1022 *(unsigned*)pval = stream->param.output_latency_ms;
1023 return PJ_SUCCESS;
Benny Prijono2ce87f12013-02-05 05:15:01 +00001024
Benny Prijono9ea1bcd2013-04-15 10:26:58 +00001025 } else if (cap==PJMEDIA_AUD_DEV_CAP_EC &&
1026 (stream->param.dir & PJMEDIA_DIR_CAPTURE))
1027 {
1028 /* EC is enablied implicitly by opening "voice" device */
1029 *(pj_bool_t*)pval = PJ_TRUE;
1030 return PJ_SUCCESS;
Sauw Ming152532f2012-06-01 04:29:56 +00001031 } else {
1032 return PJMEDIA_EAUD_INVCAP;
1033 }
1034}
1035
1036
Benny Prijono2ce87f12013-02-05 05:15:01 +00001037/*
1038 * API: set capability
1039 * Currently just supporting toggle between speaker and earpiece
1040 */
Sauw Ming152532f2012-06-01 04:29:56 +00001041static pj_status_t bb10_stream_set_cap(pjmedia_aud_stream *strm,
1042 pjmedia_aud_dev_cap cap,
1043 const void *value)
1044{
Benny Prijono9ea1bcd2013-04-15 10:26:58 +00001045
Benny Prijono2ce87f12013-02-05 05:15:01 +00001046 struct bb10_stream *stream = (struct bb10_stream*)strm;
Sauw Ming152532f2012-06-01 04:29:56 +00001047
Benny Prijono9ea1bcd2013-04-15 10:26:58 +00001048 if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE &&
1049 (stream->param.dir & PJMEDIA_DIR_PLAYBACK))
1050 {
1051 pjmedia_aud_dev_route route;
1052 pj_status_t ret;
Benny Prijono2ce87f12013-02-05 05:15:01 +00001053
Benny Prijono9ea1bcd2013-04-15 10:26:58 +00001054 PJ_ASSERT_RETURN(value, PJ_EINVAL);
1055
1056 route = *((pjmedia_aud_dev_route*)value);
Benny Prijono2ce87f12013-02-05 05:15:01 +00001057 /* Use the initialization function which lazy-inits the
1058 * handle for routing
1059 */
1060 if (route == PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER) {
1061 ret = bb10_initialize_playback_ctrl(stream,true);
1062 } else {
1063 ret = bb10_initialize_playback_ctrl(stream,false);
1064 }
Benny Prijono9ea1bcd2013-04-15 10:26:58 +00001065 return ret;
1066
1067 } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE &&
1068 (stream->param.dir & PJMEDIA_DIR_PLAYBACK))
1069 {
1070 /* EC is always enabled. Silently ignore the request */
1071 return PJ_SUCCESS;
Benny Prijono2ce87f12013-02-05 05:15:01 +00001072 }
1073
Benny Prijono9ea1bcd2013-04-15 10:26:58 +00001074 TRACE_((THIS_FILE,"bb10_stream_set_cap() = PJMEDIA_EAUD_INVCAP"));
1075 return PJMEDIA_EAUD_INVCAP;
Sauw Ming152532f2012-06-01 04:29:56 +00001076}
1077
1078
1079/* API: start stream */
1080static pj_status_t bb10_stream_start (pjmedia_aud_stream *s)
1081{
1082 struct bb10_stream *stream = (struct bb10_stream*)s;
1083 pj_status_t status = PJ_SUCCESS;
1084
1085 stream->quit = 0;
1086 if (stream->param.dir & PJMEDIA_DIR_PLAYBACK) {
1087 status = pj_thread_create (stream->pool,
1088 "bb10sound_playback",
1089 pb_thread_func,
1090 stream,
1091 0,
1092 0,
1093 &stream->pb_thread);
1094 if (status != PJ_SUCCESS)
1095 return status;
1096 }
1097
1098 if (stream->param.dir & PJMEDIA_DIR_CAPTURE) {
1099 status = pj_thread_create (stream->pool,
1100 "bb10sound_playback",
1101 ca_thread_func,
1102 stream,
1103 0,
1104 0,
1105 &stream->ca_thread);
1106 if (status != PJ_SUCCESS) {
1107 stream->quit = PJ_TRUE;
1108 pj_thread_join(stream->pb_thread);
1109 pj_thread_destroy(stream->pb_thread);
1110 stream->pb_thread = NULL;
1111 }
1112 }
1113
1114 return status;
1115}
1116
1117
1118/* API: stop stream */
1119static pj_status_t bb10_stream_stop (pjmedia_aud_stream *s)
1120{
1121 struct bb10_stream *stream = (struct bb10_stream*)s;
1122
1123 stream->quit = 1;
1124 TRACE_((THIS_FILE,"bb10_stream_stop()"));
1125
1126 if (stream->pb_thread) {
1127 pj_thread_join (stream->pb_thread);
1128 pj_thread_destroy(stream->pb_thread);
1129 stream->pb_thread = NULL;
1130 }
1131
1132 if (stream->ca_thread) {
1133 pj_thread_join (stream->ca_thread);
1134 pj_thread_destroy(stream->ca_thread);
1135 stream->ca_thread = NULL;
1136 }
1137
1138 return PJ_SUCCESS;
1139}
1140
1141static pj_status_t bb10_stream_destroy (pjmedia_aud_stream *s)
1142{
1143 struct bb10_stream *stream = (struct bb10_stream*)s;
Benny Prijonocc61b742012-08-14 08:39:59 +00001144
Sauw Ming152532f2012-06-01 04:29:56 +00001145 TRACE_((THIS_FILE,"bb10_stream_destroy()"));
1146
1147 bb10_stream_stop (s);
1148
Sauw Ming152532f2012-06-01 04:29:56 +00001149 pj_pool_release (stream->pool);
1150
1151 return PJ_SUCCESS;
1152}
1153
1154#endif /* PJMEDIA_AUDIO_DEV_HAS_BB10 */