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"
29 /*---------------------------------------------------------------------------------------
30 | GLOBAL VARIABLE DEFINITIONS for internal |
31 ---------------------------------------------------------------------------------------*/
32 #define MM_CAMCORDER_START_CHANGE_STATE _MMCamcorderStartHelperFunc((void *)hcamcorder)
33 #define MM_CAMCORDER_STOP_CHANGE_STATE _MMCamcorderStopHelperFunc((void *)hcamcorder)
34 /*---------------------------------------------------------------------------------------
35 | LOCAL VARIABLE DEFINITIONS for internal |
36 ---------------------------------------------------------------------------------------*/
37 #define RESET_PAUSE_TIME 0
38 #define _MMCAMCORDER_AUDIO_MINIMUM_SPACE (100*1024)
39 #define _MMCAMCORDER_AUDIO_MARGIN_SPACE (1*1024)
40 #define _MMCAMCORDER_RETRIAL_COUNT 10
41 #define _MMCAMCORDER_FRAME_WAIT_TIME 20000 /* micro second */
42 #define _MMCAMCORDER_FREE_SPACE_CHECK_INTERVAL 5
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 void __mmcamcorder_audiorec_pad_added_cb(GstElement *element, GstPad *pad, 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 char *aenc_name = NULL;
64 char *mux_name = NULL;
67 GstPad *srcpad = NULL;
68 GstPad *sinkpad = NULL;
69 GList *element_list = NULL;
71 _MMCamcorderAudioInfo *info = NULL;
72 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
73 _MMCamcorderSubContext *sc = NULL;
74 type_element *aenc_elem = NULL;
75 type_element *mux_elem = NULL;
77 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
78 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
80 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
81 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
82 mmf_return_val_if_fail(sc->info_audio, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
84 info = (_MMCamcorderAudioInfo *)sc->info_audio;
88 mux_elem = _mmcamcorder_get_type_element(handle, MM_CAM_FILE_FORMAT);
89 err = _mmcamcorder_conf_get_value_element_name( mux_elem, &mux_name );
91 if (!mux_name || !strcmp( mux_name, "wavenc" ) ) /* IF MUX in not chosen then record in raw amr file */
93 //But shoud we support non-mux recording??
94 _mmcam_dbg_log("Record without muxing.");
95 info->bMuxing = FALSE;
99 _mmcam_dbg_log("Record with mux.");
100 info->bMuxing = TRUE;
103 //Create gstreamer element
105 __ta__(" camcorder_pipeline",
106 _MMCAMCORDER_PIPELINE_MAKE(sc, _MMCAMCORDER_MAIN_PIPE, "camcorder_pipeline", err);
109 __ta__(" __mmcamcorder_create_audiosrc_bin",
110 err = _mmcamcorder_create_audiosrc_bin(handle);
112 if (err != MM_ERROR_NONE) {
117 /* Muxing. can use encodebin. */
118 __ta__(" _mmcamcorder_create_encodesink_bin",
119 err = _mmcamcorder_create_encodesink_bin((MMHandleType)hcamcorder, MM_CAMCORDER_ENCBIN_PROFILE_AUDIO);
121 if (err != MM_ERROR_NONE ) {
125 /* without muxing. can't use encodebin. */
126 aenc_elem = _mmcamcorder_get_type_element(handle, MM_CAM_AUDIO_ENCODER);
129 _mmcam_dbg_err("Fail to get type element");
130 err = MM_ERROR_CAMCORDER_RESOURCE_CREATION;
131 goto pipeline_creation_error;
134 err = _mmcamcorder_conf_get_value_element_name(aenc_elem, &aenc_name);
136 if ((!err) || (!aenc_name))
138 _mmcam_dbg_err("Fail to get element name");
139 err = MM_ERROR_CAMCORDER_RESOURCE_CREATION;
140 goto pipeline_creation_error;
143 __ta__(" audiopipeline_audioqueue",
144 _MMCAMCORDER_ELEMENT_MAKE(sc, _MMCAMCORDER_ENCSINK_AQUE, "queue", NULL, element_list, err);
147 if( strcmp( aenc_name, "wavenc" ) != 0 )
149 __ta__(" audiopipeline_audioconvertor",
150 _MMCAMCORDER_ELEMENT_MAKE(sc, _MMCAMCORDER_ENCSINK_CONV, "audioconvert", NULL, element_list, err);
154 __ta__(" audiopipeline_audioencoder",
155 _MMCAMCORDER_ELEMENT_MAKE(sc, _MMCAMCORDER_ENCSINK_AENC, aenc_name, NULL, element_list, err);
158 __ta__(" audiopipeline_filesink",
159 _MMCAMCORDER_ELEMENT_MAKE(sc, _MMCAMCORDER_ENCSINK_SINK, "filesink", NULL, element_list, err);
162 /* audio encoder attribute setting */
163 if(strcmp(aenc_name,"ari_amrnbenc") == 0) //ari_armnbenc supports attatching amr header
165 MMCAMCORDER_G_OBJECT_SET( sc->element[_MMCAMCORDER_ENCSINK_AENC].gst, "write-header", TRUE);
170 //Set basic infomation
172 if (info->bMuxing) /* IF MUX is indicated create MUX */
174 gst_bin_add_many(GST_BIN(sc->element[_MMCAMCORDER_MAIN_PIPE].gst),
175 sc->element[_MMCAMCORDER_AUDIOSRC_BIN].gst,
176 sc->element[_MMCAMCORDER_ENCSINK_BIN].gst,
179 srcpad = gst_element_get_static_pad (sc->element[_MMCAMCORDER_AUDIOSRC_BIN].gst, "src");
180 sinkpad = gst_element_get_static_pad (sc->element[_MMCAMCORDER_ENCSINK_BIN].gst, "audio_sink0");
181 _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error);
183 else /* IF MUX in not chosen then record in raw amr file */
185 if( !strcmp( aenc_name, "wavenc" ) )
187 gst_bin_add_many( GST_BIN(sc->element[_MMCAMCORDER_MAIN_PIPE].gst),
188 sc->element[_MMCAMCORDER_AUDIOSRC_BIN].gst,
189 sc->element[_MMCAMCORDER_ENCSINK_AQUE].gst,
190 sc->element[_MMCAMCORDER_ENCSINK_AENC].gst,
191 sc->element[_MMCAMCORDER_ENCSINK_SINK].gst,
194 if (!_MM_GST_ELEMENT_LINK_MANY( sc->element[_MMCAMCORDER_AUDIOSRC_BIN].gst,
195 sc->element[_MMCAMCORDER_ENCSINK_AQUE].gst,
196 sc->element[_MMCAMCORDER_ENCSINK_AENC].gst,
197 sc->element[_MMCAMCORDER_ENCSINK_SINK].gst,
200 err = MM_ERROR_CAMCORDER_GST_LINK;
201 goto pipeline_creation_error;
206 gst_bin_add_many( GST_BIN(sc->element[_MMCAMCORDER_MAIN_PIPE].gst),
207 sc->element[_MMCAMCORDER_AUDIOSRC_BIN].gst,
208 sc->element[_MMCAMCORDER_ENCSINK_AQUE].gst,
209 sc->element[_MMCAMCORDER_ENCSINK_CONV].gst,
210 sc->element[_MMCAMCORDER_ENCSINK_AENC].gst,
211 sc->element[_MMCAMCORDER_ENCSINK_SINK].gst,
214 if (!_MM_GST_ELEMENT_LINK_MANY( sc->element[_MMCAMCORDER_AUDIOSRC_BIN].gst,
215 sc->element[_MMCAMCORDER_ENCSINK_AQUE].gst,
216 sc->element[_MMCAMCORDER_ENCSINK_CONV].gst,
217 sc->element[_MMCAMCORDER_ENCSINK_AENC].gst,
218 sc->element[_MMCAMCORDER_ENCSINK_SINK].gst,
221 err = MM_ERROR_CAMCORDER_GST_LINK;
222 goto pipeline_creation_error;
228 //set data probe function
229 srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_AUDIOSRC_SRC].gst, "src");
230 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_AUDIOREC,
231 __mmcamcorder_audio_dataprobe_voicerecorder, hcamcorder);
232 gst_object_unref(srcpad);
237 MMCAMCORDER_SIGNAL_CONNECT(sc->element[_MMCAMCORDER_ENCSINK_MUX].gst,
238 _MMCAMCORDER_HANDLER_AUDIOREC,
240 __mmcamcorder_audiorec_pad_added_cb,
245 srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_AENC].gst, "src");
246 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_AUDIOREC,
247 __mmcamcorder_audio_dataprobe_record, hcamcorder);
248 gst_object_unref(srcpad);
253 * To get the message callback from the gstreamer.
254 * This can be used to make the API calls asynchronous
255 * as per LiMO compliancy
257 bus = gst_pipeline_get_bus(GST_PIPELINE(sc->element[_MMCAMCORDER_MAIN_PIPE].gst));
258 hcamcorder->pipeline_cb_event_id = gst_bus_add_watch( bus, (GstBusFunc)_mmcamcorder_pipeline_cb_message, hcamcorder );
259 gst_bus_set_sync_handler(bus, gst_bus_sync_signal_handler, hcamcorder, NULL);
260 gst_object_unref(bus);
262 return MM_ERROR_NONE;
264 pipeline_creation_error:
270 _mmcamcorder_create_audio_pipeline(MMHandleType handle)
272 mmf_camcorder_t *hcamcorder= MMF_CAMCORDER(handle);
273 _MMCamcorderSubContext *sc = NULL;
275 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
276 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
278 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
280 return __mmcamcorder_create_audiop_with_encodebin(handle);
285 * This function destroy audio pipeline.
287 * @param[in] handle Handle of camcorder.
290 * @see _mmcamcorder_destroy_pipeline()
294 _mmcamcorder_destroy_audio_pipeline(MMHandleType handle)
296 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
297 _MMCamcorderSubContext *sc = NULL;
298 _MMCamcorderAudioInfo *info = NULL;
299 mmf_return_if_fail(hcamcorder);
300 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
302 mmf_return_if_fail(sc && sc->info_audio);
303 mmf_return_if_fail(sc->element);
305 info = sc->info_audio;
309 if(sc->element[_MMCAMCORDER_MAIN_PIPE].gst)
311 _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_NULL);
313 _mmcamcorder_remove_all_handlers((MMHandleType)hcamcorder, _MMCAMCORDER_HANDLER_CATEGORY_ALL);
317 GstPad *reqpad = NULL;
318 //release request pad
321 The ref_count of mux is always # of streams in here, i don't know why it happens.
322 So, i unref the mux manually
324 reqpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "audio");
325 gst_element_release_request_pad(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, reqpad);
326 gst_object_unref(reqpad);
328 if(GST_IS_ELEMENT(sc->element[_MMCAMCORDER_ENCSINK_MUX].gst) && \
329 GST_OBJECT_REFCOUNT_VALUE(sc->element[_MMCAMCORDER_ENCSINK_MUX].gst) > 1)
330 gst_object_unref(sc->element[_MMCAMCORDER_ENCSINK_MUX].gst);
332 gst_object_unref(sc->element[_MMCAMCORDER_MAIN_PIPE].gst);
333 // sc->element[_MMCAMCORDER_MAIN_PIPE].gst = NULL;
340 * This function operates each command on audio mode.
342 * @param c [in] Handle of camcorder context.
343 * @param command [in] command type received from Multimedia Framework.
345 * @return This function returns MM_ERROR_NONE on success, or the other values
348 * @see _mmcamcorder_set_functions()
354 void* _MMCamcorderStartHelperFunc(void *handle)
356 mmf_camcorder_t *hcamcorder= MMF_CAMCORDER(handle);
357 _mmcamcorder_set_state((MMHandleType)hcamcorder, hcamcorder->target_state);
362 void* _MMCamcorderStopHelperFunc(void *handle)
364 mmf_camcorder_t *hcamcorder= MMF_CAMCORDER(handle);
365 _mmcamcorder_set_state((MMHandleType)hcamcorder, hcamcorder->target_state);
372 _mmcamcorder_audio_command(MMHandleType handle, int command)
375 int ret = MM_ERROR_NONE;
378 guint64 free_space = 0;
379 char *dir_name = NULL;
380 char *err_attr_name = NULL;
382 GstElement *pipeline = NULL;
383 GstElement *audioSrc = NULL;
385 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
386 _MMCamcorderSubContext *sc = NULL;
387 _MMCamcorderAudioInfo *info = NULL;
389 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
390 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
392 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
393 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
394 mmf_return_val_if_fail(sc->info_audio, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
395 pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
396 info = sc->info_audio;
400 pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
401 audioSrc = sc->element[_MMCAMCORDER_AUDIOSRC_SRC].gst;
404 case _MMCamcorder_CMD_RECORD:
405 //check status for resume case
406 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) != MM_CAMCORDER_STATE_PAUSED)
410 char *temp_filename = NULL;
412 if(sc->pipeline_time) {
413 gst_element_set_start_time(GST_ELEMENT(pipeline), sc->pipeline_time);
415 sc->pipeline_time = RESET_PAUSE_TIME;
417 ret = mm_camcorder_get_attributes(handle, &err_attr_name,
418 MMCAM_TARGET_MAX_SIZE, &imax_size,
419 MMCAM_TARGET_TIME_LIMIT, &imax_time,
420 MMCAM_FILE_FORMAT, &(info->fileformat),
421 MMCAM_TARGET_FILENAME, &temp_filename, &size,
423 if (ret != MM_ERROR_NONE) {
424 _mmcam_dbg_warn("failed to get attribute. (%s:%x)", err_attr_name, ret);
425 SAFE_FREE (err_attr_name);
426 goto _ERR_CAMCORDER_AUDIO_COMMAND;
429 info->filename = strdup(temp_filename);
432 _mmcam_dbg_err("STRDUP was failed");
433 goto _ERR_CAMCORDER_AUDIO_COMMAND;
436 _mmcam_dbg_log("Record start : set file name using attribute - %s\n ",info->filename);
438 MMCAMCORDER_G_OBJECT_SET( sc->element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename);
440 sc->ferror_send = FALSE;
441 sc->ferror_count = 0;
442 sc->bget_eos = FALSE;
446 if (imax_size <= 0) {
447 info->max_size = 0; /* do not check */
449 info->max_size = ((guint64)imax_size) << 10; /* to byte */
453 if (imax_time <= 0) {
454 info->max_time = 0; /* do not check */
456 info->max_time = ((guint64)imax_time) * 1000; /* to millisecond */
459 dir_name = g_path_get_dirname(info->filename);
461 err = _mmcamcorder_get_freespace(dir_name, &free_space);
463 _mmcam_dbg_warn("current space for recording - %s : [%" G_GUINT64_FORMAT "]",
464 dir_name, free_space);
469 _mmcam_dbg_err("failed to get directory name");
473 if ((err == -1) || free_space <= (_MMCAMCORDER_AUDIO_MINIMUM_SPACE+(5*1024))) {
474 _mmcam_dbg_err("OUT of STORAGE [err:%d or free space [%" G_GUINT64_FORMAT "] is smaller than [%d]",
475 err, free_space, (_MMCAMCORDER_AUDIO_MINIMUM_SPACE+(5*1024)));
476 return MM_ERROR_OUT_OF_STORAGE;
480 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
482 goto _ERR_CAMCORDER_AUDIO_COMMAND;
486 case _MMCamcorder_CMD_PAUSE:
488 GstClock *clock = NULL;
491 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++)
499 if(info->filesize > 0)
503 else if(count == _MMCAMCORDER_RETRIAL_COUNT)
505 _mmcam_dbg_err("Pause fail, we are waiting for 100 milisecond, but still file size is %" G_GUINT64_FORMAT "",
507 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
511 _mmcam_dbg_warn("Pause is Waiting for enough audio frame, retrial count[%d], file size is %" G_GUINT64_FORMAT "",
512 count, info->filesize);
514 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
517 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PAUSED);
519 goto _ERR_CAMCORDER_AUDIO_COMMAND;
521 //fixed me. consider delay.
522 clock = gst_pipeline_get_clock(GST_PIPELINE(pipeline));
523 sc->pipeline_time = gst_clock_get_time(clock) - gst_element_get_base_time(GST_ELEMENT(sc->element[_MMCAMCORDER_MAIN_PIPE].gst));
527 case _MMCamcorder_CMD_CANCEL:
528 if (info->b_commiting)
530 _mmcam_dbg_warn("now on commiting previous file!!(cmd : %d)", cmd);
531 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
534 // ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_NULL);
535 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
537 goto _ERR_CAMCORDER_AUDIO_COMMAND;
542 MMCAMCORDER_G_OBJECT_SET( sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
546 MMCAMCORDER_G_OBJECT_SET( sc->element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", FALSE);
549 _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_ENCSINK_SINK].gst, GST_STATE_NULL);
551 sc->pipeline_time = 0;
553 sc->isMaxsizePausing = FALSE;
554 sc->isMaxtimePausing = FALSE;
558 _mmcam_dbg_log("file delete(%s)", info->filename);
559 unlink(info->filename);
560 g_free(info->filename);
561 info->filename = NULL;
565 case _MMCamcorder_CMD_COMMIT:
568 g_print("\n\n _MMCamcorder_CMD_COMMIT\n\n");
570 if (info->b_commiting)
572 _mmcam_dbg_warn("now on commiting previous file!!(cmd : %d)", cmd);
573 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
577 _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT : start");
578 info->b_commiting = TRUE;
581 for(count=0; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++)
583 if(info->filesize > 0)
587 else if(count == _MMCAMCORDER_RETRIAL_COUNT)
589 _mmcam_dbg_err("Commit fail, we are waiting for 100 milisecond, but still file size is %" G_GUINT64_FORMAT "",
591 info->b_commiting = FALSE;
592 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
596 _mmcam_dbg_warn("Commit is Waiting for enough audio frame, retrial count[%d], file size is %" G_GUINT64_FORMAT "",
597 count, info->filesize);
599 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
603 GstPad *pad = gst_element_get_static_pad (audioSrc, "src");
604 // gst_pad_push_event (pad, gst_event_new_eos());
605 ret = gst_element_send_event(audioSrc, gst_event_new_eos());
606 gst_object_unref(pad);
608 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) == MM_CAMCORDER_STATE_PAUSED) // for pause -> commit case
610 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
612 goto _ERR_CAMCORDER_AUDIO_COMMAND;
618 //wait until finishing EOS
619 _mmcam_dbg_log("Start to wait EOS");
620 if ((ret =_mmcamcorder_get_eos_message(handle)) != MM_ERROR_NONE)
622 goto _ERR_CAMCORDER_AUDIO_COMMAND;
625 while ((!sc->get_eos)&&((retry_cnt--) > 0)) {
626 if ((gMessage = gst_bus_timed_pop (bus, timeout)) != NULL)
628 if (GST_MESSAGE_TYPE(gMessage) ==GST_MESSAGE_EOS)
630 _mmcam_dbg_log("Get a EOS message");
631 gst_message_unref(gMessage);
636 _mmcam_dbg_log("Get another message(%x). Post this message to bus again.", GST_MESSAGE_TYPE(gMessage));
637 gst_bus_post(bus, gMessage);
642 _mmcam_dbg_log("timeout of gst_bus_timed_pop()");
646 usleep(100); //To Prevent busy waiting
649 _mmcamcorder_audio_handle_eos((MMHandleType)hcamcorder);
656 case _MMCamcorder_CMD_PREVIEW_START:
657 // MM_CAMCORDER_START_CHANGE_STATE;
659 case _MMCamcorder_CMD_PREVIEW_STOP:
660 // MM_CAMCORDER_STOP_CHANGE_STATE;
665 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
669 _ERR_CAMCORDER_AUDIO_COMMAND:
675 _mmcamcorder_audio_handle_eos(MMHandleType handle)
677 mmf_camcorder_t *hcamcorder= MMF_CAMCORDER(handle);
678 _MMCamcorderSubContext *sc = NULL;
679 _MMCamcorderAudioInfo *info = NULL;
680 GstElement *pipeline = NULL;
681 _MMCamcorderMsgItem msg;
682 MMCamRecordingReport * report;
684 int err = MM_ERROR_NONE;
686 mmf_return_val_if_fail(hcamcorder, FALSE);
687 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
689 mmf_return_val_if_fail(sc, FALSE);
690 mmf_return_val_if_fail(sc->info_audio, FALSE);
694 info = sc->info_audio;
696 //changing pipeline for display
697 pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
700 __ta__(" _MMCamcorder_CMD_COMMIT:GST_STATE_READY",
701 err = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
704 if( err != MM_ERROR_NONE )
706 _mmcam_dbg_warn( "Failed:_MMCamcorder_CMD_COMMIT:GST_STATE_READY. err[%x]", err );
709 // __ta__(" _MMCamcorder_CMD_COMMIT:GST_STATE_NULL",
710 // _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_NULL);
714 //Send recording report to application
715 msg.id = MM_MESSAGE_CAMCORDER_AUDIO_CAPTURED;
717 report = (MMCamRecordingReport*) malloc(sizeof(MMCamRecordingReport)); //who free this?
721 _mmcam_dbg_err("Recording report fail(%s). Out of memory.", info->filename);
725 report->recording_filename = strdup(info->filename);
726 msg.param.data= report;
728 _mmcamcroder_send_message(handle, &msg);
733 MMCAMCORDER_G_OBJECT_SET( sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
737 MMCAMCORDER_G_OBJECT_SET( sc->element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", FALSE);
739 _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_ENCSINK_SINK].gst, GST_STATE_NULL);
741 sc->pipeline_time = 0;
743 sc->isMaxsizePausing = FALSE;
744 sc->isMaxtimePausing = FALSE;
746 g_free(info->filename);
747 info->filename = NULL;
749 _mmcam_dbg_err("_MMCamcorder_CMD_COMMIT : end");
751 info->b_commiting = FALSE;
758 __mmcamcorder_get_decibel(unsigned char* raw, int size, MMCamcorderAudioFormat format)
760 #define MAX_AMPLITUDE_MEAN_16BIT 23170.115738161934
761 #define MAX_AMPLITUDE_MEAN_08BIT 89.803909382810
772 unsigned long long square_sum = 0;
774 if (format == MM_CAMCORDER_AUDIO_FORMAT_PCM_S16_LE)
776 else //MM_CAMCORDER_AUDIO_FORMAT_PCM_U8
779 for( ; i < size ; i += (depthByte<<1) )
783 pcm8 = (char *)(raw + i);
784 square_sum += (*pcm8)*(*pcm8);
788 pcm16 = (short*)(raw + i);
789 square_sum += (*pcm16)*(*pcm16);
795 rms = sqrt( square_sum/count );
798 db = 20 * log10( rms/MAX_AMPLITUDE_MEAN_08BIT );
800 db = 20 * log10( rms/MAX_AMPLITUDE_MEAN_16BIT );
803 _mmcam_dbg_log("size[%d],depthByte[%d],count[%d],rms[%f],db[%f]",
804 size, depthByte, count, rms, db);
811 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_voicerecorder(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
813 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
818 _MMCamcorderMsgItem msg;
819 int err = MM_ERROR_UNKNOWN;
820 char *err_name = NULL;
821 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
822 GstMapInfo mapinfo = GST_MAP_INFO_INIT;
824 mmf_return_val_if_fail(hcamcorder, FALSE);
826 /* Set volume to audio input */
827 err = mm_camcorder_get_attributes((MMHandleType)hcamcorder, &err_name,
828 MMCAM_AUDIO_VOLUME, &volume,
829 MMCAM_AUDIO_FORMAT, &format,
830 MMCAM_AUDIO_CHANNEL, &channel,
834 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, err);
835 SAFE_FREE (err_name);
839 if(volume == 0) //mute
841 gst_buffer_map(buffer, &mapinfo, GST_MAP_WRITE);
842 memset(mapinfo.data, 0, mapinfo.size);
843 gst_buffer_unmap(buffer, &mapinfo);
846 /* Get current volume level of real input stream */
847 gst_buffer_map(buffer, &mapinfo, GST_MAP_READ);
848 // currms = __mmcamcorder_get_RMS(GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer), depth);
849 __ta__( "__mmcamcorder_get_decibel",
850 curdcb = __mmcamcorder_get_decibel(mapinfo.data, mapinfo.size, format);
853 msg.id = MM_MESSAGE_CAMCORDER_CURRENT_VOLUME;
854 msg.param.rec_volume_dB = curdcb;
855 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
857 /* CALL audio stream callback */
858 if ((hcamcorder->astream_cb) && buffer && mapinfo.data && mapinfo.size > 0)
860 MMCamcorderAudioStreamDataType stream;
862 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) < MM_CAMCORDER_STATE_PREPARE)
864 _mmcam_dbg_warn("Not ready for stream callback");
865 gst_buffer_unmap(buffer, &mapinfo);
866 return GST_PAD_PROBE_OK;
870 _mmcam_dbg_log("Call audio steramCb, data[%p], format[%d], channel[%d], length[%d], volume_dB[%f]",
871 GST_BUFFER_DATA(buffer), format, channel, GST_BUFFER_SIZE(buffer), curdcb);
874 stream.data = mapinfo.data;
875 stream.format = format;
876 stream.channel = channel;
877 stream.length = mapinfo.size;
878 stream.timestamp = (unsigned int)(GST_BUFFER_TIMESTAMP(buffer)/1000000); //nano -> msecond
879 stream.volume_dB = curdcb;
881 _MMCAMCORDER_LOCK_ASTREAM_CALLBACK( hcamcorder );
883 if(hcamcorder->astream_cb)
885 hcamcorder->astream_cb(&stream, hcamcorder->astream_cb_param);
888 _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK( hcamcorder );
890 gst_buffer_unmap(buffer, &mapinfo);
892 return GST_PAD_PROBE_OK;
897 __mmcamcorder_audiorec_pad_added_cb(GstElement *element, GstPad *pad, MMHandleType handle)
899 mmf_camcorder_t *hcamcorder= MMF_CAMCORDER(handle);
901 _mmcam_dbg_log("ENTER(%s)", GST_PAD_NAME(pad));
902 //FIXME : the name of audio sink pad of wavparse, oggmux doesn't have 'audio'. How could I handle the name?
903 if((strstr(GST_PAD_NAME(pad), "audio")) || (strstr(GST_PAD_NAME(pad), "sink")))
905 MMCAMCORDER_ADD_BUFFER_PROBE(pad, _MMCAMCORDER_HANDLER_AUDIOREC,
906 __mmcamcorder_audio_dataprobe_record, hcamcorder);
910 _mmcam_dbg_warn("Unknow pad is added, check it : [%s]", GST_PAD_NAME(pad));
917 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
919 static int count = 0;
920 guint64 rec_pipe_time = 0;
921 guint64 free_space = 0;
922 guint64 buffer_size = 0;
923 guint64 trailer_size = 0;
924 char *filename = NULL;
925 unsigned long long remained_time = 0;
927 _MMCamcorderSubContext *sc = NULL;
928 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
929 _MMCamcorderAudioInfo *audioinfo = NULL;
930 _MMCamcorderMsgItem msg;
931 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
933 mmf_return_val_if_fail(hcamcorder, FALSE);
934 mmf_return_val_if_fail(buffer, FALSE);
936 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
937 mmf_return_val_if_fail(sc && sc->info_audio, FALSE);
938 audioinfo = sc->info_audio;
940 if (sc->isMaxtimePausing || sc->isMaxsizePausing) {
941 _mmcam_dbg_warn("isMaxtimePausing[%d],isMaxsizePausing[%d]",
942 sc->isMaxtimePausing, sc->isMaxsizePausing);
943 return GST_PAD_PROBE_DROP;
946 buffer_size = gst_buffer_get_size(buffer);
948 if (audioinfo->filesize == 0) {
949 if (audioinfo->fileformat == MM_FILE_FORMAT_WAV) {
950 audioinfo->filesize += 44; /* wave header size */
951 } else if (audioinfo->fileformat == MM_FILE_FORMAT_AMR) {
952 audioinfo->filesize += 6; /* amr header size */
955 audioinfo->filesize += buffer_size;
956 return GST_PAD_PROBE_OK;
959 if (sc->ferror_send) {
960 _mmcam_dbg_warn("file write error, drop frames");
961 return GST_PAD_PROBE_DROP;
964 /* get trailer size */
965 if (audioinfo->fileformat == MM_FILE_FORMAT_3GP ||
966 audioinfo->fileformat == MM_FILE_FORMAT_MP4 ||
967 audioinfo->fileformat == MM_FILE_FORMAT_AAC) {
968 MMCAMCORDER_G_OBJECT_GET(sc->element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
969 /*_mmcam_dbg_log("trailer_size %d", trailer_size);*/
971 trailer_size = 0; /* no trailer */
974 filename = audioinfo->filename;
976 /* to minimizing free space check overhead */
977 count = count % _MMCAMCORDER_FREE_SPACE_CHECK_INTERVAL;
979 gint free_space_ret = _mmcamcorder_get_freespace(filename, &free_space);
981 /*_mmcam_dbg_log("check free space for recording");*/
983 switch (free_space_ret) {
984 case -2: /* file not exist */
985 case -1: /* failed to get free space */
986 _mmcam_dbg_err("Error occured. [%d]", free_space_ret);
987 if (sc->ferror_count == 2 && sc->ferror_send == FALSE) {
988 sc->ferror_send = TRUE;
989 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
990 if (free_space_ret == -2) {
991 msg.param.code = MM_ERROR_FILE_NOT_FOUND;
993 msg.param.code = MM_ERROR_FILE_READ;
995 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1000 return GST_PAD_PROBE_DROP; /* skip this buffer */
1002 default: /* succeeded to get free space */
1003 /* check free space for recording */
1004 if (free_space < (guint64)(_MMCAMCORDER_AUDIO_MINIMUM_SPACE + buffer_size + trailer_size)) {
1005 _mmcam_dbg_warn("No more space for recording!!!");
1006 _mmcam_dbg_warn("Free Space : [%" G_GUINT64_FORMAT "], file size : [%" G_GUINT64_FORMAT "]",
1007 free_space, audioinfo->filesize);
1009 if (audioinfo->bMuxing) {
1010 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1012 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", TRUE);
1015 sc->isMaxsizePausing = TRUE;
1016 msg.id = MM_MESSAGE_CAMCORDER_NO_FREE_SPACE;
1017 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1019 return GST_PAD_PROBE_DROP; /* skip this buffer */
1025 if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_TIMESTAMP(buffer))) {
1026 _mmcam_dbg_err("Buffer timestamp is invalid, check it");
1030 rec_pipe_time = GST_TIME_AS_MSECONDS(GST_BUFFER_TIMESTAMP(buffer));
1032 /* calculate remained time can be recorded */
1033 if (audioinfo->max_time > 0 && audioinfo->max_time < (remained_time + rec_pipe_time)) {
1034 remained_time = audioinfo->max_time - rec_pipe_time;
1035 } else if (audioinfo->max_size > 0) {
1036 long double max_size = (long double)audioinfo->max_size;
1037 long double current_size = (long double)(audioinfo->filesize + buffer_size + trailer_size);
1039 remained_time = (unsigned long long)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1042 /*_mmcam_dbg_log("remained time : %u", remained_time);*/
1044 /* check max size of recorded file */
1045 if (audioinfo->max_size > 0 &&
1046 audioinfo->max_size < audioinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) {
1047 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1048 _mmcam_dbg_warn("Max [%" G_GUINT64_FORMAT "], file [%" G_GUINT64_FORMAT "], trailer : [%" G_GUINT64_FORMAT "]", \
1049 audioinfo->max_size, audioinfo->filesize, trailer_size);
1051 /* just same as pause status. After blocking two queue, this function will not call again. */
1052 if (audioinfo->bMuxing) {
1053 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1055 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", TRUE);
1058 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1059 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1060 msg.param.recording_status.filesize = (unsigned long long)((audioinfo->filesize + trailer_size) >> 10);
1061 msg.param.recording_status.remained_time = 0;
1062 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1064 _mmcam_dbg_log("Last filesize sent by message : %d", audioinfo->filesize + trailer_size);
1066 sc->isMaxsizePausing = TRUE;
1067 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1068 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1070 /* skip this buffer */
1071 return GST_PAD_PROBE_DROP;
1074 /* check recording time limit and send recording status message */
1075 if (audioinfo->max_time > 0 && rec_pipe_time > audioinfo->max_time) {
1076 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1077 rec_pipe_time, audioinfo->max_time);
1079 if (audioinfo->bMuxing) {
1080 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1082 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_ENCSINK_AQUE].gst, "empty-buffers", TRUE);
1085 sc->isMaxtimePausing = TRUE;
1086 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1087 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1089 /* skip this buffer */
1090 return GST_PAD_PROBE_DROP;
1093 /* send message for recording time and recorded file size */
1094 if (audioinfo->b_commiting == FALSE) {
1095 audioinfo->filesize += buffer_size;
1097 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1098 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1099 msg.param.recording_status.filesize = (unsigned long long)((audioinfo->filesize + trailer_size) >> 10);
1100 msg.param.recording_status.remained_time = remained_time;
1101 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1103 return GST_PAD_PROBE_OK;
1105 /* skip this buffer if commit process has been started */
1106 return GST_PAD_PROBE_DROP;