Fix typo
[platform/core/multimedia/libmm-camcorder.git] / src / mm_camcorder_audiorec.c
1 /*
2  * libmm-camcorder
3  *
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Jeongmo Yang <jm80.yang@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 /*=======================================================================================
23 |  INCLUDE FILES                                                                        |
24 =======================================================================================*/
25 #include "mm_camcorder_internal.h"
26 #include "mm_camcorder_audiorec.h"
27 #include "mm_camcorder_util.h"
28 #include <math.h>
29
30 /*---------------------------------------------------------------------------------------
31 |    GLOBAL VARIABLE DEFINITIONS for internal                                           |
32 ---------------------------------------------------------------------------------------*/
33
34 /*---------------------------------------------------------------------------------------
35 |    LOCAL VARIABLE DEFINITIONS for internal                                            |
36 ---------------------------------------------------------------------------------------*/
37 #define RESET_PAUSE_TIME                        0
38 #define _MMCAMCORDER_AUDIO_MINIMUM_SPACE        ((100*1024) + (5*1024))
39 #define _MMCAMCORDER_RETRIAL_COUNT              15
40 #define _MMCAMCORDER_FRAME_WAIT_TIME            200000  /* us */
41 #define _MMCAMCORDER_FREE_SPACE_CHECK_INTERVAL  10
42
43 /*---------------------------------------------------------------------------------------
44 |    LOCAL FUNCTION PROTOTYPES:                                                         |
45 ---------------------------------------------------------------------------------------*/
46 /* STATIC INTERNAL FUNCTION */
47 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_voicerecorder(GstPad *pad, GstPadProbeInfo *info, gpointer u_data);
48 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data);
49 static int __mmcamcorder_create_audiop_with_encodebin(MMHandleType handle);
50 static gboolean __mmcamcorder_audio_add_metadata_info_m4a(MMHandleType handle);
51
52 /*=======================================================================================
53 |  FUNCTION DEFINITIONS                                                                 |
54 =======================================================================================*/
55
56 /*---------------------------------------------------------------------------------------
57 |    GLOBAL FUNCTION DEFINITIONS:                                                       |
58 ---------------------------------------------------------------------------------------*/
59
60 static int __mmcamcorder_create_audiop_with_encodebin(MMHandleType handle)
61 {
62         int err = MM_ERROR_NONE;
63         int file_name_len = 0;
64
65         char *file_name = NULL;
66         const char *aenc_name = NULL;
67         const char *mux_name = NULL;
68         const char *sink_name = NULL;
69
70         GstBus *bus = NULL;
71         GstPad *srcpad = NULL;
72         GstPad *sinkpad = NULL;
73         GList *element_list = NULL;
74
75         _MMCamcorderAudioInfo *info = NULL;
76         mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
77         _MMCamcorderSubContext *sc = NULL;
78         type_element *aenc_elem = NULL;
79         type_element *mux_elem = NULL;
80         type_element *sink_elem = NULL;
81
82         mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
83         sc = MMF_CAMCORDER_SUBCONTEXT(handle);
84
85         mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
86         mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
87         mmf_return_val_if_fail(sc->info_audio, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
88
89         info = (_MMCamcorderAudioInfo *)sc->info_audio;
90
91         _mmcam_dbg_log("");
92
93         mux_elem = _mmcamcorder_get_type_element(handle, MM_CAM_FILE_FORMAT);
94         err = _mmcamcorder_conf_get_value_element_name(mux_elem, &mux_name);
95
96         if (!mux_name || !strcmp(mux_name, "wavenc")) {
97                 /* IF MUX in not chosen then record in raw file */
98                 _mmcam_dbg_log("Record without muxing.");
99                 info->bMuxing = FALSE;
100         } else {
101                 _mmcam_dbg_log("Record with mux.");
102                 info->bMuxing = TRUE;
103         }
104
105         /* Create GStreamer pipeline */
106         _MMCAMCORDER_PIPELINE_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCODE_MAIN_PIPE, "camcorder_pipeline", err);
107
108         err = _mmcamcorder_create_audiosrc_bin(handle);
109         if (err != MM_ERROR_NONE)
110                 return err;
111
112         if (info->bMuxing) {
113                 /* Muxing. can use encodebin. */
114                 err = _mmcamcorder_create_encodesink_bin((MMHandleType)hcamcorder, MM_CAMCORDER_ENCBIN_PROFILE_AUDIO);
115                 if (err != MM_ERROR_NONE)
116                         return err;
117
118                 /* Add and link elements */
119                 gst_bin_add_many(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst),
120                         sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst,
121                         sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst,
122                         NULL);
123
124                 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst, "src");
125                 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst, "audio_sink0");
126                 _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error);
127         } else {
128                 err = mm_camcorder_get_attributes(handle, NULL,
129                         MMCAM_TARGET_FILENAME, &file_name, &file_name_len,
130                         NULL);
131                 if (err != MM_ERROR_NONE) {
132                         _mmcam_dbg_err("failed to get filename [0x%x]", err);
133                         err = MM_ERROR_CAMCORDER_RESOURCE_CREATION;
134                         goto pipeline_creation_error;
135                 }
136
137                 /* without muxing. can't use encodebin. */
138                 aenc_elem = _mmcamcorder_get_type_element(handle, MM_CAM_AUDIO_ENCODER);
139                 if (!aenc_elem) {
140                         _mmcam_dbg_err("Fail to get type element");
141                         err = MM_ERROR_CAMCORDER_RESOURCE_CREATION;
142                         goto pipeline_creation_error;
143                 }
144
145                 err = _mmcamcorder_conf_get_value_element_name(aenc_elem, &aenc_name);
146                 if ((!err) || (!aenc_name)) {
147                         _mmcam_dbg_err("Fail to get element name");
148                         err = MM_ERROR_CAMCORDER_RESOURCE_CREATION;
149                         goto pipeline_creation_error;
150                 }
151
152                 element_list = g_list_append(element_list, &sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN]);
153
154                 _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_AQUE, "queue",  NULL, element_list, err);
155
156                 if (strcmp(aenc_name, "wavenc") != 0)
157                         _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_CONV, "audioconvert",  NULL, element_list, err);
158
159                 _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_AENC, aenc_name, NULL, element_list, err);
160
161                 _mmcamcorder_conf_get_element(handle, hcamcorder->conf_main,
162                         CONFIGURE_CATEGORY_MAIN_RECORD,
163                         "RecordsinkElement",
164                         &sink_elem);
165                 _mmcamcorder_conf_get_value_element_name(sink_elem, &sink_name);
166
167                 _mmcam_dbg_log("encode sink : %s", sink_name);
168
169                 _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_SINK, sink_name, NULL, element_list, err);
170
171                 _mmcamcorder_conf_set_value_element_property(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, sink_elem);
172
173                 /* add elements to encode pipeline */
174                 if (!_mmcamcorder_add_elements_to_bin(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), element_list)) {
175                         _mmcam_dbg_err("add encode elements error.");
176                         err = MM_ERROR_CAMCORDER_RESOURCE_CREATION;
177                         goto pipeline_creation_error;
178                 }
179
180                 /* link elements */
181                 if (!_mmcamcorder_link_elements(element_list)) {
182                         _mmcam_dbg_err("encode element link error.");
183                         err = MM_ERROR_CAMCORDER_GST_LINK;
184                         goto pipeline_creation_error;
185                 }
186         }
187
188         /* set data probe function */
189         srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst, "src");
190         MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_AUDIOREC,
191                 __mmcamcorder_audio_dataprobe_voicerecorder, hcamcorder);
192         gst_object_unref(srcpad);
193         srcpad = NULL;
194
195         srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst, "src");
196         MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_AUDIOREC,
197                 __mmcamcorder_audio_dataprobe_record, hcamcorder);
198
199         /* for replay gain tag */
200         MMCAMCORDER_ADD_EVENT_PROBE(srcpad, _MMCAMCORDER_HANDLER_AUDIOREC,
201                 __mmcamcorder_eventprobe_monitor, hcamcorder);
202
203         gst_object_unref(srcpad);
204         srcpad = NULL;
205
206         sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "sink");
207         MMCAMCORDER_ADD_BUFFER_PROBE(sinkpad, _MMCAMCORDER_HANDLER_AUDIOREC,
208                 __mmcamcorder_muxed_dataprobe, hcamcorder);
209         MMCAMCORDER_ADD_EVENT_PROBE(sinkpad, _MMCAMCORDER_HANDLER_AUDIOREC,
210                 __mmcamcorder_eventprobe_monitor, hcamcorder);
211         gst_object_unref(sinkpad);
212         sinkpad = NULL;
213
214         bus = gst_pipeline_get_bus(GST_PIPELINE(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst));
215
216         /* register message callback  */
217         hcamcorder->pipeline_cb_event_id = gst_bus_add_watch(bus, (GstBusFunc)_mmcamcorder_pipeline_cb_message, hcamcorder);
218
219         /* set sync callback */
220         gst_bus_set_sync_handler(bus, _mmcamcorder_encode_pipeline_bus_sync_callback, hcamcorder, NULL);
221
222         gst_object_unref(bus);
223         bus = NULL;
224
225         if (element_list) {
226                 g_list_free(element_list);
227                 element_list = NULL;
228         }
229
230         return MM_ERROR_NONE;
231
232 pipeline_creation_error:
233         _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCODE_MAIN_PIPE);
234         _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_AUDIOSRC_BIN);
235         _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_AQUE);
236         _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_CONV);
237         _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_AENC);
238         _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_SINK);
239         _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_BIN);
240
241         if (element_list) {
242                 g_list_free(element_list);
243                 element_list = NULL;
244         }
245
246         return err;
247 }
248
249
250 int
251 _mmcamcorder_create_audio_pipeline(MMHandleType handle)
252 {
253         mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
254         _MMCamcorderSubContext *sc = NULL;
255
256         mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
257         sc = MMF_CAMCORDER_SUBCONTEXT(handle);
258
259         mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
260
261         return __mmcamcorder_create_audiop_with_encodebin(handle);
262 }
263
264
265 /**
266  * This function destroy audio pipeline.
267  *
268  * @param[in]   handle          Handle of camcorder.
269  * @return      void
270  * @remarks
271  * @see         _mmcamcorder_destroy_pipeline()
272  *
273  */
274 void
275 _mmcamcorder_destroy_audio_pipeline(MMHandleType handle)
276 {
277         mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
278         _MMCamcorderSubContext *sc = NULL;
279         _MMCamcorderAudioInfo *info = NULL;
280         mmf_return_if_fail(hcamcorder);
281         sc = MMF_CAMCORDER_SUBCONTEXT(handle);
282
283         mmf_return_if_fail(sc && sc->info_audio);
284         mmf_return_if_fail(sc->element);
285
286         info = sc->info_audio;
287
288         _mmcam_dbg_log("start");
289
290         if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst) {
291                 _mmcam_dbg_warn("release audio pipeline");
292
293                 _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_NULL);
294
295                 _mmcamcorder_remove_all_handlers((MMHandleType)hcamcorder, _MMCAMCORDER_HANDLER_CATEGORY_ALL);
296
297                 if (info->bMuxing) {
298                         GstPad *reqpad = NULL;
299                         /* FIXME:
300                             Release request pad
301                             The ref_count of mux is always # of streams in here, i don't know why it happens.
302                             So, i unref the mux manually
303                         */
304                         reqpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "audio");
305                         gst_element_release_request_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, reqpad);
306                         gst_object_unref(reqpad);
307
308                         if (GST_IS_ELEMENT(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst) &&
309                            GST_OBJECT_REFCOUNT(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst) > 1) {
310                                 gst_object_unref(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst);
311                         }
312                 }
313                 gst_object_unref(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst);
314         }
315
316         _mmcam_dbg_log("done");
317
318         return;
319 }
320
321
322 int
323 _mmcamcorder_audio_command(MMHandleType handle, int command)
324 {
325         int cmd = command;
326         int ret = MM_ERROR_NONE;
327         char *err_attr_name = NULL;
328
329         GstElement *pipeline = NULL;
330         GstElement *audioSrc = NULL;
331
332         mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
333         _MMCamcorderSubContext *sc = NULL;
334         _MMCamcorderAudioInfo *info = NULL;
335
336         mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
337         sc = MMF_CAMCORDER_SUBCONTEXT(handle);
338
339         mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
340         mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
341         mmf_return_val_if_fail(sc->info_audio, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
342         pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
343         info = sc->info_audio;
344
345         _mmcam_dbg_log("");
346
347         pipeline = sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst;
348         audioSrc = sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst;
349         switch (cmd) {
350         case _MMCamcorder_CMD_RECORD:
351                 /* check status for resume case */
352                 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) != MM_CAMCORDER_STATE_PAUSED) {
353                         gboolean storage_validity = FALSE;
354                         guint imax_size = 0;
355                         guint imax_time = 0;
356                         char *target_filename = NULL;
357                         int filename_length = 0;
358                         int root_directory_length = 0;
359
360                         if (sc->pipeline_time)
361                                 gst_element_set_start_time(pipeline, sc->pipeline_time);
362
363                         sc->pipeline_time = RESET_PAUSE_TIME;
364
365                         ret = mm_camcorder_get_attributes(handle, &err_attr_name,
366                                 MMCAM_TARGET_MAX_SIZE, &imax_size,
367                                 MMCAM_TARGET_TIME_LIMIT, &imax_time,
368                                 MMCAM_FILE_FORMAT, &(info->fileformat),
369                                 MMCAM_TARGET_FILENAME, &target_filename, &filename_length,
370                                 MMCAM_ROOT_DIRECTORY, &hcamcorder->root_directory, &root_directory_length,
371                                 NULL);
372                         if (ret != MM_ERROR_NONE) {
373                                 _mmcam_dbg_warn("failed to get attribute. (%s:%x)", err_attr_name, ret);
374                                 SAFE_FREE(err_attr_name);
375                                 goto _ERR_CAMCORDER_AUDIO_COMMAND;
376                         }
377
378                         if (!target_filename && !hcamcorder->mstream_cb) {
379                                 _mmcam_dbg_err("filename is not set and muxed stream cb is NULL");
380                                 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
381                                 goto _ERR_CAMCORDER_AUDIO_COMMAND;
382                         }
383
384                         SAFE_G_FREE(info->filename);
385
386                         if (target_filename) {
387                                 info->filename = g_strdup(target_filename);
388                                 if (!info->filename) {
389                                         _mmcam_dbg_err("STRDUP was failed for [%s]", target_filename);
390                                         goto _ERR_CAMCORDER_AUDIO_COMMAND;
391                                 }
392
393                                 _mmcam_dbg_log("Record start : file name [%s]", info->filename);
394
395                                 MMCAMCORDER_G_OBJECT_SET_POINTER(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename);
396                         } else {
397                                 _mmcam_dbg_log("Recorded data will be written in [%s]", _MMCamcorder_FILENAME_NULL);
398                                 MMCAMCORDER_G_OBJECT_SET_POINTER(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", _MMCamcorder_FILENAME_NULL);
399                         }
400
401                         sc->ferror_send = FALSE;
402                         sc->ferror_count = 0;
403                         sc->bget_eos = FALSE;
404                         sc->muxed_stream_offset = 0;
405                         info->filesize = 0;
406
407                         /* set max size */
408                         if (imax_size <= 0)
409                                 info->max_size = 0; /* do not check */
410                         else
411                                 info->max_size = ((guint64)imax_size) << 10; /* to byte */
412
413                         /* set max time */
414                         if (imax_time <= 0)
415                                 info->max_time = 0; /* do not check */
416                         else
417                                 info->max_time = ((guint64)imax_time) * 1000; /* to millisecond */
418
419                         ret = _mmcamcorder_get_storage_validity(handle, info->filename,
420                                 _MMCAMCORDER_AUDIO_MINIMUM_SPACE, &storage_validity);
421                         if (ret != MM_ERROR_NONE || !storage_validity) {
422                                 _mmcam_dbg_err("storage validation failed[0x%x]:%d", ret, storage_validity);
423                                 return ret;
424                         }
425
426                         _mmcamcorder_adjust_recording_max_size(info->filename, &info->max_size);
427                 }
428
429                 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
430                 if (ret != MM_ERROR_NONE)
431                         goto _ERR_CAMCORDER_AUDIO_COMMAND;
432
433                 break;
434
435         case _MMCamcorder_CMD_PAUSE:
436         {
437                 GstClock *pipe_clock = NULL;
438                 int count = 0;
439
440                 if (info->b_committing) {
441                         _mmcam_dbg_warn("now on commiting previous file!!(cmd : %d)", cmd);
442                         return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
443                 }
444
445                 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
446                         if (info->filesize > 0) {
447                                 break;
448                         } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
449                                 _mmcam_dbg_err("Pause fail, wait 200 ms, but file size is %"G_GUINT64_FORMAT,
450                                         info->filesize);
451                                 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
452                         } else {
453                                 _mmcam_dbg_warn("Wait for enough audio frame, retry count[%d], file size is %"G_GUINT64_FORMAT,
454                                         count, info->filesize);
455                         }
456                         usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
457                 }
458
459                 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PAUSED);
460                 if (ret != MM_ERROR_NONE)
461                         goto _ERR_CAMCORDER_AUDIO_COMMAND;
462
463                 /* FIXME: consider delay. */
464                 pipe_clock = gst_pipeline_get_clock(GST_PIPELINE(pipeline));
465                 sc->pipeline_time = gst_clock_get_time(pipe_clock) - gst_element_get_base_time(GST_ELEMENT(pipeline));
466                 break;
467         }
468
469         case _MMCamcorder_CMD_CANCEL:
470                 if (info->b_committing) {
471                         _mmcam_dbg_warn("now on commiting previous file!!(cmd : %d)", cmd);
472                         return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
473                 }
474
475                 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
476                 if (ret != MM_ERROR_NONE)
477                         goto _ERR_CAMCORDER_AUDIO_COMMAND;
478
479                 if (info->bMuxing)
480                         MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
481                 else
482                         MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", FALSE);
483
484                 _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_NULL);
485
486                 /* Reconstruct audio encoding pipeline,
487                    because some muxer(ex:avmux_amr) should be created newly for next recording after cancel.
488                    Otherwise, it returns error when start next recording. */
489                 _mmcamcorder_destroy_audio_pipeline(handle);
490                 ret = _mmcamcorder_create_audio_pipeline(handle);
491
492                 sc->pipeline_time = 0;
493                 sc->pause_time = 0;
494                 sc->isMaxsizePausing = FALSE;
495                 sc->isMaxtimePausing = FALSE;
496
497                 if (info->filename) {
498                         _mmcam_dbg_log("file delete(%s)", info->filename);
499                         unlink(info->filename);
500                         SAFE_G_FREE(info->filename);
501                 }
502                 break;
503
504         case _MMCamcorder_CMD_COMMIT:
505         {
506                 int count = 0;
507                 guint64 free_space = 0;
508                 _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT");
509
510                 if (info->b_committing) {
511                         _mmcam_dbg_warn("now on commiting previous file!!(cmd : %d)", cmd);
512                         return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
513                 } else {
514                         _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT : start");
515                         info->b_committing = TRUE;
516                 }
517
518                 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
519                         if (info->filesize > 0) {
520                                 break;
521                         } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
522                                 _mmcam_dbg_err("Commit fail, waited 200 ms, but file size is %"G_GUINT64_FORMAT, info->filesize);
523                                         info->b_committing = FALSE;
524                                 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
525                         } else {
526                                 _mmcam_dbg_warn("Waiting for enough audio frame, re-count[%d], file size is %"G_GUINT64_FORMAT,
527                                         count, info->filesize);
528                         }
529                         usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
530                 }
531
532                 _mmcamcorder_get_freespace(hcamcorder->storage_info.type, &free_space);
533                 if (free_space < _MMCAMCORDER_AUDIO_MINIMUM_SPACE) {
534                         _mmcam_dbg_warn("_MMCamcorder_CMD_COMMIT out of storage [%" G_GUINT64_FORMAT "]", free_space);
535                         ret = MM_ERROR_OUT_OF_STORAGE;
536                         goto _ERR_CAMCORDER_AUDIO_COMMAND;
537                 }
538
539                 if (audioSrc) {
540                         if (gst_element_send_event(audioSrc, gst_event_new_eos()) == FALSE) {
541                                 _mmcam_dbg_err("send EOS failed");
542                                 info->b_committing = FALSE;
543                                 ret = MM_ERROR_CAMCORDER_INTERNAL;
544                                 goto _ERR_CAMCORDER_AUDIO_COMMAND;
545                         }
546
547                         _mmcam_dbg_log("send EOS done");
548
549                         /* for pause -> commit case */
550                         if (_mmcamcorder_get_state((MMHandleType)hcamcorder) == MM_CAMCORDER_STATE_PAUSED) {
551                                 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
552                                 if (ret != MM_ERROR_NONE) {
553                                         info->b_committing = FALSE;
554                                         goto _ERR_CAMCORDER_AUDIO_COMMAND;
555                                 }
556                         }
557                 } else {
558                         _mmcam_dbg_err("No audio stream source");
559                         info->b_committing = FALSE;
560                         ret = MM_ERROR_CAMCORDER_INTERNAL;
561                         goto _ERR_CAMCORDER_AUDIO_COMMAND;
562                 }
563
564                 /* wait until finishing EOS */
565                 _mmcam_dbg_log("Start to wait EOS");
566                 if ((ret = _mmcamcorder_get_eos_message(handle)) != MM_ERROR_NONE) {
567                         info->b_committing = FALSE;
568                         goto _ERR_CAMCORDER_AUDIO_COMMAND;
569                 }
570                 break;
571         }
572
573         case _MMCamcorder_CMD_PREVIEW_START:
574                 /*MM_CAMCORDER_START_CHANGE_STATE;*/
575                 break;
576
577         case _MMCamcorder_CMD_PREVIEW_STOP:
578                 /*MM_CAMCORDER_STOP_CHANGE_STATE;*/
579                 /*void*/
580                 break;
581
582         default:
583                 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
584                 break;
585         }
586
587 _ERR_CAMCORDER_AUDIO_COMMAND:
588         return ret;
589 }
590
591 int _mmcamcorder_audio_handle_eos(MMHandleType handle)
592 {
593         int err = MM_ERROR_NONE;
594         mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
595         _MMCamcorderSubContext *sc = NULL;
596         _MMCamcorderAudioInfo *info = NULL;
597         GstElement *pipeline = NULL;
598         _MMCamcorderMsgItem msg;
599         MMCamRecordingReport * report;
600
601         mmf_return_val_if_fail(hcamcorder, FALSE);
602         sc = MMF_CAMCORDER_SUBCONTEXT(handle);
603
604         mmf_return_val_if_fail(sc, FALSE);
605         mmf_return_val_if_fail(sc->info_audio, FALSE);
606
607         _mmcam_dbg_err("");
608
609         info = sc->info_audio;
610
611         pipeline = sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst;
612
613         err = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
614         if (err != MM_ERROR_NONE)
615                 _mmcam_dbg_warn("Failed:_MMCamcorder_CMD_COMMIT:GST_STATE_READY. err[%x]", err);
616
617         /* Send recording report message to application */
618         msg.id = MM_MESSAGE_CAMCORDER_AUDIO_CAPTURED;
619         report = (MMCamRecordingReport*) g_malloc(sizeof(MMCamRecordingReport));
620         if (!report) {
621                 _mmcam_dbg_err("Recording report fail(%s). Out of memory.", info->filename);
622                 return FALSE;
623         }
624
625 /* START TAG HERE */
626         /* MM_AUDIO_CODEC_AAC + MM_FILE_FORMAT_MP4 */
627         if (info->fileformat == MM_FILE_FORMAT_3GP || info->fileformat == MM_FILE_FORMAT_MP4)
628                 __mmcamcorder_audio_add_metadata_info_m4a(handle);
629 /* END TAG HERE */
630
631         report->recording_filename = g_strdup(info->filename);
632         msg.param.data = report;
633
634         _mmcamcorder_send_message(handle, &msg);
635
636         if (info->bMuxing)
637                 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
638         else
639                 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", FALSE);
640
641         _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_NULL);
642
643         sc->pipeline_time = 0;
644         sc->pause_time = 0;
645         sc->isMaxsizePausing = FALSE;
646         sc->isMaxtimePausing = FALSE;
647
648         SAFE_G_FREE(info->filename);
649
650         _mmcam_dbg_err("_MMCamcorder_CMD_COMMIT : end");
651
652         info->b_committing = FALSE;
653
654         return TRUE;
655 }
656
657
658 static float
659 __mmcamcorder_get_decibel(unsigned char* raw, int size, MMCamcorderAudioFormat format)
660 {
661         #define MAX_AMPLITUDE_MEAN_16BIT (23170.115738161934)
662         #define MAX_AMPLITUDE_MEAN_08BIT (89.803909382810)
663         #define DEFAULT_DECIBEL          (-80.0)
664
665         int i = 0;
666         int depthByte = 0;
667         int count = 0;
668
669         short* pcm16 = 0;
670         char* pcm8 = 0;
671
672         float db = DEFAULT_DECIBEL;
673         float rms = 0.0;
674         unsigned long long square_sum = 0;
675
676         if (format == MM_CAMCORDER_AUDIO_FORMAT_PCM_S16_LE)
677                 depthByte = 2;
678         else            /*MM_CAMCORDER_AUDIO_FORMAT_PCM_U8*/
679                 depthByte = 1;
680
681         for ( ; i < size ; i += (depthByte<<1)) {
682                 if (depthByte == 1) {
683                         pcm8 = (char *)(raw + i);
684                         square_sum += (*pcm8) * (*pcm8);
685                 } else { /*2byte*/
686                         pcm16 = (short*)(raw + i);
687                         square_sum += (*pcm16) * (*pcm16);
688                 }
689
690                 count++;
691         }
692
693         if (count > 0) {
694                 rms = sqrt((double)square_sum/(double)count);
695
696                 if (depthByte == 1)
697                         db = 20 * log10(rms/MAX_AMPLITUDE_MEAN_08BIT);
698                 else
699                         db = 20 * log10(rms/MAX_AMPLITUDE_MEAN_16BIT);
700         }
701
702         /*
703         _mmcam_dbg_log("size[%d],depthByte[%d],count[%d],rms[%f],db[%f]",
704                                         size, depthByte, count, rms, db);
705         */
706
707         return db;
708 }
709
710
711 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_voicerecorder(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
712 {
713         mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
714         double volume = 0.0;
715         int current_state = MM_CAMCORDER_STATE_NONE;
716         int format = 0;
717         int channel = 0;
718         float curdcb = 0.0;
719         _MMCamcorderMsgItem msg;
720         int err = MM_ERROR_UNKNOWN;
721         char *err_name = NULL;
722         GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
723         GstMapInfo mapinfo;
724
725         mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
726
727         current_state = _mmcamcorder_get_state((MMHandleType)hcamcorder);
728         if (current_state < MM_CAMCORDER_STATE_PREPARE) {
729                 _mmcam_dbg_warn("Not ready for stream callback");
730                 return GST_PAD_PROBE_OK;
731         }
732
733         memset(&mapinfo, 0x0, sizeof(GstMapInfo));
734
735         /* Set volume to audio input */
736         err = mm_camcorder_get_attributes((MMHandleType)hcamcorder, &err_name,
737                 MMCAM_AUDIO_VOLUME, &volume,
738                 MMCAM_AUDIO_FORMAT, &format,
739                 MMCAM_AUDIO_CHANNEL, &channel,
740                 NULL);
741
742         if (err < 0) {
743                 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, err);
744                 SAFE_FREE(err_name);
745                 return err;
746         }
747
748         gst_buffer_map(buffer, &mapinfo, GST_MAP_READWRITE);
749
750         if (volume == 0)
751                 memset(mapinfo.data, 0, mapinfo.size);
752
753         /* Get current volume level of real input stream */
754         curdcb = __mmcamcorder_get_decibel(mapinfo.data, mapinfo.size, format);
755
756         msg.id = MM_MESSAGE_CAMCORDER_CURRENT_VOLUME;
757         msg.param.rec_volume_dB = curdcb;
758         _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
759
760         _MMCAMCORDER_LOCK_ASTREAM_CALLBACK(hcamcorder);
761
762         /* CALL audio stream callback */
763         if (hcamcorder->astream_cb && buffer && mapinfo.data && mapinfo.size > 0) {
764                 MMCamcorderAudioStreamDataType stream;
765
766                 /*
767                 _mmcam_dbg_log("Call audio steramCb, data[%p], format[%d], channel[%d], length[%d], volume_dB[%f]",
768                         GST_BUFFER_DATA(buffer), format, channel, GST_BUFFER_SIZE(buffer), curdcb);
769                 */
770
771                 stream.data = (void *)mapinfo.data;
772                 stream.format = format;
773                 stream.channel = channel;
774                 stream.length = mapinfo.size;
775                 stream.timestamp = (unsigned int)(GST_BUFFER_PTS(buffer)/1000000);      /* nano -> msecond */
776                 stream.volume_dB = curdcb;
777
778                 hcamcorder->astream_cb(&stream, hcamcorder->astream_cb_param);
779         }
780
781         _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK(hcamcorder);
782
783         gst_buffer_unmap(buffer, &mapinfo);
784
785         return GST_PAD_PROBE_OK;
786 }
787
788
789 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
790 {
791         static int count = 0;
792         guint64 rec_pipe_time = 0;
793         guint64 free_space = 0;
794         guint64 buffer_size = 0;
795         guint64 trailer_size = 0;
796         unsigned long long remained_time = 0;
797         int get_trailer_size = 0;
798
799         _MMCamcorderSubContext *sc = NULL;
800         mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
801         _MMCamcorderAudioInfo *audioinfo = NULL;
802         _MMCamcorderMsgItem msg;
803         GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
804
805         mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_DROP);
806         mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
807
808         sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
809         mmf_return_val_if_fail(sc && sc->info_audio, GST_PAD_PROBE_DROP);
810         audioinfo = sc->info_audio;
811
812         if (sc->isMaxtimePausing || sc->isMaxsizePausing) {
813                 _mmcam_dbg_warn("isMaxtimePausing[%d],isMaxsizePausing[%d]",
814                         sc->isMaxtimePausing, sc->isMaxsizePausing);
815                 return GST_PAD_PROBE_DROP;
816         }
817
818         buffer_size = gst_buffer_get_size(buffer);
819
820         if (audioinfo->filesize == 0) {
821                 if (audioinfo->fileformat == MM_FILE_FORMAT_WAV)
822                         audioinfo->filesize += 44; /* wave header size */
823                 else if (audioinfo->fileformat == MM_FILE_FORMAT_AMR)
824                         audioinfo->filesize += 6; /* amr header size */
825
826                 audioinfo->filesize += buffer_size;
827                 return GST_PAD_PROBE_OK;
828         }
829
830         if (sc->ferror_send) {
831                 _mmcam_dbg_warn("file write error, drop frames");
832                 return GST_PAD_PROBE_DROP;
833         }
834
835         /* get trailer size */
836         get_trailer_size = (
837                 audioinfo->fileformat == MM_FILE_FORMAT_3GP ||
838                 audioinfo->fileformat == MM_FILE_FORMAT_MP4 ||
839                 audioinfo->fileformat == MM_FILE_FORMAT_AAC) ? TRUE : FALSE;
840         if (get_trailer_size) {
841                 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
842                 /*_mmcam_dbg_log("trailer_size %d", trailer_size);*/
843         } else {
844                 trailer_size = 0; /* no trailer */
845         }
846
847         /* to minimizing free space check overhead */
848         count = count % _MMCAMCORDER_FREE_SPACE_CHECK_INTERVAL;
849         if (count++ == 0) {
850                 gint free_space_ret = 0;
851                 storage_state_e storage_state = STORAGE_STATE_UNMOUNTABLE;
852
853                 /* check free space */
854                 free_space_ret = _mmcamcorder_get_freespace(hcamcorder->storage_info.type, &free_space);
855                 if (free_space_ret != 0) {
856                         _mmcam_dbg_err("Error occured. [%d]", free_space_ret);
857                         if (sc->ferror_count == 2 && sc->ferror_send == FALSE) {
858                                 sc->ferror_send = TRUE;
859
860                                 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
861                                 msg.param.code = MM_ERROR_FILE_READ;
862
863                                 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
864                         } else {
865                                 sc->ferror_count++;
866                         }
867
868                         return GST_PAD_PROBE_DROP; /* skip this buffer */
869                 }
870
871                 if (free_space == 0) {
872                         /* check storage state */
873                         storage_get_state(hcamcorder->storage_info.id, &storage_state);
874
875                         _mmcam_dbg_warn("storage state %d", storage_state);
876
877                         if (storage_state == STORAGE_STATE_REMOVED ||
878                                 storage_state == STORAGE_STATE_UNMOUNTABLE) {
879                                 _mmcam_dbg_err("storage was removed!");
880
881                                 _MMCAMCORDER_LOCK(hcamcorder);
882
883                                 if (sc->ferror_send == FALSE) {
884                                         _mmcam_dbg_err("OUT_OF_STORAGE error");
885
886                                         sc->ferror_send = TRUE;
887
888                                         _MMCAMCORDER_UNLOCK(hcamcorder);
889
890                                         msg.id = MM_MESSAGE_CAMCORDER_ERROR;
891                                         msg.param.code = MM_ERROR_OUT_OF_STORAGE;
892
893                                         _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
894                                 } else {
895                                         _MMCAMCORDER_UNLOCK(hcamcorder);
896                                         _mmcam_dbg_warn("error was already sent");
897                                 }
898
899                                 return GST_PAD_PROBE_DROP;
900                         }
901                 }
902
903                 if (free_space < (guint64)(_MMCAMCORDER_AUDIO_MINIMUM_SPACE + buffer_size + trailer_size)) {
904                         _mmcam_dbg_warn("No more space for recording!!!");
905                         _mmcam_dbg_warn("Free Space : [%" G_GUINT64_FORMAT "], file size : [%" G_GUINT64_FORMAT "]",
906                                 free_space, audioinfo->filesize);
907
908                         if (audioinfo->bMuxing)
909                                 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
910                         else
911                                 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", TRUE);
912
913                         sc->isMaxsizePausing = TRUE;
914                         msg.id = MM_MESSAGE_CAMCORDER_NO_FREE_SPACE;
915                         _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
916
917                         return GST_PAD_PROBE_DROP; /* skip this buffer */
918                 }
919         }
920
921         if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_PTS(buffer))) {
922                 _mmcam_dbg_err("Buffer timestamp is invalid, check it");
923                 return GST_PAD_PROBE_DROP;
924         }
925
926         rec_pipe_time = GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer));
927
928         /* calculate remained time can be recorded */
929         if (audioinfo->max_time > 0 && audioinfo->max_time < (remained_time + rec_pipe_time)) {
930                 remained_time = audioinfo->max_time - rec_pipe_time;
931         } else if (audioinfo->max_size > 0) {
932                 long double max_size = (long double)audioinfo->max_size;
933                 long double current_size = (long double)(audioinfo->filesize + buffer_size + trailer_size);
934
935                 remained_time = (unsigned long long)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
936         }
937
938         /*_mmcam_dbg_log("remained time : %u", remained_time);*/
939
940         /* check max size of recorded file */
941         if (audioinfo->max_size > 0 &&
942                         audioinfo->max_size < audioinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) {
943                 _mmcam_dbg_warn("Max size!!! Recording is paused.");
944                 _mmcam_dbg_warn("Max [%" G_GUINT64_FORMAT "], file [%" G_GUINT64_FORMAT "], trailer : [%" G_GUINT64_FORMAT "]", \
945                         audioinfo->max_size, audioinfo->filesize, trailer_size);
946
947                 /* just same as pause status. After blocking two queue, this function will not call again. */
948                 if (audioinfo->bMuxing)
949                         MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
950                 else
951                         MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", TRUE);
952
953                 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
954                 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
955                 msg.param.recording_status.filesize = (unsigned long long)((audioinfo->filesize + trailer_size) >> 10);
956                 msg.param.recording_status.remained_time = 0;
957                 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
958
959                 _mmcam_dbg_warn("Last filesize sent by message : %"G_GUINT64_FORMAT, audioinfo->filesize + trailer_size);
960
961                 sc->isMaxsizePausing = TRUE;
962                 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
963                 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
964
965                 /* skip this buffer */
966                 return GST_PAD_PROBE_DROP;
967         }
968
969         /* check recording time limit and send recording status message */
970         if (audioinfo->max_time > 0 && rec_pipe_time > audioinfo->max_time) {
971                 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
972                         rec_pipe_time, audioinfo->max_time);
973
974                 if (audioinfo->bMuxing)
975                         MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
976                 else
977                         MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", TRUE);
978
979                 sc->isMaxtimePausing = TRUE;
980                 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
981                 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
982
983                 /* skip this buffer */
984                 return GST_PAD_PROBE_DROP;
985         }
986
987         /* send message for recording time and recorded file size */
988         if (audioinfo->b_committing == FALSE) {
989                 audioinfo->filesize += buffer_size;
990
991                 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
992                 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
993                 msg.param.recording_status.filesize = (unsigned long long)((audioinfo->filesize + trailer_size) >> 10);
994                 msg.param.recording_status.remained_time = remained_time;
995                 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
996
997                 return GST_PAD_PROBE_OK;
998         } else {
999                 /* skip this buffer if commit process has been started */
1000                 return GST_PAD_PROBE_DROP;
1001         }
1002 }
1003
1004 /* START TAG HERE */
1005 static gboolean __mmcamcorder_audio_add_metadata_info_m4a(MMHandleType handle)
1006 {
1007         FILE *f = NULL;
1008         guchar buf[4];
1009         guint64 udta_size = 0;
1010         gint64 current_pos = 0;
1011         gint64 moov_pos = 0;
1012         gint64 udta_pos = 0;
1013         /* supporting audio geo tag for mobile */
1014         int gps_enable = 0;
1015         gdouble longitude = 0;
1016         gdouble latitude = 0;
1017         gdouble altitude = 0;
1018         _MMCamcorderLocationInfo geo_info = {0, 0, 0};
1019         _MMCamcorderLocationInfo loc_info = {0, 0, 0};
1020
1021         char err_msg[128] = {'\0',};
1022
1023         _MMCamcorderAudioInfo *info = NULL;
1024         mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1025         _MMCamcorderSubContext *sc = NULL;
1026
1027         mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1028         sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1029
1030         mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1031         mmf_return_val_if_fail(sc->info_audio, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1032
1033         info = sc->info_audio;
1034         mm_camcorder_get_attributes(handle, NULL,
1035                 MMCAM_TAG_GPS_ENABLE, &gps_enable,
1036                 NULL);
1037
1038         if (gps_enable) {
1039                 mm_camcorder_get_attributes(handle, NULL,
1040                         MMCAM_TAG_LATITUDE, &latitude,
1041                         MMCAM_TAG_LONGITUDE, &longitude,
1042                         MMCAM_TAG_ALTITUDE, &altitude,
1043                         NULL);
1044                 loc_info.longitude = _mmcamcorder_double_to_fix(longitude);
1045                 loc_info.latitude = _mmcamcorder_double_to_fix(latitude);
1046                 loc_info.altitude = _mmcamcorder_double_to_fix(altitude);
1047                 geo_info.longitude = longitude *10000;
1048                 geo_info.latitude = latitude *10000;
1049                 geo_info.altitude = altitude *10000;
1050         }
1051
1052         f = fopen64(info->filename, "rb+");
1053         if (f == NULL) {
1054                 strerror_r(errno, err_msg, 128);
1055                 _mmcam_dbg_err("file open failed [%s]", err_msg);
1056                 return FALSE;
1057         }
1058
1059         /* find udta container.
1060            if, there are udta container, write loci box after that
1061            else, make udta container and write loci box. */
1062         if (_mmcamcorder_find_fourcc(f, MMCAM_FOURCC('u', 'd', 't', 'a'), TRUE)) {
1063                 size_t nread = 0;
1064
1065                 _mmcam_dbg_log("find udta container");
1066
1067                 /* read size */
1068                 if (fseek(f, -8L, SEEK_CUR) != 0)
1069                         goto fail;
1070
1071                 udta_pos = ftello(f);
1072                 if (udta_pos < 0)
1073                         goto ftell_fail;
1074
1075                 nread = fread(&buf, sizeof(char), sizeof(buf), f);
1076
1077                 _mmcam_dbg_log("recorded file fread %zu", nread);
1078
1079                 udta_size = _mmcamcorder_get_container_size(buf);
1080
1081                 /* goto end of udta and write 'smta' box */
1082                 if (fseek(f, (udta_size-4L), SEEK_CUR) != 0)
1083                         goto fail;
1084
1085                 if (gps_enable) {
1086                         if (!_mmcamcorder_write_loci(f, loc_info))
1087                                 goto fail;
1088
1089                         if (!_mmcamcorder_write_geodata(f, geo_info))
1090                                 goto fail;
1091                 }
1092
1093                 current_pos = ftello(f);
1094                 if (current_pos < 0)
1095                         goto ftell_fail;
1096
1097                 if (!_mmcamcorder_update_size(f, udta_pos, current_pos))
1098                         goto fail;
1099         } else {
1100                 _mmcam_dbg_log("No udta container");
1101                 if (fseek(f, 0, SEEK_END) != 0)
1102                         goto fail;
1103
1104                 if (!_mmcamcorder_write_udta(f, gps_enable, loc_info, geo_info))
1105                         goto fail;
1106         }
1107
1108         /* find moov container.
1109            update moov container size. */
1110         if ((current_pos = ftello(f)) < 0)
1111                 goto ftell_fail;
1112
1113         if (_mmcamcorder_find_fourcc(f, MMCAM_FOURCC('m', 'o', 'o', 'v'), TRUE)) {
1114
1115                 _mmcam_dbg_log("found moov container");
1116                 if (fseek(f, -8L, SEEK_CUR) != 0)
1117                         goto fail;
1118
1119                 moov_pos = ftello(f);
1120                 if (moov_pos < 0)
1121                         goto ftell_fail;
1122
1123                 if (!_mmcamcorder_update_size(f, moov_pos, current_pos))
1124                         goto fail;
1125
1126         } else {
1127                 _mmcam_dbg_err("No 'moov' container");
1128                 goto fail;
1129         }
1130
1131         fclose(f);
1132
1133         return TRUE;
1134
1135 fail:
1136         fclose(f);
1137         return FALSE;
1138
1139 ftell_fail:
1140         _mmcam_dbg_err("ftell() returns negative value.");
1141         fclose(f);
1142         return FALSE;
1143 }
1144
1145 /* END TAG HERE */
1146