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