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/video/cameracontrol.h>
26 #include <gst/app/gstappsrc.h>
27 #include "mm_camcorder_internal.h"
28 #include "mm_camcorder_videorec.h"
30 /*---------------------------------------------------------------------------------------
31 | GLOBAL VARIABLE DEFINITIONS for internal |
32 ---------------------------------------------------------------------------------------*/
33 #define _MMCAMCORDER_LOCATION_INFO // for add gps information
35 /*---------------------------------------------------------------------------------------
36 | LOCAL VARIABLE DEFINITIONS for internal |
37 ---------------------------------------------------------------------------------------*/
38 #define _MMCAMCORDER_MINIMUM_FRAME 5
39 #define _MMCAMCORDER_RETRIAL_COUNT 10
40 #define _MMCAMCORDER_FRAME_WAIT_TIME 200000 /* ms */
41 #define _MMCAMCORDER_CAPTURE_WAIT_TIME 5 /* seconds */
42 #define _OFFSET_COMPOSITION_MATRIX 40L
43 #define _GOP_GEN_INTERVAL 1000000000 /*nano seconds*/
45 /*---------------------------------------------------------------------------------------
46 | LOCAL FUNCTION PROTOTYPES: |
47 ---------------------------------------------------------------------------------------*/
48 /* STATIC INTERNAL FUNCTION */
49 static void __mmcamcorder_video_stream_cb(GstElement *element, GstSample *sample, gpointer u_data);
50 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_check(GstPad *pad, GstPadProbeInfo *info, gpointer u_data);
51 static GstPadProbeReturn __mmcamcorder_video_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data);
52 static GstPadProbeReturn __mmcamcorder_audioque_dataprobe(GstPad *pad, GstPadProbeInfo *info, gpointer u_data);
53 static GstPadProbeReturn __mmcamcorder_video_dataprobe_audio_disable(GstPad *pad, GstPadProbeInfo *info, gpointer u_data);
54 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_audio_mute(GstPad *pad, GstPadProbeInfo *info, gpointer u_data);
55 static gboolean __mmcamcorder_add_metadata(MMHandleType handle, int fileformat);
56 static gboolean __mmcamcorder_add_metadata_mp4(MMHandleType handle);
57 static GstPadProbeReturn __mmcamcorder_eventprobe_monitor(GstPad *pad, GstPadProbeInfo *info, gpointer u_data);
59 /*=======================================================================================
60 | FUNCTION DEFINITIONS |
61 =======================================================================================*/
62 /*---------------------------------------------------------------------------------------
63 | GLOBAL FUNCTION DEFINITIONS: |
64 ---------------------------------------------------------------------------------------*/
65 static void __mmcamcorder_video_stream_cb(GstElement *element, GstSample *sample, gpointer u_data)
67 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
68 _MMCamcorderSubContext *sc = NULL;
70 GstBuffer *buffer = gst_sample_get_buffer(sample);
71 mmf_return_if_fail(buffer);
72 mmf_return_if_fail(gst_buffer_n_memory(buffer));
73 mmf_return_if_fail(hcamcorder);
75 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
76 mmf_return_if_fail(sc);
79 _mmcam_dbg_log("ENTER - push_encoding_buffer %d, buffer %p, MALLOCDATA %p, size %d",
80 sc->info_video->push_encoding_buffer, buffer, GST_BUFFER_MALLOCDATA(buffer), GST_BUFFER_SIZE(buffer));
83 /* push buffer in appsrc to encode */
84 if (sc->info_video->push_encoding_buffer == PUSH_ENCODING_BUFFER_RUN &&
85 sc->info_video->record_dual_stream &&
86 sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst) {
87 GstFlowReturn ret = 0;
88 GstClock *pipe_clock = NULL;
90 if(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst) {
91 if(sc->info_video->is_firstframe) {
92 sc->info_video->is_firstframe = FALSE;
93 pipe_clock = GST_ELEMENT_CLOCK(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst);
95 gst_object_ref(pipe_clock);
96 sc->info_video->base_video_ts = GST_BUFFER_PTS(buffer) - (gst_clock_get_time(pipe_clock) - GST_ELEMENT(sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst)->base_time);
97 gst_object_unref(pipe_clock);
101 if(sc->info_video->is_firstframe) {
102 sc->info_video->is_firstframe = FALSE;
103 sc->info_video->base_video_ts = GST_BUFFER_PTS(buffer);
107 GST_BUFFER_PTS(buffer) = GST_BUFFER_PTS (buffer) - sc->info_video->base_video_ts;
109 ret = gst_app_src_push_buffer((GstAppSrc *)sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, buffer);
110 if (ret != GST_FLOW_OK &&
111 ret != GST_FLOW_FLUSHING) {
112 _mmcam_dbg_err("gst_app_src_push_buffer failed [0x%x]", ret);
113 gst_buffer_unref(buffer);
117 /*_mmcam_dbg_log("push buffer result : 0x%x", ret);*/
119 _mmcam_dbg_warn("unref video buffer immediately - push encoding buffer %d",
120 sc->info_video->push_encoding_buffer);
122 gst_buffer_unref(buffer);
130 int _mmcamcorder_create_recorder_pipeline(MMHandleType handle)
133 int err = MM_ERROR_NONE;
134 int audio_disable = FALSE;
135 const char* gst_element_rsink_name = NULL;
138 GstPad *srcpad = NULL;
139 GstPad *sinkpad = NULL;
141 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
142 _MMCamcorderSubContext *sc = NULL;
144 type_element *RecordsinkElement = NULL;
146 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
148 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
149 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
150 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
152 _mmcam_dbg_warn("start");
154 err = _mmcamcorder_check_videocodec_fileformat_compatibility(handle);
155 if (err != MM_ERROR_NONE) {
160 if(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst) {
161 _mmcam_dbg_log("pipeline is exist so need to remove pipeline _MMCAMCORDER_ENCODE_MAIN_PIPE = %p",
162 sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst);
163 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
166 _MMCAMCORDER_PIPELINE_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCODE_MAIN_PIPE, "recorder_pipeline", err);
168 /* get audio disable */
169 mm_camcorder_get_attributes(handle, NULL,
170 MMCAM_AUDIO_DISABLE, &audio_disable,
173 if (sc->is_modified_rate || audio_disable) {
174 sc->audio_disable = TRUE;
176 sc->audio_disable = FALSE;
178 _mmcam_dbg_log("AUDIO DISABLE : %d (is_modified_rate %d, audio_disable %d)",
179 sc->audio_disable, sc->is_modified_rate, audio_disable);
181 if (sc->audio_disable == FALSE) {
182 /* create audiosrc bin */
183 err = _mmcamcorder_create_audiosrc_bin((MMHandleType)hcamcorder);
184 if (err != MM_ERROR_NONE) {
189 err = _mmcamcorder_create_encodesink_bin((MMHandleType)hcamcorder, MM_CAMCORDER_ENCBIN_PROFILE_VIDEO);
190 if (err != MM_ERROR_NONE) {
194 if (sc->audio_disable == FALSE) {
195 gst_bin_add(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst),
196 sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst);
199 /* add element and encodesink bin to encode main pipeline */
200 gst_bin_add_many(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst),
201 sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst,
202 sc->encode_element[_MMCAMCORDER_ENCSINK_FILT].gst,
203 sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst,
206 /* Link each element : appsrc - capsfilter - encodesink bin */
207 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, "src");
208 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_FILT].gst, "sink");
209 _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error);
211 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_FILT].gst, "src");
212 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst, "video_sink0");
213 _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error);
215 if (sc->audio_disable == FALSE) {
216 srcpad = gst_element_get_static_pad (sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst, "src");
217 sinkpad = gst_element_get_static_pad (sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst, "audio_sink0");
218 _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error);
221 _mmcamcorder_conf_get_element(hcamcorder->conf_main,
222 CONFIGURE_CATEGORY_MAIN_RECORD,
225 _mmcamcorder_conf_get_value_element_name(RecordsinkElement, &gst_element_rsink_name);
227 /* set data probe function */
229 /* register message cb */
231 /* set data probe function for audio */
233 if (sc->audio_disable == FALSE) {
234 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst, "sink");
235 MMCAMCORDER_ADD_BUFFER_PROBE(sinkpad, _MMCAMCORDER_HANDLER_VIDEOREC,
236 __mmcamcorder_audioque_dataprobe, hcamcorder);
237 gst_object_unref(sinkpad);
241 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst, "src");
242 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
243 __mmcamcorder_audio_dataprobe_audio_mute, hcamcorder);
244 gst_object_unref(srcpad);
247 if (sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst) {
248 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst, "src");
249 MMCAMCORDER_ADD_EVENT_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
250 __mmcamcorder_eventprobe_monitor, hcamcorder);
251 gst_object_unref(srcpad);
256 if (sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst) {
257 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst, "src");
258 MMCAMCORDER_ADD_EVENT_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
259 __mmcamcorder_eventprobe_monitor, hcamcorder);
260 gst_object_unref(srcpad);
264 if (sc->audio_disable) {
265 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC].gst, "sink");
266 MMCAMCORDER_ADD_BUFFER_PROBE(sinkpad, _MMCAMCORDER_HANDLER_VIDEOREC,
267 __mmcamcorder_video_dataprobe_audio_disable, hcamcorder);
268 gst_object_unref(sinkpad);
272 if (!strcmp(gst_element_rsink_name, "filesink")) {
273 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC].gst, "src");
274 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
275 __mmcamcorder_video_dataprobe_record, hcamcorder);
276 gst_object_unref(srcpad);
279 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst, "src");
280 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
281 __mmcamcorder_audio_dataprobe_check, hcamcorder);
282 gst_object_unref(srcpad);
286 bus = gst_pipeline_get_bus(GST_PIPELINE(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst));
288 /* register pipeline message callback */
289 hcamcorder->encode_pipeline_cb_event_id = gst_bus_add_watch(bus, (GstBusFunc)_mmcamcorder_pipeline_cb_message, hcamcorder);
291 gst_object_unref(bus);
294 return MM_ERROR_NONE;
296 pipeline_creation_error:
297 for (i = _MMCAMCORDER_AUDIOSRC_BIN ; i <= _MMCAMCORDER_ENCSINK_SINK ; i++) {
298 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, i);
300 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCODE_MAIN_PIPE);
305 int _mmcamcorder_remove_audio_pipeline(MMHandleType handle)
307 GstPad *srcpad = NULL;
308 GstPad *sinkpad = NULL;
309 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
310 _MMCamcorderSubContext *sc = NULL;
312 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
314 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
315 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
316 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
320 if (sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst != NULL) {
321 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst, "src");
322 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst, "audio_sink0");
323 _MM_GST_PAD_UNLINK_UNREF(srcpad, sinkpad);
325 /* release audiosrc bin */
326 gst_bin_remove(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst),
327 sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst);
330 To avoid conflicting between old elements and newly created elements,
331 I clean element handles here. Real elements object will be finalized as the 'unref' process goes on.
332 This is a typical problem of unref. Even though I unref bin here, it takes much time to finalize each elements.
333 So I clean handles first, make them unref later. Audio recording, however, isn't needed this process.
334 It's because the pipeline of audio recording destroys at the same time,
335 and '_mmcamcorder_element_release_noti' will perfom removing handle.
337 _mmcamcorder_remove_element_handle(handle, (void *)sc->encode_element, _MMCAMCORDER_AUDIOSRC_BIN, _MMCAMCORDER_AUDIOSRC_VOL);
339 _mmcam_dbg_log("Audio pipeline removed");
342 return MM_ERROR_NONE;
346 int _mmcamcorder_remove_encode_pipeline(MMHandleType handle)
348 GstPad *reqpad = NULL;
349 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
350 _MMCamcorderSubContext *sc = NULL;
352 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
354 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
355 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
356 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
360 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst != NULL) {
361 /* release request pad */
362 reqpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "audio");
364 gst_element_release_request_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, reqpad);
365 gst_object_unref(reqpad);
369 reqpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "video");
371 gst_element_release_request_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, reqpad);
372 gst_object_unref(reqpad);
376 /* release encode main pipeline */
377 gst_object_unref(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst);
380 To avoid conflicting between old elements and newly created elements,
381 I clean element handles here. Real elements object will be finalized as the 'unref' process goes on.
382 This is a typical problem of unref. Even though I unref bin here, it takes much time to finalize each elements.
383 So I clean handles first, make them unref later. Audio recording, however, isn't needed this process.
384 It's because the pipeline of audio recording destroys at the same time,
385 and '_mmcamcorder_element_release_noti' will perfom removing handle.
387 //_mmcamcorder_remove_element_handle(handle, (void *)sc->encode_element, _MMCAMCORDER_ENCODE_MAIN_PIPE, _MMCAMCORDER_ENCSINK_SINK);
389 _mmcam_dbg_log("Encoder pipeline removed");
392 return MM_ERROR_NONE;
396 int _mmcamcorder_remove_recorder_pipeline(MMHandleType handle)
398 int ret = MM_ERROR_NONE;
399 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
400 _MMCamcorderSubContext *sc = NULL;
404 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
405 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
406 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
408 _mmcam_dbg_log("start");
410 if (!sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst) {
411 _mmcam_dbg_warn("pipeline is not existed.");
412 return MM_ERROR_NONE;
415 _mmcamcorder_remove_all_handlers((MMHandleType)hcamcorder, _MMCAMCORDER_HANDLER_VIDEOREC);
417 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_NULL);
418 if (ret != MM_ERROR_NONE) {
419 _mmcam_dbg_err("Faile to change encode main pipeline [0x%x]", ret);
423 bus = gst_pipeline_get_bus(GST_PIPELINE(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst));
425 /* remove audio pipeline first */
426 ret = _mmcamcorder_remove_audio_pipeline(handle);
427 if (ret != MM_ERROR_NONE) {
428 _mmcam_dbg_err("Fail to remove audio pipeline");
432 ret = _mmcamcorder_remove_encode_pipeline(handle);
433 if (ret != MM_ERROR_NONE) {
434 _mmcam_dbg_err("Fail to remove encoder pipeline");
438 /* Remove pipeline message callback */
439 if (hcamcorder->encode_pipeline_cb_event_id != 0) {
440 g_source_remove(hcamcorder->encode_pipeline_cb_event_id);
441 hcamcorder->encode_pipeline_cb_event_id = 0;
444 /* Remove remained message */
446 GstMessage *gst_msg = NULL;
447 while ((gst_msg = gst_bus_pop(bus)) != NULL) {
448 _mmcamcorder_pipeline_cb_message(bus, gst_msg, (gpointer)hcamcorder);
449 gst_message_unref(gst_msg);
452 gst_object_unref(bus);
456 _mmcam_dbg_log("done");
462 int _mmcamcorder_video_command(MMHandleType handle, int command)
467 int ret = MM_ERROR_NONE;
468 double motion_rate = _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE;
469 char *err_name = NULL;
470 char *temp_filename = NULL;
473 GstElement *pipeline = NULL;
475 _MMCamcorderVideoInfo *info = NULL;
476 _MMCamcorderSubContext *sc = NULL;
477 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
479 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
481 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
482 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
483 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
484 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
486 info = sc->info_video;
488 _mmcam_dbg_log("Command(%d)", command);
490 pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
493 case _MMCamcorder_CMD_RECORD:
495 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) != MM_CAMCORDER_STATE_PAUSED) {
501 int ret_free_space = 0;
502 char *dir_name = NULL;
503 guint64 free_space = 0;
504 int file_system_type = 0;
507 _mmcam_dbg_log("Record Start - dual stream %d", info->support_dual_stream);
509 /* init record_dual_stream */
510 info->record_dual_stream = FALSE;
512 ret = mm_camcorder_get_attributes(handle, &err_name,
513 MMCAM_CAMERA_FPS, &fps,
514 MMCAM_CAMERA_WIDTH, &(info->preview_width),
515 MMCAM_CAMERA_HEIGHT, &(info->preview_height),
516 MMCAM_VIDEO_WIDTH, &(info->video_width),
517 MMCAM_VIDEO_HEIGHT, &(info->video_height),
518 MMCAM_FILE_FORMAT, &fileformat,
519 MMCAM_TARGET_FILENAME, &temp_filename, &size,
520 MMCAM_TARGET_MAX_SIZE, &imax_size,
521 MMCAM_TARGET_TIME_LIMIT, &imax_time,
522 MMCAM_FILE_FORMAT, &(info->fileformat),
523 MMCAM_CAMERA_RECORDING_MOTION_RATE, &motion_rate,
525 if (ret != MM_ERROR_NONE) {
526 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, ret);
527 SAFE_FREE (err_name);
528 goto _ERR_CAMCORDER_VIDEO_COMMAND;
531 if (temp_filename == NULL) {
532 _mmcam_dbg_err("filename is not set");
533 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
534 goto _ERR_CAMCORDER_VIDEO_COMMAND;
538 if (imax_size <= 0) {
539 info->max_size = 0; /* do not check */
541 info->max_size = ((guint64)imax_size) << 10; /* to byte */
545 if (imax_time <= 0) {
546 info->max_time = 0; /* do not check */
548 info->max_time = ((guint64)imax_time) * 1000; /* to millisecond */
551 dir_name = g_path_get_dirname(temp_filename);
553 ret_free_space = _mmcamcorder_get_freespace(dir_name, hcamcorder->root_directory, &free_space);
555 _mmcam_dbg_warn("current space - %s [%" G_GUINT64_FORMAT "]", dir_name, free_space);
557 if (_mmcamcorder_get_file_system_type(dir_name, &file_system_type) == 0) {
558 /* MSDOS_SUPER_MAGIC : 0x4d44 */
559 if (file_system_type == MSDOS_SUPER_MAGIC &&
560 (info->max_size == 0 || info->max_size > FAT32_FILE_SYSTEM_MAX_SIZE)) {
561 _mmcam_dbg_warn("FAT32 and too large max[%"G_GUINT64_FORMAT"], set max as %"G_GUINT64_FORMAT,
562 info->max_size, FAT32_FILE_SYSTEM_MAX_SIZE);
563 info->max_size = FAT32_FILE_SYSTEM_MAX_SIZE;
565 _mmcam_dbg_warn("file system 0x%x, max size %"G_GUINT64_FORMAT,
566 file_system_type, info->max_size);
569 _mmcam_dbg_warn("_mmcamcorder_get_file_system_type failed");
575 _mmcam_dbg_err("failed to get directory name");
579 if ((ret_free_space == -1) || free_space <= (_MMCAMCORDER_MINIMUM_SPACE<<1)) {
580 _mmcam_dbg_err("OUT of STORAGE [ret_free_space:%d or free space [%" G_GUINT64_FORMAT "] is smaller than [%d]",
581 ret_free_space, free_space, (_MMCAMCORDER_MINIMUM_SPACE<<1));
582 return MM_ERROR_OUT_OF_STORAGE;
585 pthread_mutex_lock(&(hcamcorder->task_thread_lock));
586 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst == NULL &&
587 hcamcorder->task_thread_state == _MMCAMCORDER_TASK_THREAD_STATE_NONE) {
588 /* Play record start sound */
589 _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_FILEPATH_REC_START_SND, FALSE);
591 pthread_mutex_unlock(&(hcamcorder->task_thread_lock));
593 _mmcam_dbg_warn("video size [%dx%d]", info->video_width, info->video_height);
595 if (info->video_width == 0 || info->video_height == 0) {
596 _mmcam_dbg_warn("video size is invalid [%dx%d] use preview size [%dx%d]",
597 info->video_width, info->video_height, info->preview_width, info->preview_height);
598 info->video_width = info->preview_width;
599 info->video_height = info->preview_height;
602 if (info->support_dual_stream) {
603 _mmcam_dbg_warn("DUAL STREAM MODE");
605 info->record_dual_stream = TRUE;
607 /* No need to restart preview */
608 info->restart_preview = FALSE;
610 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "video-width", info->video_width);
611 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "video-height", info->video_height);
612 } else if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
613 info->preview_width == info->video_width &&
614 info->preview_height == info->video_height) {
615 _mmcam_dbg_log("H264 preview mode and same resolution");
617 /* No need to restart preview */
618 info->restart_preview = FALSE;
620 /* always need to restart preview */
621 info->restart_preview = TRUE;
624 /* set recording hint */
625 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", TRUE);
627 if (info->restart_preview) {
628 /* stop preview and set new size */
629 _mmcam_dbg_log("restart preview");
631 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
632 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
634 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
635 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264) {
636 if (!_mmcamcorder_recreate_decoder_for_encoded_preview(handle)) {
637 ret = MM_ERROR_CAMCORDER_INTERNAL;
638 goto _ERR_CAMCORDER_VIDEO_COMMAND;
642 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
643 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
645 if (ret != MM_ERROR_NONE) {
646 goto _ERR_CAMCORDER_VIDEO_COMMAND;
649 if (!_mmcamcorder_set_camera_resolution(handle, info->video_width, info->video_height)) {
650 ret = MM_ERROR_CAMCORDER_INTERNAL;
651 goto _ERR_CAMCORDER_VIDEO_COMMAND;
654 /* Start preview again with new setting */
655 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
656 if (ret != MM_ERROR_NONE) {
657 goto _ERR_CAMCORDER_VIDEO_COMMAND;
660 if (motion_rate < 1.0) {
661 _mmcam_dbg_warn("wait for stabilization of frame");
665 _mmcam_dbg_log("no need to restart preview");
668 _mmcamcorder_conf_get_value_int(hcamcorder->conf_main,
669 CONFIGURE_CATEGORY_MAIN_RECORD,
673 _mmcamcorder_conf_get_value_int(hcamcorder->conf_main,
674 CONFIGURE_CATEGORY_MAIN_RECORD,
675 "PassFirstVideoFrame",
676 &(sc->pass_first_vframe));
678 _mmcam_dbg_log("Drop video frame count[%d], Pass fisrt video frame count[%d]",
679 sc->drop_vframe, sc->pass_first_vframe);
681 info->record_drop_count = (guint)motion_rate;
682 info->record_motion_rate = motion_rate;
683 if (sc->is_modified_rate) {
684 info->record_timestamp_ratio = (_MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE/motion_rate);
686 info->record_timestamp_ratio = _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE;
689 _mmcam_dbg_warn("recording fps %d, motion rate %f, timestamp_ratio %f",
690 fps, info->record_motion_rate, info->record_timestamp_ratio);
692 /* set push buffer flag */
693 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_INIT;
694 info->base_video_ts = 0;
696 /* connect video stream cb signal */
697 /*130826 Connect video stream cb for handling fast record frame cb*/
698 if (info->record_dual_stream) {
699 if (_mmcamcorder_connect_video_stream_cb_signal((MMHandleType)hcamcorder) != MM_ERROR_NONE) {
700 goto _ERR_CAMCORDER_VIDEO_COMMAND;
704 /* start video stream */
705 if (info->record_dual_stream) {
706 GstCameraControl *control = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
708 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
710 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_START");
711 gst_camera_control_set_record_command(control, GST_CAMERA_CONTROL_RECORD_COMMAND_START);
713 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
715 _mmcam_dbg_err("could not get camera control");
719 /* check pre-created encode pipeline */
720 pthread_mutex_lock(&(hcamcorder->task_thread_lock));
721 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst == NULL &&
722 hcamcorder->task_thread_state == _MMCAMCORDER_TASK_THREAD_STATE_NONE) {
723 /* create encoding pipeline */
724 ret =_mmcamcorder_video_prepare_record((MMHandleType)hcamcorder);
725 if (ret != MM_ERROR_NONE) {
726 pthread_mutex_unlock(&(hcamcorder->task_thread_lock));
727 goto _ERR_CAMCORDER_VIDEO_COMMAND;
730 pthread_mutex_unlock(&(hcamcorder->task_thread_lock));
732 /* check recording start sound */
733 _mmcamcorder_sound_solo_play_wait(handle);
735 /**< To fix video recording hanging
736 1. use gst_element_set_start_time() instead of gst_pipeline_set_new_stream_time()
737 2. Set (GstClockTime)1 instead of (GstClockTime)0. Because of strict check in gstreamer 0.25,
738 basetime wouldn't change if you set (GstClockTime)0.
739 3. Move set start time position below PAUSED of pipeline.
741 //gst_element_set_start_time(GST_ELEMENT(sc->element[_MMCAMCORDER_MAIN_PIPE].gst), (GstClockTime)1);
742 //gst_element_set_start_time(GST_ELEMENT(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), (GstClockTime)1);
744 info->video_frame_count = 0;
745 info->is_firstframe = TRUE;
746 info->audio_frame_count = 0;
748 sc->ferror_send = FALSE;
749 sc->ferror_count = 0;
750 hcamcorder->error_occurs = FALSE;
751 sc->bget_eos = FALSE;
753 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PLAYING);
754 if (ret != MM_ERROR_NONE) {
755 /* stop video stream */
756 if (info->record_dual_stream) {
757 GstCameraControl *control = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
759 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
761 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
762 gst_camera_control_set_record_command(control, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
764 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
766 _mmcam_dbg_err("failed to get camera control");
770 /* Remove recorder pipeline and recording file which size maybe zero */
771 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
772 if (info->filename) {
773 _mmcam_dbg_log("file delete(%s)", info->filename);
774 unlink(info->filename);
776 goto _ERR_CAMCORDER_VIDEO_COMMAND;
782 mm_camcorder_get_attributes(handle, NULL, MMCAM_VIDEO_ENCODER, &video_enc, NULL);
783 if (video_enc == MM_VIDEO_CODEC_MPEG4) {
784 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC].gst, "force-i-frame", TRUE);
787 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", FALSE);
789 _mmcam_dbg_log("Object property settings done");
793 case _MMCamcorder_CMD_PAUSE:
795 if (info->b_commiting) {
796 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
797 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
800 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
801 if (sc->audio_disable) {
802 /* check only video frame */
803 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
805 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
806 _mmcam_dbg_err("Pause fail, frame count %llu",
807 info->video_frame_count);
808 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
810 _mmcam_dbg_warn("Waiting for enough video frame, retrial[%d], frame %llu",
811 count, info->video_frame_count);
814 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
816 /* check both of video and audio frame */
817 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
819 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
820 _mmcam_dbg_err("Pause fail, frame count VIDEO[%llu], AUDIO [%llu]",
821 info->video_frame_count, info->audio_frame_count);
822 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
824 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%llu], AUDIO [%llu]",
825 count, info->video_frame_count, info->audio_frame_count);
828 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
832 /* block encodebin */
833 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", TRUE);
836 case _MMCamcorder_CMD_CANCEL:
838 if (info->b_commiting) {
839 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
840 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
843 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
845 if (hcamcorder->capture_in_recording == FALSE) {
847 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
848 _mmcam_dbg_err("Failed to Wait capture data");
849 hcamcorder->capture_in_recording = FALSE;
852 _mmcam_dbg_warn("Waiting for capture data - retrial [%d]", count);
855 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
858 /* block push buffer */
859 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP;
861 ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
862 if (ret != MM_ERROR_NONE) {
863 goto _ERR_CAMCORDER_VIDEO_COMMAND;
866 /* set recording hint */
867 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", FALSE);
869 /* stop video stream */
870 if (info->record_dual_stream) {
871 GstCameraControl *control = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
873 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
875 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
876 gst_camera_control_set_record_command(control, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
878 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
880 _mmcam_dbg_err("failed to get camera control");
884 if (info->restart_preview) {
885 /* restart preview */
886 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
887 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
889 ret =_mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
891 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264) {
892 if (!_mmcamcorder_recreate_decoder_for_encoded_preview(handle)) {
893 ret = MM_ERROR_CAMCORDER_INTERNAL;
896 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
897 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
898 if (ret != MM_ERROR_NONE) {
899 goto _ERR_CAMCORDER_VIDEO_COMMAND;
902 /* reset restart_preview for inset window layout */
903 info->restart_preview = FALSE;
905 if (!_mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height)) {
906 ret = MM_ERROR_CAMCORDER_INTERNAL;
907 goto _ERR_CAMCORDER_VIDEO_COMMAND;
910 ret =_mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
911 if (ret != MM_ERROR_NONE) {
912 goto _ERR_CAMCORDER_VIDEO_COMMAND;
916 /* remove target file */
917 if (info->filename) {
918 _mmcam_dbg_log("file delete(%s)", info->filename);
919 unlink(info->filename);
922 sc->isMaxsizePausing = FALSE;
923 sc->isMaxtimePausing = FALSE;
925 sc->display_interval = 0;
926 sc->previous_slot_time = 0;
927 info->video_frame_count = 0;
928 info->audio_frame_count = 0;
930 hcamcorder->capture_in_recording = FALSE;
933 case _MMCamcorder_CMD_COMMIT:
935 if (info->b_commiting) {
936 _mmcam_dbg_err("now on commiting previous file!!(command : %d)", command);
937 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
939 _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT : start");
940 info->b_commiting = TRUE;
941 sc->bget_eos = FALSE;
944 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
945 if (sc->audio_disable) {
946 /* check only video frame */
947 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME &&
948 hcamcorder->capture_in_recording == FALSE) {
950 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
951 _mmcam_dbg_err("Commit fail, frame count is %llu, capturing %d",
952 info->video_frame_count, hcamcorder->capture_in_recording);
954 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
955 _mmcam_dbg_warn("video frames are enough. keep going...");
957 info->b_commiting = FALSE;
958 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
961 _mmcam_dbg_warn("Waiting for enough video frame, retrial [%d], frame %llu, capturing %d",
962 count, info->video_frame_count, hcamcorder->capture_in_recording);
965 /* check both of video and audio frame */
966 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME &&
967 info->audio_frame_count &&
968 hcamcorder->capture_in_recording == FALSE) {
970 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
971 _mmcam_dbg_err("Commit fail, VIDEO[%llu], AUDIO [%llu], capturing %d",
972 info->video_frame_count, info->audio_frame_count, hcamcorder->capture_in_recording);
974 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
975 _mmcam_dbg_warn("video/audio frames are enough. keep going...");
977 info->b_commiting = FALSE;
978 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
981 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%llu], AUDIO [%llu], capturing %d",
982 count, info->video_frame_count, info->audio_frame_count, hcamcorder->capture_in_recording);
986 if (hcamcorder->capture_in_recording) {
987 struct timespec timeout;
989 struct timeval tv_to_add;
990 struct timeval tv_result;
992 gettimeofday(&tv, NULL);
994 tv_to_add.tv_sec = _MMCAMCORDER_CAPTURE_WAIT_TIME;
995 tv_to_add.tv_usec = 0;
997 timeradd(&tv, &tv_to_add, &tv_result);
998 timeout.tv_sec = tv_result.tv_sec;
999 timeout.tv_nsec = tv_result.tv_usec * 1000;
1001 if (_MMCAMCORDER_CMD_TIMED_WAIT(handle, timeout)) {
1002 _mmcam_dbg_warn("timeout");
1004 _mmcam_dbg_warn("signal received");
1007 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
1011 /* block push buffer */
1012 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP;
1013 _mmcam_dbg_log("block push buffer to appsrc");
1015 if (sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst != NULL) {
1016 if (gst_element_send_event(sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, gst_event_new_eos())) {
1017 _mmcam_dbg_warn("VIDEO: send eos to appsrc done");
1019 _mmcam_dbg_err("VIDEO: send EOS failed");
1020 info->b_commiting = FALSE;
1021 ret = MM_ERROR_CAMCORDER_INTERNAL;
1022 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1025 _mmcam_dbg_err("No video stream source");
1026 info->b_commiting = FALSE;
1027 ret = MM_ERROR_CAMCORDER_INTERNAL;
1028 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1031 if (sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst != NULL) {
1032 if (gst_element_send_event(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst, gst_event_new_eos())) {
1033 _mmcam_dbg_warn("AUDIO: send eos to audiosrc done");
1035 _mmcam_dbg_err("AUDIO: send EOS failed");
1036 info->b_commiting = FALSE;
1037 ret = MM_ERROR_CAMCORDER_INTERNAL;
1038 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1041 _mmcam_dbg_log("No audio stream");
1045 sc->display_interval = 0;
1046 sc->previous_slot_time = 0;
1049 _mmcam_dbg_log("Start to wait EOS");
1050 ret =_mmcamcorder_get_eos_message(handle);
1051 if (ret != MM_ERROR_NONE) {
1052 info->b_commiting = FALSE;
1053 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1057 hcamcorder->capture_in_recording = FALSE;
1061 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
1062 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1065 return MM_ERROR_NONE;
1067 _ERR_CAMCORDER_VIDEO_COMMAND:
1068 if (command == _MMCamcorder_CMD_RECORD) {
1069 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
1076 int _mmcamcorder_video_handle_eos(MMHandleType handle)
1078 int ret = MM_ERROR_NONE;
1080 guint64 file_size = 0;
1082 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1083 _MMCamcorderSubContext *sc = NULL;
1084 _MMCamcorderVideoInfo *info = NULL;
1085 _MMCamcorderMsgItem msg;
1086 MMCamRecordingReport *report = NULL;
1088 mmf_return_val_if_fail(hcamcorder, FALSE);
1090 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1091 mmf_return_val_if_fail(sc, FALSE);
1092 mmf_return_val_if_fail(sc->info_video, FALSE);
1094 info = sc->info_video;
1098 if (hcamcorder->state_change_by_system != _MMCAMCORDER_STATE_CHANGE_BY_ASM) {
1099 /* Play record stop sound */
1100 _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_FILEPATH_REC_STOP_SND, FALSE);
1102 _mmcam_dbg_warn("Play stop sound through pulseaudio");
1104 #ifdef _MMCAMCORDER_UPLOAD_SAMPLE
1105 _mmcamcorder_sound_init(handle, _MMCAMCORDER_FILEPATH_REC_STOP_SND);
1106 #else /* _MMCAMCORDER_UPLOAD_SAMPLE */
1107 _mmcamcorder_sound_init(handle);
1108 #endif /* _MMCAMCORDER_UPLOAD_SAMPLE */
1110 _mmcamcorder_sound_play((MMHandleType)hcamcorder, _MMCAMCORDER_SAMPLE_SOUND_NAME_REC_STOP, TRUE);
1112 _mmcamcorder_sound_finalize(handle);
1115 /* remove blocking part */
1116 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
1118 mm_camcorder_get_attributes(handle, NULL,
1119 MMCAM_RECORDER_TAG_ENABLE, &enabletag,
1122 ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
1123 if (ret != MM_ERROR_NONE) {
1124 _mmcam_dbg_warn("_MMCamcorder_CMD_COMMIT:__mmcamcorder_remove_recorder_pipeline failed. error[%x]", ret);
1127 /* set recording hint */
1128 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", FALSE);
1130 /* stop video stream */
1131 if (info->record_dual_stream) {
1132 GstCameraControl *control = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
1134 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
1136 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
1137 gst_camera_control_set_record_command(control, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
1139 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
1141 _mmcam_dbg_err("failed to get camera control");
1145 if (enabletag && !(sc->ferror_send)) {
1146 ret = __mmcamcorder_add_metadata((MMHandleType)hcamcorder, info->fileformat);
1148 _mmcam_dbg_log("Writing location information SUCCEEDED !!");
1150 _mmcam_dbg_err("Writing location information FAILED !!");
1154 /* Check file size */
1155 if (info->max_size > 0) {
1156 _mmcamcorder_get_file_size(info->filename, &file_size);
1157 _mmcam_dbg_log("MAX size %lld byte - created filesize %lld byte",
1158 info->max_size, file_size);
1160 if (file_size > info->max_size) {
1161 _MMCamcorderMsgItem message;
1162 _mmcam_dbg_err("File size is greater than max size !!");
1163 message.id = MM_MESSAGE_CAMCORDER_ERROR;
1164 message.param.code = MM_ERROR_CAMCORDER_FILE_SIZE_OVER;
1165 _mmcamcorder_send_message((MMHandleType)hcamcorder, &message);
1169 if (info->restart_preview) {
1171 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
1172 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
1174 _mmcam_dbg_log("Set state of pipeline as READY");
1175 ret = _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_READY);
1177 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264) {
1178 if (!_mmcamcorder_recreate_decoder_for_encoded_preview(handle)) {
1179 ret = MM_ERROR_CAMCORDER_INTERNAL;
1184 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
1185 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
1186 if (ret != MM_ERROR_NONE) {
1187 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1188 msg.param.code = ret;
1189 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1190 _mmcam_dbg_err("Failed to set state READY[%x]", ret);
1193 /* reset restart_preview for inset window layout */
1194 info->restart_preview = FALSE;
1196 /* recover preview size */
1197 _mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height);
1199 ret =_mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_PLAYING);
1200 /* Do not return when error is occurred.
1201 Recording file was created successfully, but starting pipeline failed */
1202 if (ret != MM_ERROR_NONE) {
1203 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1204 msg.param.code = ret;
1205 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1206 _mmcam_dbg_err("Failed to set state PLAYING[%x]", ret);
1209 _mmcam_dbg_log("No need to restart preview");
1212 /* Send recording report to application */
1213 msg.id = MM_MESSAGE_CAMCORDER_VIDEO_CAPTURED;
1214 report = (MMCamRecordingReport *)malloc(sizeof(MMCamRecordingReport));
1216 _mmcam_dbg_err("Recording report fail(%s). Out of memory.", info->filename);
1218 report->recording_filename = strdup(info->filename);
1219 msg.param.data= report;
1221 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1225 sc->pipeline_time = 0;
1227 sc->isMaxsizePausing = FALSE; /*In async function, this variable should set in callback function. */
1228 sc->isMaxtimePausing = FALSE;
1229 hcamcorder->error_occurs = FALSE;
1231 info->video_frame_count = 0;
1232 info->audio_frame_count = 0;
1234 info->b_commiting = FALSE;
1236 if (hcamcorder->state_change_by_system != _MMCAMCORDER_STATE_CHANGE_BY_ASM) {
1237 /* check recording stop sound */
1238 _mmcamcorder_sound_solo_play_wait(handle);
1241 _mmcam_dbg_err("_MMCamcorder_CMD_COMMIT : end");
1248 * This function is record video data probing function.
1249 * If this function is linked with certain pad by gst_pad_add_buffer_probe(),
1250 * this function will be called when data stream pass through the pad.
1252 * @param[in] pad probing pad which calls this function.
1253 * @param[in] buffer buffer which contains stream data.
1254 * @param[in] u_data user data.
1255 * @return This function returns true on success, or false value with error
1259 static GstPadProbeReturn __mmcamcorder_eventprobe_monitor(GstPad *pad, GstPadProbeInfo *info, gpointer u_data){
1260 GstEvent *event = GST_PAD_PROBE_INFO_EVENT(info);
1261 switch (GST_EVENT_TYPE(event)) {
1262 case GST_EVENT_UNKNOWN:
1263 /* upstream events */
1265 case GST_EVENT_SEEK:
1266 case GST_EVENT_NAVIGATION:
1267 case GST_EVENT_LATENCY:
1268 /* downstream serialized events */
1269 case GST_EVENT_SEGMENT :
1271 case GST_EVENT_BUFFERSIZE:
1272 _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1275 _mmcam_dbg_warn("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1277 /* bidirectional events */
1278 case GST_EVENT_FLUSH_START:
1279 case GST_EVENT_FLUSH_STOP:
1280 _mmcam_dbg_err("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1283 _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1287 return GST_PAD_PROBE_OK;
1291 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_check(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1293 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1294 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1296 _MMCamcorderSubContext *sc = NULL;
1297 _MMCamcorderVideoInfo *videoinfo = NULL;
1298 _MMCamcorderMsgItem msg;
1299 guint64 buffer_size = 0;
1300 guint64 trailer_size = 0;
1302 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1303 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1304 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1306 mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK);
1307 videoinfo = sc->info_video;
1309 /* get buffer size */
1310 if (!gst_buffer_map(buffer, &mapinfo, GST_MAP_READ)) {
1311 _mmcam_dbg_warn("map failed : buffer %p", buffer);
1312 return GST_PAD_PROBE_OK;
1315 buffer_size = mapinfo.size;
1316 gst_buffer_unmap(buffer, &mapinfo);
1318 /*_mmcam_dbg_err("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1320 pthread_mutex_lock(&(videoinfo->size_check_lock));
1322 if (videoinfo->audio_frame_count == 0) {
1323 videoinfo->filesize += buffer_size;
1324 videoinfo->audio_frame_count++;
1325 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1326 return GST_PAD_PROBE_OK;
1329 if (sc->ferror_send || sc->isMaxsizePausing) {
1330 _mmcam_dbg_warn("Recording is paused, drop frames");
1331 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1332 return GST_PAD_PROBE_DROP;
1335 /* get trailer size */
1336 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1337 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1342 /* check max size of recorded file */
1343 if (videoinfo->max_size > 0 &&
1344 videoinfo->max_size < videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) {
1345 GstState pipeline_state = GST_STATE_VOID_PENDING;
1346 GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1347 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1348 _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \
1349 " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]",
1350 videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size);
1352 if (!sc->isMaxsizePausing) {
1353 sc->isMaxsizePausing = TRUE;
1354 gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ;
1355 if (pipeline_state == GST_STATE_PLAYING) {
1356 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1359 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1360 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1363 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1368 videoinfo->filesize += buffer_size;
1369 videoinfo->audio_frame_count++;
1371 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1373 return GST_PAD_PROBE_OK;
1377 static GstPadProbeReturn __mmcamcorder_video_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1382 guint64 free_space = 0;
1383 guint64 buffer_size = 0;
1384 guint64 trailer_size = 0;
1385 guint64 queued_buffer = 0;
1386 char *dir_name = NULL;
1387 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1390 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1391 _MMCamcorderMsgItem msg;
1392 _MMCamcorderSubContext *sc = NULL;
1393 _MMCamcorderVideoInfo *videoinfo = NULL;
1395 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1396 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1398 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1399 mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK);
1400 videoinfo = sc->info_video;
1402 /*_mmcam_dbg_log("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1403 if (sc->ferror_send) {
1404 _mmcam_dbg_warn("file write error, drop frames");
1405 return GST_PAD_PROBE_DROP;
1408 gst_buffer_map(buffer, &mapinfo, GST_MAP_READ);
1409 buffer_size = mapinfo.size;
1410 gst_buffer_unmap(buffer, &mapinfo);
1412 videoinfo->video_frame_count++;
1413 if (videoinfo->video_frame_count <= (guint64)_MMCAMCORDER_MINIMUM_FRAME) {
1414 /* _mmcam_dbg_log("Pass minimum frame: info->video_frame_count: %" G_GUINT64_FORMAT " ",
1415 info->video_frame_count); */
1416 pthread_mutex_lock(&(videoinfo->size_check_lock));
1417 videoinfo->filesize += buffer_size;
1418 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1419 return GST_PAD_PROBE_OK;
1422 /* get trailer size */
1423 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1424 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1429 dir_name = g_path_get_dirname(videoinfo->filename);
1431 ret = _mmcamcorder_get_freespace(dir_name, hcamcorder->root_directory, &free_space);
1435 _mmcam_dbg_err("failed to get dir name from [%s]", videoinfo->filename);
1439 /*_mmcam_dbg_log("check free space for recording");*/
1442 case -2: /* file not exist */
1443 case -1: /* failed to get free space */
1444 _mmcam_dbg_err("Error occured. [%d]", ret);
1445 if (sc->ferror_count == 2 && sc->ferror_send == FALSE) {
1446 sc->ferror_send = TRUE;
1447 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1449 msg.param.code = MM_ERROR_FILE_NOT_FOUND;
1451 msg.param.code = MM_ERROR_FILE_READ;
1453 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1458 return GST_PAD_PROBE_DROP; /* skip this buffer */
1460 default: /* succeeded to get free space */
1461 /* check free space for recording */
1462 /* get queued buffer size */
1463 if (sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst) {
1464 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst, "current-level-bytes", &aq_size);
1466 if (sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst) {
1467 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst, "current-level-bytes", &vq_size);
1470 queued_buffer = aq_size + vq_size;
1472 /* check free space */
1473 if (free_space < (_MMCAMCORDER_MINIMUM_SPACE + buffer_size + trailer_size + queued_buffer)) {
1474 _mmcam_dbg_warn("No more space for recording!!! Recording is paused.");
1475 _mmcam_dbg_warn("Free Space : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]," \
1476 " buffer size : [%" G_GUINT64_FORMAT "], queued buffer size : [%" G_GUINT64_FORMAT "]", \
1477 free_space, trailer_size, buffer_size, queued_buffer);
1479 if (!sc->isMaxsizePausing) {
1480 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1481 sc->isMaxsizePausing = TRUE;
1483 msg.id = MM_MESSAGE_CAMCORDER_NO_FREE_SPACE;
1484 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1487 return GST_PAD_PROBE_DROP;
1492 pthread_mutex_lock(&(videoinfo->size_check_lock));
1494 /* check max size of recorded file */
1495 if (videoinfo->max_size > 0 &&
1496 videoinfo->max_size < videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) {
1497 GstState pipeline_state = GST_STATE_VOID_PENDING;
1498 GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1499 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1500 _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \
1501 " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]",
1502 videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size);
1504 if (!sc->isMaxsizePausing) {
1505 sc->isMaxsizePausing = TRUE;
1506 gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ;
1507 if (pipeline_state == GST_STATE_PLAYING) {
1508 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1511 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1512 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1515 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1517 return GST_PAD_PROBE_DROP;
1520 videoinfo->filesize += (guint64)buffer_size;
1523 _mmcam_dbg_log("filesize %lld Byte, ", videoinfo->filesize);
1526 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1528 return GST_PAD_PROBE_OK;
1532 static GstPadProbeReturn __mmcamcorder_video_dataprobe_audio_disable(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1534 guint64 trailer_size = 0;
1535 guint64 rec_pipe_time = 0;
1536 unsigned int remained_time = 0;
1538 GstClockTime b_time;
1540 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1541 _MMCamcorderMsgItem msg;
1542 _MMCamcorderSubContext *sc = NULL;
1543 _MMCamcorderVideoInfo *videoinfo = NULL;
1545 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1547 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1548 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1550 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1551 mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK);
1552 mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK);
1554 videoinfo = sc->info_video;
1556 b_time = GST_BUFFER_PTS(buffer);
1558 rec_pipe_time = GST_TIME_AS_MSECONDS(b_time);
1560 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1561 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1566 /* check max time */
1567 if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) {
1568 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1569 rec_pipe_time, videoinfo->max_time);
1571 if (!sc->isMaxtimePausing) {
1572 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1574 sc->isMaxtimePausing = TRUE;
1576 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1577 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1578 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1579 msg.param.recording_status.remained_time = 0;
1580 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1582 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1583 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1586 return GST_PAD_PROBE_DROP;
1589 /* calculate remained time can be recorded */
1590 if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) {
1591 remained_time = videoinfo->max_time - rec_pipe_time;
1592 } else if (videoinfo->max_size > 0) {
1593 long double max_size = (long double)videoinfo->max_size;
1594 long double current_size = (long double)(videoinfo->filesize + trailer_size);
1596 remained_time = (unsigned int)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1599 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1600 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1601 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1602 msg.param.recording_status.remained_time = remained_time;
1603 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1606 _mmcam_dbg_log("time [%" GST_TIME_FORMAT "], size [%d]",
1607 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1610 if (videoinfo->record_timestamp_ratio != _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) {
1611 guint record_motion_rate = (guint)videoinfo->record_motion_rate;
1614 _mmcam_dbg_log("record_motion_rate %d, videoinfo->record_drop_count %d",
1615 record_motion_rate, videoinfo->record_drop_count);
1618 /* drop some frame if fast motion */
1619 if (videoinfo->record_motion_rate > _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) {
1620 if (record_motion_rate != (videoinfo->record_drop_count++)) {
1622 _mmcam_dbg_warn("drop frame");
1624 return GST_PAD_PROBE_DROP;
1627 videoinfo->record_drop_count = 1;
1629 _mmcam_dbg_warn("pass frame");
1633 GST_BUFFER_PTS(buffer) = b_time * (videoinfo->record_timestamp_ratio);
1636 return GST_PAD_PROBE_OK;
1640 static GstPadProbeReturn __mmcamcorder_audioque_dataprobe(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1642 _MMCamcorderMsgItem msg;
1643 guint64 trailer_size = 0;
1644 guint64 rec_pipe_time = 0;
1645 _MMCamcorderSubContext *sc = NULL;
1646 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1647 _MMCamcorderVideoInfo *videoinfo = NULL;
1648 unsigned int remained_time = 0;
1649 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1651 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1652 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1653 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1655 mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK);
1656 mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK);
1657 mmf_return_val_if_fail(sc->element, GST_PAD_PROBE_OK);
1659 videoinfo = sc->info_video;
1661 if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_PTS(buffer))) {
1662 _mmcam_dbg_err( "Buffer timestamp is invalid, check it");
1663 return GST_PAD_PROBE_OK;
1666 rec_pipe_time = GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer));
1668 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1669 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1674 /* calculate remained time can be recorded */
1675 if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) {
1676 remained_time = videoinfo->max_time - rec_pipe_time;
1677 } else if (videoinfo->max_size > 0) {
1678 long double max_size = (long double)videoinfo->max_size;
1679 long double current_size = (long double)(videoinfo->filesize + trailer_size);
1681 remained_time = (unsigned long long)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1684 if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) {
1685 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1686 rec_pipe_time, videoinfo->max_time);
1688 if (!sc->isMaxtimePausing) {
1689 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1691 sc->isMaxtimePausing = TRUE;
1693 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1694 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1695 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1696 msg.param.recording_status.remained_time = 0;
1697 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1699 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1700 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1703 return GST_PAD_PROBE_DROP;
1706 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1707 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1708 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1709 msg.param.recording_status.remained_time = remained_time;
1710 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1713 _mmcam_dbg_log("audio data probe :: time [%" GST_TIME_FORMAT "], size [%lld KB]",
1714 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1717 return GST_PAD_PROBE_OK;
1721 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_audio_mute(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1723 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1724 double volume = 0.0;
1727 int err = MM_ERROR_UNKNOWN;
1728 char *err_name = NULL;
1729 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1732 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1733 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_DROP);
1735 /*_mmcam_dbg_log("AUDIO SRC time stamp : [%" GST_TIME_FORMAT "] \n", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1736 err = mm_camcorder_get_attributes((MMHandleType)hcamcorder, &err_name,
1737 MMCAM_AUDIO_VOLUME, &volume,
1738 MMCAM_AUDIO_FORMAT, &format,
1739 MMCAM_AUDIO_CHANNEL, &channel,
1741 if (err != MM_ERROR_NONE) {
1742 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, err);
1743 SAFE_FREE(err_name);
1747 memset(&mapinfo, 0x0, sizeof(GstMapInfo));
1749 gst_buffer_map(buffer, &mapinfo, GST_MAP_READWRITE);
1751 /* Set audio stream NULL */
1752 if (volume == 0.0) {
1753 memset(mapinfo.data, 0, mapinfo.size);
1756 /* CALL audio stream callback */
1757 if (hcamcorder->astream_cb && buffer && mapinfo.data && mapinfo.size > 0) {
1758 MMCamcorderAudioStreamDataType stream;
1760 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) < MM_CAMCORDER_STATE_PREPARE) {
1761 _mmcam_dbg_warn("Not ready for stream callback");
1762 gst_buffer_unmap(buffer, &mapinfo);
1763 return GST_PAD_PROBE_OK;
1766 /*_mmcam_dbg_log("Call video steramCb, data[%p], Width[%d],Height[%d], Format[%d]",
1767 GST_BUFFER_DATA(buffer), width, height, format);*/
1769 stream.data = (void *)mapinfo.data;
1770 stream.format = format;
1771 stream.channel = channel;
1772 stream.length = mapinfo.size;
1773 stream.timestamp = (unsigned int)(GST_BUFFER_PTS(buffer)/1000000); /* nano -> milli second */
1775 _MMCAMCORDER_LOCK_ASTREAM_CALLBACK(hcamcorder);
1777 if (hcamcorder->astream_cb) {
1778 hcamcorder->astream_cb(&stream, hcamcorder->astream_cb_param);
1781 _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK(hcamcorder);
1784 gst_buffer_unmap(buffer, &mapinfo);
1785 return GST_PAD_PROBE_OK;
1789 static gboolean __mmcamcorder_add_metadata(MMHandleType handle, int fileformat)
1791 gboolean bret = FALSE;
1793 switch (fileformat) {
1794 case MM_FILE_FORMAT_3GP:
1795 case MM_FILE_FORMAT_MP4:
1796 bret = __mmcamcorder_add_metadata_mp4(handle);
1799 _mmcam_dbg_warn("Unsupported fileformat to insert location info (%d)", fileformat);
1807 static gboolean __mmcamcorder_add_metadata_mp4(MMHandleType handle)
1811 guint64 udta_size = 0;
1812 gint64 current_pos = 0;
1813 gint64 moov_pos = 0;
1814 gint64 udta_pos = 0;
1815 gdouble longitude = 0;
1816 gdouble latitude = 0;
1817 gdouble altitude = 0;
1819 int orientation = 0;
1821 char *err_name = NULL;
1822 char err_msg[MAX_ERROR_MESSAGE_LEN] = {'\0',};
1823 _MMCamcorderLocationInfo location_info = {0,0,0};
1824 _MMCamcorderLocationInfo geo_info = {0,0,0};
1826 _MMCamcorderVideoInfo *info = NULL;
1827 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1828 _MMCamcorderSubContext *sc = NULL;
1830 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1831 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1833 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1834 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1838 info = sc->info_video;
1840 f = fopen64(info->filename, "rb+");
1842 strerror_r(errno, err_msg, MAX_ERROR_MESSAGE_LEN);
1843 _mmcam_dbg_err("file open failed [%s]", err_msg);
1847 mm_camcorder_get_attributes(handle, &err_name,
1848 MMCAM_TAG_LATITUDE, &latitude,
1849 MMCAM_TAG_LONGITUDE, &longitude,
1850 MMCAM_TAG_ALTITUDE, &altitude,
1851 MMCAM_TAG_VIDEO_ORIENTATION, &orientation,
1852 MMCAM_TAG_GPS_ENABLE, &gps_enable,
1855 _mmcam_dbg_warn("Get tag attrs fail. (%s:%x)", err_name, err);
1856 SAFE_FREE (err_name);
1859 location_info.longitude = _mmcamcorder_double_to_fix(longitude);
1860 location_info.latitude = _mmcamcorder_double_to_fix(latitude);
1861 location_info.altitude = _mmcamcorder_double_to_fix(altitude);
1862 geo_info.longitude = longitude *10000;
1863 geo_info.latitude = latitude *10000;
1864 geo_info.altitude = altitude *10000;
1865 /* find udta container.
1866 if, there are udta container, write loci box after that
1867 else, make udta container and write loci box. */
1868 if (_mmcamcorder_find_fourcc(f, MMCAM_FOURCC('u','d','t','a'), TRUE)) {
1871 _mmcam_dbg_log("find udta container");
1874 if (fseek(f, -8L, SEEK_CUR) != 0) {
1878 udta_pos = ftello(f);
1883 nread = fread(&buf, sizeof(char), sizeof(buf), f);
1885 _mmcam_dbg_log("recorded file fread %d", nread);
1887 udta_size = _mmcamcorder_get_container_size(buf);
1889 /* goto end of udta and write 'loci' box */
1890 if (fseek(f, (udta_size-4L), SEEK_CUR) != 0) {
1895 if (!_mmcamcorder_write_loci(f, location_info)) {
1896 _mmcam_dbg_err("failed to write loci");
1900 if (!_mmcamcorder_write_geodata(f, geo_info)) {
1901 _mmcam_dbg_err("failed to write geodata");
1906 current_pos = ftello(f);
1907 if (current_pos < 0) {
1911 if (!_mmcamcorder_update_size(f, udta_pos, current_pos)) {
1915 _mmcam_dbg_log("No udta container");
1916 if (fseek(f, 0, SEEK_END) != 0) {
1920 if (!_mmcamcorder_write_udta(f, gps_enable, location_info, geo_info)) {
1921 _mmcam_dbg_err("failed to write udta");
1926 /* find moov container.
1927 update moov container size. */
1928 if((current_pos = ftello(f))<0)
1931 if (_mmcamcorder_find_tag(f, MMCAM_FOURCC('m','o','o','v'), TRUE)) {
1932 gint64 internal_pos = ftello(f);
1934 _mmcam_dbg_log("found moov container");
1936 if (internal_pos < 0) {
1940 if (fseek(f, -8L, SEEK_CUR) !=0) {
1944 moov_pos = ftello(f);
1949 if (!_mmcamcorder_update_size(f, moov_pos, current_pos)) {
1953 /* add orientation info */
1954 if (fseeko(f, internal_pos, SEEK_SET) < 0) {
1955 _mmcam_dbg_err("fseek failed : errno %d", errno);
1959 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t','r','a','k'), FALSE)) {
1960 _mmcam_dbg_err("failed to find [trak] tag");
1964 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t','k','h','d'), FALSE)) {
1965 _mmcam_dbg_err("failed to find [tkhd] tag");
1969 _mmcam_dbg_log("found [tkhd] tag");
1971 /* seek to start position of composition matrix */
1972 fseek(f, _OFFSET_COMPOSITION_MATRIX, SEEK_CUR);
1974 /* update composition matrix for orientation */
1975 _mmcamcorder_update_composition_matrix(f, orientation);
1977 _mmcam_dbg_err("No 'moov' container");
1989 _mmcam_dbg_err("ftell() returns negative value.");
1995 int _mmcamcorder_connect_video_stream_cb_signal(MMHandleType handle)
1997 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1998 _MMCamcorderSubContext *sc = NULL;
2000 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2002 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
2003 mmf_return_val_if_fail(sc && sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2005 /* check video source element */
2006 if (sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst) {
2007 _mmcam_dbg_warn("connect video stream cb signal to _MMCAMCORDER_VIDEOSRC_SRC");
2008 MMCAMCORDER_SIGNAL_CONNECT(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst,
2009 _MMCAMCORDER_HANDLER_VIDEOREC, "video-stream-cb",
2010 G_CALLBACK(__mmcamcorder_video_stream_cb),
2012 return MM_ERROR_NONE;
2014 _mmcam_dbg_err("videosrc element is not created yet");
2015 return MM_ERROR_CAMCORDER_NOT_INITIALIZED;
2020 int _mmcamcorder_video_prepare_record(MMHandleType handle)
2022 int ret = MM_ERROR_NONE;
2024 _MMCamcorderVideoInfo *info = NULL;
2025 _MMCamcorderSubContext *sc = NULL;
2026 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
2028 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2030 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
2031 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2032 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2034 info = sc->info_video;
2036 _mmcam_dbg_warn("start");
2038 /* create encoding pipeline */
2039 ret =_mmcamcorder_create_recorder_pipeline((MMHandleType)hcamcorder);
2040 if (ret != MM_ERROR_NONE) {
2041 goto _ERR_PREPARE_RECORD;
2044 if (info->filename == NULL) {
2045 char *temp_filename = NULL;
2048 mm_camcorder_get_attributes(handle, NULL,
2049 MMCAM_TARGET_FILENAME, &temp_filename, &size,
2051 if (temp_filename) {
2052 info->filename = strdup(temp_filename);
2055 if (!info->filename) {
2056 _mmcam_dbg_err("strdup[src:%p] was failed", temp_filename);
2057 goto _ERR_PREPARE_RECORD;
2061 _mmcam_dbg_log("Record file name [%s]", info->filename);
2063 MMCAMCORDER_G_OBJECT_SET_POINTER(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename);
2064 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", 0);
2066 /* Adjust display FPS */
2067 sc->display_interval = 0;
2068 sc->previous_slot_time = 0;
2070 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PAUSED);
2071 if (ret != MM_ERROR_NONE) {
2072 goto _ERR_PREPARE_RECORD;
2075 _mmcam_dbg_warn("done");
2079 _ERR_PREPARE_RECORD:
2080 /* Remove recorder pipeline and recording file which size maybe zero */
2081 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
2082 if (info && info->filename) {
2083 _mmcam_dbg_log("file delete(%s)", info->filename);
2084 unlink(info->filename);