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