4 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
6 * Contact: Jeongmo Yang <jm80.yang@samsung.com>
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 /*=======================================================================================
24 =======================================================================================*/
25 #include "mm_camcorder_internal.h"
26 #include "mm_camcorder_audiorec.h"
27 #include "mm_camcorder_util.h"
30 /*---------------------------------------------------------------------------------------
31 | GLOBAL VARIABLE DEFINITIONS for internal |
32 ---------------------------------------------------------------------------------------*/
33 #define MM_CAMCORDER_START_CHANGE_STATE _MMCamcorderStartHelperFunc((void *)hcamcorder)
34 #define MM_CAMCORDER_STOP_CHANGE_STATE _MMCamcorderStopHelperFunc((void *)hcamcorder)
35 /*---------------------------------------------------------------------------------------
36 | LOCAL VARIABLE DEFINITIONS for internal |
37 ---------------------------------------------------------------------------------------*/
38 #define RESET_PAUSE_TIME 0
39 #define _MMCAMCORDER_AUDIO_MINIMUM_SPACE (100*1024)
40 #define _MMCAMCORDER_AUDIO_MARGIN_SPACE (1*1024)
41 #define _MMCAMCORDER_RETRIAL_COUNT 10
42 #define _MMCAMCORDER_FRAME_WAIT_TIME 20000 /* micro second */
43 #define _MMCAMCORDER_FREE_SPACE_CHECK_INTERVAL 10
45 /*---------------------------------------------------------------------------------------
46 | LOCAL FUNCTION PROTOTYPES: |
47 ---------------------------------------------------------------------------------------*/
48 /* STATIC INTERNAL FUNCTION */
49 static gboolean __mmcamcorder_audio_dataprobe_voicerecorder(GstPad *pad, GstBuffer *buffer, gpointer u_data);
50 static gboolean __mmcamcorder_audio_dataprobe_record(GstPad *pad, GstBuffer *buffer, gpointer u_data);
51 static int __mmcamcorder_create_audiop_with_encodebin(MMHandleType handle);
52 static void __mmcamcorder_audiorec_pad_added_cb(GstElement *element, GstPad *pad, MMHandleType handle);
53 static gboolean __mmcamcorder_audio_add_metadata_info_m4a(MMHandleType handle);
55 /*=======================================================================================
56 | FUNCTION DEFINITIONS |
57 =======================================================================================*/
59 /*---------------------------------------------------------------------------------------
60 | GLOBAL FUNCTION DEFINITIONS: |
61 ---------------------------------------------------------------------------------------*/
63 static int __mmcamcorder_create_audiop_with_encodebin(MMHandleType handle)
65 int err = MM_ERROR_NONE;
66 const char *aenc_name = NULL;
67 const char *mux_name = NULL;
70 GstPad *srcpad = NULL;
71 GstPad *sinkpad = NULL;
72 GList *element_list = NULL;
74 _MMCamcorderAudioInfo *info = NULL;
75 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
76 _MMCamcorderSubContext *sc = NULL;
77 type_element *aenc_elem = NULL;
78 type_element *mux_elem = NULL;
80 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
81 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
83 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
84 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
85 mmf_return_val_if_fail(sc->info_audio, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
87 info = (_MMCamcorderAudioInfo *)sc->info_audio;
91 mux_elem = _mmcamcorder_get_type_element(handle, MM_CAM_FILE_FORMAT);
92 err = _mmcamcorder_conf_get_value_element_name( mux_elem, &mux_name );
94 if (!mux_name || !strcmp(mux_name, "wavenc")) {
95 /* IF MUX in not chosen then record in raw file */
96 _mmcam_dbg_log("Record without muxing.");
97 info->bMuxing = FALSE;
99 _mmcam_dbg_log("Record with mux.");
100 info->bMuxing = TRUE;
103 /* Create GStreamer pipeline */
104 _MMCAMCORDER_PIPELINE_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCODE_MAIN_PIPE, "camcorder_pipeline", err);
106 err = _mmcamcorder_create_audiosrc_bin(handle);
107 if (err != MM_ERROR_NONE) {
112 /* Muxing. can use encodebin. */
113 err = _mmcamcorder_create_encodesink_bin((MMHandleType)hcamcorder, MM_CAMCORDER_ENCBIN_PROFILE_AUDIO);
114 if (err != MM_ERROR_NONE) {
118 /* without muxing. can't use encodebin. */
119 aenc_elem = _mmcamcorder_get_type_element(handle, MM_CAM_AUDIO_ENCODER);
121 _mmcam_dbg_err("Fail to get type element");
122 err = MM_ERROR_CAMCORDER_RESOURCE_CREATION;
123 goto pipeline_creation_error;
126 err = _mmcamcorder_conf_get_value_element_name(aenc_elem, &aenc_name);
127 if ((!err) || (!aenc_name)) {
128 _mmcam_dbg_err("Fail to get element name");
129 err = MM_ERROR_CAMCORDER_RESOURCE_CREATION;
130 goto pipeline_creation_error;
133 _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_AQUE, "queue", NULL, element_list, err);
135 if (strcmp(aenc_name, "wavenc") != 0) {
136 _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_CONV, "audioconvert", NULL, element_list, err);
139 _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_AENC, aenc_name, NULL, element_list, err);
141 _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_SINK, "filesink", NULL, element_list, err);
144 /* Add and link elements */
146 /* IF MUX is indicated create MUX */
147 gst_bin_add_many(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst),
148 sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst,
149 sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst,
152 srcpad = gst_element_get_static_pad (sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst, "src");
153 sinkpad = gst_element_get_static_pad (sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst, "audio_sink0");
154 _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error);
156 /* IF MUX in not chosen then record in raw amr file */
157 if (!strcmp(aenc_name, "wavenc")) {
158 gst_bin_add_many(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst),
159 sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst,
160 sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst,
161 sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst,
162 sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst,
165 if (!_MM_GST_ELEMENT_LINK_MANY(sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst,
166 sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst,
167 sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst,
168 sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst,
170 err = MM_ERROR_CAMCORDER_GST_LINK;
171 goto pipeline_creation_error;
174 gst_bin_add_many(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst),
175 sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst,
176 sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst,
177 sc->encode_element[_MMCAMCORDER_ENCSINK_CONV].gst,
178 sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst,
179 sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst,
182 if (!_MM_GST_ELEMENT_LINK_MANY(sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst,
183 sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst,
184 sc->encode_element[_MMCAMCORDER_ENCSINK_CONV].gst,
185 sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst,
186 sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst,
188 err = MM_ERROR_CAMCORDER_GST_LINK;
189 goto pipeline_creation_error;
194 /* set data probe function */
195 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst, "src");
196 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_AUDIOREC,
197 __mmcamcorder_audio_dataprobe_voicerecorder, hcamcorder);
198 gst_object_unref(srcpad);
202 MMCAMCORDER_SIGNAL_CONNECT(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst,
203 _MMCAMCORDER_HANDLER_AUDIOREC,
205 __mmcamcorder_audiorec_pad_added_cb,
208 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst, "src");
209 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_AUDIOREC,
210 __mmcamcorder_audio_dataprobe_record, hcamcorder);
211 gst_object_unref(srcpad);
215 bus = gst_pipeline_get_bus(GST_PIPELINE(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst));
217 /* register message callback */
218 hcamcorder->pipeline_cb_event_id = gst_bus_add_watch(bus, (GstBusFunc)_mmcamcorder_pipeline_cb_message, hcamcorder);
220 /* set sync callback */
221 gst_bus_set_sync_handler(bus, _mmcamcorder_audio_pipeline_bus_sync_callback, hcamcorder);
223 gst_object_unref(bus);
227 g_list_free(element_list);
231 return MM_ERROR_NONE;
233 pipeline_creation_error:
234 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCODE_MAIN_PIPE);
235 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_AUDIOSRC_BIN);
236 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_AQUE);
237 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_CONV);
238 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_AENC);
239 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_SINK);
240 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_BIN);
243 g_list_free(element_list);
252 _mmcamcorder_create_audio_pipeline(MMHandleType handle)
254 mmf_camcorder_t *hcamcorder= MMF_CAMCORDER(handle);
255 _MMCamcorderSubContext *sc = NULL;
257 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
258 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
260 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
262 return __mmcamcorder_create_audiop_with_encodebin(handle);
267 * This function destroy audio pipeline.
269 * @param[in] handle Handle of camcorder.
272 * @see _mmcamcorder_destroy_pipeline()
276 _mmcamcorder_destroy_audio_pipeline(MMHandleType handle)
278 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
279 _MMCamcorderSubContext *sc = NULL;
280 _MMCamcorderAudioInfo *info = NULL;
281 mmf_return_if_fail(hcamcorder);
282 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
284 mmf_return_if_fail(sc && sc->info_audio);
285 mmf_return_if_fail(sc->element);
287 info = sc->info_audio;
289 _mmcam_dbg_log("start");
291 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst) {
292 _mmcam_dbg_warn("release audio pipeline");
294 _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_NULL);
296 _mmcamcorder_remove_all_handlers((MMHandleType)hcamcorder, _MMCAMCORDER_HANDLER_CATEGORY_ALL);
299 GstPad *reqpad = NULL;
302 The ref_count of mux is always # of streams in here, i don't know why it happens.
303 So, i unref the mux manually
305 reqpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "audio");
306 gst_element_release_request_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, reqpad);
307 gst_object_unref(reqpad);
309 if(GST_IS_ELEMENT(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst) &&
310 GST_OBJECT_REFCOUNT(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst) > 1) {
311 gst_object_unref(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst);
314 gst_object_unref(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst);
317 _mmcam_dbg_log("done");
324 * This function operates each command on audio mode.
326 * @param c [in] Handle of camcorder context.
327 * @param command [in] command type received from Multimedia Framework.
329 * @return This function returns MM_ERROR_NONE on success, or the other values
332 * @see _mmcamcorder_set_functions()
338 void* _MMCamcorderStartHelperFunc(void *handle)
340 mmf_camcorder_t *hcamcorder= MMF_CAMCORDER(handle);
341 _mmcamcorder_set_state((MMHandleType)hcamcorder, hcamcorder->target_state);
346 void* _MMCamcorderStopHelperFunc(void *handle)
348 mmf_camcorder_t *hcamcorder= MMF_CAMCORDER(handle);
349 _mmcamcorder_set_state((MMHandleType)hcamcorder, hcamcorder->target_state);
356 _mmcamcorder_audio_command(MMHandleType handle, int command)
359 int ret = MM_ERROR_NONE;
362 guint64 free_space = 0;
363 guint64 free_space_exceptsystem=0;
364 char *dir_name = NULL;
365 char *err_attr_name = NULL;
367 GstElement *pipeline = NULL;
368 GstElement *audioSrc = NULL;
370 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
371 _MMCamcorderSubContext *sc = NULL;
372 _MMCamcorderAudioInfo *info = NULL;
374 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
375 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
377 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
378 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
379 mmf_return_val_if_fail(sc->info_audio, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
380 pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
381 info = sc->info_audio;
385 pipeline = sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst;
386 audioSrc = sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst;
388 case _MMCamcorder_CMD_RECORD:
389 /* check status for resume case */
390 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) != MM_CAMCORDER_STATE_PAUSED) {
393 char *temp_filename = NULL;
394 int file_system_type = 0;
396 if(sc->pipeline_time) {
397 gst_pipeline_set_new_stream_time(GST_PIPELINE(pipeline), sc->pipeline_time);
399 sc->pipeline_time = RESET_PAUSE_TIME;
401 ret = mm_camcorder_get_attributes(handle, &err_attr_name,
402 MMCAM_TARGET_MAX_SIZE, &imax_size,
403 MMCAM_TARGET_TIME_LIMIT, &imax_time,
404 MMCAM_FILE_FORMAT, &(info->fileformat),
405 MMCAM_TARGET_FILENAME, &temp_filename, &size,
407 if (ret != MM_ERROR_NONE) {
408 _mmcam_dbg_warn("failed to get attribute. (%s:%x)", err_attr_name, ret);
409 SAFE_FREE(err_attr_name);
410 goto _ERR_CAMCORDER_AUDIO_COMMAND;
413 if (temp_filename == NULL) {
414 _mmcam_dbg_err("filename is not set");
415 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
416 goto _ERR_CAMCORDER_AUDIO_COMMAND;
419 info->filename = strdup(temp_filename);
420 if (!info->filename) {
421 _mmcam_dbg_err("STRDUP was failed");
422 goto _ERR_CAMCORDER_AUDIO_COMMAND;
425 _mmcam_dbg_log("Record start : set file name using attribute - %s\n ",info->filename);
427 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename);
429 sc->ferror_send = FALSE;
430 sc->ferror_count = 0;
431 sc->bget_eos = FALSE;
435 if (imax_size <= 0) {
436 info->max_size = 0; /* do not check */
438 info->max_size = ((guint64)imax_size) << 10; /* to byte */
442 if (imax_time <= 0) {
443 info->max_time = 0; /* do not check */
445 info->max_time = ((guint64)imax_time) * 1000; /* to millisecond */
448 /* TODO : check free space before recording start, need to more discussion */
449 dir_name = g_path_get_dirname(info->filename);
451 err = _mmcamcorder_get_freespace(dir_name, &free_space);
452 if(_mmcamcorder_check_file_path(dir_name)) {
453 err = _mmcamcorder_get_freespace_except_system(&free_space_exceptsystem);
454 hcamcorder->system_memory = free_space - free_space_exceptsystem;
455 free_space = free_space - hcamcorder->system_memory;
458 _mmcam_dbg_warn("current space - %s [%" G_GUINT64_FORMAT "], system [%" G_GUINT64_FORMAT "]",
459 dir_name, free_space, hcamcorder->system_memory);
461 if (_mmcamcorder_get_file_system_type(dir_name, &file_system_type) == 0) {
462 /* MSDOS_SUPER_MAGIC : 0x4d44 */
463 if (file_system_type == MSDOS_SUPER_MAGIC &&
464 (info->max_size == 0 || info->max_size > FAT32_FILE_SYSTEM_MAX_SIZE)) {
465 _mmcam_dbg_warn("FAT32 and too large max[%"G_GUINT64_FORMAT"], set max as %"G_GUINT64_FORMAT,
466 info->max_size, FAT32_FILE_SYSTEM_MAX_SIZE);
467 info->max_size = FAT32_FILE_SYSTEM_MAX_SIZE;
469 _mmcam_dbg_warn("file system 0x%x, max size %"G_GUINT64_FORMAT,
470 file_system_type, info->max_size);
473 _mmcam_dbg_warn("_mmcamcorder_get_file_system_type failed");
479 _mmcam_dbg_err("failed to get directory name");
483 if ((err == -1) || free_space <= (_MMCAMCORDER_AUDIO_MINIMUM_SPACE+(5*1024))) {
484 _mmcam_dbg_err("OUT of STORAGE [err:%d or free space [%" G_GUINT64_FORMAT "] is smaller than [%d]",
485 err, free_space, (_MMCAMCORDER_AUDIO_MINIMUM_SPACE+(5*1024)));
486 return MM_ERROR_OUT_OF_STORAGE;
490 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
491 if (ret != MM_ERROR_NONE) {
492 goto _ERR_CAMCORDER_AUDIO_COMMAND;
496 case _MMCamcorder_CMD_PAUSE:
498 GstClock *pipe_clock = NULL;
501 if (info->b_commiting) {
502 _mmcam_dbg_warn("now on commiting previous file!!(cmd : %d)", cmd);
503 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
506 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
507 if (info->filesize > 0) {
509 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
510 _mmcam_dbg_err("Pause fail, wait 200 ms, but file size is %lld",
512 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
514 _mmcam_dbg_warn("Wait for enough audio frame, retry count[%d], file size is %lld",
515 count, info->filesize);
517 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
520 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PAUSED);
521 if (ret != MM_ERROR_NONE) {
522 goto _ERR_CAMCORDER_AUDIO_COMMAND;
525 /* FIXME: consider delay. */
526 pipe_clock = gst_pipeline_get_clock(GST_PIPELINE(pipeline));
527 sc->pipeline_time = gst_clock_get_time(pipe_clock) - gst_element_get_base_time(GST_ELEMENT(pipeline));
531 case _MMCamcorder_CMD_CANCEL:
532 if (info->b_commiting) {
533 _mmcam_dbg_warn("now on commiting previous file!!(cmd : %d)", cmd);
534 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
537 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
538 if (ret != MM_ERROR_NONE) {
539 goto _ERR_CAMCORDER_AUDIO_COMMAND;
543 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
545 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", FALSE);
548 _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, GST_STATE_NULL);
550 sc->pipeline_time = 0;
552 sc->isMaxsizePausing = FALSE;
553 sc->isMaxtimePausing = FALSE;
555 if (info->filename) {
556 _mmcam_dbg_log("file delete(%s)", info->filename);
557 unlink(info->filename);
558 g_free(info->filename);
559 info->filename = NULL;
563 case _MMCamcorder_CMD_COMMIT:
567 _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT");
569 if (info->b_commiting) {
570 _mmcam_dbg_warn("now on commiting previous file!!(cmd : %d)", cmd);
571 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
573 _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT : start");
574 info->b_commiting = TRUE;
577 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
578 if (info->filesize > 0) {
580 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
581 _mmcam_dbg_err("Commit fail, waited 200 ms, but file size is %lld", info->filesize);
582 info->b_commiting = FALSE;
583 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
585 _mmcam_dbg_warn("Waiting for enough audio frame, re-count[%d], file size is %lld",
586 count, info->filesize);
588 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
592 ret = gst_element_send_event(audioSrc, gst_event_new_eos());
593 /* for pause -> commit case */
594 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) == MM_CAMCORDER_STATE_PAUSED) {
595 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
596 if (ret != MM_ERROR_NONE) {
597 info->b_commiting = FALSE;
598 goto _ERR_CAMCORDER_AUDIO_COMMAND;
603 /* wait until finishing EOS */
604 _mmcam_dbg_log("Start to wait EOS");
605 if ((ret =_mmcamcorder_get_eos_message(handle)) != MM_ERROR_NONE) {
606 info->b_commiting = FALSE;
607 goto _ERR_CAMCORDER_AUDIO_COMMAND;
612 case _MMCamcorder_CMD_PREVIEW_START:
613 //MM_CAMCORDER_START_CHANGE_STATE;
616 case _MMCamcorder_CMD_PREVIEW_STOP:
617 //MM_CAMCORDER_STOP_CHANGE_STATE;
622 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
626 _ERR_CAMCORDER_AUDIO_COMMAND:
630 int _mmcamcorder_audio_handle_eos(MMHandleType handle)
632 int err = MM_ERROR_NONE;
633 mmf_camcorder_t *hcamcorder= MMF_CAMCORDER(handle);
634 _MMCamcorderSubContext *sc = NULL;
635 _MMCamcorderAudioInfo *info = NULL;
636 GstElement *pipeline = NULL;
637 _MMCamcorderMsgItem msg;
638 MMCamRecordingReport * report;
640 mmf_return_val_if_fail(hcamcorder, FALSE);
641 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
643 mmf_return_val_if_fail(sc, FALSE);
644 mmf_return_val_if_fail(sc->info_audio, FALSE);
648 info = sc->info_audio;
650 pipeline = sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst;
652 err = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
653 if (err != MM_ERROR_NONE) {
654 _mmcam_dbg_warn("Failed:_MMCamcorder_CMD_COMMIT:GST_STATE_READY. err[%x]", err);
657 /* Send recording report message to application */
658 msg.id = MM_MESSAGE_CAMCORDER_AUDIO_CAPTURED;
659 report = (MMCamRecordingReport*) malloc(sizeof(MMCamRecordingReport));
661 _mmcam_dbg_err("Recording report fail(%s). Out of memory.", info->filename);
666 // MM_AUDIO_CODEC_AAC + MM_FILE_FORMAT_MP4
667 if(info->fileformat == MM_FILE_FORMAT_3GP || info->fileformat == MM_FILE_FORMAT_MP4){
668 __mmcamcorder_audio_add_metadata_info_m4a(handle);
672 report->recording_filename = strdup(info->filename);
673 msg.param.data= report;
675 _mmcamcroder_send_message(handle, &msg);
678 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
680 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", FALSE);
683 _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_NULL);
685 sc->pipeline_time = 0;
687 sc->isMaxsizePausing = FALSE;
688 sc->isMaxtimePausing = FALSE;
690 g_free(info->filename);
691 info->filename = NULL;
693 _mmcam_dbg_err("_MMCamcorder_CMD_COMMIT : end");
695 info->b_commiting = FALSE;
702 __mmcamcorder_get_decibel(unsigned char* raw, int size, MMCamcorderAudioFormat format)
704 #define MAX_AMPLITUDE_MEAN_16BIT (23170.115738161934)
705 #define MAX_AMPLITUDE_MEAN_08BIT (89.803909382810)
706 #define DEFAULT_DECIBEL (-80.0)
715 float db = DEFAULT_DECIBEL;
717 unsigned long long square_sum = 0;
719 if (format == MM_CAMCORDER_AUDIO_FORMAT_PCM_S16_LE)
721 else //MM_CAMCORDER_AUDIO_FORMAT_PCM_U8
724 for( ; i < size ; i += (depthByte<<1) )
728 pcm8 = (char *)(raw + i);
729 square_sum += (*pcm8)*(*pcm8);
733 pcm16 = (short*)(raw + i);
734 square_sum += (*pcm16)*(*pcm16);
741 rms = sqrt( square_sum/count );
742 if (depthByte == 1) {
743 db = 20 * log10( rms/MAX_AMPLITUDE_MEAN_08BIT );
745 db = 20 * log10( rms/MAX_AMPLITUDE_MEAN_16BIT );
750 _mmcam_dbg_log("size[%d],depthByte[%d],count[%d],rms[%f],db[%f]",
751 size, depthByte, count, rms, db);
759 __mmcamcorder_audio_dataprobe_voicerecorder(GstPad *pad, GstBuffer *buffer, gpointer u_data)
761 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
766 _MMCamcorderMsgItem msg;
767 int err = MM_ERROR_UNKNOWN;
768 char *err_name = NULL;
770 mmf_return_val_if_fail(hcamcorder, FALSE);
772 /* Set volume to audio input */
773 err = mm_camcorder_get_attributes((MMHandleType)hcamcorder, &err_name,
774 MMCAM_AUDIO_VOLUME, &volume,
775 MMCAM_AUDIO_FORMAT, &format,
776 MMCAM_AUDIO_CHANNEL, &channel,
780 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, err);
781 SAFE_FREE (err_name);
785 if(volume == 0) //mute
786 memset (GST_BUFFER_DATA(buffer), 0, GST_BUFFER_SIZE(buffer));
788 /* Get current volume level of real input stream */
789 curdcb = __mmcamcorder_get_decibel(GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer), format);
791 msg.id = MM_MESSAGE_CAMCORDER_CURRENT_VOLUME;
792 msg.param.rec_volume_dB = curdcb;
793 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
795 /* CALL audio stream callback */
796 if ((hcamcorder->astream_cb) && buffer && GST_BUFFER_DATA(buffer))
798 MMCamcorderAudioStreamDataType stream;
800 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) < MM_CAMCORDER_STATE_PREPARE)
802 _mmcam_dbg_warn("Not ready for stream callback");
807 _mmcam_dbg_log("Call audio steramCb, data[%p], format[%d], channel[%d], length[%d], volume_dB[%f]",
808 GST_BUFFER_DATA(buffer), format, channel, GST_BUFFER_SIZE(buffer), curdcb);
811 stream.data = (void *)GST_BUFFER_DATA(buffer);
812 stream.format = format;
813 stream.channel = channel;
814 stream.length = GST_BUFFER_SIZE(buffer);
815 stream.timestamp = (unsigned int)(GST_BUFFER_TIMESTAMP(buffer)/1000000); //nano -> msecond
816 stream.volume_dB = curdcb;
818 _MMCAMCORDER_LOCK_ASTREAM_CALLBACK( hcamcorder );
820 if(hcamcorder->astream_cb)
822 hcamcorder->astream_cb(&stream, hcamcorder->astream_cb_param);
825 _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK( hcamcorder );
833 __mmcamcorder_audiorec_pad_added_cb(GstElement *element, GstPad *pad, MMHandleType handle)
835 mmf_camcorder_t *hcamcorder= MMF_CAMCORDER(handle);
837 _mmcam_dbg_log("ENTER(%s)", GST_PAD_NAME(pad));
838 //FIXME : the name of audio sink pad of wavparse, oggmux doesn't have 'audio'. How could I handle the name?
839 if((strstr(GST_PAD_NAME(pad), "audio")) || (strstr(GST_PAD_NAME(pad), "sink")))
841 MMCAMCORDER_ADD_BUFFER_PROBE(pad, _MMCAMCORDER_HANDLER_AUDIOREC,
842 __mmcamcorder_audio_dataprobe_record, hcamcorder);
846 _mmcam_dbg_warn("Unknow pad is added, check it : [%s]", GST_PAD_NAME(pad));
853 static gboolean __mmcamcorder_audio_dataprobe_record(GstPad *pad, GstBuffer *buffer, gpointer u_data)
855 static int count = 0;
856 guint64 rec_pipe_time = 0;
857 guint64 free_space = 0;
858 guint64 buffer_size = 0;
859 guint64 trailer_size = 0;
860 char *filename = NULL;
861 unsigned long long remained_time = 0;
863 _MMCamcorderSubContext *sc = NULL;
864 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
865 _MMCamcorderAudioInfo *info = NULL;
866 _MMCamcorderMsgItem msg;
868 mmf_return_val_if_fail(hcamcorder, FALSE);
869 mmf_return_val_if_fail(buffer, FALSE);
871 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
872 mmf_return_val_if_fail(sc && sc->info_audio, FALSE);
873 info = sc->info_audio;
875 if (sc->isMaxtimePausing || sc->isMaxsizePausing) {
876 _mmcam_dbg_warn("isMaxtimePausing[%d],isMaxsizePausing[%d]",
877 sc->isMaxtimePausing, sc->isMaxsizePausing);
881 buffer_size = (guint64)GST_BUFFER_SIZE(buffer);
883 if (info->filesize == 0) {
884 if (info->fileformat == MM_FILE_FORMAT_WAV) {
885 info->filesize += 44; /* wave header size */
886 } else if (info->fileformat == MM_FILE_FORMAT_AMR) {
887 info->filesize += 6; /* amr header size */
890 info->filesize += buffer_size;
894 if (sc->ferror_send) {
895 _mmcam_dbg_warn("file write error, drop frames");
899 /* get trailer size */
900 if (info->fileformat == MM_FILE_FORMAT_3GP ||
901 info->fileformat == MM_FILE_FORMAT_MP4 ||
902 info->fileformat == MM_FILE_FORMAT_AAC) {
903 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
904 /*_mmcam_dbg_log("trailer_size %d", trailer_size);*/
906 trailer_size = 0; /* no trailer */
909 filename = info->filename;
911 /* to minimizing free space check overhead */
912 count = count % _MMCAMCORDER_FREE_SPACE_CHECK_INTERVAL;
914 gint free_space_ret = _mmcamcorder_get_freespace(filename, &free_space);
916 if(_mmcamcorder_check_file_path(filename) && hcamcorder->system_memory) {
917 free_space = free_space - hcamcorder->system_memory;
920 /*_mmcam_dbg_log("check free space for recording");*/
922 switch (free_space_ret) {
923 case -2: /* file not exist */
924 case -1: /* failed to get free space */
925 _mmcam_dbg_err("Error occured. [%d]", free_space_ret);
926 if (sc->ferror_count == 2 && sc->ferror_send == FALSE) {
927 sc->ferror_send = TRUE;
928 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
929 if (free_space_ret == -2) {
930 msg.param.code = MM_ERROR_FILE_NOT_FOUND;
932 msg.param.code = MM_ERROR_FILE_READ;
934 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
939 return FALSE; /* skip this buffer */
941 default: /* succeeded to get free space */
942 /* check free space for recording */
943 if (free_space < (guint64)(_MMCAMCORDER_AUDIO_MINIMUM_SPACE + buffer_size + trailer_size)) {
944 _mmcam_dbg_warn("No more space for recording!!!");
945 _mmcam_dbg_warn("Free Space : [%" G_GUINT64_FORMAT "], file size : [%" G_GUINT64_FORMAT "]",
946 free_space, info->filesize);
949 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
951 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", TRUE);
954 sc->isMaxsizePausing = TRUE;
955 msg.id = MM_MESSAGE_CAMCORDER_NO_FREE_SPACE;
956 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
958 return FALSE; /* skip this buffer */
964 if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_TIMESTAMP(buffer))) {
965 _mmcam_dbg_err("Buffer timestamp is invalid, check it");
969 rec_pipe_time = GST_TIME_AS_MSECONDS(GST_BUFFER_TIMESTAMP(buffer));
971 /* calculate remained time can be recorded */
972 if (info->max_time > 0 && info->max_time < (remained_time + rec_pipe_time)) {
973 remained_time = info->max_time - rec_pipe_time;
974 } else if (info->max_size > 0) {
975 long double max_size = (long double)info->max_size;
976 long double current_size = (long double)(info->filesize + buffer_size + trailer_size);
978 remained_time = (unsigned long long)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
981 /*_mmcam_dbg_log("remained time : %u", remained_time);*/
983 /* check max size of recorded file */
984 if (info->max_size > 0 &&
985 info->max_size < info->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) {
986 _mmcam_dbg_warn("Max size!!! Recording is paused.");
987 _mmcam_dbg_warn("Max [%" G_GUINT64_FORMAT "], file [%" G_GUINT64_FORMAT "], trailer : [%" G_GUINT64_FORMAT "]", \
988 info->max_size, info->filesize, trailer_size);
990 /* just same as pause status. After blocking two queue, this function will not call again. */
992 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
994 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", TRUE);
997 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
998 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
999 msg.param.recording_status.filesize = (unsigned long long)((info->filesize + trailer_size) >> 10);
1000 msg.param.recording_status.remained_time = 0;
1001 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1003 _mmcam_dbg_warn("Last filesize sent by message : %d", info->filesize + trailer_size);
1005 sc->isMaxsizePausing = TRUE;
1006 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1007 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1009 /* skip this buffer */
1013 /* check recording time limit and send recording status message */
1014 if (info->max_time > 0 && rec_pipe_time > info->max_time) {
1015 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1016 rec_pipe_time, info->max_time);
1018 if (info->bMuxing) {
1019 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1021 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", TRUE);
1024 sc->isMaxtimePausing = TRUE;
1025 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1026 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1028 /* skip this buffer */
1032 /* send message for recording time and recorded file size */
1033 if (info->b_commiting == FALSE) {
1034 info->filesize += buffer_size;
1036 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1037 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1038 msg.param.recording_status.filesize = (unsigned long long)((info->filesize + trailer_size) >> 10);
1039 msg.param.recording_status.remained_time = remained_time;
1040 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1044 /* skip this buffer if commit process has been started */
1049 /* START TAG HERE */
1050 static gboolean __mmcamcorder_audio_add_metadata_info_m4a(MMHandleType handle)
1054 guint64 udta_size = 0;
1055 gint64 current_pos = 0;
1056 gint64 moov_pos = 0;
1057 gint64 udta_pos = 0;
1058 // supporting audio geo tag for mobile
1060 char *err_name = NULL;
1061 gdouble longitude = 0;
1062 gdouble latitude = 0;
1063 gdouble altitude = 0;
1064 _MMCamcorderLocationInfo geo_info = {0,0,0};
1065 _MMCamcorderLocationInfo loc_info = {0,0,0};
1067 char err_msg[128] = {'\0',};
1069 _MMCamcorderAudioInfo *info = NULL;
1070 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1071 _MMCamcorderSubContext *sc = NULL;
1073 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1074 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1076 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1077 mmf_return_val_if_fail(sc->info_audio, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1079 info = sc->info_audio;
1080 mm_camcorder_get_attributes(handle, &err_name,
1081 MMCAM_TAG_GPS_ENABLE, &gps_enable,
1085 mm_camcorder_get_attributes(handle, &err_name,
1086 MMCAM_TAG_LATITUDE, &latitude,
1087 MMCAM_TAG_LONGITUDE, &longitude,
1088 MMCAM_TAG_ALTITUDE, &altitude,
1090 loc_info.longitude = _mmcamcorder_double_to_fix(longitude);
1091 loc_info.latitude = _mmcamcorder_double_to_fix(latitude);
1092 loc_info.altitude = _mmcamcorder_double_to_fix(altitude);
1093 geo_info.longitude = longitude *10000;
1094 geo_info.latitude = latitude *10000;
1095 geo_info.altitude = altitude *10000;
1098 f = fopen(info->filename, "rb+");
1100 strerror_r(errno, err_msg, 128);
1101 _mmcam_dbg_err("file open failed [%s]", err_msg);
1109 /* find udta container.
1110 if, there are udta container, write loci box after that
1111 else, make udta container and write loci box. */
1112 if (_mmcamcorder_find_fourcc(f, MMCAM_FOURCC('u','d','t','a'), TRUE)) {
1115 _mmcam_dbg_log("find udta container");
1118 if (fseek(f, -8L, SEEK_CUR) != 0) {
1122 udta_pos = ftell(f);
1127 nread = fread(&buf, sizeof(char), sizeof(buf), f);
1129 _mmcam_dbg_log("recorded file fread %d", nread);
1131 udta_size = _mmcamcorder_get_container_size(buf);
1133 /* goto end of udta and write 'smta' box */
1134 if (fseek(f, (udta_size-4L), SEEK_CUR) != 0) {
1139 if (!_mmcamcorder_write_loci(f, loc_info)) {
1143 if (!_mmcamcorder_write_geodata( f, geo_info )) {
1148 current_pos = ftell(f);
1149 if (current_pos < 0) {
1153 if (!_mmcamcorder_update_size(f, udta_pos, current_pos)) {
1157 _mmcam_dbg_log("No udta container");
1158 if (fseek(f, 0, SEEK_END) != 0) {
1162 if (!_mmcamcorder_write_udta(f, gps_enable, loc_info, geo_info)) {
1167 /* find moov container.
1168 update moov container size. */
1169 if((current_pos = ftell(f))<0)
1172 if (_mmcamcorder_find_fourcc(f, MMCAM_FOURCC('m','o','o','v'), TRUE)) {
1174 _mmcam_dbg_log("found moov container");
1175 if (fseek(f, -8L, SEEK_CUR) !=0) {
1179 moov_pos = ftell(f);
1184 if (!_mmcamcorder_update_size(f, moov_pos, current_pos)) {
1190 _mmcam_dbg_err("No 'moov' container");
1210 _mmcam_dbg_err("ftell() returns negative value.");