blob: f1ad9a10ef9d910a26450922c6b8f8e778427c01 [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $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.
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
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
36#ifndef PJ_BBSDK_VER
37 /* Format: 0xMMNNRR: MM: major, NN: minor, RR: revision */
38# define PJ_BBSDK_VER 0x100006
39#endif
40
41#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>
48#if PJ_BBSDK_VER >= 0x100006
49#include <audio/audio_manager_routing.h>
50#endif
51
52
53#define THIS_FILE "bb10_dev.c"
54#define BB10_DEVICE_NAME "plughw:%d,%d"
55
56
57/* Set to 1 to enable tracing */
58#if 1
59# define TRACE_(expr) PJ_LOG(4,expr)
60#else
61# define TRACE_(expr)
62#endif
63
64/*
65 * Factory prototypes
66 */
67static pj_status_t bb10_factory_init(pjmedia_aud_dev_factory *f);
68static pj_status_t bb10_factory_destroy(pjmedia_aud_dev_factory *f);
69static pj_status_t bb10_factory_refresh(pjmedia_aud_dev_factory *f);
70static unsigned bb10_factory_get_dev_count(pjmedia_aud_dev_factory *f);
71static pj_status_t bb10_factory_get_dev_info(pjmedia_aud_dev_factory *f,
72 unsigned index,
73 pjmedia_aud_dev_info *info);
74static pj_status_t bb10_factory_default_param(pjmedia_aud_dev_factory *f,
75 unsigned index,
76 pjmedia_aud_param *param);
77static pj_status_t bb10_factory_create_stream(pjmedia_aud_dev_factory *f,
78 const pjmedia_aud_param *param,
79 pjmedia_aud_rec_cb rec_cb,
80 pjmedia_aud_play_cb play_cb,
81 void *user_data,
82 pjmedia_aud_stream **p_strm);
83
84/*
85 * Stream prototypes
86 */
87static pj_status_t bb10_stream_get_param(pjmedia_aud_stream *strm,
88 pjmedia_aud_param *param);
89static pj_status_t bb10_stream_get_cap(pjmedia_aud_stream *strm,
90 pjmedia_aud_dev_cap cap,
91 void *value);
92static pj_status_t bb10_stream_set_cap(pjmedia_aud_stream *strm,
93 pjmedia_aud_dev_cap cap,
94 const void *value);
95static pj_status_t bb10_stream_start(pjmedia_aud_stream *strm);
96static pj_status_t bb10_stream_stop(pjmedia_aud_stream *strm);
97static pj_status_t bb10_stream_destroy(pjmedia_aud_stream *strm);
98
99
100struct bb10_factory
101{
102 pjmedia_aud_dev_factory base;
103 pj_pool_factory *pf;
104 pj_pool_t *pool;
105 pj_pool_t *base_pool;
106 unsigned dev_cnt;
107 pjmedia_aud_dev_info devs[1];
108};
109
110struct bb10_stream
111{
112 pjmedia_aud_stream base;
113
114 /* Common */
115 pj_pool_t *pool;
116 struct bb10_factory *af;
117 void *user_data;
118 pjmedia_aud_param param; /* Running parameter */
119 int rec_id; /* Capture device id */
120 int quit;
121
122 /* Playback */
123 unsigned int pb_ctrl_audio_manager_handle;
124 snd_pcm_t *pb_pcm;
125 unsigned int pb_audio_manager_handle;
126 unsigned long pb_frames; /* samples_per_frame */
127 pjmedia_aud_play_cb pb_cb;
128 unsigned pb_buf_size;
129 char *pb_buf;
130 pj_thread_t *pb_thread;
131
132 /* Capture */
133 snd_pcm_t *ca_pcm;
134 unsigned int ca_audio_manager_handle;
135 unsigned long ca_frames; /* samples_per_frame */
136 pjmedia_aud_rec_cb ca_cb;
137 unsigned ca_buf_size;
138 char *ca_buf;
139 pj_thread_t *ca_thread;
140};
141
142static pjmedia_aud_dev_factory_op bb10_factory_op =
143{
144 &bb10_factory_init,
145 &bb10_factory_destroy,
146 &bb10_factory_get_dev_count,
147 &bb10_factory_get_dev_info,
148 &bb10_factory_default_param,
149 &bb10_factory_create_stream,
150 &bb10_factory_refresh
151};
152
153static pjmedia_aud_stream_op bb10_stream_op =
154{
155 &bb10_stream_get_param,
156 &bb10_stream_get_cap,
157 &bb10_stream_set_cap,
158 &bb10_stream_start,
159 &bb10_stream_stop,
160 &bb10_stream_destroy
161};
162
163/*
164 * BB10 - tests loads the audio units and sets up the driver structure
165 */
166static pj_status_t bb10_add_dev (struct bb10_factory *af)
167{
168 pjmedia_aud_dev_info *adi;
169 int pb_result, ca_result;
170 unsigned int handle;
171 snd_pcm_t *pcm_handle;
172
173 if (af->dev_cnt >= PJ_ARRAY_SIZE(af->devs))
174 return PJ_ETOOMANY;
175
176 adi = &af->devs[af->dev_cnt];
177
178 TRACE_((THIS_FILE, "bb10_add_dev Enter"));
179
180 if ((pb_result = audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT,
181 &pcm_handle,
182 &handle,
183 (char*)"voice",
184 SND_PCM_OPEN_PLAYBACK))
185 >= 0)
186 {
187 snd_pcm_close (pcm_handle);
188 audio_manager_free_handle(handle);
189 } else {
190 TRACE_((THIS_FILE, "Try to open the device for playback - failure"));
191 }
192
193 if ((ca_result = audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT,
194 &pcm_handle,
195 &handle,
196 (char*)"voice",
197 SND_PCM_OPEN_CAPTURE))
198 >= 0)
199 {
200 snd_pcm_close (pcm_handle);
201 audio_manager_free_handle(handle);
202
203 } else {
204 TRACE_((THIS_FILE, "Try to open the device for capture - failure"));
205 }
206
207 if (pb_result < 0 && ca_result < 0) {
208 TRACE_((THIS_FILE, "Unable to open sound device", "preferred"));
209 return PJMEDIA_EAUD_NODEV;
210 }
211
212 /* Reset device info */
213 pj_bzero(adi, sizeof(*adi));
214
215 /* Set device name */
216 strcpy(adi->name, "preferred");
217
218 /* Check the number of playback channels */
219 adi->output_count = (pb_result >= 0) ? 1 : 0;
220
221 /* Check the number of capture channels */
222 adi->input_count = (ca_result >= 0) ? 1 : 0;
223
224 /* Set the default sample rate */
225 adi->default_samples_per_sec = 8000;
226
227 /* Driver name */
228 strcpy(adi->driver, "BB10");
229
230 ++af->dev_cnt;
231
232 PJ_LOG (4,(THIS_FILE, "Added sound device %s", adi->name));
233
234 return PJ_SUCCESS;
235}
236
237/* Create BB10 audio driver. */
238pjmedia_aud_dev_factory* pjmedia_bb10_factory(pj_pool_factory *pf)
239{
240 struct bb10_factory *af;
241 pj_pool_t *pool;
242
243 pool = pj_pool_create(pf, "bb10_aud_base", 256, 256, NULL);
244 af = PJ_POOL_ZALLOC_T(pool, struct bb10_factory);
245 af->pf = pf;
246 af->base_pool = pool;
247 af->base.op = &bb10_factory_op;
248
249 return &af->base;
250}
251
252
253/* API: init factory */
254static pj_status_t bb10_factory_init(pjmedia_aud_dev_factory *f)
255{
256 pj_status_t status;
257
258 status = bb10_factory_refresh(f);
259 if (status != PJ_SUCCESS)
260 return status;
261
262 PJ_LOG(4,(THIS_FILE, "BB10 initialized"));
263 return PJ_SUCCESS;
264}
265
266
267/* API: destroy factory */
268static pj_status_t bb10_factory_destroy(pjmedia_aud_dev_factory *f)
269{
270 struct bb10_factory *af = (struct bb10_factory*)f;
271
272 if (af->pool) {
273 TRACE_((THIS_FILE, "bb10_factory_destroy() - 1"));
274 pj_pool_release(af->pool);
275 }
276
277 if (af->base_pool) {
278 pj_pool_t *pool = af->base_pool;
279 af->base_pool = NULL;
280 TRACE_((THIS_FILE, "bb10_factory_destroy() - 2"));
281 pj_pool_release(pool);
282 }
283
284 return PJ_SUCCESS;
285}
286
287
288/* API: refresh the device list */
289static pj_status_t bb10_factory_refresh(pjmedia_aud_dev_factory *f)
290{
291 struct bb10_factory *af = (struct bb10_factory*)f;
292 int err;
293
294 TRACE_((THIS_FILE, "bb10_factory_refresh()"));
295
296 if (af->pool != NULL) {
297 pj_pool_release(af->pool);
298 af->pool = NULL;
299 }
300
301 af->pool = pj_pool_create(af->pf, "bb10_aud", 256, 256, NULL);
302 af->dev_cnt = 0;
303
304 err = bb10_add_dev(af);
305
306 PJ_LOG(4,(THIS_FILE, "BB10 driver found %d devices", af->dev_cnt));
307
308 return err;
309}
310
311
312/* API: get device count */
313static unsigned bb10_factory_get_dev_count(pjmedia_aud_dev_factory *f)
314{
315 struct bb10_factory *af = (struct bb10_factory*)f;
316 return af->dev_cnt;
317}
318
319
320/* API: get device info */
321static pj_status_t bb10_factory_get_dev_info(pjmedia_aud_dev_factory *f,
322 unsigned index,
323 pjmedia_aud_dev_info *info)
324{
325 struct bb10_factory *af = (struct bb10_factory*)f;
326
327 PJ_ASSERT_RETURN(index>=0 && index<af->dev_cnt, PJ_EINVAL);
328
329 pj_memcpy(info, &af->devs[index], sizeof(*info));
330 info->caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
331 PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY |
332 PJMEDIA_AUD_DEV_CAP_EC;
333
334 return PJ_SUCCESS;
335}
336
337/* API: create default parameter */
338static pj_status_t bb10_factory_default_param(pjmedia_aud_dev_factory *f,
339 unsigned index,
340 pjmedia_aud_param *param)
341{
342 struct bb10_factory *af = (struct bb10_factory*)f;
343 pjmedia_aud_dev_info *adi;
344
345 PJ_ASSERT_RETURN(index>=0 && index<af->dev_cnt, PJ_EINVAL);
346
347 adi = &af->devs[index];
348
349 pj_bzero(param, sizeof(*param));
350 if (adi->input_count && adi->output_count) {
351 param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
352 param->rec_id = index;
353 param->play_id = index;
354 } else if (adi->input_count) {
355 param->dir = PJMEDIA_DIR_CAPTURE;
356 param->rec_id = index;
357 param->play_id = PJMEDIA_AUD_INVALID_DEV;
358 } else if (adi->output_count) {
359 param->dir = PJMEDIA_DIR_PLAYBACK;
360 param->play_id = index;
361 param->rec_id = PJMEDIA_AUD_INVALID_DEV;
362 } else {
363 return PJMEDIA_EAUD_INVDEV;
364 }
365
366 param->clock_rate = adi->default_samples_per_sec;
367 param->channel_count = 1;
368 param->samples_per_frame = adi->default_samples_per_sec * 20 / 1000;
369 param->bits_per_sample = 16;
370 param->flags = adi->caps;
371 param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
372 param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
373
374 TRACE_((THIS_FILE, "bb10_factory_default_param clock = %d flags = %d"
375 " spf = %d", param->clock_rate, param->flags,
376 param->samples_per_frame));
377
378 return PJ_SUCCESS;
379}
380
381
382static void close_play_pcm(struct bb10_stream *stream)
383{
384 if (stream != NULL && stream->pb_pcm != NULL) {
385 snd_pcm_close(stream->pb_pcm);
386 stream->pb_pcm = NULL;
387
388 if (stream->pb_audio_manager_handle != 0) {
389 audio_manager_free_handle(stream->pb_audio_manager_handle);
390 stream->pb_audio_manager_handle = 0;
391 }
392
393 if (stream->pb_ctrl_audio_manager_handle != 0) {
394 audio_manager_free_handle(stream->pb_ctrl_audio_manager_handle);
395 stream->pb_ctrl_audio_manager_handle = 0;
396 }
397 }
398}
399
400static void flush_play(struct bb10_stream *stream)
401{
402 if (stream != NULL && stream->pb_pcm != NULL) {
403 snd_pcm_plugin_flush (stream->pb_pcm, SND_PCM_CHANNEL_PLAYBACK);
404 }
405}
406
407static void close_capture_pcm(struct bb10_stream *stream)
408{
409 if (stream != NULL && stream->ca_pcm != NULL) {
410 snd_pcm_close(stream->ca_pcm);
411 stream->ca_pcm = NULL;
412
413 if (stream->ca_audio_manager_handle != 0) {
414 audio_manager_free_handle(stream->ca_audio_manager_handle);
415 stream->ca_audio_manager_handle = 0;
416 }
417 }
418}
419
420static void flush_capture(struct bb10_stream *stream)
421{
422 if (stream != NULL && stream->ca_pcm != NULL) {
423 snd_pcm_plugin_flush (stream->ca_pcm, SND_PCM_CHANNEL_CAPTURE);
424 }
425}
426
427
428/**
429 * Play audio received from PJMEDIA
430 */
431static int pb_thread_func (void *arg)
432{
433 struct bb10_stream* stream = (struct bb10_stream *) arg;
434 int size = stream->pb_buf_size;
435 unsigned long nframes = stream->pb_frames;
436 void *user_data = stream->user_data;
437 char *buf = stream->pb_buf;
438 pj_timestamp tstamp;
439 int result = 0;
440 int policy;
441 struct sched_param param;
442
443 TRACE_((THIS_FILE, "pb_thread_func: size = %d ", size));
444
445 if (pthread_getschedparam(pthread_self(), &policy, &param) == 0) {
446 param.sched_priority = 18;
447 pthread_setschedparam (pthread_self(), policy, &param);
448 }
449
450 pj_bzero (buf, size);
451 tstamp.u64 = 0;
452
453 /* Do the final initialization now the thread has started. */
454 if ((result = snd_pcm_plugin_prepare(stream->pb_pcm,
455 SND_PCM_CHANNEL_PLAYBACK)) < 0)
456 {
457 close_play_pcm(stream);
458 TRACE_((THIS_FILE, "pb_thread_func failed prepare = %d", result));
459 return PJ_SUCCESS;
460 }
461
462 while (!stream->quit) {
463 pjmedia_frame frame;
464
465 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
466 /* pointer to buffer filled by PJMEDIA */
467 frame.buf = buf;
468 frame.size = size;
469 frame.timestamp.u64 = tstamp.u64;
470 frame.bit_info = 0;
471
472 /* Read the audio from pjmedia */
473 result = stream->pb_cb (user_data, &frame);
474 if (result != PJ_SUCCESS || stream->quit)
475 break;
476
477 if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
478 pj_bzero (buf, size);
479
480 /* Write 640 to play unit */
481 result = snd_pcm_plugin_write(stream->pb_pcm,buf,size);
482 if (result != size || result < 0) {
483 /* either the write to output device has failed or not the
484 * full amount of bytes have been written. This usually happens
485 * when audio routing is being changed by another thread
486 * Use a status variable for reading the error
487 */
488 snd_pcm_channel_status_t status;
489 status.channel = SND_PCM_CHANNEL_PLAYBACK;
490 if (snd_pcm_plugin_status (stream->pb_pcm, &status) < 0) {
491 /* Call has failed nothing we can do except log and
492 * continue */
493 PJ_LOG(4,(THIS_FILE,
494 "underrun: playback channel status error"));
495 } else {
496 /* The status of the error has been read
497 * RIM say these are expected so we can "re-prepare" the stream
498 */
499 PJ_LOG(4,(THIS_FILE,"PLAY thread ERROR status = %d",
500 status.status));
501 if (status.status == SND_PCM_STATUS_READY ||
502 status.status == SND_PCM_STATUS_UNDERRUN ||
503 status.status == SND_PCM_STATUS_ERROR )
504 {
505 if (snd_pcm_plugin_prepare (stream->pb_pcm,
506 SND_PCM_CHANNEL_PLAYBACK) < 0)
507 {
508 PJ_LOG(4,(THIS_FILE,
509 "underrun: playback channel prepare error"));
510 }
511 }
512 }
513 }
514 tstamp.u64 += nframes;
515 }
516
517 flush_play(stream);
518 close_play_pcm(stream);
519 TRACE_((THIS_FILE, "pb_thread_func: Stopped"));
520
521 return PJ_SUCCESS;
522}
523
524
525
526static int ca_thread_func (void *arg)
527{
528 struct bb10_stream* stream = (struct bb10_stream *) arg;
529 int size = stream->ca_buf_size;
530 unsigned long nframes = stream->ca_frames;
531 void *user_data = stream->user_data;
532 /* Buffer to fill for PJMEDIA */
533 char *buf = stream->ca_buf;
534 pj_timestamp tstamp;
535 int result;
536 int policy;
537 struct sched_param param;
538
539 TRACE_((THIS_FILE, "ca_thread_func: size = %d ", size));
540
541 if (pthread_getschedparam(pthread_self(), &policy, &param) == 0) {
542 param.sched_priority = 18;
543 pthread_setschedparam (pthread_self(), policy, &param);
544 }
545
546 pj_bzero (buf, size);
547 tstamp.u64 = 0;
548
549 /* Final init now the thread has started */
550 if ((result = snd_pcm_plugin_prepare (stream->ca_pcm,
551 SND_PCM_CHANNEL_CAPTURE)) < 0)
552 {
553 close_capture_pcm(stream);
554 TRACE_((THIS_FILE, "ca_thread_func failed prepare = %d", result));
555 return PJ_SUCCESS;
556 }
557
558 while (!stream->quit) {
559 pjmedia_frame frame;
560
561 //pj_bzero (buf, size);
562
563 /* read the input device */
564 result = snd_pcm_plugin_read(stream->ca_pcm, buf,size);
565 if(result <0 || result != size) {
566 /* We expect result to be size (640)
567 * It's not so we have to read the status error and "prepare"
568 * the channel. This usually happens when output audio routing
569 * has been changed by another thread.
570 * We won't "continue", instead just do what we can and leave
571 * the end of the loop to write what's in the buffer. Not entirely
572 * correct but saves a potential underrun in PJMEDIA
573 */
574 PJ_LOG (4,(THIS_FILE,
575 "snd_pcm_plugin_read ERROR read = %d required = %d",
576 result,size));
577 snd_pcm_channel_status_t status;
578 status.channel = SND_PCM_CHANNEL_CAPTURE;
579 if ((result = snd_pcm_plugin_status (stream->ca_pcm, &status)) < 0)
580 {
581 /* Should not fail but all we can do is continue */
582 PJ_LOG(4,(THIS_FILE, "capture: snd_pcm_plugin_status ret = %d",
583 result));
584 } else {
585 /* RIM say these are the errors that we should "prepare"
586 * after */
587 if (status.status == SND_PCM_STATUS_READY ||
588 status.status == SND_PCM_STATUS_OVERRUN ||
589 status.status == SND_PCM_STATUS_ERROR)
590 {
591 if (snd_pcm_plugin_prepare (stream->ca_pcm,
592 SND_PCM_CHANNEL_CAPTURE) < 0)
593 {
594 PJ_LOG (4,(THIS_FILE,
595 "overrun: capture channel prepare error"));
596 }
597 }
598 }
599 }
600
601 if (stream->quit)
602 break;
603
604 /* Write the capture audio data to PJMEDIA */
605 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
606 frame.buf = (void *) buf;
607 frame.size = size;
608 frame.timestamp.u64 = tstamp.u64;
609 frame.bit_info = 0;
610
611 result = stream->ca_cb (user_data, &frame);
612 if (result != PJ_SUCCESS || stream->quit)
613 break;
614
615 tstamp.u64 += nframes;
616 }
617
618 flush_capture(stream);
619 close_capture_pcm(stream);
620 TRACE_((THIS_FILE, "ca_thread_func: Stopped"));
621
622 return PJ_SUCCESS;
623}
624
625/* Audio routing, speaker/headset */
626static pj_status_t bb10_initialize_playback_ctrl(struct bb10_stream *stream,
627 bool speaker)
628{
629 /* Although the play and capture have audio manager handles, audio routing
630 * requires a separate handle
631 */
632 int ret = PJ_SUCCESS;
633
634 if (stream->pb_ctrl_audio_manager_handle == 0) {
635 /* lazy init an audio manager handle */
636 ret = audio_manager_get_handle(AUDIO_TYPE_VOICE, 0, false,
637 &stream->pb_ctrl_audio_manager_handle);
638 if (ret != 0) {
639 TRACE_((THIS_FILE, "audio_manager_get_handle ret = %d",ret));
640 return PJMEDIA_EAUD_SYSERR;
641 }
642 }
643
644 /* Set for either speaker or earpiece */
645 if (speaker) {
646 ret = audio_manager_set_handle_type(
647 stream->pb_ctrl_audio_manager_handle,
648 AUDIO_TYPE_VIDEO_CHAT,
649 AUDIO_DEVICE_DEFAULT,
650 AUDIO_DEVICE_DEFAULT);
651 } else {
652 ret = audio_manager_set_handle_type(
653 stream->pb_ctrl_audio_manager_handle,
654 AUDIO_TYPE_VOICE,
655 AUDIO_DEVICE_DEFAULT,
656 AUDIO_DEVICE_DEFAULT);
657 }
658
659 if (ret != 0) {
660 return PJMEDIA_EAUD_SYSERR;
661 }else{
662 return PJ_SUCCESS;
663 }
664}
665
666static int32_t get_alsa_pcm_fmt(const pjmedia_aud_param *param)
667{
668 switch (param->bits_per_sample) {
669 case 8:
670 return SND_PCM_SFMT_S8;
671 case 16:
672 return SND_PCM_SFMT_S16_LE;
673 case 24:
674 return SND_PCM_SFMT_S24_LE;
675 case 32:
676 return SND_PCM_SFMT_S32_LE;
677 default:
678 PJ_ASSERT_RETURN(!"Unsupported bits_per_frame", SND_PCM_SFMT_S16_LE);
679 }
680}
681
682static pj_status_t bb10_open_playback (struct bb10_stream *stream,
683 const pjmedia_aud_param *param)
684{
685 int ret = 0;
686 snd_pcm_channel_info_t pi;
687 snd_pcm_channel_setup_t setup;
688 snd_mixer_group_t group;
689 snd_pcm_channel_params_t pp;
690 unsigned int rate;
691 unsigned long tmp_buf_size;
692
693 if (param->play_id < 0 || param->play_id >= stream->af->dev_cnt) {
694 return PJMEDIA_EAUD_INVDEV;
695 }
696
697 PJ_ASSERT_RETURN(param->bits_per_sample == 16, PJMEDIA_EAUD_SAMPFORMAT);
698
699 /* Use the bb10 audio manager API to open as opposed to QNX core audio
700 * Echo cancellation built in
701 */
702 if ((ret = audio_manager_snd_pcm_open_name(
703 AUDIO_TYPE_VIDEO_CHAT,
704 &stream->pb_pcm, &stream->pb_audio_manager_handle,
705 (char*)"voice",
706 SND_PCM_OPEN_PLAYBACK)) < 0)
707 {
708 TRACE_((THIS_FILE, "audio_manager_snd_pcm_open_name ret = %d", ret));
709 return PJMEDIA_EAUD_SYSERR;
710 }
711
712 /* Required call from January 2013 gold OS release */
713 snd_pcm_plugin_set_disable(stream->pb_pcm, PLUGIN_DISABLE_MMAP);
714
715 /* Required call from January 2013 gold OS release */
716 snd_pcm_plugin_set_enable(stream->pb_pcm, PLUGIN_ROUTING);
717
718 memset (&pi, 0, sizeof (pi));
719 pi.channel = SND_PCM_CHANNEL_PLAYBACK;
720 if ((ret = snd_pcm_plugin_info (stream->pb_pcm, &pi)) < 0) {
721 TRACE_((THIS_FILE, "snd_pcm_plugin_info ret = %d", ret));
722 return PJMEDIA_EAUD_SYSERR;
723 }
724
725 memset (&pp, 0, sizeof (pp));
726
727 /* Request VoIP compatible capabilities */
728 pp.mode = SND_PCM_MODE_BLOCK;
729 pp.channel = SND_PCM_CHANNEL_PLAYBACK;
730 pp.start_mode = SND_PCM_START_FULL;
731 pp.stop_mode = SND_PCM_STOP_ROLLOVER;
732 pp.buf.block.frag_size = param->samples_per_frame * param->bits_per_sample / 8;
733 /* RIM recommends maximum of 5 */
734 pp.buf.block.frags_max = 5;
735 pp.buf.block.frags_min = 1;
736 pp.format.interleave = 1;
737 pp.format.rate = param->clock_rate;
738 pp.format.voices = param->channel_count;
739 pp.format.format = get_alsa_pcm_fmt(param);
740
741 /* Make the calls as per the wave sample */
742 if ((ret = snd_pcm_plugin_params (stream->pb_pcm, &pp)) < 0) {
743 TRACE_((THIS_FILE, "snd_pcm_plugin_params ret = %d", ret));
744 return PJMEDIA_EAUD_SYSERR;
745 }
746
747 memset (&setup, 0, sizeof (setup));
748 memset (&group, 0, sizeof (group));
749 setup.channel = SND_PCM_CHANNEL_PLAYBACK;
750 setup.mixer_gid = &group.gid;
751
752 if ((ret = snd_pcm_plugin_setup (stream->pb_pcm, &setup)) < 0) {
753 TRACE_((THIS_FILE, "snd_pcm_plugin_setup ret = %d", ret));
754 return PJMEDIA_EAUD_SYSERR;
755 }
756
757 if (group.gid.name[0] == 0) {
758 return PJMEDIA_EAUD_SYSERR;
759 }
760
761 rate = param->clock_rate;
762 /* Set the sound device buffer size and latency */
763 if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY) {
764 tmp_buf_size = rate * param->output_latency_ms / 1000;
765 } else {
766 tmp_buf_size = rate * PJMEDIA_SND_DEFAULT_PLAY_LATENCY / 1000;
767 }
768 /* Set period size to samples_per_frame frames. */
769 stream->pb_frames = param->samples_per_frame / param->channel_count;
770 stream->param.output_latency_ms = tmp_buf_size * 1000 / rate;
771
772 /* Set our buffer */
773 stream->pb_buf_size = stream->pb_frames * param->channel_count *
774 param->bits_per_sample / 8;
775 stream->pb_buf = (char *) pj_pool_alloc(stream->pool, stream->pb_buf_size);
776
777 TRACE_((THIS_FILE, "bb10_open_playback: pb_frames = %d clock = %d",
778 stream->pb_frames, param->clock_rate));
779
780 return PJ_SUCCESS;
781}
782
783static pj_status_t bb10_open_capture (struct bb10_stream *stream,
784 const pjmedia_aud_param *param)
785{
786 int ret = 0;
787 unsigned int rate;
788 unsigned long tmp_buf_size;
789 int frame_size;
790 snd_pcm_channel_info_t pi;
791 snd_mixer_group_t group;
792 snd_pcm_channel_params_t pp;
793 snd_pcm_channel_setup_t setup;
794
795 if (param->rec_id < 0 || param->rec_id >= stream->af->dev_cnt)
796 return PJMEDIA_EAUD_INVDEV;
797
798 PJ_ASSERT_RETURN(param->bits_per_sample == 16, PJMEDIA_EAUD_SAMPFORMAT);
799
800 if ((ret=audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT,
801 &stream->ca_pcm,
802 &stream->ca_audio_manager_handle,
803 (char*)"voice",
804 SND_PCM_OPEN_CAPTURE)) < 0)
805 {
806 TRACE_((THIS_FILE, "audio_manager_snd_pcm_open_name ret = %d", ret));
807 return PJMEDIA_EAUD_SYSERR;
808 }
809 /* Required call from January 2013 gold OS release */
810 snd_pcm_plugin_set_disable (stream->ca_pcm, PLUGIN_DISABLE_MMAP);
811
812 /* Required call from January 2013 gold OS release */
813 snd_pcm_plugin_set_enable(stream->ca_pcm, PLUGIN_ROUTING);
814
815 /* sample reads the capabilities of the capture */
816 memset (&pi, 0, sizeof (pi));
817 pi.channel = SND_PCM_CHANNEL_CAPTURE;
818 if ((ret = snd_pcm_plugin_info (stream->ca_pcm, &pi)) < 0) {
819 TRACE_((THIS_FILE, "snd_pcm_plugin_info ret = %d", ret));
820 return PJMEDIA_EAUD_SYSERR;
821 }
822
823 /* Request the VoIP parameters
824 * These parameters are different to waverec sample
825 */
826 memset (&pp, 0, sizeof (pp));
827 /* Blocking read */
828 pp.mode = SND_PCM_MODE_BLOCK;
829 pp.channel = SND_PCM_CHANNEL_CAPTURE;
830 pp.start_mode = SND_PCM_START_FULL;
831 /* Auto-recover from errors */
832 pp.stop_mode = SND_PCM_STOP_ROLLOVER;
833 pp.buf.block.frag_size = param->samples_per_frame * param->bits_per_sample / 8;
834 /* From January 2013 gold OS release. RIM recommend these for capture */
835 pp.buf.block.frags_max = 3;
836 pp.buf.block.frags_min = 1;
837 pp.format.interleave = 1;
838 pp.format.rate = param->clock_rate;
839 pp.format.voices = param->channel_count;
840 pp.format.format = get_alsa_pcm_fmt(param);
841
842 /* make the request */
843 if ((ret = snd_pcm_plugin_params (stream->ca_pcm, &pp)) < 0) {
844 TRACE_((THIS_FILE, "snd_pcm_plugin_params ret = %d", ret));
845 return PJMEDIA_EAUD_SYSERR;
846 }
847
848 /* Again based on the sample */
849 memset (&setup, 0, sizeof (setup));
850 memset (&group, 0, sizeof (group));
851 setup.channel = SND_PCM_CHANNEL_CAPTURE;
852 setup.mixer_gid = &group.gid;
853 if ((ret = snd_pcm_plugin_setup (stream->ca_pcm, &setup)) < 0) {
854 TRACE_((THIS_FILE, "snd_pcm_plugin_setup ret = %d", ret));
855 return PJMEDIA_EAUD_SYSERR;
856 }
857
858 frame_size = setup.buf.block.frag_size;
859
860 if (group.gid.name[0] == 0) {
861 } else {
862 }
863
864 frame_size = setup.buf.block.frag_size;
865
866 /* Set clock rate */
867 rate = param->clock_rate;
868 stream->ca_frames = (unsigned long) param->samples_per_frame /
869 param->channel_count;
870
871 /* Set the sound device buffer size and latency */
872 if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY) {
873 tmp_buf_size = rate * param->input_latency_ms / 1000;
874 } else {
875 tmp_buf_size = rate * PJMEDIA_SND_DEFAULT_REC_LATENCY / 1000;
876 }
877
878 stream->param.input_latency_ms = tmp_buf_size * 1000 / rate;
879
880 /* Set our buffer */
881 stream->ca_buf_size = stream->ca_frames * param->channel_count *
882 param->bits_per_sample / 8;
883 stream->ca_buf = (char *)pj_pool_alloc (stream->pool, stream->ca_buf_size);
884
885 TRACE_((THIS_FILE, "bb10_open_capture: ca_frames = %d clock = %d",
886 stream->ca_frames, param->clock_rate));
887
888 return PJ_SUCCESS;
889}
890
891
892/* API: create stream */
893static pj_status_t bb10_factory_create_stream(pjmedia_aud_dev_factory *f,
894 const pjmedia_aud_param *param,
895 pjmedia_aud_rec_cb rec_cb,
896 pjmedia_aud_play_cb play_cb,
897 void *user_data,
898 pjmedia_aud_stream **p_strm)
899{
900 struct bb10_factory *af = (struct bb10_factory*)f;
901 pj_status_t status;
902 pj_pool_t* pool;
903 struct bb10_stream* stream;
904
905 pool = pj_pool_create (af->pf, "bb10%p", 1024, 1024, NULL);
906 if (!pool)
907 return PJ_ENOMEM;
908
909 /* Allocate and initialize comon stream data */
910 stream = PJ_POOL_ZALLOC_T (pool, struct bb10_stream);
911 stream->base.op = &bb10_stream_op;
912 stream->pool = pool;
913 stream->af = af;
914 stream->user_data = user_data;
915 stream->pb_cb = play_cb;
916 stream->ca_cb = rec_cb;
917 stream->quit = 0;
918 pj_memcpy(&stream->param, param, sizeof(*param));
919
920 /* Init playback */
921 if (param->dir & PJMEDIA_DIR_PLAYBACK) {
922 status = bb10_open_playback (stream, param);
923 if (status != PJ_SUCCESS) {
924 pj_pool_release (pool);
925 return status;
926 }
927 }
928
929 /* Init capture */
930 if (param->dir & PJMEDIA_DIR_CAPTURE) {
931 status = bb10_open_capture (stream, param);
932 if (status != PJ_SUCCESS) {
933 if (param->dir & PJMEDIA_DIR_PLAYBACK) {
934 close_play_pcm(stream);
935 }
936 pj_pool_release (pool);
937 return status;
938 }
939 }
940
941 /* Set the audio routing ONLY if app explicitly asks one */
942 if ((param->dir & PJMEDIA_DIR_PLAYBACK) &&
943 (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE))
944 {
945 status = bb10_stream_set_cap(&stream->base,
946 PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
947 &param->output_route);
948 if (status != PJ_SUCCESS) {
949 TRACE_((THIS_FILE, "Error setting output route"));
950 bb10_stream_destroy(&stream->base);
951 return status;
952 }
953 } else {
954 /* Legacy behavior: if none specified, set to speaker */
955 status = bb10_initialize_playback_ctrl(stream, false);
956 }
957
958 *p_strm = &stream->base;
959 return PJ_SUCCESS;
960}
961
962
963/*
964 * API: get running parameter
965 * based on ALSA template
966 */
967static pj_status_t bb10_stream_get_param(pjmedia_aud_stream *s,
968 pjmedia_aud_param *pi)
969{
970 struct bb10_stream *stream = (struct bb10_stream*)s;
971
972 PJ_ASSERT_RETURN(s && pi, PJ_EINVAL);
973
974 pj_memcpy(pi, &stream->param, sizeof(*pi));
975
976 return PJ_SUCCESS;
977}
978
979
980/*
981 * API: get capability
982 * based on ALSA template
983*/
984static pj_status_t bb10_stream_get_cap(pjmedia_aud_stream *s,
985 pjmedia_aud_dev_cap cap,
986 void *pval)
987{
988 struct bb10_stream *stream = (struct bb10_stream*)s;
989
990 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
991
992 if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
993 (stream->param.dir & PJMEDIA_DIR_CAPTURE))
994 {
995 /* Recording latency */
996 *(unsigned*)pval = stream->param.input_latency_ms;
997 return PJ_SUCCESS;
998
999 } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
1000 (stream->param.dir & PJMEDIA_DIR_PLAYBACK))
1001 {
1002 /* Playback latency */
1003 *(unsigned*)pval = stream->param.output_latency_ms;
1004 return PJ_SUCCESS;
1005
1006 } else if (cap==PJMEDIA_AUD_DEV_CAP_EC &&
1007 (stream->param.dir & PJMEDIA_DIR_CAPTURE))
1008 {
1009 /* EC is enablied implicitly by opening "voice" device */
1010 *(pj_bool_t*)pval = PJ_TRUE;
1011 return PJ_SUCCESS;
1012 } else {
1013 return PJMEDIA_EAUD_INVCAP;
1014 }
1015}
1016
1017
1018/*
1019 * API: set capability
1020 * Currently just supporting toggle between speaker and earpiece
1021 */
1022static pj_status_t bb10_stream_set_cap(pjmedia_aud_stream *strm,
1023 pjmedia_aud_dev_cap cap,
1024 const void *value)
1025{
1026
1027 struct bb10_stream *stream = (struct bb10_stream*)strm;
1028
1029 if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE &&
1030 (stream->param.dir & PJMEDIA_DIR_PLAYBACK))
1031 {
1032 pjmedia_aud_dev_route route;
1033 pj_status_t ret;
1034
1035 PJ_ASSERT_RETURN(value, PJ_EINVAL);
1036
1037 route = *((pjmedia_aud_dev_route*)value);
1038 /* Use the initialization function which lazy-inits the
1039 * handle for routing
1040 */
1041 if (route == PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER) {
1042 ret = bb10_initialize_playback_ctrl(stream,true);
1043 } else {
1044 ret = bb10_initialize_playback_ctrl(stream,false);
1045 }
1046 return ret;
1047
1048 } else if (cap==PJMEDIA_AUD_DEV_CAP_EC &&
1049 (stream->param.dir & PJMEDIA_DIR_CAPTURE))
1050 {
1051 /* EC is always enabled. Silently ignore the request */
1052 return PJ_SUCCESS;
1053 }
1054
1055 TRACE_((THIS_FILE,"bb10_stream_set_cap() = PJMEDIA_EAUD_INVCAP"));
1056 return PJMEDIA_EAUD_INVCAP;
1057}
1058
1059
1060/* API: start stream */
1061static pj_status_t bb10_stream_start (pjmedia_aud_stream *s)
1062{
1063 struct bb10_stream *stream = (struct bb10_stream*)s;
1064 pj_status_t status = PJ_SUCCESS;
1065
1066 stream->quit = 0;
1067 if (stream->param.dir & PJMEDIA_DIR_PLAYBACK) {
1068 status = pj_thread_create (stream->pool,
1069 "bb10sound_playback",
1070 pb_thread_func,
1071 stream,
1072 0,
1073 0,
1074 &stream->pb_thread);
1075 if (status != PJ_SUCCESS)
1076 return status;
1077 }
1078
1079 if (stream->param.dir & PJMEDIA_DIR_CAPTURE) {
1080 status = pj_thread_create (stream->pool,
1081 "bb10sound_playback",
1082 ca_thread_func,
1083 stream,
1084 0,
1085 0,
1086 &stream->ca_thread);
1087 if (status != PJ_SUCCESS) {
1088 stream->quit = PJ_TRUE;
1089 pj_thread_join(stream->pb_thread);
1090 pj_thread_destroy(stream->pb_thread);
1091 stream->pb_thread = NULL;
1092 }
1093 }
1094
1095 return status;
1096}
1097
1098
1099/* API: stop stream */
1100static pj_status_t bb10_stream_stop (pjmedia_aud_stream *s)
1101{
1102 struct bb10_stream *stream = (struct bb10_stream*)s;
1103
1104 stream->quit = 1;
1105 TRACE_((THIS_FILE,"bb10_stream_stop()"));
1106
1107 if (stream->pb_thread) {
1108 pj_thread_join (stream->pb_thread);
1109 pj_thread_destroy(stream->pb_thread);
1110 stream->pb_thread = NULL;
1111 }
1112
1113 if (stream->ca_thread) {
1114 pj_thread_join (stream->ca_thread);
1115 pj_thread_destroy(stream->ca_thread);
1116 stream->ca_thread = NULL;
1117 }
1118
1119 return PJ_SUCCESS;
1120}
1121
1122static pj_status_t bb10_stream_destroy (pjmedia_aud_stream *s)
1123{
1124 struct bb10_stream *stream = (struct bb10_stream*)s;
1125
1126 TRACE_((THIS_FILE,"bb10_stream_destroy()"));
1127
1128 bb10_stream_stop (s);
1129
1130 pj_pool_release (stream->pool);
1131
1132 return PJ_SUCCESS;
1133}
1134
1135#endif /* PJMEDIA_AUDIO_DEV_HAS_BB10 */