Revise code related to coding conventions and adjust some log levels
[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         bool stop_by_user;
60
61 } __mmsound_mgr_codec_handle_t;
62
63 static MMSoundPluginType *g_codec_plugins = NULL;
64 static __mmsound_mgr_codec_handle_t g_slots[MANAGER_HANDLE_MAX];
65 static mmsound_codec_interface_t g_plugins[MM_SOUND_SUPPORTED_CODEC_NUM];
66 static pthread_mutex_t g_slot_mutex;
67 static GSourceFunc g_shutdown_cb;
68 static guint g_timer_id = 0;
69
70 #ifdef DEBUG_DETAIL
71 #define SLOT_LOCK() do { \
72         debug_msg("Before Slot_mutex LOCK"); \
73         pthread_mutex_lock(&g_slot_mutex); \
74         debug_msg("After Slot_mutex LOCK"); \
75 } while (0)
76 #define SLOT_UNLOCK() do { \
77         debug_msg("Before Slot_mutex UNLOCK"); \
78         pthread_mutex_unlock(&g_slot_mutex); \
79         debug_msg("After Slot_mutex UNLOCK"); \
80 } while (0)
81 #else
82 #define SLOT_LOCK() do { pthread_mutex_lock(&g_slot_mutex); } while (0)
83 #define SLOT_UNLOCK() do { pthread_mutex_unlock(&g_slot_mutex); } while (0)
84 #endif
85
86 static int _MMSoundMgrCodecRegisterInterface(MMSoundPluginType *plugin);
87 static int _MMSoundMgrCodecStopCallback(int param);
88 static gboolean _mm_sound_mgr_codec_slot_is_empty();
89
90 static gboolean _idle_cb(gpointer user_data)
91 {
92         if (_mm_sound_mgr_codec_slot_is_empty()) {
93                 debug_msg("slot is empty, ready to shutdown! %p", g_shutdown_cb);
94                 if (g_shutdown_cb)
95                         g_shutdown_cb(NULL);
96         } else {
97                 debug_warning("slot is not empty!!! no shutdown...");
98         }
99
100         return FALSE;
101 }
102
103 static gboolean _timeout_cb(gpointer user_data)
104 {
105         debug_warning("TIMEOUT : add idle callback now...");
106
107         g_idle_add(_idle_cb, NULL);
108         g_timer_id = 0;
109
110         return FALSE;
111 }
112
113
114 /* FIXME : critical section for g_timer_id? */
115 static void _mm_sound_mgr_codec_shutdown_timer_start()
116 {
117         if (g_timer_id > 0) {
118                 debug_error("Already active timer [%d] exists", g_timer_id);
119                 return;
120         }
121         if (g_shutdown_cb) {
122                 g_timer_id = g_timeout_add_seconds(SHUTDOWN_TIMEOUT_SEC, _timeout_cb, NULL);
123                 debug_warning("TIMER : new timer [%d]", g_timer_id);
124         } else {
125                 debug_warning("No Timer started due to invalid shutdown callback");
126         }
127 }
128
129 static void _mm_sound_mgr_codec_shutdown_timer_stop()
130 {
131         if (g_timer_id > 0) {
132                 debug_warning("TIMER : remove timer id [%d]", g_timer_id);
133                 g_source_remove(g_timer_id);
134                 g_timer_id = 0;
135         } else {
136                 debug_log("No Timer to stop...");
137         }
138 }
139
140 static int _mm_sound_mgr_codec_slot_get_empty(int *slot)
141 {
142         int slotid = 0;
143         int err = MM_ERROR_NONE;
144
145         SLOT_LOCK();
146
147         while (1) {
148                 for (slotid = SOUND_SLOT_START; slotid < MANAGER_HANDLE_MAX ; slotid++) {
149                         if (g_slots[slotid].status == STATUS_IDLE) {
150                                 g_slots[slotid].status = STATUS_SOUND;
151                                 break;
152                         }
153                 }
154
155                 if (slotid < MANAGER_HANDLE_MAX) {
156                         debug_msg("New handle allocated (codec slot ID : [%d])", slotid);
157                         *slot = slotid;
158                         break;
159                 }
160
161                 /* FIXME: avoiding infinite wait is required */
162                 debug_warning("Handle is full..wait for a while and will retry...");
163                 sleep(1);
164         }
165
166         _mm_sound_mgr_codec_shutdown_timer_stop();
167
168         SLOT_UNLOCK();
169
170         return err;
171 }
172
173 static gboolean _mm_sound_mgr_codec_slot_is_empty()
174 {
175         int slotid = 0;
176
177         for (slotid = SOUND_SLOT_START; slotid < MANAGER_HANDLE_MAX ; slotid++) {
178                 if (g_slots[slotid].status == STATUS_SOUND)
179                         break;
180         }
181
182         return (slotid == MANAGER_HANDLE_MAX) ? TRUE : FALSE;
183 }
184
185 static void _mm_sound_mgr_codec_slot_clear(int slotid, bool dump)
186 {
187         memset(&g_slots[slotid], 0, sizeof(__mmsound_mgr_codec_handle_t));
188         g_slots[slotid].status = STATUS_IDLE;
189
190         if (dump)
191                 debug_warning("SlotID [%d] cleared", slotid);
192 }
193
194 int MMSoundMgrCodecInit(const char *targetdir, GSourceFunc _shutdown_cb)
195 {
196         int loop = 0;
197         int slotid = 0;
198
199         debug_enter();
200
201         memset(g_slots, 0, sizeof(g_slots));
202
203         if (pthread_mutex_init(&g_slot_mutex, NULL)) {
204                 debug_error("pthread_mutex_init failed");
205                 return MM_ERROR_SOUND_INTERNAL;
206         }
207
208         for (slotid = 0; slotid < MANAGER_HANDLE_MAX; slotid++)
209                 _mm_sound_mgr_codec_slot_clear(slotid, false);
210
211         if (g_codec_plugins) {
212                 debug_warning("Please Check Init twice");
213                 MMSoundPluginRelease(g_codec_plugins);
214         }
215
216         MMSoundPluginScan(targetdir, MM_SOUND_PLUGIN_TYPE_CODEC, &g_codec_plugins);
217         if (g_codec_plugins) {
218                 while (g_codec_plugins[loop].type != MM_SOUND_PLUGIN_TYPE_NONE)
219                         _MMSoundMgrCodecRegisterInterface(&g_codec_plugins[loop++]);
220         }
221
222         if (_shutdown_cb)
223                 g_shutdown_cb = _shutdown_cb;
224         else
225                 debug_warning("shutdown callback is NULL");
226
227         debug_leave();
228         return MM_ERROR_NONE;
229 }
230
231 int MMSoundMgrCodecFini(void)
232 {
233         debug_enter();
234
235         memset(g_plugins, 0, sizeof(mmsound_codec_interface_t) * MM_SOUND_SUPPORTED_CODEC_NUM);
236         MMSoundPluginRelease(g_codec_plugins);
237         g_codec_plugins = NULL;
238         pthread_mutex_destroy(&g_slot_mutex);
239
240         debug_leave();
241         return MM_ERROR_NONE;
242 }
243
244 static int _MMSoundMgrCodecFindCodecPluginID(enum MMSoundSupportedCodec codec_to_find)
245 {
246         int plugin_id = 0;
247         int *codec_type;
248
249         for (plugin_id = 0; plugin_id < MM_SOUND_SUPPORTED_CODEC_NUM; plugin_id++) {
250                 if (g_plugins[plugin_id].GetSupportTypes) {
251                         codec_type = g_plugins[plugin_id].GetSupportTypes();
252                         if (codec_type[0] == codec_to_find)
253                                 return plugin_id;
254                 }
255         }
256
257         return -1;
258 }
259
260 int MMSoundMgrCodecPlayWithStreamInfo(int *slotid, const mmsound_mgr_codec_param_t *param)
261 {
262         int plugin_id = 0;
263         mmsound_codec_info_t info;
264         mmsound_codec_param_t codec_param;
265         int err = MM_ERROR_NONE;
266
267 #ifdef DEBUG_DETAIL
268         debug_enter();
269 #endif
270         plugin_id = _MMSoundMgrCodecFindCodecPluginID(MM_SOUND_SUPPORTED_CODEC_WAVE);
271         if (plugin_id == -1) {
272                 debug_error("Could not find proper codec plugin!!!");
273                 err = MM_ERROR_SOUND_INTERNAL;
274                 goto cleanup;
275         }
276
277         err = g_plugins[plugin_id].Parse(param->pfilename, &info);
278         if (err != MM_ERROR_NONE) {
279                 debug_error("Could not parse file [%s] by plugin[%d]", param->pfilename, plugin_id);
280                 goto cleanup;
281         }
282
283         err = _mm_sound_mgr_codec_slot_get_empty(slotid);
284         if (err != MM_ERROR_NONE || *slotid < 0) {
285                 debug_error("Empty g_slot is not found");
286                 goto cleanup;
287         }
288
289         codec_param.volume_config = -1; //setting volume config to -1 since using stream info instead of volume type
290         codec_param.repeat_count = param->repeat_count;
291         codec_param.volume = param->volume;
292         codec_param.pfilename = param->pfilename;
293         codec_param.stop_cb = _MMSoundMgrCodecStopCallback;
294         codec_param.param = *slotid;
295         codec_param.pid = (int)param->param;
296         codec_param.stream_index = param->stream_index;
297         MMSOUND_STRNCPY(codec_param.stream_type, param->stream_type, MAX_STREAM_TYPE_LEN);
298         SLOT_LOCK();
299
300         /* Codec id WAV or MP3 */
301         g_slots[*slotid].pluginid = plugin_id;
302         g_slots[*slotid].param    = param->param;               /* This arg is used callback data */
303
304         debug_msg("Using Slotid : [%d] Slot Status : [%d]", *slotid, g_slots[*slotid].status);
305
306         err = g_plugins[g_slots[*slotid].pluginid].Create(&codec_param, &info, &(g_slots[*slotid].plughandle));
307         debug_msg("Created audio handle : [%p]", g_slots[*slotid].plughandle);
308         if (err != MM_ERROR_NONE) {
309                 debug_error("Plugin create fail : 0x%08X", err);
310                 g_slots[*slotid].status = STATUS_IDLE;
311                 SLOT_UNLOCK();
312                 goto cleanup;
313         }
314
315         err = g_plugins[g_slots[*slotid].pluginid].Play(g_slots[*slotid].plughandle);
316         if (err != MM_ERROR_NONE) {
317                 debug_error("Fail to play : 0x%08X", err);
318                 g_plugins[g_slots[*slotid].pluginid].Destroy(g_slots[*slotid].plughandle);
319         }
320
321         SLOT_UNLOCK();
322
323 cleanup:
324         if (_mm_sound_mgr_codec_slot_is_empty())
325                 _mm_sound_mgr_codec_shutdown_timer_start();
326
327 #ifdef DEBUG_DETAIL
328         debug_leave();
329 #endif
330
331         return err;
332
333 }
334
335 int MMSoundMgrCodecPlayDtmfWithStreamInfo(int *slotid, const mmsound_mgr_codec_param_t *param)
336 {
337         int plugin_id = 0;
338         mmsound_codec_info_t info;
339         mmsound_codec_param_t codec_param;
340         int err = MM_ERROR_NONE;
341
342 #ifdef DEBUG_DETAIL
343         debug_enter();
344 #endif
345         plugin_id = _MMSoundMgrCodecFindCodecPluginID(MM_SOUND_SUPPORTED_CODEC_DTMF);
346         if (plugin_id == -1) {
347                 debug_error("Could not find proper codec plugin!!!");
348                 err = MM_ERROR_SOUND_INTERNAL;
349                 goto cleanup;
350         }
351
352         /*The count num means codec type DTMF */
353         debug_msg("DTMF[%d] Repeat[%d] Volume[%f] plugin_codec[%d]", param->tone, param->repeat_count, param->volume, plugin_id);
354
355         if (g_plugins[plugin_id].GetSupportTypes == NULL) { /* Codec not found */
356                 debug_error("unsupported file type %d", plugin_id);
357                 printf("unsupported file type %d", plugin_id);
358                 err = MM_ERROR_SOUND_UNSUPPORTED_MEDIA_TYPE;
359                 goto cleanup;
360         }
361
362 #ifdef DEBUG_DETAIL
363         debug_msg("Get New handle");
364 #endif
365
366         err = _mm_sound_mgr_codec_slot_get_empty(slotid);
367         if (err != MM_ERROR_NONE || *slotid < 0) {
368                 debug_error("Empty g_slot is not found");
369                 goto cleanup;
370         }
371
372         codec_param.tone = param->tone;
373         codec_param.repeat_count = param->repeat_count;
374         codec_param.volume = param->volume;
375         codec_param.stop_cb = _MMSoundMgrCodecStopCallback;
376         codec_param.param = *slotid;
377         codec_param.pid = (int)param->param;
378         codec_param.volume_config = -1; //setting volume config to -1 since using stream info instead of volume type
379         codec_param.stream_index = param->stream_index;
380         MMSOUND_STRNCPY(codec_param.stream_type, param->stream_type, MAX_STREAM_TYPE_LEN);
381         SLOT_LOCK();
382
383         g_slots[*slotid].pluginid = plugin_id;
384         g_slots[*slotid].param    = param->param;               /* This arg is used callback data */
385
386 #ifdef DEBUG_DETAIL
387         debug_msg("Using Slotid : [%d] Slot Status : [%d]", *slotid, g_slots[*slotid].status);
388 #endif
389
390         err = g_plugins[g_slots[*slotid].pluginid].Create(&codec_param, &info, &(g_slots[*slotid].plughandle));
391         debug_msg("Created audio handle : [%p]", g_slots[*slotid].plughandle);
392         if (err != MM_ERROR_NONE) {
393                 debug_error("Plugin create fail : 0x%08X", err);
394                 g_slots[*slotid].status = STATUS_IDLE;
395                 SLOT_UNLOCK();
396                 goto cleanup;
397         }
398
399         err = g_plugins[g_slots[*slotid].pluginid].Play(g_slots[*slotid].plughandle);
400         if (err != MM_ERROR_NONE) {
401                 debug_error("Fail to play : 0x%08X", err);
402                 g_plugins[g_slots[*slotid].pluginid].Destroy(g_slots[*slotid].plughandle);
403         }
404
405         SLOT_UNLOCK();
406         debug_msg("Using Slotid : [%d] Slot Status : [%d]", *slotid, g_slots[*slotid].status);
407
408 cleanup:
409 #ifdef DEBUG_DETAIL
410         debug_leave();
411 #endif
412
413         return err;
414
415 }
416
417 int MMSoundMgrCodecStop(const int slotid)
418 {
419         int err = MM_ERROR_NONE;
420
421         debug_enter("(Slotid : [%d])", slotid);
422
423         if (slotid < 0 || MANAGER_HANDLE_MAX <= slotid)
424                 return MM_ERROR_INVALID_ARGUMENT;
425
426         SLOT_LOCK();
427         if (g_slots[slotid].status == STATUS_IDLE) {
428                 err = MM_ERROR_SOUND_INVALID_STATE;
429                 debug_warning("The playing slots is not found, Slot ID : [%d]", slotid);
430                 goto cleanup;
431         }
432 #ifdef DEBUG_DETAIL
433         debug_msg("Found slot, Slotid [%d] State [%d]", slotid, g_slots[slotid].status);
434 #endif
435
436         g_slots[slotid].stop_by_user = true;
437
438         err = g_plugins[g_slots[slotid].pluginid].Stop(g_slots[slotid].plughandle);
439         if (err != MM_ERROR_NONE)
440                 debug_error("Fail to STOP Code : 0x%08X", err);
441
442 cleanup:
443         SLOT_UNLOCK();
444         debug_leave("(err : 0x%08X)", err);
445
446         return err;
447 }
448
449 static int _MMSoundMgrCodecStopCallback(int param)
450 {
451         int err = MM_ERROR_NONE;
452
453         if (param < 0 || param >= MANAGER_HANDLE_MAX) {
454                 debug_error("Slot(%d) is invalid", param);
455                 return MM_ERROR_INVALID_ARGUMENT;
456         }
457
458         debug_enter("Slot(%d) : stop-by-user(%d)", param, g_slots[param].stop_by_user);
459
460         if (!g_slots[param].stop_by_user) {
461                 SLOT_LOCK();
462                 __mm_sound_mgr_ipc_notify_play_file_end(param);
463                 debug_msg("Client callback msg_type (instance) : [%d]", (int)g_slots[param].param);
464         }
465
466         debug_msg("Handle allocated handle : [%p]", g_slots[param].plughandle);
467         err = g_plugins[g_slots[param].pluginid].Destroy(g_slots[param].plughandle);
468         if (err < 0)
469                 debug_critical("Slot(%d) : Fail to destroy slot, err [0x%x]", param, err);
470
471         _mm_sound_mgr_codec_slot_clear(param, true);
472         if (_mm_sound_mgr_codec_slot_is_empty())
473                 _mm_sound_mgr_codec_shutdown_timer_start();
474
475         if (!g_slots[param].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