Correct signature of internal function that does not require return value
[platform/core/multimedia/libmm-sound.git] / server / plugin / wav / mm_sound_plugin_codec_wave.c
1 /*
2  * libmm-sound
3  *
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Seungbae Shin <seungbae.shin@samsung.com>
7  *
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
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
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.
19  *
20  */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdint.h>
26
27
28 #include <mm_error.h>
29 #include <mm_debug.h>
30 #include <mm_sound_pa_client.h>
31
32 #include "../../include/mm_sound_plugin_codec.h"
33 #include "../../../include/mm_sound_common.h"
34 #include <unistd.h>
35
36 #include <sndfile.h>
37 #include <pthread.h>
38 #include <pulse/pulseaudio.h>
39
40 typedef struct {
41         int handle;
42         int repeat_count;
43         int (*stop_cb)(int);
44         char filename[MM_SOUND_MAX_FILENAME];
45         int cb_param;
46         char stream_type[MAX_STREAM_TYPE_LEN];
47         int stream_index;
48
49         pa_threaded_mainloop *m;
50         pa_context *c;
51         pa_stream* s;
52         pa_sample_spec spec;
53         SNDFILE* sf;
54         SF_INFO si;
55 } wave_info_t;
56
57 static int _sound_prepare(wave_info_t *h)
58 {
59         memset(&h->si, 0, sizeof(SF_INFO));
60
61         h->sf = sf_open(h->filename, SFM_READ, &h->si);
62         if (!h->sf) {
63                 debug_error("sf_open error. path(%s), error(%d,%s)", h->filename, sf_error(h->sf), sf_strerror(h->sf));
64                 return (sf_error(h->sf) == SF_ERR_SYSTEM) ? MM_ERROR_SOUND_INTERNAL : MM_ERROR_SOUND_UNSUPPORTED_MEDIA_TYPE;
65         }
66
67         sf_command(h->sf, SFC_SET_SCALE_FLOAT_INT_READ, NULL, SF_TRUE);
68
69         h->spec.rate = h->si.samplerate;
70         h->spec.channels = h->si.channels;
71         h->spec.format = PA_SAMPLE_S16LE;
72
73         debug_msg("SF_INFO : frames = %lld, samplerate = %d, channels = %d, format = 0x%X, sections = %d, seekable = %d",
74                         h->si.frames, h->si.samplerate, h->si.channels, h->si.format, h->si.sections, h->si.seekable);
75
76         return 0;
77 }
78
79 static int _sound_rewind(wave_info_t *h)
80 {
81         return (sf_seek(h->sf, 0, SEEK_SET) != -1) ? 0 : -1;
82 }
83
84 static int _sound_is_rewind_needed(wave_info_t *h)
85 {
86         return (h->repeat_count == -1 || h->repeat_count > 1);
87 }
88
89 static void _sound_unprepare(wave_info_t *h)
90 {
91         if (h->sf) {
92                 sf_close(h->sf);
93                 h->sf = NULL;
94         }
95 }
96
97 /* Context Callbacks */
98 static void _pa_context_state_callback(pa_context *c, void *userdata)
99 {
100         pa_threaded_mainloop *m = (pa_threaded_mainloop *)userdata;
101         assert(c);
102
103         switch (pa_context_get_state(c)) {
104         case PA_CONTEXT_CONNECTING:
105         case PA_CONTEXT_AUTHORIZING:
106         case PA_CONTEXT_SETTING_NAME:
107                 debug_log("context(%p), state(%d)", c, pa_context_get_state(c));
108                 break;
109
110         case PA_CONTEXT_READY:
111         case PA_CONTEXT_TERMINATED:
112         case PA_CONTEXT_FAILED:
113                 debug_warning("context(%p), state(%d)", c, pa_context_get_state(c));
114                 pa_threaded_mainloop_signal(m, 0);
115                 break;
116
117         default:
118                 break;
119         }
120 }
121
122 static void *_cleanup_thread_func(void *userdata)
123 {
124         pa_threaded_mainloop *m = (pa_threaded_mainloop *)userdata;
125
126         if (m) {
127                 debug_error("now stop and free threaded_mainloop(%p) here", m);
128                 pa_threaded_mainloop_stop(m);
129                 pa_threaded_mainloop_free(m);
130         } else {
131                 debug_warning("thread mainloop is already null");
132         }
133
134         pthread_exit(NULL);
135 }
136
137 static void _cleanup_threaded_mainloop(pa_threaded_mainloop *m)
138 {
139         int ret = 0;
140         pthread_t thread_id;
141         pthread_attr_t attr;
142
143         ret = pthread_attr_init(&attr);
144         if (ret != 0) {
145                 debug_error("failed to init pthread attr!!! errno=%d", ret);
146                 return;
147         }
148
149         ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
150         if (ret != 0) {
151                 debug_error("failed to set detach state!!! errno=%d", ret);
152                 goto finish;
153         }
154
155         ret = pthread_create(&thread_id, &attr, _cleanup_thread_func, m);
156         if (ret != 0)
157                 debug_error("failed to create _cleanup_thread_func!!! errno=%d", ret);
158
159 finish:
160         ret = pthread_attr_destroy(&attr);
161         if (ret != 0)
162                 debug_error("failed to destroy pthread attr!!! errno=%d", ret);
163
164         return;
165 }
166
167
168 static void _pa_context_drain_complete_callback(pa_context *c, void *userdata)
169 {
170         debug_msg("context drain completed, cleanup context and mainloop");
171
172         pa_context_disconnect(c);
173         pa_context_unref(c);
174
175         _cleanup_threaded_mainloop((pa_threaded_mainloop *)userdata);
176 }
177
178 /* Stream Callbacks */
179 static void _pa_stream_state_callback(pa_stream *s, void *userdata)
180 {
181         pa_threaded_mainloop *m = (pa_threaded_mainloop *)userdata;
182
183         assert(s);
184
185         switch (pa_stream_get_state(s)) {
186         case PA_STREAM_CREATING:
187                 debug_log("stream(%p), state(%d)", s, pa_stream_get_state(s));
188                 break;
189         case PA_STREAM_READY:
190         case PA_STREAM_FAILED:
191         case PA_STREAM_TERMINATED:
192                 debug_warning("stream(%p), state(%d)", s, pa_stream_get_state(s));
193                 pa_threaded_mainloop_signal(m, 0);
194                 break;
195         default:
196                 break;
197         }
198 }
199
200 static void _pa_stream_drain_complete_callback(pa_stream* s, int success, void *userdata)
201 {
202         pa_operation *o = NULL;
203         wave_info_t *h = (wave_info_t *)userdata;
204
205         debug_msg("drain complete : %p, %d", s, success);
206
207         if (!success) {
208                 debug_error("drain failed. s(%p), success(%d)", s, success);
209                 //pa_threaded_mainloop_signal(h->m, 0);
210         }
211
212         pa_stream_disconnect(h->s);
213         pa_stream_unref(h->s);
214         h->s = NULL;
215
216         if (!(o = pa_context_drain(h->c, _pa_context_drain_complete_callback, h->m))) {
217                 debug_error("failed to drain context!");
218                 pa_context_disconnect(h->c);
219                 pa_context_unref(h->c);
220                 h->c = NULL;
221         } else {
222                 pa_operation_unref(o);
223         }
224
225         debug_msg("Call stop callback(%p, %d) of mgr_codec", h->stop_cb, h->cb_param);
226         if (h->stop_cb)
227                 h->stop_cb(h->cb_param);
228 }
229
230 static void _pa_stream_moved_callback(pa_stream *s, void *userdata)
231 {
232         assert(s);
233         debug_msg("stream moved callback : %p", s);
234 }
235
236 static void _pa_stream_underflow_callback(pa_stream *s, void *userdata)
237 {
238         wave_info_t *h = (wave_info_t *)userdata;
239         assert(s);
240
241         debug_msg("stream underflow callback : %p, file(%s)", s, h->filename);
242 }
243
244 static void _pa_stream_buffer_attr_callback(pa_stream *s, void *userdata)
245 {
246         assert(s);
247         debug_msg("stream underflow callback : %p", s);
248 }
249
250 static void _pa_stream_write_callback(pa_stream *s, size_t length, void *userdata)
251 {
252         sf_count_t bytes = 0;
253         void *data = NULL;
254         size_t data_length = 0;
255         size_t frame_size;
256         pa_operation *o = NULL;
257         wave_info_t *h = (wave_info_t *)userdata;
258
259         if (!s || length <= 0) {
260                 debug_error("write error. stream(%p), length(%d)", s, length);
261                 return;
262         }
263
264         frame_size = pa_frame_size(&h->spec);
265         data_length = length;
266
267         if (frame_size == 0) {
268                 debug_error("frame size can't be 0");
269                 return;
270         }
271
272         if (pa_stream_begin_write(s, &data, &data_length) < 0) {
273                 debug_error("failed to pa_stream_begin_write()");
274                 return;
275         }
276
277         if ((bytes = sf_readf_short(h->sf, data, (sf_count_t)(data_length / frame_size))) > 0)
278                 bytes *= (sf_count_t)frame_size;
279
280         debug_log("=== %lld / %d ===", bytes, data_length);
281
282         if (bytes > 0)
283                 pa_stream_write(s, data, (size_t)bytes, NULL, 0, PA_SEEK_RELATIVE);
284         else
285                 pa_stream_cancel_write(s);
286
287         /* If No more data, drain stream */
288         if (bytes < (sf_count_t)data_length) {
289                 debug_msg("EOS!!!!! %lld/%d", bytes, data_length);
290
291                 /* Handle loop */
292                 if (_sound_is_rewind_needed(h)) {
293                         debug_msg("repeat count = %d", h->repeat_count);
294                         /* do not decrease it in case of -1 for infinite play */
295                         if (h->repeat_count != -1)
296                                 h->repeat_count--;
297
298                         if (_sound_rewind(h) == 0)
299                                 return;
300
301                         debug_error("REWIND failed....");
302                         /* can't loop anymore, fallback and do drain */
303                 }
304
305                 /* EOS callback will be notified after drain is completed */
306                 pa_stream_set_write_callback(s, NULL, NULL);
307                 o = pa_stream_drain(s, _pa_stream_drain_complete_callback, h);
308                 if (o)
309                         pa_operation_unref(o);
310                 else
311                         debug_error("failed to drain stream %p", s);
312         }
313 }
314
315 static int _pa_context_connect(wave_info_t *h)
316 {
317         pa_threaded_mainloop *m = NULL;
318         pa_context *c = NULL;
319
320         /* Mainloop */
321         if (!(m = pa_threaded_mainloop_new())) {
322                 debug_error("mainloop create failed");
323                 return -1;
324         }
325
326         /* Context */
327         if (!(c = pa_context_new(pa_threaded_mainloop_get_api(m), NULL))) {
328                 debug_error("context create failed");
329                 goto error_context_new;
330         }
331
332         pa_context_set_state_callback(c, _pa_context_state_callback, m);
333
334         pa_threaded_mainloop_lock(m);
335
336         if (pa_threaded_mainloop_start(m) < 0) {
337                 debug_error("mainloop start failed");
338                 goto error;
339         }
340
341         if (pa_context_connect(c, NULL, 0, NULL) < 0) {
342                 debug_error("context connect failed");
343                 goto error;
344         }
345
346         for (;;) {
347                 pa_context_state_t state = pa_context_get_state(c);
348                 if (state == PA_CONTEXT_READY)
349                         break;
350
351                 if (!PA_CONTEXT_IS_GOOD(state)) {
352                         debug_error("Context error!!!! %d", pa_context_errno(c));
353                         goto error;
354                 }
355
356                 pa_threaded_mainloop_wait(m);
357         }
358
359         h->m = m;
360         h->c = c;
361
362         pa_threaded_mainloop_unlock(m);
363
364         return 0;
365
366 error:
367         pa_context_unref(c);
368         pa_threaded_mainloop_unlock(m);
369 error_context_new:
370         pa_threaded_mainloop_free(m);
371
372         return -1;
373 }
374
375 static int _pa_stream_connect(wave_info_t *h)
376 {
377         pa_stream *s = NULL;
378         pa_proplist *proplist = NULL;
379         pa_stream_state_t state;
380
381         proplist = pa_proplist_new();
382         if (!proplist)
383                 return -1;
384         pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, h->stream_type);
385         if (h->stream_index != -1)
386                 pa_proplist_setf(proplist, PA_PROP_MEDIA_PARENT_ID, "%d", h->stream_index);
387
388         pa_threaded_mainloop_lock(h->m);
389
390         s = pa_stream_new_with_proplist(h->c, "wav-player", &h->spec, NULL, proplist);
391         pa_proplist_free(proplist);
392         if (!s) {
393                 debug_error("pa_stream_new failed. file(%s)", h->filename);
394                 goto error;
395         }
396
397         pa_stream_set_state_callback(s, _pa_stream_state_callback, h->m);
398         pa_stream_set_write_callback(s, _pa_stream_write_callback, h);
399         pa_stream_set_moved_callback(s, _pa_stream_moved_callback, h);
400         pa_stream_set_underflow_callback(s, _pa_stream_underflow_callback, h);
401         pa_stream_set_buffer_attr_callback(s, _pa_stream_buffer_attr_callback, h);
402
403         pa_stream_connect_playback(s, NULL, NULL,
404                                                         PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE,
405                                                         NULL, NULL);
406         for (;;) {
407                 state = pa_stream_get_state(s);
408
409                 if (state == PA_STREAM_READY)
410                         break;
411
412                 if (!PA_STREAM_IS_GOOD(state)) {
413                         debug_error("Stream error!!!! %d", state);
414                         goto error;
415                 }
416
417                 /* Wait until the stream is ready */
418                 pa_threaded_mainloop_wait(h->m);
419         }
420
421         h->s = s;
422
423         pa_threaded_mainloop_unlock(h->m);
424
425         return 0;
426
427 error:
428         if (s)
429                 pa_stream_unref(s);
430
431         pa_threaded_mainloop_unlock(h->m);
432
433         return -1;
434 }
435
436 static void _pa_stream_uncork(wave_info_t *h)
437 {
438         pa_operation *o = NULL;
439
440         assert(h);
441         assert(h->m);
442         assert(h->s);
443
444         pa_threaded_mainloop_lock(h->m);
445
446         if ((o = pa_stream_cork(h->s, 0, NULL, NULL)))
447                 pa_operation_unref(o);
448         else
449                 debug_error("stream uncork failed");
450
451         pa_threaded_mainloop_unlock(h->m);
452 }
453
454 static void _pa_stream_stop_disconnect(wave_info_t *h)
455 {
456         assert(h);
457         assert(h->m);
458
459         pa_threaded_mainloop_lock(h->m);
460         if (h->s) {
461                 pa_stream_disconnect(h->s);
462                 pa_stream_unref(h->s);
463                 h->s = NULL;
464         }
465         if (h->c) {
466                 pa_context_disconnect(h->c);
467                 pa_context_unref(h->c);
468                 h->c = NULL;
469         }
470         pa_threaded_mainloop_unlock(h->m);
471
472         pa_threaded_mainloop_free(h->m);
473         h->m = NULL;
474 }
475
476 static int* _mm_sound_plug_codec_wave_get_supported_types(void)
477 {
478         static int suported[2] = { MM_SOUND_SUPPORTED_CODEC_WAVE, 0 };
479         return suported;
480 }
481
482 static int _mm_sound_plug_codec_wave_parse(const char *filename, mmsound_codec_info_t *info)
483 {
484         SNDFILE* sf = NULL;
485         SF_INFO si;
486
487         if (!filename || !info) {
488                 debug_error("filename(%p) or info(%p) is invalid...", filename, info);
489                 return MM_ERROR_INVALID_ARGUMENT;
490         }
491
492         /* FIXME : following sndfile code should be encapsulated */
493         memset(&si, 0, sizeof(SF_INFO));
494         sf = sf_open(filename, SFM_READ, &si);
495         if (!sf) {
496                 debug_error("sf_open error. path(%s), error(%d, %s)", filename, sf_error(sf), sf_strerror(sf));
497                 if (sf_error(sf) == SF_ERR_SYSTEM)
498                         return MM_ERROR_SOUND_INTERNAL;
499                 else
500                         return MM_ERROR_SOUND_UNSUPPORTED_MEDIA_TYPE;
501         }
502
503         info->codec = MM_SOUND_SUPPORTED_CODEC_WAVE;
504         info->channels = si.channels;
505         info->samplerate = si.samplerate;
506
507         debug_msg("filename = %s, frames[%lld], samplerate[%d], channels[%d], format[%x], sections[%d], seekable[%d]",
508                         filename, si.frames, si.samplerate, si.channels, si.format, si.sections, si.seekable);
509         sf_close(sf);
510
511         return MM_ERROR_NONE;
512 }
513
514 static int _mm_sound_plug_codec_wave_create(mmsound_codec_param_t *param, mmsound_codec_info_t *info, MMHandleType *handle)
515 {
516         wave_info_t* p = NULL;
517         int ret = 0;
518
519 #ifdef DEBUG_DETAIL
520         debug_enter();
521 #endif
522
523         p = (wave_info_t *) malloc(sizeof(wave_info_t));
524         if (p == NULL) {
525                 debug_error("memory allocation failed");
526                 return MM_ERROR_OUT_OF_MEMORY;
527         }
528
529         memset(p, 0, sizeof(wave_info_t));
530         p->handle = 0;
531         p->repeat_count = param->repeat_count;
532         p->stop_cb = param->stop_cb;
533         p->cb_param = param->param;
534         MMSOUND_STRNCPY(p->filename, param->pfilename, MM_SOUND_MAX_FILENAME);
535         p->stream_index = param->stream_index;
536         MMSOUND_STRNCPY(p->stream_type, param->stream_type, MAX_STREAM_TYPE_LEN);
537
538         ret = _sound_prepare(p);
539         if (ret < 0) {
540                 debug_error("failed to prepare sound");
541                 goto error;
542         }
543
544         ret = _pa_context_connect(p);
545         if (ret < 0) {
546                 debug_error("failed to connect context...");
547                 goto error;
548         }
549
550         ret = _pa_stream_connect(p);
551         if (ret < 0) {
552                 debug_error("failed to connect stream...");
553                 goto error;
554         }
555
556         *handle = (MMHandleType)p;
557
558 #ifdef DEBUG_DETAIL
559         debug_leave("%p", p);
560 #endif
561         return MM_ERROR_NONE;
562
563 error:
564         _sound_unprepare(p);
565         free(p);
566         return MM_ERROR_SOUND_INTERNAL;
567 }
568
569
570 static int _mm_sound_plug_codec_wave_play(MMHandleType handle)
571 {
572         wave_info_t *p = (wave_info_t *) handle;
573
574         debug_msg("Start handle %p", p);
575         _pa_stream_uncork(p);
576
577         return MM_ERROR_NONE;
578 }
579
580 static int _mm_sound_plug_codec_wave_stop(MMHandleType handle)
581 {
582         wave_info_t *p = (wave_info_t*) handle;
583
584         if (!p) {
585                 debug_error("The handle is null");
586                 return MM_ERROR_SOUND_INTERNAL;
587         }
588
589         debug_msg("Handle %p stop requested", p);
590
591         _pa_stream_stop_disconnect(p);
592
593         if (p->stop_cb)
594                 p->stop_cb(p->cb_param);
595
596         return MM_ERROR_NONE;
597 }
598
599 static int _mm_sound_plug_codec_wave_destroy(MMHandleType handle)
600 {
601         wave_info_t *p = (wave_info_t *)handle;
602
603         if (!p) {
604                 debug_error("Can not destroy handle :: handle is invalid");
605                 return MM_ERROR_SOUND_INVALID_POINTER;
606         }
607
608         _sound_unprepare(p);
609
610         free(p);
611         p = NULL;
612
613         return MM_ERROR_NONE;
614 }
615
616 EXPORT_API
617 int MMSoundPlugCodecGetInterface(mmsound_codec_interface_t *intf)
618 {
619         assert(intf);
620
621         intf->GetSupportTypes   = _mm_sound_plug_codec_wave_get_supported_types;
622         intf->Parse             = _mm_sound_plug_codec_wave_parse;
623         intf->Create            = _mm_sound_plug_codec_wave_create;
624         intf->Play              = _mm_sound_plug_codec_wave_play;
625         intf->Stop              = _mm_sound_plug_codec_wave_stop;
626         intf->Destroy           = _mm_sound_plug_codec_wave_destroy;
627         intf->SetThreadPool     = NULL;
628
629         return MM_ERROR_NONE;
630 }
631
632 EXPORT_API
633 int MMSoundGetPluginType(void)
634 {
635         return MM_SOUND_PLUGIN_TYPE_CODEC;
636 }
637
638