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 <gst/interfaces/cameracontrol.h>
26 #include "mm_camcorder_internal.h"
27 #include "mm_camcorder_videorec.h"
29 /*---------------------------------------------------------------------------------------
30 | GLOBAL VARIABLE DEFINITIONS for internal |
31 ---------------------------------------------------------------------------------------*/
32 #define _MMCAMCORDER_LOCATION_INFO // for add gps information
33 #define MAX_ERROR_MESSAGE_LEN 128
35 /*---------------------------------------------------------------------------------------
36 | LOCAL VARIABLE DEFINITIONS for internal |
37 ---------------------------------------------------------------------------------------*/
38 #define _MMCAMCORDER_MINIMUM_FRAME 3
39 #define _MMCAMCORDER_RETRIAL_COUNT 10
40 #define _MMCAMCORDER_FRAME_WAIT_TIME 200000 /* ms */
41 #define _MMCAMCORDER_FREE_SPACE_CHECK_INTERVAL 5
42 #define _OFFSET_COMPOSITION_MATRIX 40L
44 /*---------------------------------------------------------------------------------------
45 | LOCAL FUNCTION PROTOTYPES: |
46 ---------------------------------------------------------------------------------------*/
47 /* STATIC INTERNAL FUNCTION */
48 static gboolean __mmcamcorder_audio_dataprobe_check(GstPad *pad, GstBuffer *buffer, gpointer u_data);
49 static gboolean __mmcamcorder_video_dataprobe_record(GstPad *pad, GstBuffer *buffer, gpointer u_data);
50 static gboolean __mmcamcorder_audioque_dataprobe(GstPad *pad, GstBuffer *buffer, gpointer u_data);
51 static gboolean __mmcamcorder_video_dataprobe_audio_disable(GstPad *pad, GstBuffer *buffer, gpointer u_data);
52 static gboolean __mmcamcorder_audio_dataprobe_audio_mute(GstPad *pad, GstBuffer *buffer, gpointer u_data);
53 static gboolean __mmcamcorder_add_locationinfo(MMHandleType handle, int fileformat);
54 static gboolean __mmcamcorder_add_locationinfo_mp4(MMHandleType handle);
55 static gboolean __mmcamcorder_eventprobe_monitor(GstPad *pad, GstEvent *event, gpointer u_data);
57 /*=======================================================================================
58 | FUNCTION DEFINITIONS |
59 =======================================================================================*/
60 /*---------------------------------------------------------------------------------------
61 | GLOBAL FUNCTION DEFINITIONS: |
62 ---------------------------------------------------------------------------------------*/
63 int _mmcamcorder_add_recorder_pipeline(MMHandleType handle)
65 int err = MM_ERROR_NONE;
66 int audio_disable = FALSE;
67 char* gst_element_rsink_name = NULL;
69 GstPad *srcpad = NULL;
70 GstPad *sinkpad = NULL;
72 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
73 _MMCamcorderSubContext *sc = NULL;
75 type_element *RecordsinkElement = NULL;
77 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
79 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);
85 err = _mmcamcorder_check_videocodec_fileformat_compatibility( handle );
86 if( err != MM_ERROR_NONE )
91 /* Create gstreamer element */
92 /* Check main pipeline */
93 if (!sc->element[_MMCAMCORDER_MAIN_PIPE].gst) {
94 err = MM_ERROR_CAMCORDER_RESOURCE_CREATION;
95 goto pipeline_creation_error;
98 /* get audio disable */
99 mm_camcorder_get_attributes(handle, NULL,
100 MMCAM_AUDIO_DISABLE, &audio_disable,
103 if (sc->is_modified_rate || audio_disable) {
104 sc->audio_disable = TRUE;
106 sc->audio_disable = FALSE;
108 _mmcam_dbg_log("AUDIO DISABLE : %d (is_modified_rate %d, audio_disable %d)",
109 sc->audio_disable, sc->is_modified_rate, audio_disable);
111 if (sc->audio_disable == FALSE) {
113 __ta__(" __mmcamcorder_create_audiosrc_bin",
114 err = _mmcamcorder_create_audiosrc_bin((MMHandleType)hcamcorder);
116 if (err != MM_ERROR_NONE) {
120 gst_bin_add(GST_BIN(sc->element[_MMCAMCORDER_MAIN_PIPE].gst),
121 sc->element[_MMCAMCORDER_AUDIOSRC_BIN].gst);
124 __ta__(" _mmcamcorder_create_encodesink_bin",
125 err = _mmcamcorder_create_encodesink_bin((MMHandleType)hcamcorder, MM_CAMCORDER_ENCBIN_PROFILE_VIDEO);
127 if (err != MM_ERROR_NONE) {
131 gst_bin_add(GST_BIN(sc->element[_MMCAMCORDER_MAIN_PIPE].gst),
132 sc->element[_MMCAMCORDER_ENCSINK_BIN].gst);
134 /* Link each element */
135 srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_VIDEOSRC_BIN].gst, "src1");
136 sinkpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_BIN].gst, "video_sink0");
137 _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error);
139 if (sc->audio_disable == FALSE) {
140 srcpad = gst_element_get_static_pad (sc->element[_MMCAMCORDER_AUDIOSRC_BIN].gst, "src");
141 sinkpad = gst_element_get_static_pad (sc->element[_MMCAMCORDER_ENCSINK_BIN].gst, "audio_sink0");
142 _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error);
145 _mmcamcorder_conf_get_element(hcamcorder->conf_main,
146 CONFIGURE_CATEGORY_MAIN_RECORD,
149 _mmcamcorder_conf_get_value_element_name(RecordsinkElement, &gst_element_rsink_name);
151 /* set data probe function */
153 /* register message cb */
155 /* set data probe function for audio */
157 if (sc->audio_disable == FALSE) {
158 sinkpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_AENC].gst, "sink");
159 MMCAMCORDER_ADD_BUFFER_PROBE(sinkpad, _MMCAMCORDER_HANDLER_VIDEOREC,
160 __mmcamcorder_audioque_dataprobe, hcamcorder);
161 gst_object_unref(sinkpad);
165 srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_AUDIOSRC_SRC].gst, "src");
166 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
167 __mmcamcorder_audio_dataprobe_audio_mute, hcamcorder);
168 gst_object_unref(srcpad);
171 if (sc->element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst) {
172 srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst, "src");
173 MMCAMCORDER_ADD_EVENT_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
174 __mmcamcorder_eventprobe_monitor, hcamcorder);
175 gst_object_unref(srcpad);
180 if (sc->element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst) {
181 srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst, "src");
182 MMCAMCORDER_ADD_EVENT_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
183 __mmcamcorder_eventprobe_monitor, hcamcorder);
184 gst_object_unref(srcpad);
188 if (sc->audio_disable) {
189 sinkpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_VENC].gst, "sink");
190 MMCAMCORDER_ADD_BUFFER_PROBE(sinkpad, _MMCAMCORDER_HANDLER_VIDEOREC,
191 __mmcamcorder_video_dataprobe_audio_disable, hcamcorder);
192 gst_object_unref(sinkpad);
196 if (!strcmp(gst_element_rsink_name, "filesink")) {
197 srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_VENC].gst, "src");
198 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
199 __mmcamcorder_video_dataprobe_record, hcamcorder);
200 gst_object_unref(srcpad);
203 srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_AENC].gst, "src");
204 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
205 __mmcamcorder_audio_dataprobe_check, hcamcorder);
206 gst_object_unref(srcpad);
210 return MM_ERROR_NONE;
212 pipeline_creation_error:
217 int _mmcamcorder_remove_audio_pipeline(MMHandleType handle)
219 int ret = MM_ERROR_NONE;
220 GstPad *srcpad = NULL;
221 GstPad *sinkpad = NULL;
222 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
223 _MMCamcorderSubContext *sc = NULL;
225 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
227 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
228 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
229 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
233 if (sc->element[_MMCAMCORDER_AUDIOSRC_BIN].gst != NULL) {
234 __ta__( " AudiosrcBin Set NULL",
235 ret = _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_AUDIOSRC_BIN].gst, GST_STATE_NULL);
237 if (ret != MM_ERROR_NONE) {
238 _mmcam_dbg_err("Faile to change audio source state[%d]", ret);
242 srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_AUDIOSRC_BIN].gst, "src");
243 sinkpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_BIN].gst, "audio_sink0");
244 _MM_GST_PAD_UNLINK_UNREF(srcpad, sinkpad);
246 gst_bin_remove(GST_BIN(sc->element[_MMCAMCORDER_MAIN_PIPE].gst),
247 sc->element[_MMCAMCORDER_AUDIOSRC_BIN].gst);
250 To avoid conflicting between old elements and newly created elements,
251 I clean element handles here. Real elements object will be finalized as the 'unref' process goes on.
252 This is a typical problem of unref. Even though I unref bin here, it takes much time to finalize each elements.
253 So I clean handles first, make them unref later. Audio recording, however, isn't needed this process.
254 It's because the pipeline of audio recording destroys at the same time,
255 and '_mmcamcorder_element_release_noti' will perfom removing handle.
257 _mmcamcorder_remove_element_handle(handle, _MMCAMCORDER_AUDIOSRC_BIN, _MMCAMCORDER_AUDIOSRC_NS);
259 _mmcam_dbg_log("Audio pipeline removed");
262 return MM_ERROR_NONE;
266 int _mmcamcorder_remove_encoder_pipeline(MMHandleType handle)
268 int ret = MM_ERROR_NONE;
269 GstPad *srcpad = NULL;
270 GstPad *sinkpad = NULL;
271 GstPad *reqpad = NULL;
272 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
273 _MMCamcorderSubContext *sc = NULL;
275 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
277 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
278 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
279 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
283 if (sc->element[_MMCAMCORDER_ENCSINK_BIN].gst != NULL) {
284 __ta__( " EncodeBin Set NULL",
285 ret = _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_ENCSINK_BIN].gst, GST_STATE_NULL);
287 if (ret != MM_ERROR_NONE) {
288 _mmcam_dbg_err("Faile to change encode bin state[%d]", ret);
292 srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_VIDEOSRC_BIN].gst, "src1");
293 sinkpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_BIN].gst, "video_sink0");
294 _MM_GST_PAD_UNLINK_UNREF(srcpad, sinkpad);
296 /* release request pad */
297 reqpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "audio");
299 gst_element_release_request_pad(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, reqpad);
300 gst_object_unref(reqpad);
304 reqpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "video");
306 gst_element_release_request_pad(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, reqpad);
307 gst_object_unref(reqpad);
311 gst_bin_remove(GST_BIN(sc->element[_MMCAMCORDER_MAIN_PIPE].gst),
312 sc->element[_MMCAMCORDER_ENCSINK_BIN].gst);
315 To avoid conflicting between old elements and newly created elements,
316 I clean element handles here. Real elements object will be finalized as the 'unref' process goes on.
317 This is a typical problem of unref. Even though I unref bin here, it takes much time to finalize each elements.
318 So I clean handles first, make them unref later. Audio recording, however, isn't needed this process.
319 It's because the pipeline of audio recording destroys at the same time,
320 and '_mmcamcorder_element_release_noti' will perfom removing handle.
322 _mmcamcorder_remove_element_handle(handle, _MMCAMCORDER_AUDIOSRC_QUE, _MMCAMCORDER_AUDIOSRC_ENC); /* Encode bin has audio encoder too. */
323 _mmcamcorder_remove_element_handle(handle, _MMCAMCORDER_ENCSINK_BIN, _MMCAMCORDER_ENCSINK_SINK);
325 _mmcam_dbg_log("Encoder pipeline removed");
328 return MM_ERROR_NONE;
332 int _mmcamcorder_remove_recorder_pipeline(MMHandleType handle)
334 int ret = MM_ERROR_NONE;
335 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
336 _MMCamcorderSubContext *sc = NULL;
338 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
339 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
340 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
344 if (!sc->element[_MMCAMCORDER_MAIN_PIPE].gst) {
345 _mmcam_dbg_warn("pipeline is not existed.");
346 return MM_ERROR_NONE;
349 _mmcamcorder_remove_all_handlers((MMHandleType)hcamcorder, _MMCAMCORDER_HANDLER_VIDEOREC);
351 /* remove audio pipeline first */
352 ret = _mmcamcorder_remove_audio_pipeline(handle);
353 if (ret != MM_ERROR_NONE) {
354 _mmcam_dbg_err("Fail to remove audio pipeline");
358 ret = _mmcamcorder_remove_encoder_pipeline(handle);
359 if (ret != MM_ERROR_NONE) {
360 _mmcam_dbg_err("Fail to remove encoder pipeline");
368 void _mmcamcorder_destroy_video_pipeline(MMHandleType handle)
370 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
371 _MMCamcorderSubContext *sc = NULL;
372 GstPad *reqpad1 = NULL;
373 GstPad *reqpad2 = NULL;
375 mmf_return_if_fail(hcamcorder);
376 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
378 mmf_return_if_fail(sc);
379 mmf_return_if_fail(sc->element);
383 if (sc->element[_MMCAMCORDER_MAIN_PIPE].gst) {
384 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
385 _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_NULL);
387 _mmcamcorder_remove_all_handlers((MMHandleType)hcamcorder, _MMCAMCORDER_HANDLER_CATEGORY_ALL);
389 reqpad1 = gst_element_get_static_pad(sc->element[_MMCAMCORDER_VIDEOSRC_TEE].gst, "src0");
390 reqpad2 = gst_element_get_static_pad(sc->element[_MMCAMCORDER_VIDEOSRC_TEE].gst, "src1");
391 gst_element_release_request_pad(sc->element[_MMCAMCORDER_VIDEOSRC_TEE].gst, reqpad1);
392 gst_element_release_request_pad(sc->element[_MMCAMCORDER_VIDEOSRC_TEE].gst, reqpad2);
393 gst_object_unref(reqpad1);
394 gst_object_unref(reqpad2);
396 /* object disposing problem happen. */
397 _mmcam_dbg_log("Reference count of pipeline(%d)", GST_OBJECT_REFCOUNT_VALUE(sc->element[_MMCAMCORDER_MAIN_PIPE].gst));
398 gst_object_unref(sc->element[_MMCAMCORDER_MAIN_PIPE].gst);
403 int _mmcamcorder_video_command(MMHandleType handle, int command)
407 int ret = MM_ERROR_NONE;
408 double motion_rate = _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE;
409 char *temp_filename = NULL;
410 char *err_name = NULL;
413 GstElement *pipeline = NULL;
416 _MMCamcorderVideoInfo *info = NULL;
417 _MMCamcorderSubContext *sc = NULL;
418 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
420 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
422 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
423 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
424 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
425 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
427 info = sc->info_video;
429 _mmcam_dbg_log("Command(%d)", command);
431 pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
434 case _MMCamcorder_CMD_RECORD:
436 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) != MM_CAMCORDER_STATE_PAUSED) {
440 /* Play record start sound */
441 _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_FILEPATH_REC_START_SND, TRUE);
444 _mmcam_dbg_log("Record Start");
446 /* set hybrid mode when ITLV format */
447 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ITLV_JPEG_UYVY) {
448 _mmcam_dbg_log("flush cache TRUE");
449 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "flush-cache", TRUE);
452 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
453 if (ret != MM_ERROR_NONE) {
454 goto _ERR_CAMCORDER_VIDEO_COMMAND;
457 _mmcamcorder_conf_get_value_int(hcamcorder->conf_main,
458 CONFIGURE_CATEGORY_MAIN_RECORD,
462 _mmcamcorder_conf_get_value_int(hcamcorder->conf_main,
463 CONFIGURE_CATEGORY_MAIN_RECORD,
464 "PassFirstVideoFrame",
465 &(sc->pass_first_vframe));
467 _mmcam_dbg_log("Drop video frame count[%d], Pass fisrt video frame count[%d]",
468 sc->drop_vframe, sc->pass_first_vframe);
470 ret = mm_camcorder_get_attributes(handle, &err_name,
471 MMCAM_CAMERA_FPS, &fps,
472 MMCAM_CAMERA_RECORDING_MOTION_RATE, &motion_rate,
473 MMCAM_FILE_FORMAT, &fileformat,
474 MMCAM_TARGET_FILENAME, &temp_filename, &size,
475 MMCAM_TARGET_MAX_SIZE, &imax_size,
476 MMCAM_TARGET_TIME_LIMIT, &imax_time,
477 MMCAM_FILE_FORMAT, &(info->fileformat),
479 if (ret != MM_ERROR_NONE) {
480 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, ret);
481 SAFE_FREE (err_name);
482 goto _ERR_CAMCORDER_VIDEO_COMMAND;
486 if (imax_size <= 0) {
487 info->max_size = 0; /* do not check */
489 info->max_size = ((guint64)imax_size) << 10; /* to byte */
493 if (imax_time <= 0) {
494 info->max_time = 0; /* do not check */
496 info->max_time = ((guint64)imax_time) * 1000; /* to millisecond */
499 if (sc->is_modified_rate) {
500 info->record_timestamp_ratio = (_MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE/motion_rate);
501 _mmcam_dbg_log("high speed recording fps:%d, slow_rate:%f, timestamp_ratio:%f",
502 fps, motion_rate, info->record_timestamp_ratio);
504 info->record_timestamp_ratio = _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE;
505 _mmcam_dbg_log("normal recording");
508 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "hold-af-after-capturing", TRUE);
509 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "req-negotiation", TRUE);
511 ret =_mmcamcorder_add_recorder_pipeline((MMHandleType)hcamcorder);
512 if (ret != MM_ERROR_NONE) {
513 goto _ERR_CAMCORDER_VIDEO_COMMAND;
516 info->filename = strdup(temp_filename);
517 if (!info->filename) {
518 _mmcam_dbg_err("strdup was failed");
519 goto _ERR_CAMCORDER_VIDEO_COMMAND;
522 _mmcam_dbg_log("Record start : set file name using attribute - %s ",info->filename);
524 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename);
525 MMCAMCORDER_G_OBJECT_SET( sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", 0);
527 /* Adjust display FPS */
528 sc->display_interval = 0;
529 sc->previous_slot_time = 0;
531 /* gst_element_set_base_time(GST_ELEMENT(pipeline), (GstClockTime)0);
532 if you want to use audio clock, enable this block
533 for change recorder_pipeline state to paused. */
534 __ta__(" _MMCamcorder_CMD_RECORD:GST_STATE_PAUSED2",
535 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PAUSED);
537 if (ret != MM_ERROR_NONE) {
538 /* Remove recorder pipeline and recording file which size maybe zero */
539 __ta__(" record fail:remove_recorder_pipeline",
540 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
542 if (info->filename) {
543 _mmcam_dbg_log("file delete(%s)", info->filename);
544 unlink(info->filename);
545 g_free(info->filename);
546 info->filename = NULL;
548 goto _ERR_CAMCORDER_VIDEO_COMMAND;
551 /**< To fix video recording hanging
552 1. use gst_element_set_start_time() instead of gst_pipeline_set_new_stream_time()
553 2. Set (GstClockTime)1 instead of (GstClockTime)0. Because of strict check in gstreamer 0.25,
554 basetime wouldn't change if you set (GstClockTime)0.
555 3. Move set start time position below PAUSED of pipeline.
557 gst_element_set_start_time(GST_ELEMENT(pipeline), (GstClockTime)1);
558 info->video_frame_count = 0;
559 info->audio_frame_count = 0;
561 sc->ferror_send = FALSE;
562 sc->ferror_count = 0;
563 sc->error_occurs = FALSE;
564 sc->bget_eos = FALSE;
566 __ta__(" _MMCamcorder_CMD_RECORD:GST_STATE_PLAYING2",
567 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
569 if (ret != MM_ERROR_NONE) {
570 /* Remove recorder pipeline and recording file which size maybe zero */
571 __ta__(" record fail:remove_recorder_pipeline",
572 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
574 if (info->filename) {
575 _mmcam_dbg_log("file delete(%s)", info->filename);
576 unlink(info->filename);
577 g_free(info->filename);
578 info->filename = NULL;
580 goto _ERR_CAMCORDER_VIDEO_COMMAND;
586 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", FALSE);
588 mm_camcorder_get_attributes(handle, NULL, MMCAM_VIDEO_ENCODER, &video_enc, NULL);
589 if (video_enc == MM_VIDEO_CODEC_MPEG4) {
590 MMCAMCORDER_G_OBJECT_SET( sc->element[_MMCAMCORDER_ENCSINK_VENC].gst, "force-intra", TRUE);
593 _mmcam_dbg_log("Object property settings done");
597 case _MMCamcorder_CMD_PAUSE:
601 if (info->b_commiting) {
602 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
603 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
606 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
607 if (sc->audio_disable) {
608 /* check only video frame */
609 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
611 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
612 _mmcam_dbg_err("Pause fail, frame count %" G_GUINT64_FORMAT "",
613 info->video_frame_count);
614 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
616 _mmcam_dbg_warn("Waiting for enough video frame, retrial[%d], frame %" G_GUINT64_FORMAT "",
617 count, info->video_frame_count);
620 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
622 /* check both of video and audio frame */
623 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
625 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
626 _mmcam_dbg_err("Pause fail, frame count VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]",
627 info->video_frame_count, info->audio_frame_count);
628 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
630 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]",
631 count, info->video_frame_count, info->audio_frame_count);
634 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
638 MMCAMCORDER_G_OBJECT_SET( sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", TRUE);
642 case _MMCamcorder_CMD_CANCEL:
644 if (info->b_commiting) {
645 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
646 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
649 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
650 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "hold-af-after-capturing", FALSE);
652 __ta__(" _MMCamcorder_CMD_CANCEL:GST_STATE_READY",
653 ret =_mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
655 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
656 if (ret != MM_ERROR_NONE) {
657 goto _ERR_CAMCORDER_VIDEO_COMMAND;
660 __ta__(" __mmcamcorder_remove_recorder_pipeline",
661 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
664 /* remove target file */
665 if (info->filename) {
666 _mmcam_dbg_log("file delete(%s)", info->filename);
667 unlink(info->filename);
668 g_free(info->filename);
669 info->filename = NULL;
672 sc->isMaxsizePausing = FALSE;
673 sc->isMaxtimePausing = FALSE;
675 sc->display_interval = 0;
676 sc->previous_slot_time = 0;
677 info->video_frame_count = 0;
678 info->audio_frame_count = 0;
681 /* set flush cache as FALSE */
682 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ITLV_JPEG_UYVY) {
683 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "flush-cache", FALSE);
686 __ta__(" _MMCamcorder_CMD_CANCEL:GST_STATE_PLAYING",
687 ret =_mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
689 if (ret != MM_ERROR_NONE) {
690 goto _ERR_CAMCORDER_VIDEO_COMMAND;
694 case _MMCamcorder_CMD_COMMIT:
698 if (info->b_commiting) {
699 _mmcam_dbg_err("now on commiting previous file!!(command : %d)", command);
700 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
702 _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT : start");
703 info->b_commiting = TRUE;
706 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
707 if (sc->audio_disable) {
708 /* check only video frame */
709 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
711 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
712 _mmcam_dbg_err("Commit fail, frame count is %" G_GUINT64_FORMAT "",
713 info->video_frame_count);
714 info->b_commiting = FALSE;
715 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
717 _mmcam_dbg_warn("Waiting for enough video frame, retrial [%d], frame %" G_GUINT64_FORMAT "",
718 count, info->video_frame_count);
721 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
723 /* check both of video and audio frame */
724 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
726 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
727 _mmcam_dbg_err("Commit fail, VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]",
728 info->video_frame_count, info->audio_frame_count);
730 info->b_commiting = FALSE;
731 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
733 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]",
734 count, info->video_frame_count, info->audio_frame_count);
737 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
741 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "hold-af-after-capturing", FALSE);
743 if (sc->error_occurs) {
745 GstPad *audio = NULL;
748 _mmcam_dbg_err("Committing Error case");
750 video = gst_element_get_static_pad(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "sink");
751 ret = gst_pad_send_event (video, gst_event_new_eos());
752 _mmcam_dbg_err("Sending EOS video sink : %d", ret);
753 gst_object_unref(video);
755 video = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_VENC].gst, "src");
756 gst_pad_push_event (video, gst_event_new_flush_start());
757 gst_pad_push_event (video, gst_event_new_flush_stop());
758 ret = gst_pad_push_event (video, gst_event_new_eos());
759 _mmcam_dbg_err("Sending EOS video encoder src pad : %d", ret);
760 gst_object_unref(video);
762 if (sc->audio_disable == FALSE) {
763 audio = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_AENC].gst, "src");
764 gst_pad_push_event (audio, gst_event_new_flush_start());
765 gst_pad_push_event (audio, gst_event_new_flush_stop());
766 ret = gst_element_send_event(sc->element[_MMCAMCORDER_AUDIOSRC_SRC].gst, gst_event_new_eos());
767 _mmcam_dbg_err("Sending EOS audio encoder src pad : %d", ret);
768 gst_object_unref(audio);
771 if (sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst != NULL) {
772 ret = gst_element_send_event(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, gst_event_new_eos());
773 _mmcam_dbg_warn("send eos to videosrc result : %d", ret);
776 if (sc->element[_MMCAMCORDER_AUDIOSRC_SRC].gst != NULL) {
777 pad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_AUDIOSRC_SRC].gst, "src");
778 ret = gst_element_send_event(sc->element[_MMCAMCORDER_AUDIOSRC_SRC].gst, gst_event_new_eos());
779 gst_object_unref(pad);
782 _mmcam_dbg_warn("send eos to audiosrc result : %d", ret);
786 if (hcamcorder->quick_device_close) {
787 _mmcam_dbg_warn("quick_device_close");
788 /* close device quickly */
789 MMCAMCORDER_G_OBJECT_SET( sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "quick-device-close",TRUE);
793 sc->display_interval = 0;
794 sc->previous_slot_time = 0;
797 _mmcam_dbg_log("Start to wait EOS");
798 ret =_mmcamcorder_get_eos_message(handle);
800 /* set flush cache as FALSE */
801 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ITLV_JPEG_UYVY) {
802 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "flush-cache", FALSE);
805 if (ret != MM_ERROR_NONE) {
806 info->b_commiting = FALSE;
807 goto _ERR_CAMCORDER_VIDEO_COMMAND;
812 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
813 goto _ERR_CAMCORDER_VIDEO_COMMAND;
816 return MM_ERROR_NONE;
818 _ERR_CAMCORDER_VIDEO_COMMAND:
819 if (ret != MM_ERROR_NONE && sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst != NULL) {
821 MMCAMCORDER_G_OBJECT_GET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "operation-status", &op_status);
822 _mmcam_dbg_err("Current Videosrc status[0x%x]", op_status);
829 int _mmcamcorder_video_handle_eos(MMHandleType handle)
831 int ret = MM_ERROR_NONE;
833 guint64 file_size = 0;
836 GstElement *pipeline = NULL;
838 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
839 _MMCamcorderSubContext *sc = NULL;
840 _MMCamcorderVideoInfo *info = NULL;
841 _MMCamcorderMsgItem msg;
842 MMCamRecordingReport *report = NULL;
844 mmf_return_val_if_fail(hcamcorder, FALSE);
846 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
847 mmf_return_val_if_fail(sc, FALSE);
848 mmf_return_val_if_fail(sc->info_video, FALSE);
850 info = sc->info_video;
854 MMTA_ACUM_ITEM_BEGIN(" _mmcamcorder_video_handle_eos", 0);
856 pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
858 /* remove blocking part */
859 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
860 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
862 mm_camcorder_get_attributes(handle, NULL,
863 MMCAM_TAG_ENABLE, &enabletag,
866 _mmcam_dbg_log("Set state of pipeline as PAUSED");
867 __ta__(" _MMCamcorder_CMD_COMMIT:GST_STATE_PAUSED",
868 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PAUSED);
870 if (ret != MM_ERROR_NONE) {
871 _mmcam_dbg_warn("_MMCamcorder_CMD_COMMIT:GST_STATE_READY or PAUSED failed. error[%x]", ret);
874 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
876 __ta__(" _MMCamcorder_CMD_COMMIT:__mmcamcorder_remove_recorder_pipeline",
877 ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
879 if (ret != MM_ERROR_NONE) {
880 _mmcam_dbg_warn("_MMCamcorder_CMD_COMMIT:__mmcamcorder_remove_recorder_pipeline failed. error[%x]", ret);
883 if (enabletag && !(sc->ferror_send)) {
884 __ta__( " _MMCamcorder_CMD_COMMIT:__mmcamcorder_add_locationinfo",
885 ret = __mmcamcorder_add_locationinfo((MMHandleType)hcamcorder, info->fileformat);
888 _mmcam_dbg_log("Writing location information SUCCEEDED !!");
890 _mmcam_dbg_err("Writing location information FAILED !!");
894 /* Check file size */
895 if (info->max_size > 0) {
896 _mmcamcorder_get_file_size(info->filename, &file_size);
897 _mmcam_dbg_log("MAX size %lld byte - created filesize %lld byte",
898 info->max_size, file_size);
900 if (file_size > info->max_size) {
901 _MMCamcorderMsgItem message;
902 _mmcam_dbg_err("File size is greater than max size !!");
903 message.id = MM_MESSAGE_CAMCORDER_ERROR;
904 message.param.code = MM_ERROR_CAMCORDER_FILE_SIZE_OVER;
905 _mmcamcroder_send_message((MMHandleType)hcamcorder, &message);
909 /* Flush EOS event to avoid pending pipeline */
910 _mmcam_dbg_log("Flush EOS event");
911 pad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "sink");
912 gst_pad_push_event(pad, gst_event_new_flush_start());
913 gst_pad_push_event(pad, gst_event_new_flush_stop());
914 gst_object_unref(pad);
917 pad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "src");
918 gst_pad_push_event(pad, gst_event_new_flush_start());
919 gst_pad_push_event(pad, gst_event_new_flush_stop());
920 gst_object_unref(pad);
923 _mmcam_dbg_log("Set state as PLAYING");
924 __ta__(" _MMCamcorder_CMD_COMMIT:GST_STATE_PLAYING",
925 ret =_mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
927 /* Do not return when error is occurred.
928 Recording file was created successfully, but starting pipeline failed */
929 if (ret != MM_ERROR_NONE) {
930 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
931 msg.param.code = ret;
932 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
933 _mmcam_dbg_err("Failed to set state PLAYING[%x]", ret);
936 if (hcamcorder->state_change_by_system != _MMCAMCORDER_STATE_CHANGE_BY_ASM) {
937 /* Play record stop sound */
938 __ta__(" _MMCamcorder_CMD_COMMIT:_mmcamcorder_sound_solo_play",
939 _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_FILEPATH_REC_STOP_SND, TRUE);
942 _mmcam_dbg_log("Do NOT play recording stop sound because of ASM stop");
945 /* Send recording report to application */
946 msg.id = MM_MESSAGE_CAMCORDER_VIDEO_CAPTURED;
947 report = (MMCamRecordingReport *)malloc(sizeof(MMCamRecordingReport));
949 _mmcam_dbg_err("Recording report fail(%s). Out of memory.", info->filename);
951 report->recording_filename = strdup(info->filename);
952 msg.param.data= report;
954 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
958 sc->pipeline_time = 0;
960 sc->isMaxsizePausing = FALSE; /*In async function, this variable should set in callback function. */
961 sc->isMaxtimePausing = FALSE;
962 sc->error_occurs = FALSE;
964 info->video_frame_count = 0;
965 info->audio_frame_count = 0;
967 g_free(info->filename);
968 info->filename = NULL;
969 info->b_commiting = FALSE;
971 MMTA_ACUM_ITEM_END(" _mmcamcorder_video_handle_eos", 0);
972 MMTA_ACUM_ITEM_END("Real Commit Time", 0);
974 _mmcam_dbg_err("_MMCamcorder_CMD_COMMIT : end");
981 * This function is record video data probing function.
982 * If this function is linked with certain pad by gst_pad_add_buffer_probe(),
983 * this function will be called when data stream pass through the pad.
985 * @param[in] pad probing pad which calls this function.
986 * @param[in] buffer buffer which contains stream data.
987 * @param[in] u_data user data.
988 * @return This function returns true on success, or false value with error
992 static gboolean __mmcamcorder_eventprobe_monitor(GstPad *pad, GstEvent *event, gpointer u_data)
994 switch (GST_EVENT_TYPE(event)) {
995 case GST_EVENT_UNKNOWN:
996 /* upstream events */
999 case GST_EVENT_NAVIGATION:
1000 case GST_EVENT_LATENCY:
1001 /* downstream serialized events */
1002 case GST_EVENT_NEWSEGMENT :
1004 case GST_EVENT_BUFFERSIZE:
1005 _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1008 _mmcam_dbg_warn("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1010 /* bidirectional events */
1011 case GST_EVENT_FLUSH_START:
1012 case GST_EVENT_FLUSH_STOP:
1013 _mmcam_dbg_err("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1016 _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1024 static gboolean __mmcamcorder_audio_dataprobe_check(GstPad *pad, GstBuffer *buffer, gpointer u_data)
1026 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1027 _MMCamcorderSubContext *sc = NULL;
1028 _MMCamcorderVideoInfo *info = NULL;
1030 mmf_return_val_if_fail(hcamcorder, TRUE);
1031 mmf_return_val_if_fail(buffer, FALSE);
1032 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1034 mmf_return_val_if_fail(sc && sc->info_video, TRUE);
1035 info = sc->info_video;
1037 /*_mmcam_dbg_err("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));*/
1039 if (info->audio_frame_count == 0) {
1040 info->filesize += (guint64)GST_BUFFER_SIZE(buffer);
1041 info->audio_frame_count++;
1045 if (sc->ferror_send || sc->isMaxsizePausing) {
1046 _mmcam_dbg_warn("Recording is paused, drop frames");
1050 info->filesize += (guint64)GST_BUFFER_SIZE(buffer);
1051 info->audio_frame_count++;
1057 static gboolean __mmcamcorder_video_dataprobe_record(GstPad *pad, GstBuffer *buffer, gpointer u_data)
1059 static int count = 0;
1063 guint64 free_space = 0;
1064 guint64 buffer_size = 0;
1065 guint64 trailer_size = 0;
1066 guint64 queued_buffer = 0;
1067 char *filename = NULL;
1069 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1070 _MMCamcorderMsgItem msg;
1071 _MMCamcorderSubContext *sc = NULL;
1072 _MMCamcorderVideoInfo *info = NULL;
1074 mmf_return_val_if_fail(hcamcorder, TRUE);
1075 mmf_return_val_if_fail(buffer, FALSE);
1077 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1078 mmf_return_val_if_fail(sc && sc->info_video, TRUE);
1079 info = sc->info_video;
1081 /*_mmcam_dbg_log("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));*/
1082 if (sc->ferror_send) {
1083 _mmcam_dbg_warn("file write error, drop frames");
1087 info->video_frame_count++;
1088 if (info->video_frame_count <= (guint64)_MMCAMCORDER_MINIMUM_FRAME) {
1089 /* _mmcam_dbg_log("Pass minimum frame: info->video_frame_count: %" G_GUINT64_FORMAT " ",
1090 info->video_frame_count); */
1091 info->filesize += (guint64)GST_BUFFER_SIZE(buffer);
1095 buffer_size = GST_BUFFER_SIZE(buffer);
1097 /* get trailer size */
1098 if (info->fileformat == MM_FILE_FORMAT_3GP || info->fileformat == MM_FILE_FORMAT_MP4) {
1099 MMCAMCORDER_G_OBJECT_GET(sc->element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1104 /* to minimizing free space check overhead */
1105 count = count % _MMCAMCORDER_FREE_SPACE_CHECK_INTERVAL;
1107 filename = info->filename;
1108 ret = _mmcamcorder_get_freespace(filename, &free_space);
1110 /*_mmcam_dbg_log("check free space for recording");*/
1113 case -2: /* file not exist */
1114 case -1: /* failed to get free space */
1115 _mmcam_dbg_err("Error occured. [%d]", ret);
1116 if (sc->ferror_count == 2 && sc->ferror_send == FALSE) {
1117 sc->ferror_send = TRUE;
1118 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1120 msg.param.code = MM_ERROR_FILE_NOT_FOUND;
1122 msg.param.code = MM_ERROR_FILE_READ;
1124 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1129 return FALSE; /* skip this buffer */
1131 default: /* succeeded to get free space */
1132 /* check free space for recording */
1133 /* get queued buffer size */
1134 if (sc->element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst) {
1135 MMCAMCORDER_G_OBJECT_GET(sc->element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst, "current-level-bytes", &aq_size);
1137 if (sc->element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst) {
1138 MMCAMCORDER_G_OBJECT_GET(sc->element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst, "current-level-bytes", &vq_size);
1141 queued_buffer = aq_size + vq_size;
1143 /* check free space */
1144 if (free_space < (_MMCAMCORDER_MINIMUM_SPACE + buffer_size + trailer_size + queued_buffer)) {
1145 _mmcam_dbg_warn("No more space for recording!!! Recording is paused.");
1146 _mmcam_dbg_warn("Free Space : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]," \
1147 " buffer size : [%" G_GUINT64_FORMAT "], queued buffer size : [%" G_GUINT64_FORMAT "]", \
1148 free_space, trailer_size, buffer_size, queued_buffer);
1150 if (!sc->isMaxsizePausing) {
1151 MMCAMCORDER_G_OBJECT_SET( sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1152 sc->isMaxsizePausing = TRUE;
1154 msg.id = MM_MESSAGE_CAMCORDER_NO_FREE_SPACE;
1155 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1164 /* check max size of recorded file */
1165 if (info->max_size > 0 &&
1166 info->max_size < info->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) {
1167 GstState pipeline_state = GST_STATE_VOID_PENDING;
1168 GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1169 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1170 _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \
1171 " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]",
1172 info->max_size, info->filesize, buffer_size, trailer_size);
1174 if (!sc->isMaxsizePausing) {
1175 gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ;
1176 if (pipeline_state == GST_STATE_PLAYING) {
1177 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1178 sc->isMaxsizePausing = TRUE;
1181 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1182 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1188 info->filesize += (guint64)buffer_size;
1190 _mmcam_dbg_log("filesize %lld Byte, ", info->filesize);
1196 static gboolean __mmcamcorder_video_dataprobe_audio_disable(GstPad *pad, GstBuffer *buffer, gpointer u_data)
1198 guint64 trailer_size = 0;
1199 guint64 rec_pipe_time = 0;
1200 unsigned int remained_time = 0;
1202 GstClockTime b_time;
1204 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1205 _MMCamcorderMsgItem msg;
1206 _MMCamcorderSubContext *sc = NULL;
1207 _MMCamcorderVideoInfo *info = NULL;
1209 mmf_return_val_if_fail(buffer, FALSE);
1210 mmf_return_val_if_fail(hcamcorder, TRUE);
1212 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1213 mmf_return_val_if_fail(sc, TRUE);
1214 mmf_return_val_if_fail(sc->info_video, TRUE);
1216 info = sc->info_video;
1218 b_time = GST_BUFFER_TIMESTAMP(buffer);
1220 rec_pipe_time = GST_TIME_AS_MSECONDS(b_time);
1222 if (info->fileformat == MM_FILE_FORMAT_3GP || info->fileformat == MM_FILE_FORMAT_MP4) {
1223 MMCAMCORDER_G_OBJECT_GET(sc->element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1228 /* check max time */
1229 if (info->max_time > 0 && rec_pipe_time > info->max_time) {
1230 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1231 rec_pipe_time, info->max_time);
1233 if (!sc->isMaxtimePausing) {
1234 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1236 sc->isMaxtimePausing = TRUE;
1238 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1239 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1240 msg.param.recording_status.filesize = (unsigned long long)((info->filesize + trailer_size) >> 10);
1241 msg.param.recording_status.remained_time = 0;
1242 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1244 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1245 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1251 /* calculate remained time can be recorded */
1252 if (info->max_time > 0 && info->max_time < (remained_time + rec_pipe_time)) {
1253 remained_time = info->max_time - rec_pipe_time;
1254 } else if (info->max_size > 0) {
1255 long double max_size = (long double)info->max_size;
1256 long double current_size = (long double)(info->filesize + trailer_size);
1258 remained_time = (unsigned int)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1261 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1262 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1263 msg.param.recording_status.filesize = (unsigned long long)((info->filesize + trailer_size) >> 10);
1264 msg.param.recording_status.remained_time = remained_time;
1265 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1267 _mmcam_dbg_log("time [%" GST_TIME_FORMAT "], size [%d]",
1268 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1270 if (info->record_timestamp_ratio != _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) {
1271 GST_BUFFER_TIMESTAMP(buffer) = b_time * (info->record_timestamp_ratio);
1278 static gboolean __mmcamcorder_audioque_dataprobe(GstPad *pad, GstBuffer *buffer, gpointer u_data)
1280 _MMCamcorderMsgItem msg;
1281 guint64 trailer_size = 0;
1282 guint64 rec_pipe_time = 0;
1283 _MMCamcorderSubContext *sc = NULL;
1284 GstElement *pipeline = NULL;
1285 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1286 _MMCamcorderVideoInfo *info = NULL;
1287 unsigned int remained_time = 0;
1289 mmf_return_val_if_fail(buffer, FALSE);
1290 mmf_return_val_if_fail(hcamcorder, TRUE);
1291 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1293 mmf_return_val_if_fail(sc, TRUE);
1294 mmf_return_val_if_fail(sc->info_video, TRUE);
1295 mmf_return_val_if_fail(sc->element, TRUE);
1297 info = sc->info_video;
1298 pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1300 if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_TIMESTAMP(buffer))) {
1301 _mmcam_dbg_err( "Buffer timestamp is invalid, check it");
1305 rec_pipe_time = GST_TIME_AS_MSECONDS(GST_BUFFER_TIMESTAMP(buffer));
1307 if (info->fileformat == MM_FILE_FORMAT_3GP || info->fileformat == MM_FILE_FORMAT_MP4) {
1308 MMCAMCORDER_G_OBJECT_GET(sc->element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1313 /* calculate remained time can be recorded */
1314 if (info->max_time > 0 && info->max_time < (remained_time + rec_pipe_time)) {
1315 remained_time = info->max_time - rec_pipe_time;
1316 } else if (info->max_size > 0) {
1317 long double max_size = (long double)info->max_size;
1318 long double current_size = (long double)(info->filesize + trailer_size);
1320 remained_time = (unsigned long long)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1323 if (info->max_time > 0 && rec_pipe_time > info->max_time) {
1324 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1325 rec_pipe_time, info->max_time);
1327 if (!sc->isMaxtimePausing) {
1328 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1330 sc->isMaxtimePausing = TRUE;
1332 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1333 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1334 msg.param.recording_status.filesize = (unsigned long long)((info->filesize + trailer_size) >> 10);
1335 msg.param.recording_status.remained_time = 0;
1336 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1338 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1339 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1345 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1346 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1347 msg.param.recording_status.filesize = (unsigned long long)((info->filesize + trailer_size) >> 10);
1348 msg.param.recording_status.remained_time = remained_time;
1349 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1351 _mmcam_dbg_log("audio data probe :: time [%" GST_TIME_FORMAT "], size [%lld KB]",
1352 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1358 static gboolean __mmcamcorder_audio_dataprobe_audio_mute(GstPad *pad, GstBuffer *buffer, gpointer u_data)
1360 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1361 double volume = 0.0;
1364 int err = MM_ERROR_UNKNOWN;
1365 char *err_name = NULL;
1367 mmf_return_val_if_fail(buffer, FALSE);
1368 mmf_return_val_if_fail(hcamcorder, FALSE);
1370 /*_mmcam_dbg_log("AUDIO SRC time stamp : [%" GST_TIME_FORMAT "] \n", GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));*/
1371 err = mm_camcorder_get_attributes((MMHandleType)hcamcorder, &err_name,
1372 MMCAM_AUDIO_VOLUME, &volume,
1373 MMCAM_AUDIO_FORMAT, &format,
1374 MMCAM_AUDIO_CHANNEL, &channel,
1376 if (err != MM_ERROR_NONE) {
1377 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, err);
1378 SAFE_FREE(err_name);
1382 /* Set audio stream NULL */
1383 if (volume == 0.0) {
1384 memset(GST_BUFFER_DATA(buffer), 0, GST_BUFFER_SIZE(buffer));
1387 /* CALL audio stream callback */
1388 if (hcamcorder->astream_cb && buffer && GST_BUFFER_DATA(buffer)) {
1389 MMCamcorderAudioStreamDataType stream;
1391 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) < MM_CAMCORDER_STATE_PREPARE) {
1392 _mmcam_dbg_warn("Not ready for stream callback");
1396 /*_mmcam_dbg_log("Call video steramCb, data[%p], Width[%d],Height[%d], Format[%d]",
1397 GST_BUFFER_DATA(buffer), width, height, format);*/
1399 stream.data = (void *)GST_BUFFER_DATA(buffer);
1400 stream.format = format;
1401 stream.channel = channel;
1402 stream.length = GST_BUFFER_SIZE(buffer);
1403 stream.timestamp = (unsigned int)(GST_BUFFER_TIMESTAMP(buffer)/1000000); /* nano -> milli second */
1405 _MMCAMCORDER_LOCK_ASTREAM_CALLBACK(hcamcorder);
1407 if (hcamcorder->astream_cb) {
1408 hcamcorder->astream_cb(&stream, hcamcorder->astream_cb_param);
1411 _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK(hcamcorder);
1418 static gboolean __mmcamcorder_add_locationinfo(MMHandleType handle, int fileformat)
1420 gboolean bret = FALSE;
1422 switch (fileformat) {
1423 case MM_FILE_FORMAT_3GP:
1424 case MM_FILE_FORMAT_MP4:
1425 bret = __mmcamcorder_add_locationinfo_mp4(handle);
1428 _mmcam_dbg_warn("Unsupported fileformat to insert location info (%d)", fileformat);
1436 static gboolean __mmcamcorder_add_locationinfo_mp4(MMHandleType handle)
1440 guint64 udta_size = 0;
1441 gint64 current_pos = 0;
1442 gint64 moov_pos = 0;
1443 gint64 udta_pos = 0;
1444 gdouble longitude = 0;
1445 gdouble latitude = 0;
1446 gdouble altitude = 0;
1448 int orientation = 0;
1449 char *err_name = NULL;
1450 char err_msg[MAX_ERROR_MESSAGE_LEN] = {'\0',};
1451 _MMCamcorderLocationInfo location_info = {0,};
1453 _MMCamcorderVideoInfo *info = NULL;
1454 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1455 _MMCamcorderSubContext *sc = NULL;
1457 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1458 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1460 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1461 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1465 info = sc->info_video;
1467 f = fopen(info->filename, "rb+");
1469 strerror_r(errno, err_msg, MAX_ERROR_MESSAGE_LEN);
1470 _mmcam_dbg_err("file open failed [%s]", err_msg);
1474 mm_camcorder_get_attributes(handle, &err_name,
1475 MMCAM_TAG_LATITUDE, &latitude,
1476 MMCAM_TAG_LONGITUDE, &longitude,
1477 MMCAM_TAG_ALTITUDE, &altitude,
1478 MMCAM_TAG_VIDEO_ORIENTATION, &orientation,
1481 _mmcam_dbg_warn("Get tag attrs fail. (%s:%x)", err_name, err);
1482 SAFE_FREE (err_name);
1485 location_info.longitude = _mmcamcorder_double_to_fix(longitude);
1486 location_info.latitude = _mmcamcorder_double_to_fix(latitude);
1487 location_info.altitude = _mmcamcorder_double_to_fix(altitude);
1489 /* find udta container.
1490 if, there are udta container, write loci box after that
1491 else, make udta container and write loci box. */
1492 if (_mmcamcorder_find_tag(f, MMCAM_FOURCC('u','d','t','a'), TRUE)) {
1495 _mmcam_dbg_log("find udta container");
1498 if (fseek(f, -8L, SEEK_CUR) != 0) {
1502 udta_pos = ftell(f);
1507 nread = fread(&buf, sizeof(char), sizeof(buf), f);
1509 _mmcam_dbg_log("recorded file fread %d", nread);
1511 udta_size = _mmcamcorder_get_container_size(buf);
1513 /* goto end of udta and write 'loci' box */
1514 if (fseek(f, (udta_size-4L), SEEK_CUR) != 0) {
1518 if (!_mmcamcorder_write_loci(f, location_info)) {
1522 current_pos = ftell(f);
1523 if (current_pos < 0) {
1527 if (!_mmcamcorder_update_size(f, udta_pos, current_pos)) {
1531 _mmcam_dbg_log("No udta container");
1532 if (fseek(f, 0, SEEK_END) != 0) {
1536 if (!_mmcamcorder_write_udta(f, location_info)) {
1541 /* find moov container.
1542 update moov container size. */
1543 if((current_pos = ftell(f))<0)
1546 if (_mmcamcorder_find_tag(f, MMCAM_FOURCC('m','o','o','v'), TRUE)) {
1547 gint64 internal_pos = ftell(f);
1549 _mmcam_dbg_log("found moov container");
1550 if (fseek(f, -8L, SEEK_CUR) !=0) {
1554 moov_pos = ftell(f);
1559 if (!_mmcamcorder_update_size(f, moov_pos, current_pos)) {
1563 /* add orientation info */
1564 fseek(f, internal_pos, SEEK_SET);
1565 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t','r','a','k'), FALSE)) {
1566 _mmcam_dbg_err("failed to find [trak] tag");
1570 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t','k','h','d'), FALSE)) {
1571 _mmcam_dbg_err("failed to find [tkhd] tag");
1575 _mmcam_dbg_log("found [tkhd] tag");
1577 /* seek to start position of composition matrix */
1578 fseek(f, _OFFSET_COMPOSITION_MATRIX, SEEK_CUR);
1580 /* update composition matrix for orientation */
1581 _mmcamcorder_update_composition_matrix(f, orientation);
1583 _mmcam_dbg_err("No 'moov' container");
1595 _mmcam_dbg_err("ftell() returns negative value.");