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 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 void __mmcamcorder_audiorec_pad_added_cb(GstElement *element, GstPad *pad, 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 const char *aenc_name = NULL;
66 const char *mux_name = NULL;
69 GstPad *srcpad = NULL;
70 GstPad *sinkpad = NULL;
71 GList *element_list = NULL;
73 _MMCamcorderAudioInfo *info = NULL;
74 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
75 _MMCamcorderSubContext *sc = NULL;
76 type_element *aenc_elem = NULL;
77 type_element *mux_elem = NULL;
79 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
80 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
82 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
83 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
84 mmf_return_val_if_fail(sc->info_audio, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
86 info = (_MMCamcorderAudioInfo *)sc->info_audio;
90 mux_elem = _mmcamcorder_get_type_element(handle, MM_CAM_FILE_FORMAT);
91 err = _mmcamcorder_conf_get_value_element_name( mux_elem, &mux_name );
93 if (!mux_name || !strcmp(mux_name, "wavenc")) {
94 /* IF MUX in not chosen then record in raw file */
95 _mmcam_dbg_log("Record without muxing.");
96 info->bMuxing = FALSE;
98 _mmcam_dbg_log("Record with mux.");
102 /* Create GStreamer pipeline */
103 _MMCAMCORDER_PIPELINE_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCODE_MAIN_PIPE, "camcorder_pipeline", err);
105 err = _mmcamcorder_create_audiosrc_bin(handle);
106 if (err != MM_ERROR_NONE) {
111 /* Muxing. can use encodebin. */
112 err = _mmcamcorder_create_encodesink_bin((MMHandleType)hcamcorder, MM_CAMCORDER_ENCBIN_PROFILE_AUDIO);
113 if (err != MM_ERROR_NONE) {
117 /* without muxing. can't use encodebin. */
118 aenc_elem = _mmcamcorder_get_type_element(handle, MM_CAM_AUDIO_ENCODER);
120 _mmcam_dbg_err("Fail to get type element");
121 err = MM_ERROR_CAMCORDER_RESOURCE_CREATION;
122 goto pipeline_creation_error;
125 err = _mmcamcorder_conf_get_value_element_name(aenc_elem, &aenc_name);
126 if ((!err) || (!aenc_name)) {
127 _mmcam_dbg_err("Fail to get element name");
128 err = MM_ERROR_CAMCORDER_RESOURCE_CREATION;
129 goto pipeline_creation_error;
132 _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_AQUE, "queue", NULL, element_list, err);
134 if (strcmp(aenc_name, "wavenc") != 0) {
135 _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_CONV, "audioconvert", NULL, element_list, err);
138 _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_AENC, aenc_name, NULL, element_list, err);
140 _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_SINK, "filesink", NULL, element_list, err);
143 /* Add and link elements */
145 /* IF MUX is indicated create MUX */
146 gst_bin_add_many(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst),
147 sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst,
148 sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst,
151 srcpad = gst_element_get_static_pad (sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst, "src");
152 sinkpad = gst_element_get_static_pad (sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst, "audio_sink0");
153 _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error);
155 /* IF MUX in not chosen then record in raw amr file */
156 if (!strcmp(aenc_name, "wavenc")) {
157 gst_bin_add_many(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst),
158 sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst,
159 sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst,
160 sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst,
161 sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst,
164 if (!_MM_GST_ELEMENT_LINK_MANY(sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst,
165 sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst,
166 sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst,
167 sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst,
169 err = MM_ERROR_CAMCORDER_GST_LINK;
170 goto pipeline_creation_error;
173 gst_bin_add_many(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst),
174 sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst,
175 sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst,
176 sc->encode_element[_MMCAMCORDER_ENCSINK_CONV].gst,
177 sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst,
178 sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst,
181 if (!_MM_GST_ELEMENT_LINK_MANY(sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst,
182 sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst,
183 sc->encode_element[_MMCAMCORDER_ENCSINK_CONV].gst,
184 sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst,
185 sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst,
187 err = MM_ERROR_CAMCORDER_GST_LINK;
188 goto pipeline_creation_error;
193 /* set data probe function */
194 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst, "src");
195 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_AUDIOREC,
196 __mmcamcorder_audio_dataprobe_voicerecorder, hcamcorder);
197 gst_object_unref(srcpad);
201 MMCAMCORDER_SIGNAL_CONNECT(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst,
202 _MMCAMCORDER_HANDLER_AUDIOREC,
204 __mmcamcorder_audiorec_pad_added_cb,
207 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst, "src");
208 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_AUDIOREC,
209 __mmcamcorder_audio_dataprobe_record, hcamcorder);
210 gst_object_unref(srcpad);
214 bus = gst_pipeline_get_bus(GST_PIPELINE(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst));
216 /* register message callback */
217 hcamcorder->pipeline_cb_event_id = gst_bus_add_watch(bus, (GstBusFunc)_mmcamcorder_pipeline_cb_message, hcamcorder);
219 /* set sync callback */
220 gst_bus_set_sync_handler(bus, gst_bus_sync_signal_handler, hcamcorder, NULL);
222 gst_object_unref(bus);
226 g_list_free(element_list);
230 return MM_ERROR_NONE;
232 pipeline_creation_error:
233 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCODE_MAIN_PIPE);
234 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_AUDIOSRC_BIN);
235 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_AQUE);
236 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_CONV);
237 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_AENC);
238 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_SINK);
239 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCSINK_BIN);
242 g_list_free(element_list);
251 _mmcamcorder_create_audio_pipeline(MMHandleType handle)
253 mmf_camcorder_t *hcamcorder= MMF_CAMCORDER(handle);
254 _MMCamcorderSubContext *sc = NULL;
256 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
257 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
259 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
261 return __mmcamcorder_create_audiop_with_encodebin(handle);
266 * This function destroy audio pipeline.
268 * @param[in] handle Handle of camcorder.
271 * @see _mmcamcorder_destroy_pipeline()
275 _mmcamcorder_destroy_audio_pipeline(MMHandleType handle)
277 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
278 _MMCamcorderSubContext *sc = NULL;
279 _MMCamcorderAudioInfo *info = NULL;
280 mmf_return_if_fail(hcamcorder);
281 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
283 mmf_return_if_fail(sc && sc->info_audio);
284 mmf_return_if_fail(sc->element);
286 info = sc->info_audio;
288 _mmcam_dbg_log("start");
290 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst) {
291 _mmcam_dbg_warn("release audio pipeline");
293 _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_NULL);
295 _mmcamcorder_remove_all_handlers((MMHandleType)hcamcorder, _MMCAMCORDER_HANDLER_CATEGORY_ALL);
298 GstPad *reqpad = NULL;
301 The ref_count of mux is always # of streams in here, i don't know why it happens.
302 So, i unref the mux manually
304 reqpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "audio");
305 gst_element_release_request_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, reqpad);
306 gst_object_unref(reqpad);
308 if(GST_IS_ELEMENT(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst) &&
309 GST_OBJECT_REFCOUNT(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst) > 1) {
310 gst_object_unref(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst);
313 gst_object_unref(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst);
316 _mmcam_dbg_log("done");
323 * This function operates each command on audio mode.
325 * @param c [in] Handle of camcorder context.
326 * @param command [in] command type received from Multimedia Framework.
328 * @return This function returns MM_ERROR_NONE on success, or the other values
331 * @see _mmcamcorder_set_functions()
337 void* _MMCamcorderStartHelperFunc(void *handle)
339 mmf_camcorder_t *hcamcorder= MMF_CAMCORDER(handle);
340 _mmcamcorder_set_state((MMHandleType)hcamcorder, hcamcorder->target_state);
345 void* _MMCamcorderStopHelperFunc(void *handle)
347 mmf_camcorder_t *hcamcorder= MMF_CAMCORDER(handle);
348 _mmcamcorder_set_state((MMHandleType)hcamcorder, hcamcorder->target_state);
355 _mmcamcorder_audio_command(MMHandleType handle, int command)
358 int ret = MM_ERROR_NONE;
361 guint64 free_space = 0;
362 char *dir_name = NULL;
363 char *err_attr_name = NULL;
365 GstElement *pipeline = NULL;
366 GstElement *audioSrc = NULL;
368 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
369 _MMCamcorderSubContext *sc = NULL;
370 _MMCamcorderAudioInfo *info = NULL;
372 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
373 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
375 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
376 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
377 mmf_return_val_if_fail(sc->info_audio, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
378 pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
379 info = sc->info_audio;
383 pipeline = sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst;
384 audioSrc = sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst;
386 case _MMCamcorder_CMD_RECORD:
387 /* check status for resume case */
388 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) != MM_CAMCORDER_STATE_PAUSED) {
391 char *temp_filename = NULL;
392 int file_system_type = 0;
394 if(sc->pipeline_time) {
395 gst_element_set_start_time(pipeline, sc->pipeline_time);
397 sc->pipeline_time = RESET_PAUSE_TIME;
399 ret = mm_camcorder_get_attributes(handle, &err_attr_name,
400 MMCAM_TARGET_MAX_SIZE, &imax_size,
401 MMCAM_TARGET_TIME_LIMIT, &imax_time,
402 MMCAM_FILE_FORMAT, &(info->fileformat),
403 MMCAM_TARGET_FILENAME, &temp_filename, &size,
405 if (ret != MM_ERROR_NONE) {
406 _mmcam_dbg_warn("failed to get attribute. (%s:%x)", err_attr_name, ret);
407 SAFE_FREE(err_attr_name);
408 goto _ERR_CAMCORDER_AUDIO_COMMAND;
411 if (temp_filename == NULL) {
412 _mmcam_dbg_err("filename is not set");
413 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
414 goto _ERR_CAMCORDER_AUDIO_COMMAND;
417 info->filename = strdup(temp_filename);
418 if (!info->filename) {
419 _mmcam_dbg_err("STRDUP was failed");
420 goto _ERR_CAMCORDER_AUDIO_COMMAND;
423 _mmcam_dbg_log("Record start : set file name using attribute - %s\n ",info->filename);
425 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename);
427 sc->ferror_send = FALSE;
428 sc->ferror_count = 0;
429 sc->bget_eos = FALSE;
433 if (imax_size <= 0) {
434 info->max_size = 0; /* do not check */
436 info->max_size = ((guint64)imax_size) << 10; /* to byte */
440 if (imax_time <= 0) {
441 info->max_time = 0; /* do not check */
443 info->max_time = ((guint64)imax_time) * 1000; /* to millisecond */
446 /* TODO : check free space before recording start */
447 dir_name = g_path_get_dirname(info->filename);
449 err = _mmcamcorder_get_freespace(dir_name, &free_space);
450 _mmcam_dbg_warn("current space for recording - %s :[%" G_GUINT64_FORMAT "]" ,dir_name, free_space);
452 if (_mmcamcorder_get_file_system_type(dir_name, &file_system_type) == 0) {
453 /* MSDOS_SUPER_MAGIC : 0x4d44 */
454 if (file_system_type == MSDOS_SUPER_MAGIC &&
455 (info->max_size == 0 || info->max_size > FAT32_FILE_SYSTEM_MAX_SIZE)) {
456 _mmcam_dbg_warn("FAT32 and too large max[%"G_GUINT64_FORMAT"], set max as %"G_GUINT64_FORMAT,
457 info->max_size, FAT32_FILE_SYSTEM_MAX_SIZE);
458 info->max_size = FAT32_FILE_SYSTEM_MAX_SIZE;
460 _mmcam_dbg_warn("file system 0x%x, max size %"G_GUINT64_FORMAT,
461 file_system_type, info->max_size);
464 _mmcam_dbg_warn("_mmcamcorder_get_file_system_type failed");
470 _mmcam_dbg_err("failed to get directory name");
474 if ((err == -1) || free_space <= (_MMCAMCORDER_AUDIO_MINIMUM_SPACE+(5*1024))) {
475 _mmcam_dbg_err("OUT of STORAGE [err:%d or free space [%" G_GUINT64_FORMAT "] is smaller than [%d]",
476 err, free_space, (_MMCAMCORDER_AUDIO_MINIMUM_SPACE+(5*1024)));
477 return MM_ERROR_OUT_OF_STORAGE;
481 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
482 if (ret != MM_ERROR_NONE) {
483 goto _ERR_CAMCORDER_AUDIO_COMMAND;
487 case _MMCamcorder_CMD_PAUSE:
489 GstClock *pipe_clock = NULL;
492 if (info->b_commiting) {
493 _mmcam_dbg_warn("now on commiting previous file!!(cmd : %d)", cmd);
494 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
497 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
498 if (info->filesize > 0) {
500 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
501 _mmcam_dbg_err("Pause fail, wait 200 ms, but file size is %lld",
503 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
505 _mmcam_dbg_warn("Wait for enough audio frame, retry count[%d], file size is %lld",
506 count, info->filesize);
508 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
511 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PAUSED);
512 if (ret != MM_ERROR_NONE) {
513 goto _ERR_CAMCORDER_AUDIO_COMMAND;
516 /* FIXME: consider delay. */
517 pipe_clock = gst_pipeline_get_clock(GST_PIPELINE(pipeline));
518 sc->pipeline_time = gst_clock_get_time(pipe_clock) - gst_element_get_base_time(GST_ELEMENT(pipeline));
522 case _MMCamcorder_CMD_CANCEL:
523 if (info->b_commiting) {
524 _mmcam_dbg_warn("now on commiting previous file!!(cmd : %d)", cmd);
525 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
528 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
529 if (ret != MM_ERROR_NONE) {
530 goto _ERR_CAMCORDER_AUDIO_COMMAND;
534 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
536 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", FALSE);
539 _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, GST_STATE_NULL);
541 sc->pipeline_time = 0;
543 sc->isMaxsizePausing = FALSE;
544 sc->isMaxtimePausing = FALSE;
546 if (info->filename) {
547 _mmcam_dbg_log("file delete(%s)", info->filename);
548 unlink(info->filename);
549 g_free(info->filename);
550 info->filename = NULL;
554 case _MMCamcorder_CMD_COMMIT:
558 _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT");
560 if (info->b_commiting) {
561 _mmcam_dbg_warn("now on commiting previous file!!(cmd : %d)", cmd);
562 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
564 _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT : start");
565 info->b_commiting = TRUE;
568 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
569 if (info->filesize > 0) {
571 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
572 _mmcam_dbg_err("Commit fail, waited 200 ms, but file size is %lld", info->filesize);
573 info->b_commiting = FALSE;
574 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
576 _mmcam_dbg_warn("Waiting for enough audio frame, re-count[%d], file size is %lld",
577 count, info->filesize);
579 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
583 ret = gst_element_send_event(audioSrc, gst_event_new_eos());
584 /* for pause -> commit case */
585 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) == MM_CAMCORDER_STATE_PAUSED) {
586 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
587 if (ret != MM_ERROR_NONE) {
588 info->b_commiting = FALSE;
589 goto _ERR_CAMCORDER_AUDIO_COMMAND;
594 /* wait until finishing EOS */
595 _mmcam_dbg_log("Start to wait EOS");
596 if ((ret =_mmcamcorder_get_eos_message(handle)) != MM_ERROR_NONE) {
597 info->b_commiting = FALSE;
598 goto _ERR_CAMCORDER_AUDIO_COMMAND;
603 case _MMCamcorder_CMD_PREVIEW_START:
604 //MM_CAMCORDER_START_CHANGE_STATE;
607 case _MMCamcorder_CMD_PREVIEW_STOP:
608 //MM_CAMCORDER_STOP_CHANGE_STATE;
613 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
617 _ERR_CAMCORDER_AUDIO_COMMAND:
621 int _mmcamcorder_audio_handle_eos(MMHandleType handle)
623 int err = MM_ERROR_NONE;
624 mmf_camcorder_t *hcamcorder= MMF_CAMCORDER(handle);
625 _MMCamcorderSubContext *sc = NULL;
626 _MMCamcorderAudioInfo *info = NULL;
627 GstElement *pipeline = NULL;
628 _MMCamcorderMsgItem msg;
629 MMCamRecordingReport * report;
631 mmf_return_val_if_fail(hcamcorder, FALSE);
632 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
634 mmf_return_val_if_fail(sc, FALSE);
635 mmf_return_val_if_fail(sc->info_audio, FALSE);
639 info = sc->info_audio;
641 pipeline = sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst;
643 err = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
644 if (err != MM_ERROR_NONE) {
645 _mmcam_dbg_warn("Failed:_MMCamcorder_CMD_COMMIT:GST_STATE_READY. err[%x]", err);
648 /* Send recording report message to application */
649 msg.id = MM_MESSAGE_CAMCORDER_AUDIO_CAPTURED;
650 report = (MMCamRecordingReport*) malloc(sizeof(MMCamRecordingReport));
652 _mmcam_dbg_err("Recording report fail(%s). Out of memory.", info->filename);
656 report->recording_filename = strdup(info->filename);
657 msg.param.data= report;
659 _mmcamcroder_send_message(handle, &msg);
662 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
664 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", FALSE);
667 _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_NULL);
669 sc->pipeline_time = 0;
671 sc->isMaxsizePausing = FALSE;
672 sc->isMaxtimePausing = FALSE;
674 g_free(info->filename);
675 info->filename = NULL;
677 _mmcam_dbg_err("_MMCamcorder_CMD_COMMIT : end");
679 info->b_commiting = FALSE;
686 __mmcamcorder_get_decibel(unsigned char* raw, int size, MMCamcorderAudioFormat format)
688 #define MAX_AMPLITUDE_MEAN_16BIT (23170.115738161934)
689 #define MAX_AMPLITUDE_MEAN_08BIT (89.803909382810)
690 #define DEFAULT_DECIBEL (-80.0)
699 float db = DEFAULT_DECIBEL;
701 unsigned long long square_sum = 0;
703 if (format == MM_CAMCORDER_AUDIO_FORMAT_PCM_S16_LE)
705 else //MM_CAMCORDER_AUDIO_FORMAT_PCM_U8
708 for( ; i < size ; i += (depthByte<<1) )
712 pcm8 = (char *)(raw + i);
713 square_sum += (*pcm8)*(*pcm8);
717 pcm16 = (short*)(raw + i);
718 square_sum += (*pcm16)*(*pcm16);
725 rms = sqrt( square_sum/count );
726 if (depthByte == 1) {
727 db = 20 * log10( rms/MAX_AMPLITUDE_MEAN_08BIT );
729 db = 20 * log10( rms/MAX_AMPLITUDE_MEAN_16BIT );
734 _mmcam_dbg_log("size[%d],depthByte[%d],count[%d],rms[%f],db[%f]",
735 size, depthByte, count, rms, db);
742 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_voicerecorder(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
744 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
749 _MMCamcorderMsgItem msg;
750 int err = MM_ERROR_UNKNOWN;
751 char *err_name = NULL;
752 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
753 GstMapInfo mapinfo = GST_MAP_INFO_INIT;
755 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
757 /* Set volume to audio input */
758 err = mm_camcorder_get_attributes((MMHandleType)hcamcorder, &err_name,
759 MMCAM_AUDIO_VOLUME, &volume,
760 MMCAM_AUDIO_FORMAT, &format,
761 MMCAM_AUDIO_CHANNEL, &channel,
764 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, err);
769 gst_buffer_map(buffer, &mapinfo, GST_MAP_READWRITE);
772 memset(mapinfo.data, 0, mapinfo.size);
775 /* Get current volume level of real input stream */
776 curdcb = __mmcamcorder_get_decibel(mapinfo.data, mapinfo.size, format);
778 msg.id = MM_MESSAGE_CAMCORDER_CURRENT_VOLUME;
779 msg.param.rec_volume_dB = curdcb;
780 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
782 /* CALL audio stream callback */
783 if ((hcamcorder->astream_cb) && buffer && mapinfo.data && mapinfo.size > 0)
785 MMCamcorderAudioStreamDataType stream;
787 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) < MM_CAMCORDER_STATE_PREPARE)
789 _mmcam_dbg_warn("Not ready for stream callback");
790 gst_buffer_unmap(buffer, &mapinfo);
791 return GST_PAD_PROBE_OK;
795 _mmcam_dbg_log("Call audio steramCb, data[%p], format[%d], channel[%d], length[%d], volume_dB[%f]",
796 GST_BUFFER_DATA(buffer), format, channel, GST_BUFFER_SIZE(buffer), curdcb);
799 stream.data = (void *)mapinfo.data;
800 stream.format = format;
801 stream.channel = channel;
802 stream.length = mapinfo.size;
803 stream.timestamp = (unsigned int)(GST_BUFFER_PTS(buffer)/1000000); //nano -> msecond
804 stream.volume_dB = curdcb;
806 _MMCAMCORDER_LOCK_ASTREAM_CALLBACK( hcamcorder );
808 if(hcamcorder->astream_cb)
810 hcamcorder->astream_cb(&stream, hcamcorder->astream_cb_param);
813 _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK( hcamcorder );
816 gst_buffer_unmap(buffer, &mapinfo);
817 return GST_PAD_PROBE_OK;
822 __mmcamcorder_audiorec_pad_added_cb(GstElement *element, GstPad *pad, MMHandleType handle)
824 mmf_camcorder_t *hcamcorder= MMF_CAMCORDER(handle);
826 _mmcam_dbg_log("ENTER(%s)", GST_PAD_NAME(pad));
827 //FIXME : the name of audio sink pad of wavparse, oggmux doesn't have 'audio'. How could I handle the name?
828 if((strstr(GST_PAD_NAME(pad), "audio")) || (strstr(GST_PAD_NAME(pad), "sink")))
830 MMCAMCORDER_ADD_BUFFER_PROBE(pad, _MMCAMCORDER_HANDLER_AUDIOREC,
831 __mmcamcorder_audio_dataprobe_record, hcamcorder);
835 _mmcam_dbg_warn("Unknow pad is added, check it : [%s]", GST_PAD_NAME(pad));
842 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
844 static int count = 0;
845 guint64 rec_pipe_time = 0;
846 guint64 free_space = 0;
847 guint64 buffer_size = 0;
848 guint64 trailer_size = 0;
849 char *filename = NULL;
850 unsigned long long remained_time = 0;
852 _MMCamcorderSubContext *sc = NULL;
853 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
854 _MMCamcorderAudioInfo *audioinfo = NULL;
855 _MMCamcorderMsgItem msg;
856 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
858 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_DROP);
859 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
861 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
862 mmf_return_val_if_fail(sc && sc->info_audio, GST_PAD_PROBE_DROP);
863 audioinfo = sc->info_audio;
865 if (sc->isMaxtimePausing || sc->isMaxsizePausing) {
866 _mmcam_dbg_warn("isMaxtimePausing[%d],isMaxsizePausing[%d]",
867 sc->isMaxtimePausing, sc->isMaxsizePausing);
868 return GST_PAD_PROBE_DROP;
871 buffer_size = gst_buffer_get_size(buffer);
873 if (audioinfo->filesize == 0) {
874 if (audioinfo->fileformat == MM_FILE_FORMAT_WAV) {
875 audioinfo->filesize += 44; /* wave header size */
876 } else if (audioinfo->fileformat == MM_FILE_FORMAT_AMR) {
877 audioinfo->filesize += 6; /* amr header size */
880 audioinfo->filesize += buffer_size;
881 return GST_PAD_PROBE_OK;
884 if (sc->ferror_send) {
885 _mmcam_dbg_warn("file write error, drop frames");
886 return GST_PAD_PROBE_DROP;
889 /* get trailer size */
890 if (audioinfo->fileformat == MM_FILE_FORMAT_3GP ||
891 audioinfo->fileformat == MM_FILE_FORMAT_MP4 ||
892 audioinfo->fileformat == MM_FILE_FORMAT_AAC) {
893 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
894 /*_mmcam_dbg_log("trailer_size %d", trailer_size);*/
896 trailer_size = 0; /* no trailer */
899 filename = audioinfo->filename;
901 /* to minimizing free space check overhead */
902 count = count % _MMCAMCORDER_FREE_SPACE_CHECK_INTERVAL;
904 gint free_space_ret = _mmcamcorder_get_freespace(filename, &free_space);
906 /*_mmcam_dbg_log("check free space for recording");*/
908 switch (free_space_ret) {
909 case -2: /* file not exist */
910 case -1: /* failed to get free space */
911 _mmcam_dbg_err("Error occured. [%d]", free_space_ret);
912 if (sc->ferror_count == 2 && sc->ferror_send == FALSE) {
913 sc->ferror_send = TRUE;
914 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
915 if (free_space_ret == -2) {
916 msg.param.code = MM_ERROR_FILE_NOT_FOUND;
918 msg.param.code = MM_ERROR_FILE_READ;
920 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
925 return GST_PAD_PROBE_DROP; /* skip this buffer */
927 default: /* succeeded to get free space */
928 /* check free space for recording */
929 if (free_space < (guint64)(_MMCAMCORDER_AUDIO_MINIMUM_SPACE + buffer_size + trailer_size)) {
930 _mmcam_dbg_warn("No more space for recording!!!");
931 _mmcam_dbg_warn("Free Space : [%" G_GUINT64_FORMAT "], file size : [%" G_GUINT64_FORMAT "]",
932 free_space, audioinfo->filesize);
934 if (audioinfo->bMuxing) {
935 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
937 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", TRUE);
940 sc->isMaxsizePausing = TRUE;
941 msg.id = MM_MESSAGE_CAMCORDER_NO_FREE_SPACE;
942 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
944 return GST_PAD_PROBE_DROP; /* skip this buffer */
950 if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_PTS(buffer))) {
951 _mmcam_dbg_err("Buffer timestamp is invalid, check it");
952 return GST_PAD_PROBE_DROP;
955 rec_pipe_time = GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer));
957 /* calculate remained time can be recorded */
958 if (audioinfo->max_time > 0 && audioinfo->max_time < (remained_time + rec_pipe_time)) {
959 remained_time = audioinfo->max_time - rec_pipe_time;
960 } else if (audioinfo->max_size > 0) {
961 long double max_size = (long double)audioinfo->max_size;
962 long double current_size = (long double)(audioinfo->filesize + buffer_size + trailer_size);
964 remained_time = (unsigned long long)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
967 /*_mmcam_dbg_log("remained time : %u", remained_time);*/
969 /* check max size of recorded file */
970 if (audioinfo->max_size > 0 &&
971 audioinfo->max_size < audioinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) {
972 _mmcam_dbg_warn("Max size!!! Recording is paused.");
973 _mmcam_dbg_warn("Max [%" G_GUINT64_FORMAT "], file [%" G_GUINT64_FORMAT "], trailer : [%" G_GUINT64_FORMAT "]", \
974 audioinfo->max_size, audioinfo->filesize, trailer_size);
976 /* just same as pause status. After blocking two queue, this function will not call again. */
977 if (audioinfo->bMuxing) {
978 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
980 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", TRUE);
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 = 0;
987 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
989 _mmcam_dbg_warn("Last filesize sent by message : %d", audioinfo->filesize + trailer_size);
991 sc->isMaxsizePausing = TRUE;
992 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
993 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
995 /* skip this buffer */
996 return GST_PAD_PROBE_DROP;
999 /* check recording time limit and send recording status message */
1000 if (audioinfo->max_time > 0 && rec_pipe_time > audioinfo->max_time) {
1001 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1002 rec_pipe_time, audioinfo->max_time);
1004 if (audioinfo->bMuxing) {
1005 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1007 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", TRUE);
1010 sc->isMaxtimePausing = TRUE;
1011 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1012 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1014 /* skip this buffer */
1015 return GST_PAD_PROBE_DROP;
1018 /* send message for recording time and recorded file size */
1019 if (audioinfo->b_commiting == FALSE) {
1020 audioinfo->filesize += buffer_size;
1022 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1023 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1024 msg.param.recording_status.filesize = (unsigned long long)((audioinfo->filesize + trailer_size) >> 10);
1025 msg.param.recording_status.remained_time = remained_time;
1026 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1028 return GST_PAD_PROBE_OK;
1030 /* skip this buffer if commit process has been started */
1031 return GST_PAD_PROBE_DROP;