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 200000 /* micro second */
43 #define _MMCAMCORDER_FREE_SPACE_CHECK_INTERVAL 10
45 /*---------------------------------------------------------------------------------------
46 | LOCAL FUNCTION PROTOTYPES: |
47 ---------------------------------------------------------------------------------------*/
48 /* STATIC INTERNAL FUNCTION */
49 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_voicerecorder(GstPad *pad, GstPadProbeInfo *info, gpointer u_data);
50 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data);
51 static int __mmcamcorder_create_audiop_with_encodebin(MMHandleType handle);
52 static gboolean __mmcamcorder_audio_add_metadata_info_m4a(MMHandleType handle);
54 /*=======================================================================================
55 | FUNCTION DEFINITIONS |
56 =======================================================================================*/
58 /*---------------------------------------------------------------------------------------
59 | GLOBAL FUNCTION DEFINITIONS: |
60 ---------------------------------------------------------------------------------------*/
62 static int __mmcamcorder_create_audiop_with_encodebin(MMHandleType handle)
64 int err = MM_ERROR_NONE;
65 int file_name_len = 0;
67 char *file_name = NULL;
68 const char *aenc_name = NULL;
69 const char *mux_name = NULL;
70 const char *sink_name = NULL;
73 GstPad *srcpad = NULL;
74 GstPad *sinkpad = NULL;
75 GList *element_list = NULL;
77 _MMCamcorderAudioInfo *info = NULL;
78 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
79 _MMCamcorderSubContext *sc = NULL;
80 type_element *aenc_elem = NULL;
81 type_element *mux_elem = NULL;
83 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
84 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
86 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
87 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
88 mmf_return_val_if_fail(sc->info_audio, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
90 info = (_MMCamcorderAudioInfo *)sc->info_audio;
94 mux_elem = _mmcamcorder_get_type_element(handle, MM_CAM_FILE_FORMAT);
95 err = _mmcamcorder_conf_get_value_element_name(mux_elem, &mux_name);
97 if (!mux_name || !strcmp(mux_name, "wavenc")) {
98 /* IF MUX in not chosen then record in raw file */
99 _mmcam_dbg_log("Record without muxing.");
100 info->bMuxing = FALSE;
102 _mmcam_dbg_log("Record with mux.");
103 info->bMuxing = TRUE;
106 /* Create GStreamer pipeline */
107 _MMCAMCORDER_PIPELINE_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCODE_MAIN_PIPE, "camcorder_pipeline", err);
109 err = _mmcamcorder_create_audiosrc_bin(handle);
110 if (err != MM_ERROR_NONE)
114 /* Muxing. can use encodebin. */
115 err = _mmcamcorder_create_encodesink_bin((MMHandleType)hcamcorder, MM_CAMCORDER_ENCBIN_PROFILE_AUDIO);
116 if (err != MM_ERROR_NONE)
119 /* Add and link elements */
120 gst_bin_add_many(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst),
121 sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst,
122 sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst,
125 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst, "src");
126 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst, "audio_sink0");
127 _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error);
129 err = mm_camcorder_get_attributes(handle, NULL,
130 MMCAM_TARGET_FILENAME, &file_name, &file_name_len,
132 if (err != MM_ERROR_NONE) {
133 _mmcam_dbg_err("failed to get filename [0x%x]", err);
134 err = MM_ERROR_CAMCORDER_RESOURCE_CREATION;
135 goto pipeline_creation_error;
138 /* without muxing. can't use encodebin. */
139 aenc_elem = _mmcamcorder_get_type_element(handle, MM_CAM_AUDIO_ENCODER);
141 _mmcam_dbg_err("Fail to get type element");
142 err = MM_ERROR_CAMCORDER_RESOURCE_CREATION;
143 goto pipeline_creation_error;
146 err = _mmcamcorder_conf_get_value_element_name(aenc_elem, &aenc_name);
147 if ((!err) || (!aenc_name)) {
148 _mmcam_dbg_err("Fail to get element name");
149 err = MM_ERROR_CAMCORDER_RESOURCE_CREATION;
150 goto pipeline_creation_error;
153 element_list = g_list_append(element_list, &sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN]);
155 _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_AQUE, "queue", NULL, element_list, err);
157 if (strcmp(aenc_name, "wavenc") != 0)
158 _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_CONV, "audioconvert", NULL, element_list, err);
160 _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_AENC, aenc_name, NULL, element_list, err);
163 sink_name = "filesink";
165 sink_name = "fakesink";
167 _mmcam_dbg_log("encode sink : %s", sink_name);
169 _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_SINK, sink_name, NULL, element_list, err);
171 /* add elements to encode pipeline */
172 if (!_mmcamcorder_add_elements_to_bin(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), element_list)) {
173 _mmcam_dbg_err("add encode elements error.");
174 err = MM_ERROR_CAMCORDER_RESOURCE_CREATION;
175 goto pipeline_creation_error;
179 if (!_mmcamcorder_link_elements(element_list)) {
180 _mmcam_dbg_err("encode element link error.");
181 err = MM_ERROR_CAMCORDER_GST_LINK;
182 goto pipeline_creation_error;
186 /* set data probe function */
187 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst, "src");
188 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_AUDIOREC,
189 __mmcamcorder_audio_dataprobe_voicerecorder, hcamcorder);
190 gst_object_unref(srcpad);
193 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst, "src");
194 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_AUDIOREC,
195 __mmcamcorder_audio_dataprobe_record, hcamcorder);
196 gst_object_unref(srcpad);
199 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "sink");
200 MMCAMCORDER_ADD_BUFFER_PROBE(sinkpad, _MMCAMCORDER_HANDLER_AUDIOREC,
201 __mmcamcorder_muxed_dataprobe, hcamcorder);
202 MMCAMCORDER_ADD_EVENT_PROBE(sinkpad, _MMCAMCORDER_HANDLER_AUDIOREC,
203 __mmcamcorder_eventprobe_monitor, hcamcorder);
204 gst_object_unref(sinkpad);
207 bus = gst_pipeline_get_bus(GST_PIPELINE(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst));
209 /* register message callback */
210 hcamcorder->pipeline_cb_event_id = gst_bus_add_watch(bus, (GstBusFunc)_mmcamcorder_pipeline_cb_message, hcamcorder);
212 /* set sync callback */
213 gst_bus_set_sync_handler(bus, _mmcamcorder_encode_pipeline_bus_sync_callback, hcamcorder, NULL);
215 gst_object_unref(bus);
219 g_list_free(element_list);
223 return MM_ERROR_NONE;
225 pipeline_creation_error:
226 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCODE_MAIN_PIPE);
227 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_AUDIOSRC_BIN);
228 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_AQUE);
229 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_CONV);
230 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_AENC);
231 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_SINK);
232 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_BIN);
235 g_list_free(element_list);
244 _mmcamcorder_create_audio_pipeline(MMHandleType handle)
246 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
247 _MMCamcorderSubContext *sc = NULL;
249 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
250 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
252 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
254 return __mmcamcorder_create_audiop_with_encodebin(handle);
259 * This function destroy audio pipeline.
261 * @param[in] handle Handle of camcorder.
264 * @see _mmcamcorder_destroy_pipeline()
268 _mmcamcorder_destroy_audio_pipeline(MMHandleType handle)
270 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
271 _MMCamcorderSubContext *sc = NULL;
272 _MMCamcorderAudioInfo *info = NULL;
273 mmf_return_if_fail(hcamcorder);
274 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
276 mmf_return_if_fail(sc && sc->info_audio);
277 mmf_return_if_fail(sc->element);
279 info = sc->info_audio;
281 _mmcam_dbg_log("start");
283 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst) {
284 _mmcam_dbg_warn("release audio pipeline");
286 _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_NULL);
288 _mmcamcorder_remove_all_handlers((MMHandleType)hcamcorder, _MMCAMCORDER_HANDLER_CATEGORY_ALL);
291 GstPad *reqpad = NULL;
294 The ref_count of mux is always # of streams in here, i don't know why it happens.
295 So, i unref the mux manually
297 reqpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "audio");
298 gst_element_release_request_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, reqpad);
299 gst_object_unref(reqpad);
301 if (GST_IS_ELEMENT(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst) &&
302 GST_OBJECT_REFCOUNT(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst) > 1) {
303 gst_object_unref(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst);
306 gst_object_unref(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst);
309 _mmcam_dbg_log("done");
316 * This function operates each command on audio mode.
318 * @param c [in] Handle of camcorder context.
319 * @param command [in] command type received from Multimedia Framework.
321 * @return This function returns MM_ERROR_NONE on success, or the other values
324 * @see _mmcamcorder_set_functions()
330 void* _MMCamcorderStartHelperFunc(void *handle)
332 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
333 _mmcamcorder_set_state((MMHandleType)hcamcorder, hcamcorder->target_state);
338 void* _MMCamcorderStopHelperFunc(void *handle)
340 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
341 _mmcamcorder_set_state((MMHandleType)hcamcorder, hcamcorder->target_state);
348 _mmcamcorder_audio_command(MMHandleType handle, int command)
351 int ret = MM_ERROR_NONE;
353 guint64 free_space = 0;
354 char *err_attr_name = NULL;
356 GstElement *pipeline = NULL;
357 GstElement *audioSrc = NULL;
359 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
360 _MMCamcorderSubContext *sc = NULL;
361 _MMCamcorderAudioInfo *info = NULL;
363 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
364 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
366 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
367 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
368 mmf_return_val_if_fail(sc->info_audio, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
369 pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
370 info = sc->info_audio;
374 pipeline = sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst;
375 audioSrc = sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst;
377 case _MMCamcorder_CMD_RECORD:
378 /* check status for resume case */
379 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) != MM_CAMCORDER_STATE_PAUSED) {
382 char *temp_filename = NULL;
383 char *dir_name = NULL;
384 int file_system_type = 0;
385 int filename_length = 0;
386 int root_directory_length = 0;
388 if (sc->pipeline_time)
389 gst_element_set_start_time(pipeline, sc->pipeline_time);
391 sc->pipeline_time = RESET_PAUSE_TIME;
393 ret = mm_camcorder_get_attributes(handle, &err_attr_name,
394 MMCAM_TARGET_MAX_SIZE, &imax_size,
395 MMCAM_TARGET_TIME_LIMIT, &imax_time,
396 MMCAM_FILE_FORMAT, &(info->fileformat),
397 MMCAM_TARGET_FILENAME, &temp_filename, &filename_length,
398 MMCAM_ROOT_DIRECTORY, &hcamcorder->root_directory, &root_directory_length,
400 if (ret != MM_ERROR_NONE) {
401 _mmcam_dbg_warn("failed to get attribute. (%s:%x)", err_attr_name, ret);
402 SAFE_FREE(err_attr_name);
403 goto _ERR_CAMCORDER_AUDIO_COMMAND;
406 if (!temp_filename && !hcamcorder->mstream_cb) {
407 _mmcam_dbg_err("filename is not set and muxed stream cb is NULL");
408 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
409 goto _ERR_CAMCORDER_AUDIO_COMMAND;
413 info->filename = g_strdup(temp_filename);
414 if (!info->filename) {
415 _mmcam_dbg_err("STRDUP was failed");
416 goto _ERR_CAMCORDER_AUDIO_COMMAND;
419 _mmcam_dbg_log("Record start : set file name using attribute - %s\n ", info->filename);
421 MMCAMCORDER_G_OBJECT_SET_POINTER(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename);
424 sc->ferror_send = FALSE;
425 sc->ferror_count = 0;
426 sc->bget_eos = FALSE;
427 sc->muxed_stream_offset = 0;
432 info->max_size = 0; /* do not check */
434 info->max_size = ((guint64)imax_size) << 10; /* to byte */
438 info->max_time = 0; /* do not check */
440 info->max_time = ((guint64)imax_time) * 1000; /* to millisecond */
442 /* TODO : check free space before recording start */
443 dir_name = g_path_get_dirname(info->filename);
445 err = _mmcamcorder_get_storage_info(dir_name, hcamcorder->root_directory, &hcamcorder->storage_info);
447 _mmcam_dbg_err("get storage info failed");
450 return MM_ERROR_OUT_OF_STORAGE;
453 err = _mmcamcorder_get_freespace(hcamcorder->storage_info.type, &free_space);
455 _mmcam_dbg_warn("current space - %s [%" G_GUINT64_FORMAT "]", dir_name, free_space);
457 if (_mmcamcorder_get_file_system_type(dir_name, &file_system_type) == 0) {
458 /* MSDOS_SUPER_MAGIC : 0x4d44 */
459 if (file_system_type == MSDOS_SUPER_MAGIC &&
460 (info->max_size == 0 || info->max_size > FAT32_FILE_SYSTEM_MAX_SIZE)) {
461 _mmcam_dbg_warn("FAT32 and too large max[%"G_GUINT64_FORMAT"], set max as %"G_GUINT64_FORMAT,
462 info->max_size, FAT32_FILE_SYSTEM_MAX_SIZE);
463 info->max_size = FAT32_FILE_SYSTEM_MAX_SIZE;
465 _mmcam_dbg_warn("file system 0x%x, max size %"G_GUINT64_FORMAT,
466 file_system_type, info->max_size);
469 _mmcam_dbg_warn("_mmcamcorder_get_file_system_type failed");
475 _mmcam_dbg_err("failed to get directory name");
479 if ((err == -1) || free_space <= (_MMCAMCORDER_AUDIO_MINIMUM_SPACE+(5*1024))) {
480 _mmcam_dbg_err("OUT of STORAGE [err:%d or free space [%" G_GUINT64_FORMAT "] is smaller than [%d]",
481 err, free_space, (_MMCAMCORDER_AUDIO_MINIMUM_SPACE+(5*1024)));
482 return MM_ERROR_OUT_OF_STORAGE;
486 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
487 if (ret != MM_ERROR_NONE)
488 goto _ERR_CAMCORDER_AUDIO_COMMAND;
492 case _MMCamcorder_CMD_PAUSE:
494 GstClock *pipe_clock = NULL;
497 if (info->b_commiting) {
498 _mmcam_dbg_warn("now on commiting previous file!!(cmd : %d)", cmd);
499 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
502 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
503 if (info->filesize > 0) {
505 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
506 _mmcam_dbg_err("Pause fail, wait 200 ms, but file size is %lld",
508 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
510 _mmcam_dbg_warn("Wait for enough audio frame, retry count[%d], file size is %lld",
511 count, info->filesize);
513 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
516 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PAUSED);
517 if (ret != MM_ERROR_NONE)
518 goto _ERR_CAMCORDER_AUDIO_COMMAND;
520 /* FIXME: consider delay. */
521 pipe_clock = gst_pipeline_get_clock(GST_PIPELINE(pipeline));
522 sc->pipeline_time = gst_clock_get_time(pipe_clock) - gst_element_get_base_time(GST_ELEMENT(pipeline));
526 case _MMCamcorder_CMD_CANCEL:
527 if (info->b_commiting) {
528 _mmcam_dbg_warn("now on commiting previous file!!(cmd : %d)", cmd);
529 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
532 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
533 if (ret != MM_ERROR_NONE)
534 goto _ERR_CAMCORDER_AUDIO_COMMAND;
537 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
539 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", FALSE);
542 _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, GST_STATE_NULL);
544 sc->pipeline_time = 0;
546 sc->isMaxsizePausing = FALSE;
547 sc->isMaxtimePausing = FALSE;
549 if (info->filename) {
550 _mmcam_dbg_log("file delete(%s)", info->filename);
551 unlink(info->filename);
552 SAFE_G_FREE(info->filename);
556 case _MMCamcorder_CMD_COMMIT:
560 _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT");
562 if (info->b_commiting) {
563 _mmcam_dbg_warn("now on commiting previous file!!(cmd : %d)", cmd);
564 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
566 _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT : start");
567 info->b_commiting = TRUE;
570 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
571 if (info->filesize > 0) {
573 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
574 _mmcam_dbg_err("Commit fail, waited 200 ms, but file size is %lld", info->filesize);
575 info->b_commiting = FALSE;
576 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
578 _mmcam_dbg_warn("Waiting for enough audio frame, re-count[%d], file size is %lld",
579 count, info->filesize);
581 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
585 if (gst_element_send_event(audioSrc, gst_event_new_eos()) == FALSE) {
586 _mmcam_dbg_err("send EOS failed");
587 info->b_commiting = FALSE;
588 ret = MM_ERROR_CAMCORDER_INTERNAL;
589 goto _ERR_CAMCORDER_AUDIO_COMMAND;
592 _mmcam_dbg_log("send EOS done");
594 /* for pause -> commit case */
595 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) == MM_CAMCORDER_STATE_PAUSED) {
596 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
597 if (ret != MM_ERROR_NONE) {
598 info->b_commiting = FALSE;
599 goto _ERR_CAMCORDER_AUDIO_COMMAND;
603 _mmcam_dbg_err("No audio stream source");
604 info->b_commiting = FALSE;
605 ret = MM_ERROR_CAMCORDER_INTERNAL;
606 goto _ERR_CAMCORDER_AUDIO_COMMAND;
609 /* wait until finishing EOS */
610 _mmcam_dbg_log("Start to wait EOS");
611 if ((ret = _mmcamcorder_get_eos_message(handle)) != MM_ERROR_NONE) {
612 info->b_commiting = FALSE;
613 goto _ERR_CAMCORDER_AUDIO_COMMAND;
618 case _MMCamcorder_CMD_PREVIEW_START:
619 /*MM_CAMCORDER_START_CHANGE_STATE;*/
622 case _MMCamcorder_CMD_PREVIEW_STOP:
623 /*MM_CAMCORDER_STOP_CHANGE_STATE;*/
628 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
632 _ERR_CAMCORDER_AUDIO_COMMAND:
636 int _mmcamcorder_audio_handle_eos(MMHandleType handle)
638 int err = MM_ERROR_NONE;
639 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
640 _MMCamcorderSubContext *sc = NULL;
641 _MMCamcorderAudioInfo *info = NULL;
642 GstElement *pipeline = NULL;
643 _MMCamcorderMsgItem msg;
644 MMCamRecordingReport * report;
646 mmf_return_val_if_fail(hcamcorder, FALSE);
647 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
649 mmf_return_val_if_fail(sc, FALSE);
650 mmf_return_val_if_fail(sc->info_audio, FALSE);
654 info = sc->info_audio;
656 pipeline = sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst;
658 err = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
659 if (err != MM_ERROR_NONE)
660 _mmcam_dbg_warn("Failed:_MMCamcorder_CMD_COMMIT:GST_STATE_READY. err[%x]", err);
662 /* Send recording report message to application */
663 msg.id = MM_MESSAGE_CAMCORDER_AUDIO_CAPTURED;
664 report = (MMCamRecordingReport*) g_malloc(sizeof(MMCamRecordingReport));
666 _mmcam_dbg_err("Recording report fail(%s). Out of memory.", info->filename);
671 /* MM_AUDIO_CODEC_AAC + MM_FILE_FORMAT_MP4 */
672 if (info->fileformat == MM_FILE_FORMAT_3GP || info->fileformat == MM_FILE_FORMAT_MP4)
673 __mmcamcorder_audio_add_metadata_info_m4a(handle);
676 report->recording_filename = g_strdup(info->filename);
677 msg.param.data = report;
679 _mmcamcorder_send_message(handle, &msg);
682 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
684 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", FALSE);
687 _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_NULL);
689 sc->pipeline_time = 0;
691 sc->isMaxsizePausing = FALSE;
692 sc->isMaxtimePausing = FALSE;
694 SAFE_G_FREE(info->filename);
696 _mmcam_dbg_err("_MMCamcorder_CMD_COMMIT : end");
698 info->b_commiting = FALSE;
705 __mmcamcorder_get_decibel(unsigned char* raw, int size, MMCamcorderAudioFormat format)
707 #define MAX_AMPLITUDE_MEAN_16BIT (23170.115738161934)
708 #define MAX_AMPLITUDE_MEAN_08BIT (89.803909382810)
709 #define DEFAULT_DECIBEL (-80.0)
718 float db = DEFAULT_DECIBEL;
720 unsigned long long square_sum = 0;
722 if (format == MM_CAMCORDER_AUDIO_FORMAT_PCM_S16_LE)
724 else /*MM_CAMCORDER_AUDIO_FORMAT_PCM_U8*/
727 for ( ; i < size ; i += (depthByte<<1)) {
728 if (depthByte == 1) {
729 pcm8 = (char *)(raw + i);
730 square_sum += (*pcm8) * (*pcm8);
732 pcm16 = (short*)(raw + i);
733 square_sum += (*pcm16) * (*pcm16);
740 rms = sqrt((double)square_sum/(double)count);
743 db = 20 * log10(rms/MAX_AMPLITUDE_MEAN_08BIT);
745 db = 20 * log10(rms/MAX_AMPLITUDE_MEAN_16BIT);
749 _mmcam_dbg_log("size[%d],depthByte[%d],count[%d],rms[%f],db[%f]",
750 size, depthByte, count, rms, db);
757 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_voicerecorder(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
759 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
761 int current_state = MM_CAMCORDER_STATE_NONE;
765 _MMCamcorderMsgItem msg;
766 int err = MM_ERROR_UNKNOWN;
767 char *err_name = NULL;
768 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
771 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
773 current_state = _mmcamcorder_get_state((MMHandleType)hcamcorder);
774 if (current_state < MM_CAMCORDER_STATE_PREPARE) {
775 _mmcam_dbg_warn("Not ready for stream callback");
776 return GST_PAD_PROBE_OK;
779 memset(&mapinfo, 0x0, sizeof(GstMapInfo));
781 /* Set volume to audio input */
782 err = mm_camcorder_get_attributes((MMHandleType)hcamcorder, &err_name,
783 MMCAM_AUDIO_VOLUME, &volume,
784 MMCAM_AUDIO_FORMAT, &format,
785 MMCAM_AUDIO_CHANNEL, &channel,
789 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, err);
794 gst_buffer_map(buffer, &mapinfo, GST_MAP_READWRITE);
797 memset(mapinfo.data, 0, mapinfo.size);
799 /* Get current volume level of real input stream */
800 curdcb = __mmcamcorder_get_decibel(mapinfo.data, mapinfo.size, format);
802 msg.id = MM_MESSAGE_CAMCORDER_CURRENT_VOLUME;
803 msg.param.rec_volume_dB = curdcb;
804 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
806 _MMCAMCORDER_LOCK_ASTREAM_CALLBACK(hcamcorder);
808 /* CALL audio stream callback */
809 if (hcamcorder->astream_cb && buffer && mapinfo.data && mapinfo.size > 0) {
810 MMCamcorderAudioStreamDataType stream;
813 _mmcam_dbg_log("Call audio steramCb, data[%p], format[%d], channel[%d], length[%d], volume_dB[%f]",
814 GST_BUFFER_DATA(buffer), format, channel, GST_BUFFER_SIZE(buffer), curdcb);
817 stream.data = (void *)mapinfo.data;
818 stream.format = format;
819 stream.channel = channel;
820 stream.length = mapinfo.size;
821 stream.timestamp = (unsigned int)(GST_BUFFER_PTS(buffer)/1000000); /* nano -> msecond */
822 stream.volume_dB = curdcb;
824 hcamcorder->astream_cb(&stream, hcamcorder->astream_cb_param);
827 _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK(hcamcorder);
829 gst_buffer_unmap(buffer, &mapinfo);
831 return GST_PAD_PROBE_OK;
835 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
837 static int count = 0;
838 guint64 rec_pipe_time = 0;
839 guint64 free_space = 0;
840 guint64 buffer_size = 0;
841 guint64 trailer_size = 0;
842 unsigned long long remained_time = 0;
843 int get_trailer_size = 0;
845 _MMCamcorderSubContext *sc = NULL;
846 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
847 _MMCamcorderAudioInfo *audioinfo = NULL;
848 _MMCamcorderMsgItem msg;
849 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
851 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_DROP);
852 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
854 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
855 mmf_return_val_if_fail(sc && sc->info_audio, GST_PAD_PROBE_DROP);
856 audioinfo = sc->info_audio;
858 if (sc->isMaxtimePausing || sc->isMaxsizePausing) {
859 _mmcam_dbg_warn("isMaxtimePausing[%d],isMaxsizePausing[%d]",
860 sc->isMaxtimePausing, sc->isMaxsizePausing);
861 return GST_PAD_PROBE_DROP;
864 buffer_size = gst_buffer_get_size(buffer);
866 if (audioinfo->filesize == 0) {
867 if (audioinfo->fileformat == MM_FILE_FORMAT_WAV)
868 audioinfo->filesize += 44; /* wave header size */
869 else if (audioinfo->fileformat == MM_FILE_FORMAT_AMR)
870 audioinfo->filesize += 6; /* amr header size */
872 audioinfo->filesize += buffer_size;
873 return GST_PAD_PROBE_OK;
876 if (sc->ferror_send) {
877 _mmcam_dbg_warn("file write error, drop frames");
878 return GST_PAD_PROBE_DROP;
881 /* get trailer size */
883 audioinfo->fileformat == MM_FILE_FORMAT_3GP ||
884 audioinfo->fileformat == MM_FILE_FORMAT_MP4 ||
885 audioinfo->fileformat == MM_FILE_FORMAT_AAC) ? TRUE : FALSE;
886 if (get_trailer_size) {
887 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
888 /*_mmcam_dbg_log("trailer_size %d", trailer_size);*/
890 trailer_size = 0; /* no trailer */
893 /* to minimizing free space check overhead */
894 count = count % _MMCAMCORDER_FREE_SPACE_CHECK_INTERVAL;
896 gint free_space_ret = 0;
897 storage_state_e storage_state = STORAGE_STATE_UNMOUNTABLE;
899 /* check free space */
900 free_space_ret = _mmcamcorder_get_freespace(hcamcorder->storage_info.type, &free_space);
901 if (free_space_ret != 0) {
902 _mmcam_dbg_err("Error occured. [%d]", free_space_ret);
903 if (sc->ferror_count == 2 && sc->ferror_send == FALSE) {
904 sc->ferror_send = TRUE;
906 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
907 msg.param.code = MM_ERROR_FILE_READ;
909 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
914 return GST_PAD_PROBE_DROP; /* skip this buffer */
917 if (free_space == 0) {
918 /* check storage state */
919 storage_get_state(hcamcorder->storage_info.id, &storage_state);
921 _mmcam_dbg_warn("storage state %d", storage_state);
923 if (storage_state == STORAGE_STATE_REMOVED ||
924 storage_state == STORAGE_STATE_UNMOUNTABLE) {
925 _mmcam_dbg_err("storage was removed!");
927 _MMCAMCORDER_LOCK(hcamcorder);
929 if (sc->ferror_send == FALSE) {
930 _mmcam_dbg_err("OUT_OF_STORAGE error");
932 sc->ferror_send = TRUE;
934 _MMCAMCORDER_UNLOCK(hcamcorder);
936 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
937 msg.param.code = MM_ERROR_OUT_OF_STORAGE;
939 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
941 _MMCAMCORDER_UNLOCK(hcamcorder);
942 _mmcam_dbg_warn("error was already sent");
945 return GST_PAD_PROBE_DROP;
949 if (free_space < (guint64)(_MMCAMCORDER_AUDIO_MINIMUM_SPACE + buffer_size + trailer_size)) {
950 _mmcam_dbg_warn("No more space for recording!!!");
951 _mmcam_dbg_warn("Free Space : [%" G_GUINT64_FORMAT "], file size : [%" G_GUINT64_FORMAT "]",
952 free_space, audioinfo->filesize);
954 if (audioinfo->bMuxing) {
955 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
957 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", TRUE);
960 sc->isMaxsizePausing = TRUE;
961 msg.id = MM_MESSAGE_CAMCORDER_NO_FREE_SPACE;
962 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
964 return GST_PAD_PROBE_DROP; /* skip this buffer */
968 if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_PTS(buffer))) {
969 _mmcam_dbg_err("Buffer timestamp is invalid, check it");
970 return GST_PAD_PROBE_DROP;
973 rec_pipe_time = GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer));
975 /* calculate remained time can be recorded */
976 if (audioinfo->max_time > 0 && audioinfo->max_time < (remained_time + rec_pipe_time)) {
977 remained_time = audioinfo->max_time - rec_pipe_time;
978 } else if (audioinfo->max_size > 0) {
979 long double max_size = (long double)audioinfo->max_size;
980 long double current_size = (long double)(audioinfo->filesize + buffer_size + trailer_size);
982 remained_time = (unsigned long long)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
985 /*_mmcam_dbg_log("remained time : %u", remained_time);*/
987 /* check max size of recorded file */
988 if (audioinfo->max_size > 0 &&
989 audioinfo->max_size < audioinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) {
990 _mmcam_dbg_warn("Max size!!! Recording is paused.");
991 _mmcam_dbg_warn("Max [%" G_GUINT64_FORMAT "], file [%" G_GUINT64_FORMAT "], trailer : [%" G_GUINT64_FORMAT "]", \
992 audioinfo->max_size, audioinfo->filesize, trailer_size);
994 /* just same as pause status. After blocking two queue, this function will not call again. */
995 if (audioinfo->bMuxing) {
996 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
998 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", TRUE);
1001 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1002 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1003 msg.param.recording_status.filesize = (unsigned long long)((audioinfo->filesize + trailer_size) >> 10);
1004 msg.param.recording_status.remained_time = 0;
1005 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1007 _mmcam_dbg_warn("Last filesize sent by message : %d", audioinfo->filesize + trailer_size);
1009 sc->isMaxsizePausing = TRUE;
1010 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1011 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1013 /* skip this buffer */
1014 return GST_PAD_PROBE_DROP;
1017 /* check recording time limit and send recording status message */
1018 if (audioinfo->max_time > 0 && rec_pipe_time > audioinfo->max_time) {
1019 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1020 rec_pipe_time, audioinfo->max_time);
1022 if (audioinfo->bMuxing) {
1023 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1025 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", TRUE);
1028 sc->isMaxtimePausing = TRUE;
1029 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1030 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1032 /* skip this buffer */
1033 return GST_PAD_PROBE_DROP;
1036 /* send message for recording time and recorded file size */
1037 if (audioinfo->b_commiting == FALSE) {
1038 audioinfo->filesize += buffer_size;
1040 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1041 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1042 msg.param.recording_status.filesize = (unsigned long long)((audioinfo->filesize + trailer_size) >> 10);
1043 msg.param.recording_status.remained_time = remained_time;
1044 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1046 return GST_PAD_PROBE_OK;
1048 /* skip this buffer if commit process has been started */
1049 return GST_PAD_PROBE_DROP;
1053 /* START TAG HERE */
1054 static gboolean __mmcamcorder_audio_add_metadata_info_m4a(MMHandleType handle)
1058 guint64 udta_size = 0;
1059 gint64 current_pos = 0;
1060 gint64 moov_pos = 0;
1061 gint64 udta_pos = 0;
1062 /* supporting audio geo tag for mobile */
1064 gdouble longitude = 0;
1065 gdouble latitude = 0;
1066 gdouble altitude = 0;
1067 _MMCamcorderLocationInfo geo_info = {0, 0, 0};
1068 _MMCamcorderLocationInfo loc_info = {0, 0, 0};
1070 char err_msg[128] = {'\0',};
1072 _MMCamcorderAudioInfo *info = NULL;
1073 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1074 _MMCamcorderSubContext *sc = NULL;
1076 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1077 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1079 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1080 mmf_return_val_if_fail(sc->info_audio, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1082 info = sc->info_audio;
1083 mm_camcorder_get_attributes(handle, NULL,
1084 MMCAM_TAG_GPS_ENABLE, &gps_enable,
1088 mm_camcorder_get_attributes(handle, NULL,
1089 MMCAM_TAG_LATITUDE, &latitude,
1090 MMCAM_TAG_LONGITUDE, &longitude,
1091 MMCAM_TAG_ALTITUDE, &altitude,
1093 loc_info.longitude = _mmcamcorder_double_to_fix(longitude);
1094 loc_info.latitude = _mmcamcorder_double_to_fix(latitude);
1095 loc_info.altitude = _mmcamcorder_double_to_fix(altitude);
1096 geo_info.longitude = longitude *10000;
1097 geo_info.latitude = latitude *10000;
1098 geo_info.altitude = altitude *10000;
1101 f = fopen64(info->filename, "rb+");
1103 strerror_r(errno, err_msg, 128);
1104 _mmcam_dbg_err("file open failed [%s]", err_msg);
1108 /* find udta container.
1109 if, there are udta container, write loci box after that
1110 else, make udta container and write loci box. */
1111 if (_mmcamcorder_find_fourcc(f, MMCAM_FOURCC('u', 'd', 't', 'a'), TRUE)) {
1114 _mmcam_dbg_log("find udta container");
1117 if (fseek(f, -8L, SEEK_CUR) != 0)
1120 udta_pos = ftello(f);
1124 nread = fread(&buf, sizeof(char), sizeof(buf), f);
1126 _mmcam_dbg_log("recorded file fread %d", nread);
1128 udta_size = _mmcamcorder_get_container_size(buf);
1130 /* goto end of udta and write 'smta' box */
1131 if (fseek(f, (udta_size-4L), SEEK_CUR) != 0)
1135 if (!_mmcamcorder_write_loci(f, loc_info))
1138 if (!_mmcamcorder_write_geodata(f, geo_info))
1142 current_pos = ftello(f);
1143 if (current_pos < 0)
1146 if (!_mmcamcorder_update_size(f, udta_pos, current_pos))
1149 _mmcam_dbg_log("No udta container");
1150 if (fseek(f, 0, SEEK_END) != 0)
1153 if (!_mmcamcorder_write_udta(f, gps_enable, loc_info, geo_info))
1157 /* find moov container.
1158 update moov container size. */
1159 if ((current_pos = ftello(f)) < 0)
1162 if (_mmcamcorder_find_fourcc(f, MMCAM_FOURCC('m', 'o', 'o', 'v'), TRUE)) {
1164 _mmcam_dbg_log("found moov container");
1165 if (fseek(f, -8L, SEEK_CUR) != 0)
1168 moov_pos = ftello(f);
1172 if (!_mmcamcorder_update_size(f, moov_pos, current_pos))
1176 _mmcam_dbg_err("No 'moov' container");
1189 _mmcam_dbg_err("ftell() returns negative value.");