Fix double unlock of slot mutex in user stops scneario
[platform/core/multimedia/libmm-sound.git] / server / plugin / wav / mm_sound_plugin_codec_wave.c
index 2b67cef..65ea459 100644 (file)
 #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_streams;
+       pa_stream *s;
        pa_sample_spec spec;
-       SNDFILEsf;
+       SNDFILE *sf;
        SF_INFO si;
+
+       size_t written;
 } wave_info_t;
 
 static int _sound_prepare(wave_info_t *h)
@@ -64,11 +67,13 @@ 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;
@@ -93,32 +98,84 @@ static void _sound_unprepare(wave_info_t *h)
 }
 
 /* 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 */
@@ -129,29 +186,29 @@ static void _pa_stream_state_callback(pa_stream *s, void *userdata)
        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_streams, 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);
        }
 
@@ -159,20 +216,24 @@ static void _pa_stream_drain_complete_callback(pa_stream* s, int success, void *
        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)
@@ -180,13 +241,19 @@ 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)
@@ -199,41 +266,50 @@ static void _pa_stream_write_callback(pa_stream *s, size_t length, void *userdat
        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 */
                }
 
@@ -242,6 +318,9 @@ static void _pa_stream_write_callback(pa_stream *s, size_t length, void *userdat
                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);
        }
 }
 
@@ -253,13 +332,13 @@ static int _pa_context_connect(wave_info_t *h)
        /* 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);
@@ -297,19 +376,17 @@ static int _pa_context_connect(wave_info_t *h)
        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;
@@ -317,8 +394,8 @@ static int _pa_stream_connect(wave_info_t *h)
        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);
 
@@ -336,10 +413,15 @@ static int _pa_stream_connect(wave_info_t *h)
        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);
 
@@ -347,7 +429,7 @@ static int _pa_stream_connect(wave_info_t *h)
                        break;
 
                if (!PA_STREAM_IS_GOOD(state)) {
-                       debug_error("Stream error!!!! %d", state);
+                       debug_error("stream(%p) : state(%d)", s, state);
                        goto error;
                }
 
@@ -383,12 +465,12 @@ static void _pa_stream_uncork(wave_info_t *h)
        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);
@@ -400,6 +482,7 @@ static int _pa_stream_stop_disconnect(wave_info_t *h)
                h->s = NULL;
        }
        if (h->c) {
+               pa_context_disconnect(h->c);
                pa_context_unref(h->c);
                h->c = NULL;
        }
@@ -407,11 +490,9 @@ static int _pa_stream_stop_disconnect(wave_info_t *h)
 
        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;
@@ -419,7 +500,7 @@ static int* _mm_sound_plug_codec_wave_get_supported_types(void)
 
 static int _mm_sound_plug_codec_wave_parse(const char *filename, mmsound_codec_info_t *info)
 {
-       SNDFILEsf = NULL;
+       SNDFILE *sf = NULL;
        SF_INFO si;
 
        if (!filename || !info) {
@@ -442,7 +523,7 @@ static int _mm_sound_plug_codec_wave_parse(const char *filename, mmsound_codec_i
        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);
 
@@ -451,102 +532,101 @@ static int _mm_sound_plug_codec_wave_parse(const char *filename, mmsound_codec_i
 
 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;
 }