4 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
6 * Contact: Seungbae Shin <seungbae.shin@samsung.com>
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
30 #include <mm_sound_pa_client.h>
32 #include "../../include/mm_sound_plugin_codec.h"
33 #include "../../../include/mm_sound_common.h"
38 #include <pulse/pulseaudio.h>
43 int (*stop_cb)(int, bool);
44 char filename[MM_SOUND_MAX_FILENAME];
46 char stream_type[MAX_STREAM_TYPE_LEN];
50 pa_threaded_mainloop *m;
60 static int _sound_prepare(wave_info_t *h)
62 memset(&h->si, 0, sizeof(SF_INFO));
64 h->sf = sf_open(h->filename, SFM_READ, &h->si);
66 debug_error("sf_open error. path(%s), error(%d,%s)", h->filename, sf_error(h->sf), sf_strerror(h->sf));
67 return (sf_error(h->sf) == SF_ERR_SYSTEM) ? MM_ERROR_SOUND_INTERNAL : MM_ERROR_SOUND_UNSUPPORTED_MEDIA_TYPE;
70 sf_command(h->sf, SFC_SET_SCALE_FLOAT_INT_READ, NULL, SF_TRUE);
72 h->spec.rate = h->si.samplerate;
73 h->spec.channels = h->si.channels;
74 h->spec.format = PA_SAMPLE_S16LE;
76 debug_msg("SF_INFO : frames = %"PRId64", samplerate = %d, channels = %d, format = 0x%X, sections = %d, seekable = %d",
77 h->si.frames, h->si.samplerate, h->si.channels, h->si.format, h->si.sections, h->si.seekable);
82 static int _sound_rewind(wave_info_t *h)
84 return (sf_seek(h->sf, 0, SEEK_SET) != -1) ? 0 : -1;
87 static int _sound_is_rewind_needed(wave_info_t *h)
89 return (h->repeat_count == -1 || h->repeat_count > 1);
92 static void _sound_unprepare(wave_info_t *h)
100 /* Context Callbacks */
101 static void _pa_context_state_callback(pa_context *c, void *userdata)
103 pa_threaded_mainloop *m = (pa_threaded_mainloop *)userdata;
106 switch (pa_context_get_state(c)) {
107 case PA_CONTEXT_CONNECTING:
108 case PA_CONTEXT_AUTHORIZING:
109 case PA_CONTEXT_SETTING_NAME:
110 debug_log("context(%p), state(%d)", c, pa_context_get_state(c));
113 case PA_CONTEXT_READY:
114 case PA_CONTEXT_TERMINATED:
115 case PA_CONTEXT_FAILED:
116 debug_warning("context(%p), state(%d)", c, pa_context_get_state(c));
117 pa_threaded_mainloop_signal(m, 0);
125 static void *_cleanup_thread_func(void *userdata)
127 pa_threaded_mainloop *m = (pa_threaded_mainloop *)userdata;
130 debug_error("now stop and free threaded_mainloop(%p) here", m);
131 pa_threaded_mainloop_stop(m);
132 pa_threaded_mainloop_free(m);
134 debug_warning("thread mainloop is already null");
140 static void _cleanup_threaded_mainloop(pa_threaded_mainloop *m)
146 ret = pthread_attr_init(&attr);
148 debug_error("failed to init pthread attr!!! errno=%d", ret);
152 ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
154 debug_error("failed to set detach state!!! errno=%d", ret);
158 ret = pthread_create(&thread_id, &attr, _cleanup_thread_func, m);
160 debug_error("failed to create _cleanup_thread_func!!! errno=%d", ret);
163 ret = pthread_attr_destroy(&attr);
165 debug_error("failed to destroy pthread attr!!! errno=%d", ret);
171 static void _pa_context_drain_complete_callback(pa_context *c, void *userdata)
173 debug_msg("context drain completed, cleanup context and mainloop");
175 pa_context_disconnect(c);
178 _cleanup_threaded_mainloop((pa_threaded_mainloop *)userdata);
181 /* Stream Callbacks */
182 static void _pa_stream_state_callback(pa_stream *s, void *userdata)
184 pa_threaded_mainloop *m = (pa_threaded_mainloop *)userdata;
188 switch (pa_stream_get_state(s)) {
189 case PA_STREAM_CREATING:
190 debug_log("stream(%p), state(%d)", s, pa_stream_get_state(s));
192 case PA_STREAM_READY:
193 case PA_STREAM_FAILED:
194 case PA_STREAM_TERMINATED:
195 debug_warning("stream(%p), state(%d)", s, pa_stream_get_state(s));
196 pa_threaded_mainloop_signal(m, 0);
203 static void _pa_stream_drain_complete_callback(pa_stream *s, int success, void *userdata)
205 pa_operation *o = NULL;
206 wave_info_t *h = (wave_info_t *)userdata;
208 debug_msg("stream(%p) : drain completed(%d)", s, success);
211 debug_error("stream(%p) : drain failed(%d)", s, success);
212 //pa_threaded_mainloop_signal(h->m, 0);
215 pa_stream_disconnect(h->s);
216 pa_stream_unref(h->s);
219 if (!(o = pa_context_drain(h->c, _pa_context_drain_complete_callback, h->m))) {
220 debug_error("context(%p) failed to drain context!", h->c);
221 pa_context_disconnect(h->c);
222 pa_context_unref(h->c);
225 pa_operation_unref(o);
228 debug_msg("Invoke stop callback(%p, %d) of mgr_codec", h->stop_cb, h->cb_param);
230 h->stop_cb(h->cb_param, false);
233 static void _pa_stream_moved_callback(pa_stream *s, void *userdata)
236 debug_msg("stream(%p)", s);
239 static void _pa_stream_underflow_callback(pa_stream *s, void *userdata)
241 wave_info_t *h = (wave_info_t *)userdata;
244 debug_msg("stream(%p) : file(%s)", s, h->filename);
247 static void _pa_stream_buffer_attr_callback(pa_stream *s, void *userdata)
250 debug_msg("stream(%p)", s);
253 static void _pa_stream_started_callback(pa_stream *s, void *userdata)
256 debug_msg("stream(%p)", s);
259 static void _pa_stream_write_callback(pa_stream *s, size_t length, void *userdata)
261 sf_count_t bytes = 0;
263 size_t data_length = 0;
265 pa_operation *o = NULL;
266 wave_info_t *h = (wave_info_t *)userdata;
268 if (!s || length <= 0) {
269 debug_error("stream(%p) : length(%zu)", s, length);
273 frame_size = pa_frame_size(&h->spec);
274 data_length = length;
276 if (frame_size == 0) {
277 debug_error("stream(%p) : frame size can't be 0", s);
281 if (pa_stream_begin_write(s, &data, &data_length) < 0) {
282 debug_error("stream(%p) : failed to pa_stream_begin_write()", s);
286 if ((bytes = sf_readf_short(h->sf, data, (sf_count_t)(data_length / frame_size))) > 0)
287 bytes *= (sf_count_t)frame_size;
289 debug_msg("stream(%p) : === %"PRId64" / %zu / %zu ===", s, bytes, data_length, h->written);
292 pa_stream_write(s, data, (size_t)bytes, NULL, 0, PA_SEEK_RELATIVE);
294 pa_stream_cancel_write(s);
298 /* If No more data, drain stream */
299 if (bytes < (sf_count_t)data_length) {
300 debug_msg("stream(%p) : End Of Stream", s);
303 if (_sound_is_rewind_needed(h)) {
304 debug_msg("stream(%p) : repeat count = %d", s, h->repeat_count);
305 /* do not decrease it in case of -1 for infinite play */
306 if (h->repeat_count != -1)
309 if (_sound_rewind(h) == 0)
312 debug_error("stream(%p) : REWIND failed....", s);
313 /* can't loop anymore, fallback and do drain */
316 /* EOS callback will be notified after drain is completed */
317 pa_stream_set_write_callback(s, NULL, NULL);
318 o = pa_stream_drain(s, _pa_stream_drain_complete_callback, h);
320 pa_operation_unref(o);
322 debug_error("stream(%p) : failed to drain", s);
323 debug_msg("stream(%p) : reset write callback and drain requested", s);
327 static int _pa_context_connect(wave_info_t *h)
329 pa_threaded_mainloop *m = NULL;
330 pa_context *c = NULL;
333 if (!(m = pa_threaded_mainloop_new())) {
334 debug_error("mainloop create failed");
339 if (!(c = pa_context_new(pa_threaded_mainloop_get_api(m), NULL))) {
340 debug_error("context create failed");
341 goto error_context_new;
344 pa_context_set_state_callback(c, _pa_context_state_callback, m);
346 pa_threaded_mainloop_lock(m);
348 if (pa_threaded_mainloop_start(m) < 0) {
349 debug_error("mainloop start failed");
353 if (pa_context_connect(c, NULL, 0, NULL) < 0) {
354 debug_error("context connect failed");
359 pa_context_state_t state = pa_context_get_state(c);
360 if (state == PA_CONTEXT_READY)
363 if (!PA_CONTEXT_IS_GOOD(state)) {
364 debug_error("Context error!!!! %d", pa_context_errno(c));
368 pa_threaded_mainloop_wait(m);
374 pa_threaded_mainloop_unlock(m);
380 pa_threaded_mainloop_unlock(m);
382 pa_threaded_mainloop_free(m);
387 static int _pa_stream_connect(wave_info_t *h)
391 pa_proplist *proplist = NULL;
392 pa_stream_state_t state;
394 proplist = pa_proplist_new();
397 pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, h->stream_type);
398 pa_proplist_setf(proplist, PA_PROP_APPLICATION_PROCESS_ID_ORIGIN, "%d", h->client_pid);
399 if (h->stream_index != -1)
400 pa_proplist_setf(proplist, PA_PROP_MEDIA_PARENT_ID, "%d", h->stream_index);
402 pa_threaded_mainloop_lock(h->m);
404 s = pa_stream_new_with_proplist(h->c, "wav-player", &h->spec, NULL, proplist);
405 pa_proplist_free(proplist);
407 debug_error("pa_stream_new failed. file(%s)", h->filename);
411 pa_stream_set_state_callback(s, _pa_stream_state_callback, h->m);
412 pa_stream_set_write_callback(s, _pa_stream_write_callback, h);
413 pa_stream_set_moved_callback(s, _pa_stream_moved_callback, h);
414 pa_stream_set_underflow_callback(s, _pa_stream_underflow_callback, h);
415 pa_stream_set_buffer_attr_callback(s, _pa_stream_buffer_attr_callback, h);
416 pa_stream_set_started_callback(s, _pa_stream_started_callback, h);
418 ret = pa_stream_connect_playback(s, NULL, NULL,
419 PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_START_CORKED,
422 debug_error("stream(%p) : failed to connect playback, ret(%d)", s, ret);
426 state = pa_stream_get_state(s);
428 if (state == PA_STREAM_READY)
431 if (!PA_STREAM_IS_GOOD(state)) {
432 debug_error("stream(%p) : state(%d)", s, state);
436 /* Wait until the stream is ready */
437 pa_threaded_mainloop_wait(h->m);
442 pa_threaded_mainloop_unlock(h->m);
450 pa_threaded_mainloop_unlock(h->m);
455 static void _pa_stream_uncork(wave_info_t *h)
457 pa_operation *o = NULL;
463 pa_threaded_mainloop_lock(h->m);
465 if ((o = pa_stream_cork(h->s, 0, NULL, NULL)))
466 pa_operation_unref(o);
468 debug_error("stream(%p) : uncork failed", h->s);
470 pa_threaded_mainloop_unlock(h->m);
473 static void _pa_stream_stop_disconnect(wave_info_t *h)
478 pa_threaded_mainloop_lock(h->m);
480 pa_stream_disconnect(h->s);
481 pa_stream_unref(h->s);
485 pa_context_disconnect(h->c);
486 pa_context_unref(h->c);
489 pa_threaded_mainloop_unlock(h->m);
491 pa_threaded_mainloop_free(h->m);
495 static int * _mm_sound_plug_codec_wave_get_supported_types(void)
497 static int suported[2] = { MM_SOUND_SUPPORTED_CODEC_WAVE, 0 };
501 static int _mm_sound_plug_codec_wave_parse(const char *filename, mmsound_codec_info_t *info)
506 if (!filename || !info) {
507 debug_error("filename(%p) or info(%p) is invalid...", filename, info);
508 return MM_ERROR_INVALID_ARGUMENT;
511 /* FIXME : following sndfile code should be encapsulated */
512 memset(&si, 0, sizeof(SF_INFO));
513 sf = sf_open(filename, SFM_READ, &si);
515 debug_error("sf_open error. path(%s), error(%d, %s)", filename, sf_error(sf), sf_strerror(sf));
516 if (sf_error(sf) == SF_ERR_SYSTEM)
517 return MM_ERROR_SOUND_INTERNAL;
519 return MM_ERROR_SOUND_UNSUPPORTED_MEDIA_TYPE;
522 info->codec = MM_SOUND_SUPPORTED_CODEC_WAVE;
523 info->channels = si.channels;
524 info->samplerate = si.samplerate;
526 debug_msg("filename[%s], frames[%"PRId64"], samplerate[%d], channels[%d], format[%x], sections[%d], seekable[%d]",
527 filename, si.frames, si.samplerate, si.channels, si.format, si.sections, si.seekable);
530 return MM_ERROR_NONE;
533 static int _mm_sound_plug_codec_wave_create(mmsound_codec_param_t *param, mmsound_codec_info_t *info, MMHandleType *handle)
535 wave_info_t *h = NULL;
542 h = (wave_info_t *)calloc(1, sizeof(wave_info_t));
544 debug_error("memory allocation failed");
545 return MM_ERROR_OUT_OF_MEMORY;
549 h->repeat_count = param->repeat_count;
550 h->stop_cb = param->stop_cb;
551 h->cb_param = param->param;
552 MMSOUND_STRNCPY(h->filename, param->pfilename, MM_SOUND_MAX_FILENAME);
553 h->client_pid = param->pid;
554 h->stream_index = param->stream_index;
555 MMSOUND_STRNCPY(h->stream_type, param->stream_type, MAX_STREAM_TYPE_LEN);
557 ret = _sound_prepare(h);
559 debug_error("failed to prepare sound");
563 ret = _pa_context_connect(h);
565 debug_error("failed to connect context...");
569 ret = _pa_stream_connect(h);
571 debug_error("failed to connect stream...");
575 *handle = (MMHandleType)h;
578 debug_leave("%p", h);
580 return MM_ERROR_NONE;
585 return MM_ERROR_SOUND_INTERNAL;
589 static int _mm_sound_plug_codec_wave_play(MMHandleType handle)
591 wave_info_t *h = (wave_info_t *)handle;
593 debug_msg("Start handle(%p), stream(%p), written(%zu)", h, h->s, h->written);
594 _pa_stream_uncork(h);
596 return MM_ERROR_NONE;
599 static int _mm_sound_plug_codec_wave_stop(MMHandleType handle)
601 wave_info_t *h = (wave_info_t *)handle;
603 debug_error("The handle is null");
604 return MM_ERROR_SOUND_INTERNAL;
607 debug_msg("Handle %p stop requested", h);
609 _pa_stream_stop_disconnect(h);
612 h->stop_cb(h->cb_param, true);
614 return MM_ERROR_NONE;
617 static int _mm_sound_plug_codec_wave_destroy(MMHandleType handle)
619 wave_info_t *h = (wave_info_t *)handle;
622 debug_error("Can not destroy handle :: handle is invalid");
623 return MM_ERROR_SOUND_INVALID_POINTER;
631 return MM_ERROR_NONE;
635 int MMSoundPlugCodecGetInterface(mmsound_codec_interface_t *intf)
639 intf->GetSupportTypes = _mm_sound_plug_codec_wave_get_supported_types;
640 intf->Parse = _mm_sound_plug_codec_wave_parse;
641 intf->Create = _mm_sound_plug_codec_wave_create;
642 intf->Play = _mm_sound_plug_codec_wave_play;
643 intf->Stop = _mm_sound_plug_codec_wave_stop;
644 intf->Destroy = _mm_sound_plug_codec_wave_destroy;
645 intf->SetThreadPool = NULL;
647 return MM_ERROR_NONE;
651 int MMSoundGetPluginType(void)
653 return MM_SOUND_PLUGIN_TYPE_CODEC;