#include <unistd.h>
#include <sndfile.h>
+#include <pthread.h>
#include <pulse/pulseaudio.h>
-typedef struct
-{
+typedef struct {
int handle;
int repeat_count;
- int (*stop_cb)(int);
+ int (*stop_cb)(int, bool);
char filename[MM_SOUND_MAX_FILENAME];
int cb_param;
- char stream_type[MM_SOUND_STREAM_TYPE_LEN];
+ char stream_type[MAX_STREAM_TYPE_LEN];
int stream_index;
+ int client_pid;
pa_threaded_mainloop *m;
pa_context *c;
- pa_stream* s;
+ pa_stream *s;
pa_sample_spec spec;
- SNDFILE* sf;
+ SNDFILE *sf;
SF_INFO si;
+
+ size_t written;
} wave_info_t;
static int _sound_prepare(wave_info_t *h)
return (sf_error(h->sf) == SF_ERR_SYSTEM) ? MM_ERROR_SOUND_INTERNAL : MM_ERROR_SOUND_UNSUPPORTED_MEDIA_TYPE;
}
+ sf_command(h->sf, SFC_SET_SCALE_FLOAT_INT_READ, NULL, SF_TRUE);
+
h->spec.rate = h->si.samplerate;
h->spec.channels = h->si.channels;
h->spec.format = PA_SAMPLE_S16LE;
- debug_msg("SF_INFO : frames = %lld, samplerate = %d, channels = %d, format = 0x%X, sections = %d, seekable = %d",
+ debug_msg("SF_INFO : frames = %"PRId64", samplerate = %d, channels = %d, format = 0x%X, sections = %d, seekable = %d",
h->si.frames, h->si.samplerate, h->si.channels, h->si.format, h->si.sections, h->si.seekable);
return 0;
}
/* Context Callbacks */
-static void _pa_context_state_callback(pa_context *c, void *userdata) {
+static void _pa_context_state_callback(pa_context *c, void *userdata)
+{
pa_threaded_mainloop *m = (pa_threaded_mainloop *)userdata;
assert(c);
switch (pa_context_get_state(c)) {
- case PA_CONTEXT_CONNECTING:
- case PA_CONTEXT_AUTHORIZING:
- case PA_CONTEXT_SETTING_NAME:
- debug_log("context(%p), state(%d)", c, pa_context_get_state(c));
- break;
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ debug_log("context(%p), state(%d)", c, pa_context_get_state(c));
+ break;
+
+ case PA_CONTEXT_READY:
+ case PA_CONTEXT_TERMINATED:
+ case PA_CONTEXT_FAILED:
+ debug_warning("context(%p), state(%d)", c, pa_context_get_state(c));
+ pa_threaded_mainloop_signal(m, 0);
+ break;
+
+ default:
+ break;
+ }
+}
- case PA_CONTEXT_READY:
- case PA_CONTEXT_TERMINATED:
- case PA_CONTEXT_FAILED:
- debug_warning("context(%p), state(%d)", c, pa_context_get_state(c));
- pa_threaded_mainloop_signal(m, 0);
- break;
+static void *_cleanup_thread_func(void *userdata)
+{
+ pa_threaded_mainloop *m = (pa_threaded_mainloop *)userdata;
- default:
- break;
+ if (m) {
+ debug_error("now stop and free threaded_mainloop(%p) here", m);
+ pa_threaded_mainloop_stop(m);
+ pa_threaded_mainloop_free(m);
+ } else {
+ debug_warning("thread mainloop is already null");
+ }
+
+ pthread_exit(NULL);
+}
+
+static void _cleanup_threaded_mainloop(pa_threaded_mainloop *m)
+{
+ int ret = 0;
+ pthread_t thread_id;
+ pthread_attr_t attr;
+
+ ret = pthread_attr_init(&attr);
+ if (ret != 0) {
+ debug_error("failed to init pthread attr!!! errno=%d", ret);
+ return;
+ }
+
+ ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (ret != 0) {
+ debug_error("failed to set detach state!!! errno=%d", ret);
+ goto finish;
}
+
+ ret = pthread_create(&thread_id, &attr, _cleanup_thread_func, m);
+ if (ret != 0)
+ debug_error("failed to create _cleanup_thread_func!!! errno=%d", ret);
+
+finish:
+ ret = pthread_attr_destroy(&attr);
+ if (ret != 0)
+ debug_error("failed to destroy pthread attr!!! errno=%d", ret);
+
+ return;
}
+
static void _pa_context_drain_complete_callback(pa_context *c, void *userdata)
{
+ debug_msg("context drain completed, cleanup context and mainloop");
+
pa_context_disconnect(c);
+ pa_context_unref(c);
+
+ _cleanup_threaded_mainloop((pa_threaded_mainloop *)userdata);
}
/* Stream Callbacks */
assert(s);
switch (pa_stream_get_state(s)) {
- case PA_STREAM_CREATING:
- debug_log("stream(%p), state(%d)", s, pa_stream_get_state(s));
- break;
- case PA_STREAM_READY:
- case PA_STREAM_FAILED:
- case PA_STREAM_TERMINATED:
- debug_warning("stream(%p), state(%d)", s, pa_stream_get_state(s));
- pa_threaded_mainloop_signal(m, 0);
- break;
- default:
- break;
+ case PA_STREAM_CREATING:
+ debug_log("stream(%p), state(%d)", s, pa_stream_get_state(s));
+ break;
+ case PA_STREAM_READY:
+ case PA_STREAM_FAILED:
+ case PA_STREAM_TERMINATED:
+ debug_warning("stream(%p), state(%d)", s, pa_stream_get_state(s));
+ pa_threaded_mainloop_signal(m, 0);
+ break;
+ default:
+ break;
}
}
-static void _pa_stream_drain_complete_callback(pa_stream* s, int success, void *userdata)
+static void _pa_stream_drain_complete_callback(pa_stream *s, int success, void *userdata)
{
pa_operation *o = NULL;
wave_info_t *h = (wave_info_t *)userdata;
- debug_msg("drain complete : %p, %d", s, success);
+ debug_msg("stream(%p) : drain completed(%d)", s, success);
if (!success) {
- debug_error("drain failed. s(%p), success(%d)", s, success);
+ debug_error("stream(%p) : drain failed(%d)", s, success);
//pa_threaded_mainloop_signal(h->m, 0);
}
pa_stream_unref(h->s);
h->s = NULL;
- if (!(o = pa_context_drain(h->c, _pa_context_drain_complete_callback, h)))
+ if (!(o = pa_context_drain(h->c, _pa_context_drain_complete_callback, h->m))) {
+ debug_error("context(%p) failed to drain context!", h->c);
pa_context_disconnect(h->c);
- else
+ pa_context_unref(h->c);
+ h->c = NULL;
+ } else {
pa_operation_unref(o);
+ }
- debug_msg("Call stop callback(%p, %p) of mgr_codec", h->stop_cb, h->cb_param);
+ debug_msg("Invoke stop callback(%p, %d) of mgr_codec", h->stop_cb, h->cb_param);
if (h->stop_cb)
- h->stop_cb(h->cb_param);
+ h->stop_cb(h->cb_param, false);
}
static void _pa_stream_moved_callback(pa_stream *s, void *userdata)
{
assert(s);
- debug_msg("stream moved callback : %p", s);
+ debug_msg("stream(%p)", s);
}
static void _pa_stream_underflow_callback(pa_stream *s, void *userdata)
wave_info_t *h = (wave_info_t *)userdata;
assert(s);
- debug_msg("stream underflow callback : %p, file(%s)", s, h->filename);
+ debug_msg("stream(%p) : file(%s)", s, h->filename);
}
static void _pa_stream_buffer_attr_callback(pa_stream *s, void *userdata)
{
assert(s);
- debug_msg("stream underflow callback : %p", s);
+ debug_msg("stream(%p)", s);
+}
+
+static void _pa_stream_started_callback(pa_stream *s, void *userdata)
+{
+ assert(s);
+ debug_msg("stream(%p)", s);
}
static void _pa_stream_write_callback(pa_stream *s, size_t length, void *userdata)
wave_info_t *h = (wave_info_t *)userdata;
if (!s || length <= 0) {
- debug_error("write error. stream(%p), length(%d)", s, length);
+ debug_error("stream(%p) : length(%zu)", s, length);
return;
}
frame_size = pa_frame_size(&h->spec);
data_length = length;
+ if (frame_size == 0) {
+ debug_error("stream(%p) : frame size can't be 0", s);
+ return;
+ }
+
if (pa_stream_begin_write(s, &data, &data_length) < 0) {
- debug_error("failed to pa_stream_begin_write()");
+ debug_error("stream(%p) : failed to pa_stream_begin_write()", s);
return;
}
if ((bytes = sf_readf_short(h->sf, data, (sf_count_t)(data_length / frame_size))) > 0)
bytes *= (sf_count_t)frame_size;
- debug_log("=== %lld / %d ===",bytes, data_length);
+ debug_msg("stream(%p) : === %"PRId64" / %zu / %zu ===", s, bytes, data_length, h->written);
if (bytes > 0)
pa_stream_write(s, data, (size_t)bytes, NULL, 0, PA_SEEK_RELATIVE);
else
pa_stream_cancel_write(s);
+ h->written += bytes;
+
/* If No more data, drain stream */
if (bytes < (sf_count_t)data_length) {
- debug_msg("EOS!!!!! %lld/%d", bytes, data_length);
+ debug_msg("stream(%p) : End Of Stream", s);
/* Handle loop */
if (_sound_is_rewind_needed(h)) {
- debug_msg("repeat count = %d", h->repeat_count);
- h->repeat_count--;
+ debug_msg("stream(%p) : repeat count = %d", s, h->repeat_count);
+ /* do not decrease it in case of -1 for infinite play */
+ if (h->repeat_count != -1)
+ h->repeat_count--;
if (_sound_rewind(h) == 0)
return;
- debug_error("REWIND failed....");
+ debug_error("stream(%p) : REWIND failed....", s);
/* can't loop anymore, fallback and do drain */
}
o = pa_stream_drain(s, _pa_stream_drain_complete_callback, h);
if (o)
pa_operation_unref(o);
+ else
+ debug_error("stream(%p) : failed to drain", s);
+ debug_msg("stream(%p) : reset write callback and drain requested", s);
}
}
/* Mainloop */
if (!(m = pa_threaded_mainloop_new())) {
debug_error("mainloop create failed");
- goto error;
+ return -1;
}
/* Context */
if (!(c = pa_context_new(pa_threaded_mainloop_get_api(m), NULL))) {
debug_error("context create failed");
- goto error;
+ goto error_context_new;
}
pa_context_set_state_callback(c, _pa_context_state_callback, m);
return 0;
error:
- if (c)
- pa_context_unref(c);
-
+ pa_context_unref(c);
pa_threaded_mainloop_unlock(m);
-
- if (m)
- pa_threaded_mainloop_free(m);
+error_context_new:
+ pa_threaded_mainloop_free(m);
return -1;
}
static int _pa_stream_connect(wave_info_t *h)
{
+ int ret = PA_OK;
pa_stream *s = NULL;
pa_proplist *proplist = NULL;
pa_stream_state_t state;
proplist = pa_proplist_new();
if (!proplist)
return -1;
- if (h->stream_type)
- pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, h->stream_type);
+ pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, h->stream_type);
+ pa_proplist_setf(proplist, PA_PROP_APPLICATION_PROCESS_ID_ORIGIN, "%d", h->client_pid);
if (h->stream_index != -1)
pa_proplist_setf(proplist, PA_PROP_MEDIA_PARENT_ID, "%d", h->stream_index);
pa_stream_set_moved_callback(s, _pa_stream_moved_callback, h);
pa_stream_set_underflow_callback(s, _pa_stream_underflow_callback, h);
pa_stream_set_buffer_attr_callback(s, _pa_stream_buffer_attr_callback, h);
+ pa_stream_set_started_callback(s, _pa_stream_started_callback, h);
- pa_stream_connect_playback(s, NULL, NULL,
- PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE,
- NULL, NULL);
+ ret = pa_stream_connect_playback(s, NULL, NULL,
+ PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_START_CORKED,
+ NULL, NULL);
+ if (ret < 0) {
+ debug_error("stream(%p) : failed to connect playback, ret(%d)", s, ret);
+ goto error;
+ }
for (;;) {
state = pa_stream_get_state(s);
break;
if (!PA_STREAM_IS_GOOD(state)) {
- debug_error("Stream error!!!! %d", state);
+ debug_error("stream(%p) : state(%d)", s, state);
goto error;
}
if ((o = pa_stream_cork(h->s, 0, NULL, NULL)))
pa_operation_unref(o);
else
- debug_error("stream uncork failed");
+ debug_error("stream(%p) : uncork failed", h->s);
pa_threaded_mainloop_unlock(h->m);
}
-static int _pa_stream_stop_disconnect(wave_info_t *h)
+static void _pa_stream_stop_disconnect(wave_info_t *h)
{
assert(h);
assert(h->m);
h->s = NULL;
}
if (h->c) {
+ pa_context_disconnect(h->c);
pa_context_unref(h->c);
h->c = NULL;
}
pa_threaded_mainloop_free(h->m);
h->m = NULL;
-
- return 0;
}
-static int* _mm_sound_plug_codec_wave_get_supported_types(void)
+static int * _mm_sound_plug_codec_wave_get_supported_types(void)
{
static int suported[2] = { MM_SOUND_SUPPORTED_CODEC_WAVE, 0 };
return suported;
static int _mm_sound_plug_codec_wave_parse(const char *filename, mmsound_codec_info_t *info)
{
- SNDFILE* sf = NULL;
+ SNDFILE *sf = NULL;
SF_INFO si;
if (!filename || !info) {
info->channels = si.channels;
info->samplerate = si.samplerate;
- debug_msg("filename = %s, frames[%lld], samplerate[%d], channels[%d], format[%x], sections[%d], seekable[%d]",
+ debug_msg("filename[%s], frames[%"PRId64"], samplerate[%d], channels[%d], format[%x], sections[%d], seekable[%d]",
filename, si.frames, si.samplerate, si.channels, si.format, si.sections, si.seekable);
sf_close(sf);
static int _mm_sound_plug_codec_wave_create(mmsound_codec_param_t *param, mmsound_codec_info_t *info, MMHandleType *handle)
{
- wave_info_t* p = NULL;
+ wave_info_t *h = NULL;
int ret = 0;
#ifdef DEBUG_DETAIL
debug_enter();
#endif
- p = (wave_info_t *) malloc(sizeof(wave_info_t));
- if (p == NULL) {
+ h = (wave_info_t *)calloc(1, sizeof(wave_info_t));
+ if (h == NULL) {
debug_error("memory allocation failed");
return MM_ERROR_OUT_OF_MEMORY;
}
- memset(p, 0, sizeof(wave_info_t));
- p->handle = 0;
- p->repeat_count = param ->repeat_count;
- p->stop_cb = param->stop_cb;
- p->cb_param = param->param;
- MMSOUND_STRNCPY(p->filename, param->pfilename, MM_SOUND_MAX_FILENAME);
- p->stream_index = param->stream_index;
- MMSOUND_STRNCPY(p->stream_type, param->stream_type, MM_SOUND_STREAM_TYPE_LEN);
+ h->handle = 0;
+ h->repeat_count = param->repeat_count;
+ h->stop_cb = param->stop_cb;
+ h->cb_param = param->param;
+ MMSOUND_STRNCPY(h->filename, param->pfilename, MM_SOUND_MAX_FILENAME);
+ h->client_pid = param->pid;
+ h->stream_index = param->stream_index;
+ MMSOUND_STRNCPY(h->stream_type, param->stream_type, MAX_STREAM_TYPE_LEN);
- ret = _sound_prepare(p);
+ ret = _sound_prepare(h);
if (ret < 0) {
debug_error("failed to prepare sound");
goto error;
}
- ret = _pa_context_connect(p);
+ ret = _pa_context_connect(h);
if (ret < 0) {
debug_error("failed to connect context...");
goto error;
}
- ret = _pa_stream_connect(p);
+ ret = _pa_stream_connect(h);
if (ret < 0) {
debug_error("failed to connect stream...");
goto error;
}
- *handle = (MMHandleType)p;
+ *handle = (MMHandleType)h;
#ifdef DEBUG_DETAIL
- debug_leave("%p", p);
+ debug_leave("%p", h);
#endif
return MM_ERROR_NONE;
error:
- free(p);
+ _sound_unprepare(h);
+ free(h);
return MM_ERROR_SOUND_INTERNAL;
}
static int _mm_sound_plug_codec_wave_play(MMHandleType handle)
{
- wave_info_t *p = (wave_info_t *) handle;
+ wave_info_t *h = (wave_info_t *)handle;
- debug_msg("Start handle %p", p);
- _pa_stream_uncork(p);
+ debug_msg("Start handle(%p), stream(%p), written(%zu)", h, h->s, h->written);
+ _pa_stream_uncork(h);
return MM_ERROR_NONE;
}
static int _mm_sound_plug_codec_wave_stop(MMHandleType handle)
{
- int ret = 0;
- wave_info_t *p = (wave_info_t*) handle;
-
- if (!p) {
+ wave_info_t *h = (wave_info_t *)handle;
+ if (!h) {
debug_error("The handle is null");
return MM_ERROR_SOUND_INTERNAL;
}
- debug_msg("Handle %p stop requested", p);
+ debug_msg("Handle %p stop requested", h);
- ret = _pa_stream_stop_disconnect(p);
+ _pa_stream_stop_disconnect(h);
- if (p->stop_cb)
- p->stop_cb(p->cb_param);
+ if (h->stop_cb)
+ h->stop_cb(h->cb_param, true);
- return (ret == 0) ? MM_ERROR_NONE : MM_ERROR_SOUND_INTERNAL;
+ return MM_ERROR_NONE;
}
static int _mm_sound_plug_codec_wave_destroy(MMHandleType handle)
{
- wave_info_t *p = (wave_info_t *)handle;
+ wave_info_t *h = (wave_info_t *)handle;
- if (!p) {
+ if (!h) {
debug_error("Can not destroy handle :: handle is invalid");
return MM_ERROR_SOUND_INVALID_POINTER;
}
- _sound_unprepare(p);
+ _sound_unprepare(h);
- free(p);
- p = NULL;
+ free(h);
+ h = NULL;
return MM_ERROR_NONE;
}