9b85863e680e529f405daf57f02d2541db09845f
[platform/core/multimedia/libmm-sound.git] / server / mm_sound_mgr_codec.c
1
2 /*
3  * libmm-sound
4  *
5  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
6  *
7  * Contact: Seungbae Shin <seungbae.shin@samsung.com>
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  */
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <pthread.h>
27 #include <unistd.h>
28
29 #include <mm_error.h>
30 #include <mm_types.h>
31 #include <mm_debug.h>
32
33 #include <glib.h>
34
35 #include "include/mm_sound_mgr_codec.h"
36 #include "include/mm_sound_mgr_ipc.h"
37 #include "include/mm_sound_plugin_codec.h"
38 #include "include/mm_sound_thread_pool.h"
39
40 #include "../include/mm_sound_common.h"
41 #include "../include/mm_sound.h"
42
43 #define SHUTDOWN_TIMEOUT_SEC 60
44 #define STATUS_IDLE 0
45 #define STATUS_SOUND 3
46 #define SOUND_SLOT_START 0
47 #define MANAGER_HANDLE_MAX 256
48
49 typedef struct {
50         int (*callback)(int, void *, void *, int);      /* msg_type(pid) client callback & client data info */
51         void *param;
52         int pid;
53         void *msgcallback;
54         void *msgdata;
55         MMHandleType plughandle;
56
57         int pluginid;
58         int status;
59 } __mmsound_mgr_codec_handle_t;
60
61 static MMSoundPluginType *g_codec_plugins = NULL;
62 static __mmsound_mgr_codec_handle_t g_slots[MANAGER_HANDLE_MAX];
63 static mmsound_codec_interface_t g_plugins[MM_SOUND_SUPPORTED_CODEC_NUM];
64 static pthread_mutex_t g_slot_mutex;
65 static GSourceFunc g_shutdown_cb;
66 static guint g_timer_id = 0;
67
68 #ifdef DEBUG_DETAIL
69 #define SLOT_LOCK() do { \
70         debug_msg("Before Slot_mutex LOCK"); \
71         pthread_mutex_lock(&g_slot_mutex); \
72         debug_msg("After Slot_mutex LOCK"); \
73 } while (0)
74 #define SLOT_UNLOCK() do { \
75         debug_msg("Before Slot_mutex UNLOCK"); \
76         pthread_mutex_unlock(&g_slot_mutex); \
77         debug_msg("After Slot_mutex UNLOCK"); \
78 } while (0)
79 #else
80 #define SLOT_LOCK() do { pthread_mutex_lock(&g_slot_mutex); } while (0)
81 #define SLOT_UNLOCK() do { pthread_mutex_unlock(&g_slot_mutex); } while (0)
82 #endif
83
84 static int _MMSoundMgrCodecRegisterInterface(MMSoundPluginType *plugin);
85 static int _MMSoundMgrCodecStopCallback(int slotid, bool stop_by_user);
86 static gboolean _mm_sound_mgr_codec_slot_is_empty();
87
88 static gboolean _idle_cb(gpointer user_data)
89 {
90         if (_mm_sound_mgr_codec_slot_is_empty()) {
91                 debug_msg("slot is empty, ready to shutdown! %p", g_shutdown_cb);
92                 if (g_shutdown_cb)
93                         g_shutdown_cb(NULL);
94         } else {
95                 debug_warning("slot is not empty!!! no shutdown...");
96         }
97
98         return FALSE;
99 }
100
101 static gboolean _timeout_cb(gpointer user_data)
102 {
103         debug_warning("TIMEOUT : add idle callback now...");
104
105         g_idle_add(_idle_cb, NULL);
106         g_timer_id = 0;
107
108         return FALSE;
109 }
110
111
112 /* FIXME : critical section for g_timer_id? */
113 static void _mm_sound_mgr_codec_shutdown_timer_start()
114 {
115         if (g_timer_id > 0) {
116                 debug_error("Already active timer [%d] exists", g_timer_id);
117                 return;
118         }
119         if (g_shutdown_cb) {
120                 g_timer_id = g_timeout_add_seconds(SHUTDOWN_TIMEOUT_SEC, _timeout_cb, NULL);
121                 debug_warning("TIMER : new timer [%d]", g_timer_id);
122         } else {
123                 debug_warning("No Timer started due to invalid shutdown callback");
124         }
125 }
126
127 static void _mm_sound_mgr_codec_shutdown_timer_stop()
128 {
129         if (g_timer_id > 0) {
130                 debug_warning("TIMER : remove timer id [%d]", g_timer_id);
131                 g_source_remove(g_timer_id);
132                 g_timer_id = 0;
133         } else {
134                 debug_log("No Timer to stop...");
135         }
136 }
137
138 static int _mm_sound_mgr_codec_slot_get_empty(int *slot)
139 {
140         int slotid = 0;
141         int err = MM_ERROR_NONE;
142
143         SLOT_LOCK();
144
145         while (1) {
146                 for (slotid = SOUND_SLOT_START; slotid < MANAGER_HANDLE_MAX ; slotid++) {
147                         if (g_slots[slotid].status == STATUS_IDLE) {
148                                 g_slots[slotid].status = STATUS_SOUND;
149                                 break;
150                         }
151                 }
152
153                 if (slotid < MANAGER_HANDLE_MAX) {
154                         debug_msg("New handle allocated (codec slot ID : [%d])", slotid);
155                         *slot = slotid;
156                         break;
157                 }
158
159                 /* FIXME: avoiding infinite wait is required */
160                 debug_warning("Handle is full..wait for a while and will retry...");
161                 sleep(1);
162         }
163
164         _mm_sound_mgr_codec_shutdown_timer_stop();
165
166         SLOT_UNLOCK();
167
168         return err;
169 }
170
171 static gboolean _mm_sound_mgr_codec_slot_is_empty()
172 {
173         int slotid = 0;
174
175         for (slotid = SOUND_SLOT_START; slotid < MANAGER_HANDLE_MAX ; slotid++) {
176                 if (g_slots[slotid].status == STATUS_SOUND)
177                         break;
178         }
179
180         return (slotid == MANAGER_HANDLE_MAX) ? TRUE : FALSE;
181 }
182
183 static void _mm_sound_mgr_codec_slot_clear(int slotid, bool dump)
184 {
185         memset(&g_slots[slotid], 0, sizeof(__mmsound_mgr_codec_handle_t));
186         g_slots[slotid].status = STATUS_IDLE;
187
188         if (dump)
189                 debug_warning("SlotID [%d] cleared", slotid);
190 }
191
192 int MMSoundMgrCodecInit(const char *targetdir, GSourceFunc _shutdown_cb)
193 {
194         int loop = 0;
195         int slotid = 0;
196
197         debug_enter();
198
199         memset(g_slots, 0, sizeof(g_slots));
200
201         if (pthread_mutex_init(&g_slot_mutex, NULL)) {
202                 debug_error("pthread_mutex_init failed");
203                 return MM_ERROR_SOUND_INTERNAL;
204         }
205
206         for (slotid = 0; slotid < MANAGER_HANDLE_MAX; slotid++)
207                 _mm_sound_mgr_codec_slot_clear(slotid, false);
208
209         if (g_codec_plugins) {
210                 debug_warning("Please Check Init twice");
211                 MMSoundPluginRelease(g_codec_plugins);
212         }
213
214         MMSoundPluginScan(targetdir, MM_SOUND_PLUGIN_TYPE_CODEC, &g_codec_plugins);
215         if (g_codec_plugins) {
216                 while (g_codec_plugins[loop].type != MM_SOUND_PLUGIN_TYPE_NONE)
217                         _MMSoundMgrCodecRegisterInterface(&g_codec_plugins[loop++]);
218         }
219
220         if (_shutdown_cb)
221                 g_shutdown_cb = _shutdown_cb;
222         else
223                 debug_warning("shutdown callback is NULL");
224
225         debug_leave();
226         return MM_ERROR_NONE;
227 }
228
229 int MMSoundMgrCodecFini(void)
230 {
231         debug_enter();
232
233         memset(g_plugins, 0, sizeof(mmsound_codec_interface_t) * MM_SOUND_SUPPORTED_CODEC_NUM);
234         MMSoundPluginRelease(g_codec_plugins);
235         g_codec_plugins = NULL;
236         pthread_mutex_destroy(&g_slot_mutex);
237
238         debug_leave();
239         return MM_ERROR_NONE;
240 }
241
242 static int _MMSoundMgrCodecFindCodecPluginID(enum MMSoundSupportedCodec codec_to_find)
243 {
244         int plugin_id = 0;
245         int *codec_type;
246
247         for (plugin_id = 0; plugin_id < MM_SOUND_SUPPORTED_CODEC_NUM; plugin_id++) {
248                 if (g_plugins[plugin_id].GetSupportTypes) {
249                         codec_type = g_plugins[plugin_id].GetSupportTypes();
250                         if (codec_type[0] == codec_to_find)
251                                 return plugin_id;
252                 }
253         }
254
255         return -1;
256 }
257
258 int MMSoundMgrCodecPlayWithStreamInfo(int *slotid, const mmsound_mgr_codec_param_t *param)
259 {
260         int plugin_id = 0;
261         mmsound_codec_info_t info;
262         mmsound_codec_param_t codec_param;
263         int err = MM_ERROR_NONE;
264
265 #ifdef DEBUG_DETAIL
266         debug_enter();
267 #endif
268         plugin_id = _MMSoundMgrCodecFindCodecPluginID(MM_SOUND_SUPPORTED_CODEC_WAVE);
269         if (plugin_id == -1) {
270                 debug_error("Could not find proper codec plugin!!!");
271                 err = MM_ERROR_SOUND_INTERNAL;
272                 goto cleanup;
273         }
274
275         err = g_plugins[plugin_id].Parse(param->pfilename, &info);
276         if (err != MM_ERROR_NONE) {
277                 debug_error("Could not parse file [%s] by plugin[%d]", param->pfilename, plugin_id);
278                 goto cleanup;
279         }
280
281         err = _mm_sound_mgr_codec_slot_get_empty(slotid);
282         if (err != MM_ERROR_NONE || *slotid < 0) {
283                 debug_error("Empty g_slot is not found");
284                 goto cleanup;
285         }
286
287         codec_param.volume_config = -1; //setting volume config to -1 since using stream info instead of volume type
288         codec_param.repeat_count = param->repeat_count;
289         codec_param.volume = param->volume;
290         codec_param.pfilename = param->pfilename;
291         codec_param.stop_cb = _MMSoundMgrCodecStopCallback;
292         codec_param.param = *slotid;
293         codec_param.pid = (int)param->param;
294         codec_param.stream_index = param->stream_index;
295         MMSOUND_STRNCPY(codec_param.stream_type, param->stream_type, MAX_STREAM_TYPE_LEN);
296         SLOT_LOCK();
297
298         /* Codec id WAV or MP3 */
299         g_slots[*slotid].pluginid = plugin_id;
300         g_slots[*slotid].param    = param->param;               /* This arg is used callback data */
301
302         debug_msg("Using Slotid : [%d] Slot Status : [%d]", *slotid, g_slots[*slotid].status);
303
304         err = g_plugins[g_slots[*slotid].pluginid].Create(&codec_param, &info, &(g_slots[*slotid].plughandle));
305         debug_msg("Created audio handle : [%p]", g_slots[*slotid].plughandle);
306         if (err != MM_ERROR_NONE) {
307                 debug_error("Plugin create fail : 0x%08X", err);
308                 g_slots[*slotid].status = STATUS_IDLE;
309                 SLOT_UNLOCK();
310                 goto cleanup;
311         }
312
313         err = g_plugins[g_slots[*slotid].pluginid].Play(g_slots[*slotid].plughandle);
314         if (err != MM_ERROR_NONE) {
315                 debug_error("Fail to play : 0x%08X", err);
316                 g_plugins[g_slots[*slotid].pluginid].Destroy(g_slots[*slotid].plughandle);
317         }
318
319         SLOT_UNLOCK();
320
321 cleanup:
322         if (_mm_sound_mgr_codec_slot_is_empty())
323                 _mm_sound_mgr_codec_shutdown_timer_start();
324
325 #ifdef DEBUG_DETAIL
326         debug_leave();
327 #endif
328
329         return err;
330
331 }
332
333 int MMSoundMgrCodecPlayDtmfWithStreamInfo(int *slotid, const mmsound_mgr_codec_param_t *param)
334 {
335         int plugin_id = 0;
336         mmsound_codec_info_t info;
337         mmsound_codec_param_t codec_param;
338         int err = MM_ERROR_NONE;
339
340 #ifdef DEBUG_DETAIL
341         debug_enter();
342 #endif
343         plugin_id = _MMSoundMgrCodecFindCodecPluginID(MM_SOUND_SUPPORTED_CODEC_DTMF);
344         if (plugin_id == -1) {
345                 debug_error("Could not find proper codec plugin!!!");
346                 err = MM_ERROR_SOUND_INTERNAL;
347                 goto cleanup;
348         }
349
350         /*The count num means codec type DTMF */
351         debug_msg("DTMF[%d] Repeat[%d] Volume[%f] plugin_codec[%d]", param->tone, param->repeat_count, param->volume, plugin_id);
352
353         if (g_plugins[plugin_id].GetSupportTypes == NULL) { /* Codec not found */
354                 debug_error("unsupported file type %d", plugin_id);
355                 printf("unsupported file type %d", plugin_id);
356                 err = MM_ERROR_SOUND_UNSUPPORTED_MEDIA_TYPE;
357                 goto cleanup;
358         }
359
360 #ifdef DEBUG_DETAIL
361         debug_msg("Get New handle");
362 #endif
363
364         err = _mm_sound_mgr_codec_slot_get_empty(slotid);
365         if (err != MM_ERROR_NONE || *slotid < 0) {
366                 debug_error("Empty g_slot is not found");
367                 goto cleanup;
368         }
369
370         codec_param.tone = param->tone;
371         codec_param.repeat_count = param->repeat_count;
372         codec_param.volume = param->volume;
373         codec_param.stop_cb = _MMSoundMgrCodecStopCallback;
374         codec_param.param = *slotid;
375         codec_param.pid = (int)param->param;
376         codec_param.volume_config = -1; //setting volume config to -1 since using stream info instead of volume type
377         codec_param.stream_index = param->stream_index;
378         MMSOUND_STRNCPY(codec_param.stream_type, param->stream_type, MAX_STREAM_TYPE_LEN);
379         SLOT_LOCK();
380
381         g_slots[*slotid].pluginid = plugin_id;
382         g_slots[*slotid].param    = param->param;               /* This arg is used callback data */
383
384 #ifdef DEBUG_DETAIL
385         debug_msg("Using Slotid : [%d] Slot Status : [%d]", *slotid, g_slots[*slotid].status);
386 #endif
387
388         err = g_plugins[g_slots[*slotid].pluginid].Create(&codec_param, &info, &(g_slots[*slotid].plughandle));
389         debug_msg("Created audio handle : [%p]", g_slots[*slotid].plughandle);
390         if (err != MM_ERROR_NONE) {
391                 debug_error("Plugin create fail : 0x%08X", err);
392                 g_slots[*slotid].status = STATUS_IDLE;
393                 SLOT_UNLOCK();
394                 goto cleanup;
395         }
396
397         err = g_plugins[g_slots[*slotid].pluginid].Play(g_slots[*slotid].plughandle);
398         if (err != MM_ERROR_NONE) {
399                 debug_error("Fail to play : 0x%08X", err);
400                 g_plugins[g_slots[*slotid].pluginid].Destroy(g_slots[*slotid].plughandle);
401         }
402
403         SLOT_UNLOCK();
404         debug_msg("Using Slotid : [%d] Slot Status : [%d]", *slotid, g_slots[*slotid].status);
405
406 cleanup:
407 #ifdef DEBUG_DETAIL
408         debug_leave();
409 #endif
410
411         return err;
412
413 }
414
415 int MMSoundMgrCodecStop(const int slotid)
416 {
417         int err = MM_ERROR_NONE;
418
419         debug_enter("(Slotid : [%d])", slotid);
420
421         if (slotid < 0 || MANAGER_HANDLE_MAX <= slotid)
422                 return MM_ERROR_INVALID_ARGUMENT;
423
424         SLOT_LOCK();
425         if (g_slots[slotid].status == STATUS_IDLE) {
426                 err = MM_ERROR_SOUND_INVALID_STATE;
427                 debug_warning("The playing slots is not found, Slot ID : [%d]", slotid);
428                 goto cleanup;
429         }
430 #ifdef DEBUG_DETAIL
431         debug_msg("Found slot, Slotid [%d] State [%d]", slotid, g_slots[slotid].status);
432 #endif
433
434         err = g_plugins[g_slots[slotid].pluginid].Stop(g_slots[slotid].plughandle);
435         if (err != MM_ERROR_NONE)
436                 debug_error("Fail to STOP Code : 0x%08X", err);
437
438 cleanup:
439         SLOT_UNLOCK();
440         debug_leave("(err : 0x%08X)", err);
441
442         return err;
443 }
444
445 static int _MMSoundMgrCodecStopCallback(int slotid, bool stop_by_user)
446 {
447         int err = MM_ERROR_NONE;
448
449         if (slotid < 0 || slotid >= MANAGER_HANDLE_MAX) {
450                 debug_error("Slot(%d) is invalid", slotid);
451                 return MM_ERROR_INVALID_ARGUMENT;
452         }
453
454         debug_enter("Slot(%d) : stop-by-user(%d)", slotid, stop_by_user);
455
456         if (!stop_by_user) {
457                 /* NOTE : slot is already locked by MMSoundMgrCodecStop() in user stop case */
458                 SLOT_LOCK();
459
460                 if (g_slots[slotid].pluginid == MM_SOUND_SUPPORTED_CODEC_WAVE)
461                         __mm_sound_mgr_ipc_notify_play_file_end(slotid);
462
463                 debug_msg("Client callback msg_type (instance) : [%d]", (int)g_slots[slotid].param);
464         }
465
466         debug_msg("Handle allocated handle : [%p]", g_slots[slotid].plughandle);
467         err = g_plugins[g_slots[slotid].pluginid].Destroy(g_slots[slotid].plughandle);
468         if (err < 0)
469                 debug_critical("Slot(%d) : Fail to destroy slot, err [0x%x]", slotid, err);
470
471         _mm_sound_mgr_codec_slot_clear(slotid, true);
472         if (_mm_sound_mgr_codec_slot_is_empty())
473                 _mm_sound_mgr_codec_shutdown_timer_start();
474
475         if (!stop_by_user)
476                 SLOT_UNLOCK();
477
478         debug_fleave();
479
480         return err;
481 }
482
483
484
485 static int _MMSoundMgrCodecRegisterInterface(MMSoundPluginType *plugin)
486 {
487         int err = MM_ERROR_NONE;
488         int plugin_id = 0;
489         void *getinterface = NULL;
490
491 #ifdef DEBUG_DETAIL
492         debug_enter();
493 #endif
494
495         /* find emptry slot */
496         for (plugin_id = 0; plugin_id < MM_SOUND_SUPPORTED_CODEC_NUM; plugin_id++) {
497                 if (g_plugins[plugin_id].GetSupportTypes == NULL)
498                         break;
499         }
500
501         if (plugin_id == MM_SOUND_SUPPORTED_CODEC_NUM) {
502                 debug_critical("The plugin support type is not valid");
503                 return MM_ERROR_COMMON_OUT_OF_RANGE;
504         }
505
506         err = MMSoundPluginGetSymbol(plugin, CODEC_GET_INTERFACE_FUNC_NAME, &getinterface);
507         if (err != MM_ERROR_NONE) {
508                 debug_error("Get Symbol CODEC_GET_INTERFACE_FUNC_NAME is fail : %x", err);
509                 goto cleanup;
510         }
511         debug_msg("interface[%p] empty_slot[%d]", getinterface, plugin_id);
512
513         err = MMSoundPlugCodecCastGetInterface(getinterface)(&g_plugins[plugin_id]);
514         if (err != MM_ERROR_NONE) {
515                 debug_error("Get interface fail : %x", err);
516
517 cleanup:
518                 /* If error occur, clean interface */
519                 memset(&g_plugins[plugin_id], 0, sizeof(mmsound_codec_interface_t));
520         } else {
521                 if (g_plugins[plugin_id].SetThreadPool)
522                         g_plugins[plugin_id].SetThreadPool(MMSoundThreadPoolRun);
523         }
524
525 #ifdef DEBUG_DETAIL
526         debug_leave();
527 #endif
528
529         return err;
530 }
531