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