diff options
Diffstat (limited to 'pjmedia/src/pjmedia-audiodev/bb10_dev.c')
-rw-r--r-- | pjmedia/src/pjmedia-audiodev/bb10_dev.c | 364 |
1 files changed, 269 insertions, 95 deletions
diff --git a/pjmedia/src/pjmedia-audiodev/bb10_dev.c b/pjmedia/src/pjmedia-audiodev/bb10_dev.c index 418256d..f920080 100644 --- a/pjmedia/src/pjmedia-audiodev/bb10_dev.c +++ b/pjmedia/src/pjmedia-audiodev/bb10_dev.c @@ -1,4 +1,4 @@ -/* $Id: bb10_dev.c 4151 2012-06-01 04:49:57Z ming $ */ +/* $Id: bb10_dev.c 4340 2013-02-05 05:15:01Z bennylp $ */ /* * Copyright (C) 2008-2012 Teluu Inc. (http://www.teluu.com) * @@ -33,6 +33,11 @@ #if defined(PJMEDIA_AUDIO_DEV_HAS_BB10) && PJMEDIA_AUDIO_DEV_HAS_BB10 != 0 +#ifndef PJ_BBSDK_VER + /* Format: 0xMMNNRR: MM: major, NN: minor, RR: revision */ +# define PJ_BBSDK_VER 0x100006 +#endif + #include <sys/time.h> #include <sys/types.h> #include <unistd.h> @@ -40,6 +45,9 @@ #include <pthread.h> #include <errno.h> #include <sys/asoundlib.h> +#if PJ_BBSDK_VER >= 0x100006 +#include <audio/audio_manager_routing.h> +#endif #define THIS_FILE "bb10_dev.c" @@ -114,8 +122,9 @@ struct bb10_stream int quit; /* Playback */ + unsigned int pb_ctrl_audio_manager_handle; snd_pcm_t *pb_pcm; - snd_mixer_t *pb_mixer; + unsigned int pb_audio_manager_handle; unsigned long pb_frames; /* samples_per_frame */ pjmedia_aud_play_cb pb_cb; unsigned pb_buf_size; @@ -124,7 +133,7 @@ struct bb10_stream /* Capture */ snd_pcm_t *ca_pcm; - snd_mixer_t *ca_mixer; + unsigned int ca_audio_manager_handle; unsigned long ca_frames; /* samples_per_frame */ pjmedia_aud_rec_cb ca_cb; unsigned ca_buf_size; @@ -160,8 +169,7 @@ static pj_status_t bb10_add_dev (struct bb10_factory *af) { pjmedia_aud_dev_info *adi; int pb_result, ca_result; - int card = -1; - int dev = 0; + unsigned int handle; snd_pcm_t *pcm_handle; if (af->dev_cnt >= PJ_ARRAY_SIZE(af->devs)) @@ -171,20 +179,29 @@ static pj_status_t bb10_add_dev (struct bb10_factory *af) TRACE_((THIS_FILE, "bb10_add_dev Enter")); - if ((pb_result = snd_pcm_open_preferred (&pcm_handle, &card, &dev, - SND_PCM_OPEN_PLAYBACK)) >= 0) + if ((pb_result = audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT, + &pcm_handle, + &handle, + (char*)"voice", + SND_PCM_OPEN_PLAYBACK)) + >= 0) { - TRACE_((THIS_FILE, "Try to open the device for playback - success")); snd_pcm_close (pcm_handle); + audio_manager_free_handle(handle); } else { TRACE_((THIS_FILE, "Try to open the device for playback - failure")); } - if ((ca_result = snd_pcm_open_preferred (&pcm_handle, &card, &dev, - SND_PCM_OPEN_CAPTURE)) >=0) + if ((ca_result = audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT, + &pcm_handle, + &handle, + (char*)"voice", + SND_PCM_OPEN_CAPTURE)) + >= 0) { - TRACE_((THIS_FILE, "Try to open the device for capture - success")); - snd_pcm_close (pcm_handle); + snd_pcm_close (pcm_handle); + audio_manager_free_handle(handle); + } else { TRACE_((THIS_FILE, "Try to open the device for capture - failure")); } @@ -239,7 +256,7 @@ pjmedia_aud_dev_factory* pjmedia_bb10_factory(pj_pool_factory *pf) static pj_status_t bb10_factory_init(pjmedia_aud_dev_factory *f) { pj_status_t status; - + status = bb10_factory_refresh(f); if (status != PJ_SUCCESS) return status; @@ -314,7 +331,7 @@ static pj_status_t bb10_factory_get_dev_info(pjmedia_aud_dev_factory *f, pj_memcpy(info, &af->devs[index], sizeof(*info)); info->caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY | PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY; - + return PJ_SUCCESS; } @@ -358,7 +375,7 @@ static pj_status_t bb10_factory_default_param(pjmedia_aud_dev_factory *f, TRACE_((THIS_FILE, "bb10_factory_default_param clock = %d flags = %d" " spf = %d", param->clock_rate, param->flags, param->samples_per_frame)); - + return PJ_SUCCESS; } @@ -368,14 +385,16 @@ static void close_play_pcm(struct bb10_stream *stream) if (stream != NULL && stream->pb_pcm != NULL) { snd_pcm_close(stream->pb_pcm); stream->pb_pcm = NULL; - } -} -static void close_play_mixer(struct bb10_stream *stream) -{ - if (stream != NULL && stream->pb_mixer != NULL) { - snd_mixer_close(stream->pb_mixer); - stream->pb_mixer = NULL; + if (stream->pb_audio_manager_handle != 0) { + audio_manager_free_handle(stream->pb_audio_manager_handle); + stream->pb_audio_manager_handle = 0; + } + + if (stream->pb_ctrl_audio_manager_handle != 0) { + audio_manager_free_handle(stream->pb_ctrl_audio_manager_handle); + stream->pb_ctrl_audio_manager_handle = 0; + } } } @@ -391,14 +410,11 @@ static void close_capture_pcm(struct bb10_stream *stream) if (stream != NULL && stream->ca_pcm != NULL) { snd_pcm_close(stream->ca_pcm); stream->ca_pcm = NULL; - } -} -static void close_capture_mixer(struct bb10_stream *stream) -{ - if (stream != NULL && stream->ca_mixer != NULL) { - snd_mixer_close(stream->ca_mixer); - stream->ca_mixer = NULL; + if (stream->ca_audio_manager_handle != 0) { + audio_manager_free_handle(stream->ca_audio_manager_handle); + stream->ca_audio_manager_handle = 0; + } } } @@ -435,10 +451,9 @@ static int pb_thread_func (void *arg) if ((result = snd_pcm_plugin_prepare(stream->pb_pcm, SND_PCM_CHANNEL_PLAYBACK)) < 0) { - close_play_mixer(stream); close_play_pcm(stream); TRACE_((THIS_FILE, "pb_thread_func failed prepare = %d", result)); - return PJ_SUCCESS; + return PJ_SUCCESS; } while (!stream->quit) { @@ -451,6 +466,7 @@ static int pb_thread_func (void *arg) frame.timestamp.u64 = tstamp.u64; frame.bit_info = 0; + /* Read the audio from pjmedia */ result = stream->pb_cb (user_data, &frame); if (result != PJ_SUCCESS || stream->quit) break; @@ -460,18 +476,45 @@ static int pb_thread_func (void *arg) /* Write 640 to play unit */ result = snd_pcm_plugin_write(stream->pb_pcm,buf,size); - if (result != size) { - TRACE_((THIS_FILE, "pb_thread_func failed write = %d", result)); + if (result != size || result < 0) { + /* either the write to output device has failed or not the + * full amount of bytes have been written. This usually happens + * when audio routing is being changed by another thread + * Use a status variable for reading the error + */ + snd_pcm_channel_status_t status; + status.channel = SND_PCM_CHANNEL_PLAYBACK; + if (snd_pcm_plugin_status (stream->pb_pcm, &status) < 0) { + /* Call has failed nothing we can do except log and + * continue */ + PJ_LOG(4,(THIS_FILE, + "underrun: playback channel status error")); + } else { + /* The status of the error has been read + * RIM say these are expected so we can "re-prepare" the stream + */ + PJ_LOG(4,(THIS_FILE,"PLAY thread ERROR status = %d", + status.status)); + if (status.status == SND_PCM_STATUS_READY || + status.status == SND_PCM_STATUS_UNDERRUN || + status.status == SND_PCM_STATUS_ERROR ) + { + if (snd_pcm_plugin_prepare (stream->pb_pcm, + SND_PCM_CHANNEL_PLAYBACK) < 0) + { + PJ_LOG(4,(THIS_FILE, + "underrun: playback channel prepare error")); + } + } + } } - tstamp.u64 += nframes; } flush_play(stream); - close_play_mixer(stream); close_play_pcm(stream); TRACE_((THIS_FILE, "pb_thread_func: Stopped")); - + return PJ_SUCCESS; } @@ -513,29 +556,58 @@ static int ca_thread_func (void *arg) if ((result = snd_pcm_plugin_prepare (stream->ca_pcm, SND_PCM_CHANNEL_CAPTURE)) < 0) { - close_capture_mixer(stream); close_capture_pcm(stream); TRACE_((THIS_FILE, "ca_thread_func failed prepare = %d", result)); - return PJ_SUCCESS; + return PJ_SUCCESS; } while (!stream->quit) { pjmedia_frame frame; pj_bzero (buf, size); - + + /* read the input device */ result = snd_pcm_plugin_read(stream->ca_pcm, buf,size); - if (result == -EPIPE) { - PJ_LOG (4,(THIS_FILE, "ca_thread_func: overrun!")); - snd_pcm_plugin_prepare (stream->ca_pcm, SND_PCM_CHANNEL_CAPTURE); - continue; - } else if (result < 0) { - PJ_LOG (4,(THIS_FILE, "ca_thread_func: error reading data!")); + if(result <0 || result != size) { + /* We expect result to be size (640) + * It's not so we have to read the status error and "prepare" + * the channel. This usually happens when output audio routing + * has been changed by another thread. + * We won't "continue", instead just do what we can and leave + * the end of the loop to write what's in the buffer. Not entirely + * correct but saves a potential underrun in PJMEDIA + */ + PJ_LOG (4,(THIS_FILE, + "snd_pcm_plugin_read ERROR read = %d required = %d", + result,size)); + snd_pcm_channel_status_t status; + status.channel = SND_PCM_CHANNEL_CAPTURE; + if ((result = snd_pcm_plugin_status (stream->ca_pcm, &status)) < 0) + { + /* Should not fail but all we can do is continue */ + PJ_LOG(4,(THIS_FILE, "capture: snd_pcm_plugin_status ret = %d", + result)); + } else { + /* RIM say these are the errors that we should "prepare" + * after */ + if (status.status == SND_PCM_STATUS_READY || + status.status == SND_PCM_STATUS_OVERRUN || + status.status == SND_PCM_STATUS_ERROR) + { + if (snd_pcm_plugin_prepare (stream->ca_pcm, + SND_PCM_CHANNEL_CAPTURE) < 0) + { + PJ_LOG (4,(THIS_FILE, + "overrun: capture channel prepare error")); + } + } + } } if (stream->quit) break; + /* Write the capture audio data to PJMEDIA */ frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = (void *) buf; frame.size = size; @@ -550,19 +622,68 @@ static int ca_thread_func (void *arg) } flush_capture(stream); - close_capture_mixer(stream); close_capture_pcm(stream); TRACE_((THIS_FILE, "ca_thread_func: Stopped")); return PJ_SUCCESS; } +/* Audio routing, speaker/headset */ +static pj_status_t bb10_initialize_playback_ctrl(struct bb10_stream *stream, + bool speaker) +{ + /* Although the play and capture have audio manager handles, audio routing + * requires a separate handle + */ + int ret = PJ_SUCCESS; + + if (stream->pb_ctrl_audio_manager_handle == 0) { + /* lazy init an audio manager handle */ + ret = audio_manager_get_handle(AUDIO_TYPE_VIDEO_CHAT, 0, false, + &stream->pb_ctrl_audio_manager_handle); + if (ret != 0) { + TRACE_((THIS_FILE, "audio_manager_get_handle ret = %d",ret)); + return PJMEDIA_EAUD_SYSERR; + } + } + + /* Set for either speaker or earpiece */ + if (speaker) { + ret = audio_manager_set_handle_type( + stream->pb_ctrl_audio_manager_handle, + AUDIO_TYPE_VIDEO_CHAT, + AUDIO_DEVICE_SPEAKER, + AUDIO_DEVICE_DEFAULT); + } else { + ret = audio_manager_set_handle_type( + stream->pb_ctrl_audio_manager_handle, + AUDIO_TYPE_VIDEO_CHAT, + AUDIO_DEVICE_HANDSET, + AUDIO_DEVICE_DEFAULT); + } + + if (ret == 0) { + /* RIM recommend this call */ + ret = audio_manager_set_handle_routing_conditions( + stream->pb_ctrl_audio_manager_handle, + SETTINGS_RESET_ON_DEVICE_CONNECTION); + if (ret != 0) { + TRACE_((THIS_FILE, + "audio_manager_set_handle_routing_conditions ret = %d", + ret)); + return PJMEDIA_EAUD_SYSERR; + } + } else { + TRACE_((THIS_FILE, "audio_manager_set_handle_type ret = %d", ret)); + return PJMEDIA_EAUD_SYSERR; + } + + return PJ_SUCCESS; +} static pj_status_t bb10_open_playback (struct bb10_stream *stream, const pjmedia_aud_param *param) { - int card = -1; - int dev = 0; int ret = 0; snd_pcm_channel_info_t pi; snd_pcm_channel_setup_t setup; @@ -575,34 +696,54 @@ static pj_status_t bb10_open_playback (struct bb10_stream *stream, return PJMEDIA_EAUD_INVDEV; } - if ((ret = snd_pcm_open_preferred (&stream->pb_pcm, &card, &dev, - SND_PCM_OPEN_PLAYBACK)) < 0) + /* Use the bb10 audio manager API to open as opposed to QNX core audio + * Echo cancellation built in + */ + if ((ret = audio_manager_snd_pcm_open_name( + AUDIO_TYPE_VIDEO_CHAT, + &stream->pb_pcm, &stream->pb_audio_manager_handle, + (char*)"voice", + SND_PCM_OPEN_PLAYBACK)) < 0) { - TRACE_((THIS_FILE, "snd_pcm_open_preferred ret = %d", ret)); + TRACE_((THIS_FILE, "audio_manager_snd_pcm_open_name ret = %d", ret)); return PJMEDIA_EAUD_SYSERR; } + /* Required call from January 2013 gold OS release */ + if ((ret = snd_pcm_plugin_set_disable(stream->pb_pcm, + PLUGIN_DISABLE_MMAP)) < 0) + { + TRACE_((THIS_FILE, "snd_pcm_plugin_set_disable ret = %d", ret)); + return PJMEDIA_EAUD_SYSERR; + } + + /* Required call from January 2013 gold OS release */ + if ((ret = snd_pcm_plugin_set_enable(stream->pb_pcm, + PLUGIN_ROUTING)) < 0) + { + TRACE_((THIS_FILE, "snd_pcm_plugin_set_enable ret = %d", ret)); + return PJMEDIA_EAUD_SYSERR; + } + /* TODO PJ_ZERO */ memset (&pi, 0, sizeof (pi)); pi.channel = SND_PCM_CHANNEL_PLAYBACK; if ((ret = snd_pcm_plugin_info (stream->pb_pcm, &pi)) < 0) { - TRACE_((THIS_FILE, "snd_pcm_plugin_info ret = %d", ret)); - return PJMEDIA_EAUD_SYSERR; + TRACE_((THIS_FILE, "snd_pcm_plugin_info ret = %d", ret)); + return PJMEDIA_EAUD_SYSERR; } memset (&pp, 0, sizeof (pp)); - /* Request VoIP compatible capabilities - * On simulator frag_size is always negotiated to 170 - */ + /* Request VoIP compatible capabilities */ pp.mode = SND_PCM_MODE_BLOCK; pp.channel = SND_PCM_CHANNEL_PLAYBACK; pp.start_mode = SND_PCM_START_DATA; pp.stop_mode = SND_PCM_STOP_ROLLOVER; /* HARD CODE for the time being PJMEDIA expects 640 for 16khz */ pp.buf.block.frag_size = PREFERRED_FRAME_SIZE*2; - /* Increasing this internal buffer count delays write failure in the loop */ - pp.buf.block.frags_max = 4; + /* RIM recommends maximum of 3 */ + pp.buf.block.frags_max = 3; pp.buf.block.frags_min = 1; pp.format.interleave = 1; /* HARD CODE for the time being PJMEDIA expects 16khz */ @@ -622,7 +763,7 @@ static pj_status_t bb10_open_playback (struct bb10_stream *stream, memset (&group, 0, sizeof (group)); setup.channel = SND_PCM_CHANNEL_PLAYBACK; setup.mixer_gid = &group.gid; - + if ((ret = snd_pcm_plugin_setup (stream->pb_pcm, &setup)) < 0) { TRACE_((THIS_FILE, "snd_pcm_plugin_setup ret = %d", ret)); return PJMEDIA_EAUD_SYSERR; @@ -631,14 +772,6 @@ static pj_status_t bb10_open_playback (struct bb10_stream *stream, if (group.gid.name[0] == 0) { return PJMEDIA_EAUD_SYSERR; } - - if ((ret = snd_mixer_open (&stream->pb_mixer, card, - setup.mixer_device)) < 0) - { - TRACE_((THIS_FILE, "snd_mixer_open ret = %d", ret)); - return PJMEDIA_EAUD_SYSERR; - } - rate = param->clock_rate; /* Set the sound device buffer size and latency */ @@ -658,7 +791,7 @@ static pj_status_t bb10_open_playback (struct bb10_stream *stream, TRACE_((THIS_FILE, "bb10_open_playback: pb_frames = %d clock = %d", stream->pb_frames, param->clock_rate)); - + return PJ_SUCCESS; } @@ -668,8 +801,6 @@ static pj_status_t bb10_open_capture (struct bb10_stream *stream, int ret = 0; unsigned int rate; unsigned long tmp_buf_size; - int card = -1; - int dev = 0; int frame_size; snd_pcm_channel_info_t pi; snd_mixer_group_t group; @@ -679,11 +810,27 @@ static pj_status_t bb10_open_capture (struct bb10_stream *stream, if (param->rec_id < 0 || param->rec_id >= stream->af->dev_cnt) return PJMEDIA_EAUD_INVDEV; - /* BB10 Audio init here (not prepare) */ - if ((ret = snd_pcm_open_preferred (&stream->ca_pcm, &card, &dev, - SND_PCM_OPEN_CAPTURE)) < 0) + if ((ret=audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT, + &stream->ca_pcm, + &stream->ca_audio_manager_handle, + (char*)"voice", + SND_PCM_OPEN_CAPTURE)) < 0) + { + TRACE_((THIS_FILE, "audio_manager_snd_pcm_open_name ret = %d", ret)); + return PJMEDIA_EAUD_SYSERR; + } + /* Required call from January 2013 gold OS release */ + if ((ret = snd_pcm_plugin_set_disable (stream->ca_pcm, + PLUGIN_DISABLE_MMAP)) < 0) { - TRACE_((THIS_FILE, "snd_pcm_open_preferred ret = %d", ret)); + TRACE_(("snd_pcm_plugin_set_disable failed: %d",ret)); + return PJMEDIA_EAUD_SYSERR; + } + /* Required call from January 2013 gold OS release */ + if ((ret = snd_pcm_plugin_set_enable(stream->ca_pcm, + PLUGIN_ROUTING)) < 0) + { + TRACE_(("snd_pcm_plugin_set_enable failed: %d",ret)); return PJMEDIA_EAUD_SYSERR; } @@ -707,8 +854,8 @@ static pj_status_t bb10_open_capture (struct bb10_stream *stream, pp.stop_mode = SND_PCM_STOP_ROLLOVER; /* HARD CODE for the time being PJMEDIA expects 640 for 16khz */ pp.buf.block.frag_size = PREFERRED_FRAME_SIZE*2; - /* Not applicable for capture hence -1 */ - pp.buf.block.frags_max = -1; + /* From January 2013 gold OS release. RIM recommend these for capture */ + pp.buf.block.frags_max = 1; pp.buf.block.frags_min = 1; pp.format.interleave = 1; /* HARD CODE for the time being PJMEDIA expects 16khz */ @@ -740,18 +887,8 @@ static pj_status_t bb10_open_capture (struct bb10_stream *stream, } else { } - if ((ret = snd_mixer_open (&stream->ca_mixer, card, - setup.mixer_device)) < 0) - { - TRACE_((THIS_FILE,"snd_mixer_open ret = %d",ret)); - return PJMEDIA_EAUD_SYSERR; - } - - /* frag_size should be 160 */ frame_size = setup.buf.block.frag_size; - /* END BB10 init */ - /* Set clock rate */ rate = param->clock_rate; stream->ca_frames = (unsigned long) param->samples_per_frame / @@ -820,7 +957,6 @@ static pj_status_t bb10_factory_create_stream(pjmedia_aud_dev_factory *f, status = bb10_open_capture (stream, param); if (status != PJ_SUCCESS) { if (param->dir & PJMEDIA_DIR_PLAYBACK) { - close_play_mixer(stream); close_play_pcm(stream); } pj_pool_release (pool); @@ -828,12 +964,24 @@ static pj_status_t bb10_factory_create_stream(pjmedia_aud_dev_factory *f, } } + /* Part of the play functionality but the RIM/Truphone loopback sample + * initialializes after the play and capture + * "false" is default/earpiece for output + */ + status = bb10_initialize_playback_ctrl(stream,false); + if (status != PJ_SUCCESS) { + return PJMEDIA_EAUD_SYSERR; + } + *p_strm = &stream->base; return PJ_SUCCESS; } -/* API: get running parameter */ +/* + * API: get running parameter + * based on ALSA template + */ static pj_status_t bb10_stream_get_param(pjmedia_aud_stream *s, pjmedia_aud_param *pi) { @@ -847,7 +995,10 @@ static pj_status_t bb10_stream_get_param(pjmedia_aud_stream *s, } -/* API: get capability */ +/* + * API: get capability + * based on ALSA template +*/ static pj_status_t bb10_stream_get_cap(pjmedia_aud_stream *s, pjmedia_aud_dev_cap cap, void *pval) @@ -862,28 +1013,51 @@ static pj_status_t bb10_stream_get_cap(pjmedia_aud_stream *s, /* Recording latency */ *(unsigned*)pval = stream->param.input_latency_ms; return PJ_SUCCESS; + } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY && (stream->param.dir & PJMEDIA_DIR_PLAYBACK)) { /* Playback latency */ *(unsigned*)pval = stream->param.output_latency_ms; return PJ_SUCCESS; + } else { return PJMEDIA_EAUD_INVCAP; } } -/* API: set capability */ +/* + * API: set capability + * Currently just supporting toggle between speaker and earpiece + */ static pj_status_t bb10_stream_set_cap(pjmedia_aud_stream *strm, pjmedia_aud_dev_cap cap, const void *value) { - PJ_UNUSED_ARG(strm); - PJ_UNUSED_ARG(cap); - PJ_UNUSED_ARG(value); + pj_status_t ret = PJ_SUCCESS; + struct bb10_stream *stream = (struct bb10_stream*)strm; + + if (cap != PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE || value == NULL) { + TRACE_((THIS_FILE,"bb10_stream_set_cap() = PJMEDIA_EAUD_INVCAP")); + return PJMEDIA_EAUD_INVCAP; + + } else { + pjmedia_aud_dev_route route = *((pjmedia_aud_dev_route*)value); + /* Use the initialization function which lazy-inits the + * handle for routing + */ + if (route == PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER) { + ret = bb10_initialize_playback_ctrl(stream,true); + } else { + ret = bb10_initialize_playback_ctrl(stream,false); + } + } - return PJMEDIA_EAUD_INVCAP; + if (ret != PJ_SUCCESS) { + TRACE_((THIS_FILE,"bb10_stream_set_cap() = %d",ret)); + } + return ret; } @@ -952,7 +1126,7 @@ static pj_status_t bb10_stream_stop (pjmedia_aud_stream *s) static pj_status_t bb10_stream_destroy (pjmedia_aud_stream *s) { struct bb10_stream *stream = (struct bb10_stream*)s; - + TRACE_((THIS_FILE,"bb10_stream_destroy()")); bb10_stream_stop (s); |