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 ---------------------------------------------------------------------------------------*/
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
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);
52 /*=======================================================================================
53 | FUNCTION DEFINITIONS |
54 =======================================================================================*/
56 /*---------------------------------------------------------------------------------------
57 | GLOBAL FUNCTION DEFINITIONS: |
58 ---------------------------------------------------------------------------------------*/
60 static int __mmcamcorder_create_audiop_with_encodebin(MMHandleType handle)
62 int err = MM_ERROR_NONE;
63 int file_name_len = 0;
65 char *file_name = NULL;
66 const char *aenc_name = NULL;
67 const char *mux_name = NULL;
68 const char *sink_name = NULL;
71 GstPad *srcpad = NULL;
72 GstPad *sinkpad = NULL;
73 GList *element_list = NULL;
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;
82 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
83 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
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);
89 info = (_MMCamcorderAudioInfo *)sc->info_audio;
93 mux_elem = _mmcamcorder_get_type_element(handle, MM_CAM_FILE_FORMAT);
94 err = _mmcamcorder_conf_get_value_element_name(mux_elem, &mux_name);
96 if (!mux_name || !strcmp(mux_name, "wavenc")) {
97 /* IF MUX in not chosen then record in raw file */
98 MMCAM_LOG_INFO("Record without muxing.");
99 info->bMuxing = FALSE;
101 MMCAM_LOG_INFO("Record with mux.");
102 info->bMuxing = TRUE;
105 /* Create GStreamer pipeline */
106 _MMCAMCORDER_PIPELINE_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCODE_MAIN_PIPE, "camcorder_pipeline", err);
108 err = _mmcamcorder_create_audiosrc_bin(handle);
109 if (err != MM_ERROR_NONE)
113 /* Muxing. can use encodebin. */
114 err = _mmcamcorder_create_encodesink_bin((MMHandleType)hcamcorder, MM_CAMCORDER_ENCBIN_PROFILE_AUDIO);
115 if (err != MM_ERROR_NONE)
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,
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);
128 err = mm_camcorder_get_attributes(handle, NULL,
129 MMCAM_TARGET_FILENAME, &file_name, &file_name_len,
131 if (err != MM_ERROR_NONE) {
132 MMCAM_LOG_ERROR("failed to get filename [0x%x]", err);
133 err = MM_ERROR_CAMCORDER_RESOURCE_CREATION;
134 goto pipeline_creation_error;
137 /* without muxing. can't use encodebin. */
138 aenc_elem = _mmcamcorder_get_type_element(handle, MM_CAM_AUDIO_ENCODER);
140 MMCAM_LOG_ERROR("Fail to get type element");
141 err = MM_ERROR_CAMCORDER_RESOURCE_CREATION;
142 goto pipeline_creation_error;
145 err = _mmcamcorder_conf_get_value_element_name(aenc_elem, &aenc_name);
146 if ((!err) || (!aenc_name)) {
147 MMCAM_LOG_ERROR("Fail to get element name");
148 err = MM_ERROR_CAMCORDER_RESOURCE_CREATION;
149 goto pipeline_creation_error;
152 element_list = g_list_append(element_list, &sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN]);
154 _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_AQUE, "queue", NULL, element_list, err);
156 if (strcmp(aenc_name, "wavenc") != 0)
157 _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_CONV, "audioconvert", NULL, element_list, err);
159 _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_AENC, aenc_name, NULL, element_list, err);
161 _mmcamcorder_conf_get_element(handle, hcamcorder->conf_main,
162 CONFIGURE_CATEGORY_MAIN_RECORD,
165 _mmcamcorder_conf_get_value_element_name(sink_elem, &sink_name);
167 MMCAM_LOG_INFO("encode sink : %s", sink_name);
169 _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_SINK, sink_name, NULL, element_list, err);
171 _mmcamcorder_conf_set_value_element_property(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, sink_elem);
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_LOG_ERROR("add encode elements error.");
176 err = MM_ERROR_CAMCORDER_RESOURCE_CREATION;
177 goto pipeline_creation_error;
181 if (!_mmcamcorder_link_elements(element_list)) {
182 MMCAM_LOG_ERROR("encode element link error.");
183 err = MM_ERROR_CAMCORDER_GST_LINK;
184 goto pipeline_creation_error;
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);
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);
199 /* for replay gain tag */
200 MMCAMCORDER_ADD_EVENT_PROBE(srcpad, _MMCAMCORDER_HANDLER_AUDIOREC,
201 __mmcamcorder_eventprobe_monitor, hcamcorder);
203 gst_object_unref(srcpad);
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);
214 bus = gst_pipeline_get_bus(GST_PIPELINE(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst));
216 /* set sync callback */
217 gst_bus_set_sync_handler(bus, _mmcamcorder_encode_pipeline_bus_sync_callback, hcamcorder, NULL);
219 gst_object_unref(bus);
223 g_list_free(element_list);
227 return MM_ERROR_NONE;
229 pipeline_creation_error:
230 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCODE_MAIN_PIPE);
231 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_AUDIOSRC_BIN);
232 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_AQUE);
233 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_CONV);
234 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_AENC);
235 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_SINK);
236 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_BIN);
239 g_list_free(element_list);
248 _mmcamcorder_create_audio_pipeline(MMHandleType handle)
250 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
251 _MMCamcorderSubContext *sc = NULL;
253 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
254 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
256 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
258 return __mmcamcorder_create_audiop_with_encodebin(handle);
263 * This function destroy audio pipeline.
265 * @param[in] handle Handle of camcorder.
268 * @see _mmcamcorder_destroy_pipeline()
272 _mmcamcorder_destroy_audio_pipeline(MMHandleType handle)
274 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
275 _MMCamcorderSubContext *sc = NULL;
276 _MMCamcorderAudioInfo *info = NULL;
277 mmf_return_if_fail(hcamcorder);
278 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
280 mmf_return_if_fail(sc && sc->info_audio);
281 mmf_return_if_fail(sc->element);
283 info = sc->info_audio;
285 MMCAM_LOG_INFO("start");
287 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst) {
288 MMCAM_LOG_WARNING("release audio pipeline");
290 _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_NULL);
292 _mmcamcorder_remove_all_handlers((MMHandleType)hcamcorder, _MMCAMCORDER_HANDLER_CATEGORY_ALL);
295 GstPad *reqpad = NULL;
298 The ref_count of mux is always # of streams in here, i don't know why it happens.
299 So, i unref the mux manually
301 reqpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "audio");
302 gst_element_release_request_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, reqpad);
303 gst_object_unref(reqpad);
305 if (GST_IS_ELEMENT(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst) &&
306 GST_OBJECT_REFCOUNT(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst) > 1) {
307 gst_object_unref(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst);
310 gst_object_unref(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst);
313 MMCAM_LOG_INFO("done");
320 _mmcamcorder_audio_command(MMHandleType handle, int command)
323 int ret = MM_ERROR_NONE;
324 char *err_attr_name = NULL;
326 GstElement *pipeline = NULL;
327 GstElement *audioSrc = NULL;
329 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
330 _MMCamcorderSubContext *sc = NULL;
331 _MMCamcorderAudioInfo *info = NULL;
333 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
334 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
336 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
337 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
338 mmf_return_val_if_fail(sc->info_audio, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
339 pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
340 info = sc->info_audio;
344 pipeline = sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst;
345 audioSrc = sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst;
347 case _MMCamcorder_CMD_RECORD:
348 /* check status for resume case */
349 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) != MM_CAMCORDER_STATE_PAUSED) {
350 gboolean storage_validity = FALSE;
353 char *target_filename = NULL;
354 int filename_length = 0;
355 int root_directory_length = 0;
357 if (sc->pipeline_time)
358 gst_element_set_start_time(pipeline, sc->pipeline_time);
360 sc->pipeline_time = RESET_PAUSE_TIME;
362 ret = mm_camcorder_get_attributes(handle, &err_attr_name,
363 MMCAM_TARGET_MAX_SIZE, &imax_size,
364 MMCAM_TARGET_TIME_LIMIT, &imax_time,
365 MMCAM_FILE_FORMAT, &(info->fileformat),
366 MMCAM_TARGET_FILENAME, &target_filename, &filename_length,
367 MMCAM_ROOT_DIRECTORY, &hcamcorder->root_directory, &root_directory_length,
369 if (ret != MM_ERROR_NONE) {
370 MMCAM_LOG_WARNING("failed to get attribute. (%s:%x)", err_attr_name, ret);
371 SAFE_FREE(err_attr_name);
372 goto _ERR_CAMCORDER_AUDIO_COMMAND;
375 if (!target_filename && !hcamcorder->mstream_cb) {
376 MMCAM_LOG_ERROR("filename is not set and muxed stream cb is NULL");
377 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
378 goto _ERR_CAMCORDER_AUDIO_COMMAND;
381 SAFE_G_FREE(info->filename);
383 if (target_filename) {
384 info->filename = g_strdup(target_filename);
385 if (!info->filename) {
386 MMCAM_LOG_ERROR("STRDUP was failed for [%s]", target_filename);
387 goto _ERR_CAMCORDER_AUDIO_COMMAND;
390 MMCAM_LOG_INFO("Record start : file name [%s]", info->filename);
392 MMCAMCORDER_G_OBJECT_SET_POINTER(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename);
394 MMCAM_LOG_INFO("Recorded data will be written in [%s]", _MMCamcorder_FILENAME_NULL);
395 MMCAMCORDER_G_OBJECT_SET_POINTER(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", _MMCamcorder_FILENAME_NULL);
398 sc->ferror_send = FALSE;
399 sc->ferror_count = 0;
400 sc->bget_eos = FALSE;
401 sc->muxed_stream_offset = 0;
406 info->max_size = 0; /* do not check */
408 info->max_size = ((guint64)imax_size) << 10; /* to byte */
412 info->max_time = 0; /* do not check */
414 info->max_time = ((guint64)imax_time) * 1000; /* to millisecond */
416 ret = _mmcamcorder_get_storage_validity(handle, info->filename,
417 _MMCAMCORDER_AUDIO_MINIMUM_SPACE, &storage_validity);
418 if (ret != MM_ERROR_NONE || !storage_validity) {
419 MMCAM_LOG_ERROR("storage validation failed[0x%x]:%d", ret, storage_validity);
423 _mmcamcorder_adjust_recording_max_size(info->filename, &info->max_size);
426 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
427 if (ret != MM_ERROR_NONE)
428 goto _ERR_CAMCORDER_AUDIO_COMMAND;
432 case _MMCamcorder_CMD_PAUSE:
434 GstClock *pipe_clock = NULL;
437 if (info->b_committing) {
438 MMCAM_LOG_WARNING("now on commiting previous file!!(cmd : %d)", cmd);
439 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
442 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
443 if (info->filesize > 0) {
445 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
446 MMCAM_LOG_ERROR("Pause fail, wait 200 ms, but file size is %"G_GUINT64_FORMAT,
448 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
450 MMCAM_LOG_WARNING("Wait for enough audio frame, retry count[%d], file size is %"G_GUINT64_FORMAT,
451 count, info->filesize);
453 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
456 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PAUSED);
457 if (ret != MM_ERROR_NONE)
458 goto _ERR_CAMCORDER_AUDIO_COMMAND;
460 /* FIXME: consider delay. */
461 pipe_clock = gst_pipeline_get_clock(GST_PIPELINE(pipeline));
462 sc->pipeline_time = gst_clock_get_time(pipe_clock) - gst_element_get_base_time(GST_ELEMENT(pipeline));
466 case _MMCamcorder_CMD_CANCEL:
467 if (info->b_committing) {
468 MMCAM_LOG_WARNING("now on commiting previous file!!(cmd : %d)", cmd);
469 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
472 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
473 if (ret != MM_ERROR_NONE)
474 goto _ERR_CAMCORDER_AUDIO_COMMAND;
477 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
479 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", FALSE);
481 _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_NULL);
483 /* Reconstruct audio encoding pipeline,
484 because some muxer(ex:avmux_amr) should be created newly for next recording after cancel.
485 Otherwise, it returns error when start next recording. */
486 _mmcamcorder_destroy_audio_pipeline(handle);
487 ret = _mmcamcorder_create_audio_pipeline(handle);
489 sc->pipeline_time = 0;
491 sc->isMaxsizePausing = FALSE;
492 sc->isMaxtimePausing = FALSE;
494 if (info->filename) {
495 MMCAM_LOG_INFO("file delete(%s)", info->filename);
496 unlink(info->filename);
497 SAFE_G_FREE(info->filename);
501 case _MMCamcorder_CMD_COMMIT:
504 guint64 free_space = 0;
505 MMCAM_LOG_INFO("_MMCamcorder_CMD_COMMIT");
507 if (info->b_committing) {
508 MMCAM_LOG_WARNING("now on commiting previous file!!(cmd : %d)", cmd);
509 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
511 MMCAM_LOG_INFO("_MMCamcorder_CMD_COMMIT : start");
512 info->b_committing = TRUE;
515 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
516 if (info->filesize > 0) {
518 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
519 MMCAM_LOG_ERROR("Commit fail, waited 200 ms, but file size is %"G_GUINT64_FORMAT, info->filesize);
520 info->b_committing = FALSE;
521 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
523 MMCAM_LOG_WARNING("Waiting for enough audio frame, re-count[%d], file size is %"G_GUINT64_FORMAT,
524 count, info->filesize);
526 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
529 _mmcamcorder_get_freespace(hcamcorder->storage_info.type, &free_space);
530 if (free_space < _MMCAMCORDER_AUDIO_MINIMUM_SPACE) {
531 MMCAM_LOG_WARNING("_MMCamcorder_CMD_COMMIT out of storage [%" G_GUINT64_FORMAT "]", free_space);
532 ret = MM_ERROR_OUT_OF_STORAGE;
533 goto _ERR_CAMCORDER_AUDIO_COMMAND;
537 if (gst_element_send_event(audioSrc, gst_event_new_eos()) == FALSE) {
538 MMCAM_LOG_ERROR("send EOS failed");
539 info->b_committing = FALSE;
540 ret = MM_ERROR_CAMCORDER_INTERNAL;
541 goto _ERR_CAMCORDER_AUDIO_COMMAND;
544 MMCAM_LOG_INFO("send EOS done");
546 /* for pause -> commit case */
547 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) == MM_CAMCORDER_STATE_PAUSED) {
548 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
549 if (ret != MM_ERROR_NONE) {
550 info->b_committing = FALSE;
551 goto _ERR_CAMCORDER_AUDIO_COMMAND;
555 MMCAM_LOG_ERROR("No audio stream source");
556 info->b_committing = FALSE;
557 ret = MM_ERROR_CAMCORDER_INTERNAL;
558 goto _ERR_CAMCORDER_AUDIO_COMMAND;
561 /* wait until finishing EOS */
562 MMCAM_LOG_INFO("Start to wait EOS");
563 if ((ret = _mmcamcorder_get_eos_message(handle)) != MM_ERROR_NONE) {
564 info->b_committing = FALSE;
565 goto _ERR_CAMCORDER_AUDIO_COMMAND;
570 case _MMCamcorder_CMD_PREVIEW_START:
571 /*MM_CAMCORDER_START_CHANGE_STATE;*/
574 case _MMCamcorder_CMD_PREVIEW_STOP:
575 /*MM_CAMCORDER_STOP_CHANGE_STATE;*/
580 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
584 _ERR_CAMCORDER_AUDIO_COMMAND:
588 int _mmcamcorder_audio_handle_eos(MMHandleType handle)
590 int err = MM_ERROR_NONE;
591 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
592 _MMCamcorderSubContext *sc = NULL;
593 _MMCamcorderAudioInfo *info = NULL;
594 GstElement *pipeline = NULL;
595 _MMCamcorderMsgItem msg;
596 MMCamRecordingReport * report;
598 mmf_return_val_if_fail(hcamcorder, FALSE);
599 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
601 mmf_return_val_if_fail(sc, FALSE);
602 mmf_return_val_if_fail(sc->info_audio, FALSE);
606 info = sc->info_audio;
608 pipeline = sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst;
610 err = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
611 if (err != MM_ERROR_NONE)
612 MMCAM_LOG_WARNING("Failed:_MMCamcorder_CMD_COMMIT:GST_STATE_READY. err[%x]", err);
614 /* Send recording report message to application */
615 msg.id = MM_MESSAGE_CAMCORDER_AUDIO_CAPTURED;
616 report = (MMCamRecordingReport *)g_malloc(sizeof(MMCamRecordingReport));
619 /* MM_AUDIO_CODEC_AAC + MM_FILE_FORMAT_MP4 */
620 if (info->fileformat == MM_FILE_FORMAT_3GP || info->fileformat == MM_FILE_FORMAT_MP4)
621 __mmcamcorder_audio_add_metadata_info_m4a(handle);
624 report->recording_filename = g_strdup(info->filename);
625 msg.param.data = report;
627 _mmcamcorder_send_message(handle, &msg);
630 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
632 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", FALSE);
634 _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_NULL);
636 sc->pipeline_time = 0;
638 sc->isMaxsizePausing = FALSE;
639 sc->isMaxtimePausing = FALSE;
641 SAFE_G_FREE(info->filename);
643 MMCAM_LOG_ERROR("_MMCamcorder_CMD_COMMIT : end");
645 info->b_committing = FALSE;
652 __mmcamcorder_get_decibel(unsigned char* raw, int size, MMCamcorderAudioFormat format)
654 #define MAX_AMPLITUDE_MEAN_16BIT (23170.115738161934)
655 #define MAX_AMPLITUDE_MEAN_08BIT (89.803909382810)
656 #define DEFAULT_DECIBEL (-80.0)
665 float db = DEFAULT_DECIBEL;
667 unsigned long long square_sum = 0;
669 if (format == MM_CAMCORDER_AUDIO_FORMAT_PCM_S16_LE)
671 else /*MM_CAMCORDER_AUDIO_FORMAT_PCM_U8*/
674 for ( ; i < size ; i += (depthByte<<1)) {
675 if (depthByte == 1) {
676 pcm8 = (char *)(raw + i);
677 square_sum += (*pcm8) * (*pcm8);
679 pcm16 = (short*)(raw + i);
680 square_sum += (*pcm16) * (*pcm16);
687 rms = sqrt((double)square_sum/(double)count);
690 db = 20 * log10(rms/MAX_AMPLITUDE_MEAN_08BIT);
692 db = 20 * log10(rms/MAX_AMPLITUDE_MEAN_16BIT);
696 MMCAM_LOG_INFO("size[%d],depthByte[%d],count[%d],rms[%f],db[%f]",
697 size, depthByte, count, rms, db);
704 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_voicerecorder(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
706 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
708 int current_state = MM_CAMCORDER_STATE_NONE;
712 _MMCamcorderMsgItem msg;
713 int err = MM_ERROR_UNKNOWN;
714 char *err_name = NULL;
715 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
718 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
720 current_state = _mmcamcorder_get_state((MMHandleType)hcamcorder);
721 if (current_state < MM_CAMCORDER_STATE_PREPARE) {
722 MMCAM_LOG_WARNING("Not ready for stream callback");
723 return GST_PAD_PROBE_OK;
726 memset(&mapinfo, 0x0, sizeof(GstMapInfo));
728 /* Set volume to audio input */
729 err = mm_camcorder_get_attributes((MMHandleType)hcamcorder, &err_name,
730 MMCAM_AUDIO_VOLUME, &volume,
731 MMCAM_AUDIO_FORMAT, &format,
732 MMCAM_AUDIO_CHANNEL, &channel,
736 MMCAM_LOG_WARNING("Get attrs fail. (%s:%x)", err_name, err);
738 return GST_PAD_PROBE_OK;
741 if (!gst_buffer_map(buffer, &mapinfo, GST_MAP_READWRITE)) {
742 MMCAM_LOG_WARNING("map failed : buffer[%p]", buffer);
743 return GST_PAD_PROBE_OK;
747 memset(mapinfo.data, 0, mapinfo.size);
749 /* Get current volume level of real input stream */
750 curdcb = __mmcamcorder_get_decibel(mapinfo.data, mapinfo.size, format);
752 msg.id = MM_MESSAGE_CAMCORDER_CURRENT_VOLUME;
753 msg.param.rec_volume_dB = curdcb;
754 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
756 _MMCAMCORDER_LOCK_ASTREAM_CALLBACK(hcamcorder);
758 MMCAM_LOG_DEBUG("audio stream cb[%p][%"GST_TIME_FORMAT"] - fmt[%d], ch[%d], size[%"G_GSIZE_FORMAT"], dB[%f]",
759 hcamcorder->astream_cb, GST_TIME_ARGS(GST_BUFFER_PTS(buffer)), format, channel, mapinfo.size, curdcb);
761 /* CALL audio stream callback */
762 if (hcamcorder->astream_cb) {
763 MMCamcorderAudioStreamDataType stream;
765 stream.data = (void *)mapinfo.data;
766 stream.format = format;
767 stream.channel = channel;
768 stream.length = mapinfo.size;
769 stream.timestamp = (unsigned int)(GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer)));
770 stream.volume_dB = curdcb;
772 hcamcorder->astream_cb(&stream, hcamcorder->astream_cb_param);
775 _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK(hcamcorder);
777 gst_buffer_unmap(buffer, &mapinfo);
779 return GST_PAD_PROBE_OK;
783 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
785 static int count = 0;
786 guint64 rec_pipe_time = 0;
787 guint64 free_space = 0;
788 guint64 buffer_size = 0;
789 guint64 trailer_size = 0;
790 unsigned long long remained_time = 0;
791 int get_trailer_size = 0;
793 _MMCamcorderSubContext *sc = NULL;
794 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
795 _MMCamcorderAudioInfo *audioinfo = NULL;
796 _MMCamcorderMsgItem msg;
797 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
799 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_DROP);
800 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
802 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
803 mmf_return_val_if_fail(sc && sc->info_audio, GST_PAD_PROBE_DROP);
804 audioinfo = sc->info_audio;
806 if (sc->isMaxtimePausing || sc->isMaxsizePausing) {
807 MMCAM_LOG_WARNING("isMaxtimePausing[%d],isMaxsizePausing[%d]",
808 sc->isMaxtimePausing, sc->isMaxsizePausing);
809 return GST_PAD_PROBE_DROP;
812 buffer_size = gst_buffer_get_size(buffer);
814 if (audioinfo->filesize == 0) {
815 if (audioinfo->fileformat == MM_FILE_FORMAT_WAV)
816 audioinfo->filesize += 44; /* wave header size */
817 else if (audioinfo->fileformat == MM_FILE_FORMAT_AMR)
818 audioinfo->filesize += 6; /* amr header size */
820 audioinfo->filesize += buffer_size;
821 return GST_PAD_PROBE_OK;
824 if (sc->ferror_send) {
825 MMCAM_LOG_WARNING("file write error, drop frames");
826 return GST_PAD_PROBE_DROP;
829 /* get trailer size */
831 audioinfo->fileformat == MM_FILE_FORMAT_3GP ||
832 audioinfo->fileformat == MM_FILE_FORMAT_MP4 ||
833 audioinfo->fileformat == MM_FILE_FORMAT_AAC) ? TRUE : FALSE;
834 if (get_trailer_size) {
835 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
836 MMCAM_LOG_VERBOSE("trailer_size %"G_GUINT64_FORMAT, trailer_size);
838 trailer_size = 0; /* no trailer */
841 /* to minimizing free space check overhead */
842 count = count % _MMCAMCORDER_FREE_SPACE_CHECK_INTERVAL;
844 gint free_space_ret = 0;
845 storage_state_e storage_state = STORAGE_STATE_UNMOUNTABLE;
847 /* check free space */
848 free_space_ret = _mmcamcorder_get_freespace(hcamcorder->storage_info.type, &free_space);
849 if (free_space_ret != 0) {
850 MMCAM_LOG_ERROR("Error occurred. [%d]", free_space_ret);
851 if (sc->ferror_count == 2 && sc->ferror_send == FALSE) {
852 sc->ferror_send = TRUE;
854 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
855 msg.param.code = MM_ERROR_FILE_READ;
857 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
862 return GST_PAD_PROBE_DROP; /* skip this buffer */
865 if (free_space == 0) {
866 /* check storage state */
867 storage_get_state(hcamcorder->storage_info.id, &storage_state);
869 MMCAM_LOG_WARNING("storage state %d", storage_state);
871 if (storage_state == STORAGE_STATE_REMOVED ||
872 storage_state == STORAGE_STATE_UNMOUNTABLE) {
873 MMCAM_LOG_ERROR("storage was removed!");
875 _MMCAMCORDER_LOCK(hcamcorder);
877 if (sc->ferror_send == FALSE) {
878 MMCAM_LOG_ERROR("OUT_OF_STORAGE error");
880 sc->ferror_send = TRUE;
882 _MMCAMCORDER_UNLOCK(hcamcorder);
884 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
885 msg.param.code = MM_ERROR_OUT_OF_STORAGE;
887 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
889 _MMCAMCORDER_UNLOCK(hcamcorder);
890 MMCAM_LOG_WARNING("error was already sent");
893 return GST_PAD_PROBE_DROP;
897 if (free_space < (guint64)(_MMCAMCORDER_AUDIO_MINIMUM_SPACE + buffer_size + trailer_size)) {
898 MMCAM_LOG_WARNING("No more space for recording!!!");
899 MMCAM_LOG_WARNING("Free Space : [%" G_GUINT64_FORMAT "], file size : [%" G_GUINT64_FORMAT "]",
900 free_space, audioinfo->filesize);
902 if (audioinfo->bMuxing)
903 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
905 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", TRUE);
907 sc->isMaxsizePausing = TRUE;
908 msg.id = MM_MESSAGE_CAMCORDER_NO_FREE_SPACE;
909 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
911 return GST_PAD_PROBE_DROP; /* skip this buffer */
915 if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_PTS(buffer))) {
916 MMCAM_LOG_ERROR("Buffer timestamp is invalid, check it");
917 return GST_PAD_PROBE_DROP;
920 rec_pipe_time = GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer));
922 /* calculate remained time can be recorded */
923 if (audioinfo->max_time > 0 && audioinfo->max_time < (remained_time + rec_pipe_time)) {
924 remained_time = audioinfo->max_time - rec_pipe_time;
925 } else if (audioinfo->max_size > 0) {
926 long double max_size = (long double)audioinfo->max_size;
927 long double current_size = (long double)(audioinfo->filesize + buffer_size + trailer_size);
929 remained_time = (unsigned long long)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
932 /* check max size of recorded file */
933 if (audioinfo->max_size > 0 &&
934 audioinfo->max_size < audioinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) {
935 MMCAM_LOG_WARNING("Max size!!! Recording is paused.");
936 MMCAM_LOG_WARNING("Max [%" G_GUINT64_FORMAT "], file [%" G_GUINT64_FORMAT "], trailer : [%" G_GUINT64_FORMAT "]", \
937 audioinfo->max_size, audioinfo->filesize, trailer_size);
939 /* just same as pause status. After blocking two queue, this function will not call again. */
940 if (audioinfo->bMuxing)
941 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
943 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", TRUE);
945 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
946 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
947 msg.param.recording_status.filesize = (unsigned long long)((audioinfo->filesize + trailer_size) >> 10);
948 msg.param.recording_status.remained_time = 0;
949 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
951 MMCAM_LOG_WARNING("Last filesize sent by message : %"G_GUINT64_FORMAT, audioinfo->filesize + trailer_size);
953 sc->isMaxsizePausing = TRUE;
954 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
955 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
957 /* skip this buffer */
958 return GST_PAD_PROBE_DROP;
961 /* check recording time limit and send recording status message */
962 if (audioinfo->max_time > 0 && rec_pipe_time > audioinfo->max_time) {
963 MMCAM_LOG_WARNING("Current time : [%"GST_TIME_FORMAT"], Maximum time : [%"GST_TIME_FORMAT"]",
964 GST_TIME_ARGS(GST_BUFFER_PTS(buffer)), GST_TIME_ARGS(audioinfo->max_time * GST_MSECOND));
966 if (audioinfo->bMuxing)
967 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
969 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", TRUE);
971 sc->isMaxtimePausing = TRUE;
972 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
973 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
975 /* skip this buffer */
976 return GST_PAD_PROBE_DROP;
979 /* send message for recording time and recorded file size */
980 if (audioinfo->b_committing == FALSE) {
981 audioinfo->filesize += buffer_size;
983 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
984 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
985 msg.param.recording_status.filesize = (unsigned long long)((audioinfo->filesize + trailer_size) >> 10);
986 msg.param.recording_status.remained_time = remained_time;
988 MMCAM_LOG_DEBUG("audio rec[%"GST_TIME_FORMAT"], size[%"G_GUINT64_FORMAT"(trailer:%"G_GUINT64_FORMAT")]",
989 GST_TIME_ARGS(GST_BUFFER_PTS(buffer)), audioinfo->filesize + trailer_size, trailer_size);
991 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
993 return GST_PAD_PROBE_OK;
995 /* skip this buffer if commit process has been started */
996 return GST_PAD_PROBE_DROP;
1000 /* START TAG HERE */
1001 static gboolean __mmcamcorder_audio_add_metadata_info_m4a(MMHandleType handle)
1005 guint64 udta_size = 0;
1006 gint64 current_pos = 0;
1007 gint64 moov_pos = 0;
1008 gint64 udta_pos = 0;
1009 /* supporting audio geo tag for mobile */
1011 gdouble longitude = 0;
1012 gdouble latitude = 0;
1013 gdouble altitude = 0;
1014 _MMCamcorderLocationInfo geo_info = {0, 0, 0};
1015 _MMCamcorderLocationInfo loc_info = {0, 0, 0};
1017 char err_msg[128] = {'\0',};
1019 _MMCamcorderAudioInfo *info = NULL;
1020 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1021 _MMCamcorderSubContext *sc = NULL;
1023 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1024 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1026 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1027 mmf_return_val_if_fail(sc->info_audio, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1029 info = sc->info_audio;
1030 mm_camcorder_get_attributes(handle, NULL,
1031 MMCAM_TAG_GPS_ENABLE, &gps_enable,
1035 mm_camcorder_get_attributes(handle, NULL,
1036 MMCAM_TAG_LATITUDE, &latitude,
1037 MMCAM_TAG_LONGITUDE, &longitude,
1038 MMCAM_TAG_ALTITUDE, &altitude,
1040 loc_info.longitude = _mmcamcorder_double_to_fix(longitude);
1041 loc_info.latitude = _mmcamcorder_double_to_fix(latitude);
1042 loc_info.altitude = _mmcamcorder_double_to_fix(altitude);
1043 geo_info.longitude = longitude *10000;
1044 geo_info.latitude = latitude *10000;
1045 geo_info.altitude = altitude *10000;
1048 f = fopen64(info->filename, "rb+");
1050 strerror_r(errno, err_msg, 128);
1051 MMCAM_LOG_ERROR("file open failed [%s]", err_msg);
1055 /* find udta container.
1056 if, there are udta container, write loci box after that
1057 else, make udta container and write loci box. */
1058 if (_mmcamcorder_find_fourcc(f, MMCAM_FOURCC('u', 'd', 't', 'a'), TRUE)) {
1061 MMCAM_LOG_INFO("find udta container");
1064 if (fseek(f, -8L, SEEK_CUR) != 0)
1067 udta_pos = ftello(f);
1071 nread = fread(&buf, sizeof(char), sizeof(buf), f);
1073 MMCAM_LOG_INFO("recorded file fread %zu", nread);
1075 udta_size = _mmcamcorder_get_container_size(buf);
1077 /* goto end of udta and write 'smta' box */
1078 if (fseek(f, (udta_size-4L), SEEK_CUR) != 0)
1082 if (!_mmcamcorder_write_loci(f, loc_info))
1085 if (!_mmcamcorder_write_geodata(f, geo_info))
1089 current_pos = ftello(f);
1090 if (current_pos < 0)
1093 if (!_mmcamcorder_update_size(f, udta_pos, current_pos))
1096 MMCAM_LOG_INFO("No udta container");
1097 if (fseek(f, 0, SEEK_END) != 0)
1100 if (!_mmcamcorder_write_udta(f, gps_enable, loc_info, geo_info))
1104 /* find moov container.
1105 update moov container size. */
1106 if ((current_pos = ftello(f)) < 0)
1109 if (_mmcamcorder_find_fourcc(f, MMCAM_FOURCC('m', 'o', 'o', 'v'), TRUE)) {
1111 MMCAM_LOG_INFO("found moov container");
1112 if (fseek(f, -8L, SEEK_CUR) != 0)
1115 moov_pos = ftello(f);
1119 if (!_mmcamcorder_update_size(f, moov_pos, current_pos))
1123 MMCAM_LOG_ERROR("No 'moov' container");
1136 MMCAM_LOG_ERROR("ftell() returns negative value.");