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
34 #define MAX_ERROR_MESSAGE_LEN 128
36 /*---------------------------------------------------------------------------------------
37 | LOCAL VARIABLE DEFINITIONS for internal |
38 ---------------------------------------------------------------------------------------*/
39 #define _MMCAMCORDER_MINIMUM_FRAME 5
40 #define _MMCAMCORDER_RETRIAL_COUNT 10
41 #define _MMCAMCORDER_FRAME_WAIT_TIME 200000 /* ms */
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(handle, 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;
471 GstCameraControl *CameraControl = NULL;
472 GstCameraControlChannel *CameraControlChannel = NULL;
473 const GList *controls = NULL;
474 const GList *item = NULL;
477 GstElement *pipeline = NULL;
479 _MMCamcorderVideoInfo *info = NULL;
480 _MMCamcorderSubContext *sc = NULL;
481 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
483 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
485 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
486 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
487 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
488 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
490 info = sc->info_video;
492 _mmcam_dbg_log("Command(%d)", command);
494 pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
497 case _MMCamcorder_CMD_RECORD:
499 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) != MM_CAMCORDER_STATE_PAUSED) {
505 int ret_free_space = 0;
506 char *dir_name = NULL;
507 guint64 free_space = 0;
508 int file_system_type = 0;
511 _mmcam_dbg_log("Record Start - dual stream %d", info->support_dual_stream);
513 /* init record_dual_stream */
514 info->record_dual_stream = FALSE;
516 ret = mm_camcorder_get_attributes(handle, &err_name,
517 MMCAM_CAMERA_FPS, &fps,
518 MMCAM_CAMERA_WIDTH, &(info->preview_width),
519 MMCAM_CAMERA_HEIGHT, &(info->preview_height),
520 MMCAM_VIDEO_WIDTH, &(info->video_width),
521 MMCAM_VIDEO_HEIGHT, &(info->video_height),
522 MMCAM_FILE_FORMAT, &fileformat,
523 MMCAM_TARGET_FILENAME, &temp_filename, &size,
524 MMCAM_TARGET_MAX_SIZE, &imax_size,
525 MMCAM_TARGET_TIME_LIMIT, &imax_time,
526 MMCAM_FILE_FORMAT, &(info->fileformat),
527 MMCAM_CAMERA_RECORDING_MOTION_RATE, &motion_rate,
529 if (ret != MM_ERROR_NONE) {
530 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, ret);
531 SAFE_FREE (err_name);
532 goto _ERR_CAMCORDER_VIDEO_COMMAND;
535 if (temp_filename == NULL) {
536 _mmcam_dbg_err("filename is not set");
537 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
538 goto _ERR_CAMCORDER_VIDEO_COMMAND;
542 if (imax_size <= 0) {
543 info->max_size = 0; /* do not check */
545 info->max_size = ((guint64)imax_size) << 10; /* to byte */
549 if (imax_time <= 0) {
550 info->max_time = 0; /* do not check */
552 info->max_time = ((guint64)imax_time) * 1000; /* to millisecond */
555 dir_name = g_path_get_dirname(temp_filename);
557 ret_free_space = _mmcamcorder_get_freespace(dir_name, hcamcorder->root_directory, &free_space);
559 _mmcam_dbg_warn("current space - %s [%" G_GUINT64_FORMAT "]", dir_name, free_space);
561 if (_mmcamcorder_get_file_system_type(dir_name, &file_system_type) == 0) {
562 /* MSDOS_SUPER_MAGIC : 0x4d44 */
563 if (file_system_type == MSDOS_SUPER_MAGIC &&
564 (info->max_size == 0 || info->max_size > FAT32_FILE_SYSTEM_MAX_SIZE)) {
565 _mmcam_dbg_warn("FAT32 and too large max[%"G_GUINT64_FORMAT"], set max as %"G_GUINT64_FORMAT,
566 info->max_size, FAT32_FILE_SYSTEM_MAX_SIZE);
567 info->max_size = FAT32_FILE_SYSTEM_MAX_SIZE;
569 _mmcam_dbg_warn("file system 0x%x, max size %"G_GUINT64_FORMAT,
570 file_system_type, info->max_size);
573 _mmcam_dbg_warn("_mmcamcorder_get_file_system_type failed");
579 _mmcam_dbg_err("failed to get directory name");
583 if ((ret_free_space == -1) || free_space <= (_MMCAMCORDER_MINIMUM_SPACE<<1)) {
584 _mmcam_dbg_err("OUT of STORAGE [ret_free_space:%d or free space [%" G_GUINT64_FORMAT "] is smaller than [%d]",
585 ret_free_space, free_space, (_MMCAMCORDER_MINIMUM_SPACE<<1));
586 return MM_ERROR_OUT_OF_STORAGE;
589 pthread_mutex_lock(&(hcamcorder->task_thread_lock));
590 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst == NULL &&
591 hcamcorder->task_thread_state == _MMCAMCORDER_TASK_THREAD_STATE_NONE) {
592 /* Play record start sound */
593 _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_FILEPATH_REC_START_SND, FALSE);
595 pthread_mutex_unlock(&(hcamcorder->task_thread_lock));
597 _mmcam_dbg_warn("video size [%dx%d]", info->video_width, info->video_height);
599 if (info->video_width == 0 || info->video_height == 0) {
600 _mmcam_dbg_warn("video size is invalid [%dx%d] use preview size [%dx%d]",
601 info->video_width, info->video_height, info->preview_width, info->preview_height);
602 info->video_width = info->preview_width;
603 info->video_height = info->preview_height;
606 if (info->support_dual_stream) {
607 _mmcam_dbg_warn("DUAL STREAM MODE");
609 info->record_dual_stream = TRUE;
611 /* No need to restart preview */
612 info->restart_preview = FALSE;
614 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "video-width", info->video_width);
615 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "video-height", info->video_height);
616 } else if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
617 info->preview_width == info->video_width &&
618 info->preview_height == info->video_height) {
619 _mmcam_dbg_log("H264 preview mode and same resolution");
621 /* No need to restart preview */
622 info->restart_preview = FALSE;
624 /* always need to restart preview */
625 info->restart_preview = TRUE;
628 /* set recording hint */
629 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", TRUE);
631 if (info->restart_preview) {
632 /* stop preview and set new size */
633 _mmcam_dbg_log("restart preview");
635 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
636 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
638 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
640 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
641 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
643 if (ret != MM_ERROR_NONE) {
644 goto _ERR_CAMCORDER_VIDEO_COMMAND;
647 if (!_mmcamcorder_set_camera_resolution(handle, info->video_width, info->video_height)) {
648 ret = MM_ERROR_CAMCORDER_INTERNAL;
649 goto _ERR_CAMCORDER_VIDEO_COMMAND;
652 /* Start preview again with new setting */
653 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
654 if (ret != MM_ERROR_NONE) {
655 goto _ERR_CAMCORDER_VIDEO_COMMAND;
658 if (motion_rate < 1.0) {
659 _mmcam_dbg_warn("wait for stabilization of frame");
663 _mmcam_dbg_log("no need to restart preview");
666 _mmcamcorder_conf_get_value_int(handle, hcamcorder->conf_main,
667 CONFIGURE_CATEGORY_MAIN_RECORD,
671 _mmcamcorder_conf_get_value_int(handle, hcamcorder->conf_main,
672 CONFIGURE_CATEGORY_MAIN_RECORD,
673 "PassFirstVideoFrame",
674 &(sc->pass_first_vframe));
676 _mmcam_dbg_log("Drop video frame count[%d], Pass fisrt video frame count[%d]",
677 sc->drop_vframe, sc->pass_first_vframe);
679 info->record_drop_count = (guint)motion_rate;
680 info->record_motion_rate = motion_rate;
681 if (sc->is_modified_rate) {
682 info->record_timestamp_ratio = (_MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE/motion_rate);
684 info->record_timestamp_ratio = _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE;
687 _mmcam_dbg_warn("recording fps %d, motion rate %f, timestamp_ratio %f",
688 fps, info->record_motion_rate, info->record_timestamp_ratio);
690 /* set push buffer flag */
691 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_INIT;
692 info->base_video_ts = 0;
694 /* connect video stream cb signal */
695 /*130826 Connect video stream cb for handling fast record frame cb*/
696 if (info->record_dual_stream) {
697 if (_mmcamcorder_connect_video_stream_cb_signal((MMHandleType)hcamcorder) != MM_ERROR_NONE) {
698 goto _ERR_CAMCORDER_VIDEO_COMMAND;
702 /* start video stream */
703 if (info->record_dual_stream) {
704 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
706 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
708 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_START");
709 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_START);
711 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
713 _mmcam_dbg_err("could not get camera control");
717 /* check pre-created encode pipeline */
718 pthread_mutex_lock(&(hcamcorder->task_thread_lock));
719 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst == NULL &&
720 hcamcorder->task_thread_state == _MMCAMCORDER_TASK_THREAD_STATE_NONE) {
721 /* create encoding pipeline */
722 ret =_mmcamcorder_video_prepare_record((MMHandleType)hcamcorder);
723 if (ret != MM_ERROR_NONE) {
724 pthread_mutex_unlock(&(hcamcorder->task_thread_lock));
725 goto _ERR_CAMCORDER_VIDEO_COMMAND;
728 pthread_mutex_unlock(&(hcamcorder->task_thread_lock));
730 /* check recording start sound */
731 _mmcamcorder_sound_solo_play_wait(handle);
733 /**< To fix video recording hanging
734 1. use gst_element_set_start_time() instead of gst_pipeline_set_new_stream_time()
735 2. Set (GstClockTime)1 instead of (GstClockTime)0. Because of strict check in gstreamer 0.25,
736 basetime wouldn't change if you set (GstClockTime)0.
737 3. Move set start time position below PAUSED of pipeline.
739 //gst_element_set_start_time(GST_ELEMENT(sc->element[_MMCAMCORDER_MAIN_PIPE].gst), (GstClockTime)1);
740 //gst_element_set_start_time(GST_ELEMENT(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), (GstClockTime)1);
742 info->video_frame_count = 0;
743 info->is_firstframe = TRUE;
744 info->audio_frame_count = 0;
746 sc->ferror_send = FALSE;
747 sc->ferror_count = 0;
748 hcamcorder->error_occurs = FALSE;
749 sc->bget_eos = FALSE;
751 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PLAYING);
752 if (ret != MM_ERROR_NONE) {
753 /* stop video stream */
754 if (info->record_dual_stream) {
755 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
757 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
759 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
760 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
762 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
764 _mmcam_dbg_err("failed to get camera control");
768 /* Remove recorder pipeline and recording file which size maybe zero */
769 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
770 if (info->filename) {
771 _mmcam_dbg_log("file delete(%s)", info->filename);
772 unlink(info->filename);
774 goto _ERR_CAMCORDER_VIDEO_COMMAND;
777 /*set the camera control to create the GOP so that video record will get a new key frame*/
778 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
779 GST_IS_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst)) {
780 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
781 controls = gst_camera_control_list_channels(CameraControl);
782 if (controls != NULL) {
783 for (item = controls ; item && item->data ; item = item->next) {
784 CameraControlChannel = item->data;
785 _mmcam_dbg_log("CameraControlChannel->label %s", CameraControlChannel->label);
786 if (!strcmp(CameraControlChannel->label, "new-gop")) {
787 //gst_camera_control_set_value(CameraControl, CameraControlChannel, 1);
793 _mmcam_dbg_warn("failed to find new-gop control channel");
797 _mmcam_dbg_warn("Can't cast Video source into camera control or not H264 prevew format[%d]",
798 sc->info_image->preview_format);
803 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
804 GST_IS_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst)) {
805 /* generate and I-frame on resuming */
806 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
807 controls = gst_camera_control_list_channels(CameraControl);
808 if (controls != NULL) {
809 for (item = controls ; item && item->data ; item = item->next) {
810 CameraControlChannel = item->data;
811 _mmcam_dbg_log("CameraControlChannel->label %s", CameraControlChannel->label);
812 if (!strcmp(CameraControlChannel->label, "new-gop")) {
813 //gst_camera_control_set_value(CameraControl, CameraControlChannel, 1);
819 _mmcam_dbg_warn("failed to find new-gop control channel");
824 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", FALSE);
826 _mmcam_dbg_log("Object property settings done");
830 case _MMCamcorder_CMD_PAUSE:
832 if (info->b_commiting) {
833 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
834 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
837 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
838 if (sc->audio_disable) {
839 /* check only video frame */
840 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
842 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
843 _mmcam_dbg_err("Pause fail, frame count %llu",
844 info->video_frame_count);
845 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
847 _mmcam_dbg_warn("Waiting for enough video frame, retrial[%d], frame %llu",
848 count, info->video_frame_count);
851 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
853 /* check both of video and audio frame */
854 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
856 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
857 _mmcam_dbg_err("Pause fail, frame count VIDEO[%llu], AUDIO [%llu]",
858 info->video_frame_count, info->audio_frame_count);
859 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
861 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%llu], AUDIO [%llu]",
862 count, info->video_frame_count, info->audio_frame_count);
865 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
869 /* block encodebin */
870 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", TRUE);
873 case _MMCamcorder_CMD_CANCEL:
875 if (info->b_commiting) {
876 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
877 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
880 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
882 if (hcamcorder->capture_in_recording == FALSE) {
884 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
885 _mmcam_dbg_err("Failed to Wait capture data");
886 hcamcorder->capture_in_recording = FALSE;
889 _mmcam_dbg_warn("Waiting for capture data - retrial [%d]", count);
892 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
895 /* block push buffer */
896 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP;
898 ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
899 if (ret != MM_ERROR_NONE) {
900 goto _ERR_CAMCORDER_VIDEO_COMMAND;
903 /* set recording hint */
904 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", FALSE);
906 /* stop video stream */
907 if (info->record_dual_stream) {
908 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
910 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
912 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
913 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
915 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
917 _mmcam_dbg_err("failed to get camera control");
921 if (info->restart_preview) {
922 /* restart preview */
923 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
924 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
926 ret =_mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
928 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
929 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
930 if (ret != MM_ERROR_NONE) {
931 goto _ERR_CAMCORDER_VIDEO_COMMAND;
934 /* reset restart_preview for inset window layout */
935 info->restart_preview = FALSE;
937 if (!_mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height)) {
938 ret = MM_ERROR_CAMCORDER_INTERNAL;
939 goto _ERR_CAMCORDER_VIDEO_COMMAND;
942 ret =_mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
943 if (ret != MM_ERROR_NONE) {
944 goto _ERR_CAMCORDER_VIDEO_COMMAND;
948 /* remove target file */
949 if (info->filename) {
950 _mmcam_dbg_log("file delete(%s)", info->filename);
951 unlink(info->filename);
954 sc->isMaxsizePausing = FALSE;
955 sc->isMaxtimePausing = FALSE;
957 sc->display_interval = 0;
958 sc->previous_slot_time = 0;
959 info->video_frame_count = 0;
960 info->audio_frame_count = 0;
962 hcamcorder->capture_in_recording = FALSE;
965 case _MMCamcorder_CMD_COMMIT:
967 if (info->b_commiting) {
968 _mmcam_dbg_err("now on commiting previous file!!(command : %d)", command);
969 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
971 _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT : start");
972 info->b_commiting = TRUE;
973 sc->bget_eos = FALSE;
976 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
977 if (sc->audio_disable) {
978 /* check only video frame */
979 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME &&
980 hcamcorder->capture_in_recording == FALSE) {
982 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
983 _mmcam_dbg_err("Commit fail, frame count is %llu, capturing %d",
984 info->video_frame_count, hcamcorder->capture_in_recording);
986 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
987 _mmcam_dbg_warn("video frames are enough. keep going...");
989 info->b_commiting = FALSE;
990 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
993 _mmcam_dbg_warn("Waiting for enough video frame, retrial [%d], frame %llu, capturing %d",
994 count, info->video_frame_count, hcamcorder->capture_in_recording);
997 /* check both of video and audio frame */
998 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME &&
999 info->audio_frame_count &&
1000 hcamcorder->capture_in_recording == FALSE) {
1002 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
1003 _mmcam_dbg_err("Commit fail, VIDEO[%llu], AUDIO [%llu], capturing %d",
1004 info->video_frame_count, info->audio_frame_count, hcamcorder->capture_in_recording);
1006 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
1007 _mmcam_dbg_warn("video/audio frames are enough. keep going...");
1009 info->b_commiting = FALSE;
1010 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
1013 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
1015 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%llu], AUDIO [%llu], capturing %d",
1016 count, info->video_frame_count, info->audio_frame_count, hcamcorder->capture_in_recording);
1020 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
1023 /* block push buffer */
1024 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP;
1025 _mmcam_dbg_log("block push buffer to appsrc");
1027 if (sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst != NULL) {
1028 if (gst_element_send_event(sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, gst_event_new_eos())) {
1029 _mmcam_dbg_warn("VIDEO: send eos to appsrc done");
1031 _mmcam_dbg_err("VIDEO: send EOS failed");
1032 info->b_commiting = FALSE;
1033 ret = MM_ERROR_CAMCORDER_INTERNAL;
1034 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1037 _mmcam_dbg_err("No video stream source");
1038 info->b_commiting = FALSE;
1039 ret = MM_ERROR_CAMCORDER_INTERNAL;
1040 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1043 if (sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst != NULL) {
1044 if (gst_element_send_event(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst, gst_event_new_eos())) {
1045 _mmcam_dbg_warn("AUDIO: send eos to audiosrc done");
1047 _mmcam_dbg_err("AUDIO: send EOS failed");
1048 info->b_commiting = FALSE;
1049 ret = MM_ERROR_CAMCORDER_INTERNAL;
1050 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1053 _mmcam_dbg_log("No audio stream");
1057 sc->display_interval = 0;
1058 sc->previous_slot_time = 0;
1061 _mmcam_dbg_log("Start to wait EOS");
1062 ret =_mmcamcorder_get_eos_message(handle);
1063 if (ret != MM_ERROR_NONE) {
1064 info->b_commiting = FALSE;
1065 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1069 hcamcorder->capture_in_recording = FALSE;
1073 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
1074 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1077 return MM_ERROR_NONE;
1079 _ERR_CAMCORDER_VIDEO_COMMAND:
1080 if (command == _MMCamcorder_CMD_RECORD) {
1081 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
1088 int _mmcamcorder_video_handle_eos(MMHandleType handle)
1090 int ret = MM_ERROR_NONE;
1092 guint64 file_size = 0;
1094 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1095 _MMCamcorderSubContext *sc = NULL;
1096 _MMCamcorderVideoInfo *info = NULL;
1097 _MMCamcorderMsgItem msg;
1098 MMCamRecordingReport *report = NULL;
1100 mmf_return_val_if_fail(hcamcorder, FALSE);
1102 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1103 mmf_return_val_if_fail(sc, FALSE);
1104 mmf_return_val_if_fail(sc->info_video, FALSE);
1106 info = sc->info_video;
1110 if (hcamcorder->state_change_by_system != _MMCAMCORDER_STATE_CHANGE_BY_ASM) {
1111 /* Play record stop sound */
1112 _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_FILEPATH_REC_STOP_SND, FALSE);
1114 _mmcam_dbg_warn("Play stop sound through pulseaudio");
1116 #ifdef _MMCAMCORDER_UPLOAD_SAMPLE
1117 _mmcamcorder_sound_init(handle, _MMCAMCORDER_FILEPATH_REC_STOP_SND);
1118 #else /* _MMCAMCORDER_UPLOAD_SAMPLE */
1119 _mmcamcorder_sound_init(handle);
1120 #endif /* _MMCAMCORDER_UPLOAD_SAMPLE */
1122 _mmcamcorder_sound_play((MMHandleType)hcamcorder, _MMCAMCORDER_SAMPLE_SOUND_NAME_REC_STOP, TRUE);
1124 _mmcamcorder_sound_finalize(handle);
1127 /* remove blocking part */
1128 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
1130 mm_camcorder_get_attributes(handle, NULL,
1131 MMCAM_RECORDER_TAG_ENABLE, &enabletag,
1134 ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
1135 if (ret != MM_ERROR_NONE) {
1136 _mmcam_dbg_warn("_MMCamcorder_CMD_COMMIT:__mmcamcorder_remove_recorder_pipeline failed. error[%x]", ret);
1139 /* set recording hint */
1140 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", FALSE);
1142 /* stop video stream */
1143 if (info->record_dual_stream) {
1144 GstCameraControl *control = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
1146 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
1148 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
1149 gst_camera_control_set_record_command(control, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
1151 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
1153 _mmcam_dbg_err("failed to get camera control");
1157 if (enabletag && !(sc->ferror_send)) {
1158 ret = __mmcamcorder_add_metadata((MMHandleType)hcamcorder, info->fileformat);
1160 _mmcam_dbg_log("Writing location information SUCCEEDED !!");
1162 _mmcam_dbg_err("Writing location information FAILED !!");
1166 /* Check file size */
1167 if (info->max_size > 0) {
1168 _mmcamcorder_get_file_size(info->filename, &file_size);
1169 _mmcam_dbg_log("MAX size %lld byte - created filesize %lld byte",
1170 info->max_size, file_size);
1172 if (file_size > info->max_size) {
1173 _MMCamcorderMsgItem message;
1174 _mmcam_dbg_err("File size is greater than max size !!");
1175 message.id = MM_MESSAGE_CAMCORDER_ERROR;
1176 message.param.code = MM_ERROR_CAMCORDER_FILE_SIZE_OVER;
1177 _mmcamcorder_send_message((MMHandleType)hcamcorder, &message);
1181 if (info->restart_preview) {
1183 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
1184 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
1186 _mmcam_dbg_log("Set state of pipeline as READY");
1187 ret = _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_READY);
1190 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
1191 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
1192 if (ret != MM_ERROR_NONE) {
1193 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1194 msg.param.code = ret;
1195 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1196 _mmcam_dbg_err("Failed to set state READY[%x]", ret);
1199 /* reset restart_preview for inset window layout */
1200 info->restart_preview = FALSE;
1202 /* recover preview size */
1203 _mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height);
1205 ret =_mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_PLAYING);
1206 /* Do not return when error is occurred.
1207 Recording file was created successfully, but starting pipeline failed */
1208 if (ret != MM_ERROR_NONE) {
1209 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1210 msg.param.code = ret;
1211 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1212 _mmcam_dbg_err("Failed to set state PLAYING[%x]", ret);
1215 _mmcam_dbg_log("No need to restart preview");
1218 /* Send recording report to application */
1219 msg.id = MM_MESSAGE_CAMCORDER_VIDEO_CAPTURED;
1220 report = (MMCamRecordingReport *)malloc(sizeof(MMCamRecordingReport));
1222 _mmcam_dbg_err("Recording report fail(%s). Out of memory.", info->filename);
1224 report->recording_filename = strdup(info->filename);
1225 msg.param.data= report;
1227 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1231 sc->pipeline_time = 0;
1233 sc->isMaxsizePausing = FALSE; /*In async function, this variable should set in callback function. */
1234 sc->isMaxtimePausing = FALSE;
1235 hcamcorder->error_occurs = FALSE;
1237 info->video_frame_count = 0;
1238 info->audio_frame_count = 0;
1240 info->b_commiting = FALSE;
1242 if (hcamcorder->state_change_by_system != _MMCAMCORDER_STATE_CHANGE_BY_ASM) {
1243 /* check recording stop sound */
1244 _mmcamcorder_sound_solo_play_wait(handle);
1247 _mmcam_dbg_err("_MMCamcorder_CMD_COMMIT : end");
1254 * This function is record video data probing function.
1255 * If this function is linked with certain pad by gst_pad_add_buffer_probe(),
1256 * this function will be called when data stream pass through the pad.
1258 * @param[in] pad probing pad which calls this function.
1259 * @param[in] buffer buffer which contains stream data.
1260 * @param[in] u_data user data.
1261 * @return This function returns true on success, or false value with error
1265 static GstPadProbeReturn __mmcamcorder_eventprobe_monitor(GstPad *pad, GstPadProbeInfo *info, gpointer u_data){
1266 GstEvent *event = GST_PAD_PROBE_INFO_EVENT(info);
1267 switch (GST_EVENT_TYPE(event)) {
1268 case GST_EVENT_UNKNOWN:
1269 /* upstream events */
1271 case GST_EVENT_SEEK:
1272 case GST_EVENT_NAVIGATION:
1273 case GST_EVENT_LATENCY:
1274 /* downstream serialized events */
1275 case GST_EVENT_SEGMENT :
1277 case GST_EVENT_BUFFERSIZE:
1278 _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1281 _mmcam_dbg_warn("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1283 /* bidirectional events */
1284 case GST_EVENT_FLUSH_START:
1285 case GST_EVENT_FLUSH_STOP:
1286 _mmcam_dbg_err("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1289 _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1293 return GST_PAD_PROBE_OK;
1297 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_check(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1299 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1300 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1302 _MMCamcorderSubContext *sc = NULL;
1303 _MMCamcorderVideoInfo *videoinfo = NULL;
1304 _MMCamcorderMsgItem msg;
1305 guint64 buffer_size = 0;
1306 guint64 trailer_size = 0;
1308 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1309 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1310 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1312 mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK);
1313 videoinfo = sc->info_video;
1315 /* get buffer size */
1316 if (!gst_buffer_map(buffer, &mapinfo, GST_MAP_READ)) {
1317 _mmcam_dbg_warn("map failed : buffer %p", buffer);
1318 return GST_PAD_PROBE_OK;
1321 buffer_size = mapinfo.size;
1322 gst_buffer_unmap(buffer, &mapinfo);
1324 /*_mmcam_dbg_err("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1326 pthread_mutex_lock(&(videoinfo->size_check_lock));
1328 if (videoinfo->audio_frame_count == 0) {
1329 videoinfo->filesize += buffer_size;
1330 videoinfo->audio_frame_count++;
1331 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1332 return GST_PAD_PROBE_OK;
1335 if (sc->ferror_send || sc->isMaxsizePausing) {
1336 _mmcam_dbg_warn("Recording is paused, drop frames");
1337 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1338 return GST_PAD_PROBE_DROP;
1341 /* get trailer size */
1342 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1343 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1348 /* check max size of recorded file */
1349 if (videoinfo->max_size > 0 &&
1350 videoinfo->max_size < videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) {
1351 GstState pipeline_state = GST_STATE_VOID_PENDING;
1352 GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1353 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1354 _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \
1355 " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]",
1356 videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size);
1358 if (!sc->isMaxsizePausing) {
1359 sc->isMaxsizePausing = TRUE;
1360 gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ;
1361 if (pipeline_state == GST_STATE_PLAYING) {
1362 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1365 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1366 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1369 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1374 videoinfo->filesize += buffer_size;
1375 videoinfo->audio_frame_count++;
1377 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1379 return GST_PAD_PROBE_OK;
1383 static GstPadProbeReturn __mmcamcorder_video_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1388 guint64 free_space = 0;
1389 guint64 buffer_size = 0;
1390 guint64 trailer_size = 0;
1391 guint64 queued_buffer = 0;
1392 char *dir_name = NULL;
1393 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1396 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1397 _MMCamcorderMsgItem msg;
1398 _MMCamcorderSubContext *sc = NULL;
1399 _MMCamcorderVideoInfo *videoinfo = NULL;
1401 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1402 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1404 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1405 mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK);
1406 videoinfo = sc->info_video;
1408 /*_mmcam_dbg_log("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1409 if (sc->ferror_send) {
1410 _mmcam_dbg_warn("file write error, drop frames");
1411 return GST_PAD_PROBE_DROP;
1414 gst_buffer_map(buffer, &mapinfo, GST_MAP_READ);
1415 buffer_size = mapinfo.size;
1416 gst_buffer_unmap(buffer, &mapinfo);
1418 videoinfo->video_frame_count++;
1419 if (videoinfo->video_frame_count <= (guint64)_MMCAMCORDER_MINIMUM_FRAME) {
1420 /* _mmcam_dbg_log("Pass minimum frame: info->video_frame_count: %" G_GUINT64_FORMAT " ",
1421 info->video_frame_count); */
1422 pthread_mutex_lock(&(videoinfo->size_check_lock));
1423 videoinfo->filesize += buffer_size;
1424 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1425 return GST_PAD_PROBE_OK;
1428 /* get trailer size */
1429 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1430 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1435 dir_name = g_path_get_dirname(videoinfo->filename);
1437 ret = _mmcamcorder_get_freespace(dir_name, hcamcorder->root_directory, &free_space);
1441 _mmcam_dbg_err("failed to get dir name from [%s]", videoinfo->filename);
1445 /*_mmcam_dbg_log("check free space for recording");*/
1448 case -2: /* file not exist */
1449 case -1: /* failed to get free space */
1450 _mmcam_dbg_err("Error occured. [%d]", ret);
1451 if (sc->ferror_count == 2 && sc->ferror_send == FALSE) {
1452 sc->ferror_send = TRUE;
1453 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1455 msg.param.code = MM_ERROR_FILE_NOT_FOUND;
1457 msg.param.code = MM_ERROR_FILE_READ;
1459 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1464 return GST_PAD_PROBE_DROP; /* skip this buffer */
1466 default: /* succeeded to get free space */
1467 /* check free space for recording */
1468 /* get queued buffer size */
1469 if (sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst) {
1470 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst, "current-level-bytes", &aq_size);
1472 if (sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst) {
1473 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst, "current-level-bytes", &vq_size);
1476 queued_buffer = aq_size + vq_size;
1478 /* check free space */
1479 if (free_space < (_MMCAMCORDER_MINIMUM_SPACE + buffer_size + trailer_size + queued_buffer)) {
1480 _mmcam_dbg_warn("No more space for recording!!! Recording is paused.");
1481 _mmcam_dbg_warn("Free Space : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]," \
1482 " buffer size : [%" G_GUINT64_FORMAT "], queued buffer size : [%" G_GUINT64_FORMAT "]", \
1483 free_space, trailer_size, buffer_size, queued_buffer);
1485 if (!sc->isMaxsizePausing) {
1486 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1487 sc->isMaxsizePausing = TRUE;
1489 msg.id = MM_MESSAGE_CAMCORDER_NO_FREE_SPACE;
1490 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1493 return GST_PAD_PROBE_DROP;
1498 pthread_mutex_lock(&(videoinfo->size_check_lock));
1500 /* check max size of recorded file */
1501 if (videoinfo->max_size > 0 &&
1502 videoinfo->max_size < videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) {
1503 GstState pipeline_state = GST_STATE_VOID_PENDING;
1504 GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1505 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1506 _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \
1507 " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]",
1508 videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size);
1510 if (!sc->isMaxsizePausing) {
1511 sc->isMaxsizePausing = TRUE;
1512 gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ;
1513 if (pipeline_state == GST_STATE_PLAYING) {
1514 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1517 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1518 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1521 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1523 return GST_PAD_PROBE_DROP;
1526 videoinfo->filesize += (guint64)buffer_size;
1529 _mmcam_dbg_log("filesize %lld Byte, ", videoinfo->filesize);
1532 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1534 return GST_PAD_PROBE_OK;
1538 static GstPadProbeReturn __mmcamcorder_video_dataprobe_audio_disable(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1540 guint64 trailer_size = 0;
1541 guint64 rec_pipe_time = 0;
1542 unsigned int remained_time = 0;
1544 GstClockTime b_time;
1546 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1547 _MMCamcorderMsgItem msg;
1548 _MMCamcorderSubContext *sc = NULL;
1549 _MMCamcorderVideoInfo *videoinfo = NULL;
1551 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1553 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1554 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1556 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1557 mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK);
1558 mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK);
1560 videoinfo = sc->info_video;
1562 b_time = GST_BUFFER_PTS(buffer);
1564 rec_pipe_time = GST_TIME_AS_MSECONDS(b_time);
1566 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1567 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1572 /* check max time */
1573 if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) {
1574 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1575 rec_pipe_time, videoinfo->max_time);
1577 if (!sc->isMaxtimePausing) {
1578 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1580 sc->isMaxtimePausing = TRUE;
1582 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1583 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1584 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1585 msg.param.recording_status.remained_time = 0;
1586 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1588 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1589 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1592 return GST_PAD_PROBE_DROP;
1595 /* calculate remained time can be recorded */
1596 if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) {
1597 remained_time = videoinfo->max_time - rec_pipe_time;
1598 } else if (videoinfo->max_size > 0) {
1599 long double max_size = (long double)videoinfo->max_size;
1600 long double current_size = (long double)(videoinfo->filesize + trailer_size);
1602 remained_time = (unsigned int)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1605 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1606 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1607 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1608 msg.param.recording_status.remained_time = remained_time;
1609 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1612 _mmcam_dbg_log("time [%" GST_TIME_FORMAT "], size [%d]",
1613 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1616 if (videoinfo->record_timestamp_ratio != _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) {
1617 guint record_motion_rate = (guint)videoinfo->record_motion_rate;
1620 _mmcam_dbg_log("record_motion_rate %d, videoinfo->record_drop_count %d",
1621 record_motion_rate, videoinfo->record_drop_count);
1624 /* drop some frame if fast motion */
1625 if (videoinfo->record_motion_rate > _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) {
1626 if (record_motion_rate != (videoinfo->record_drop_count++)) {
1628 _mmcam_dbg_warn("drop frame");
1630 return GST_PAD_PROBE_DROP;
1633 videoinfo->record_drop_count = 1;
1635 _mmcam_dbg_warn("pass frame");
1639 GST_BUFFER_PTS(buffer) = b_time * (videoinfo->record_timestamp_ratio);
1642 return GST_PAD_PROBE_OK;
1646 static GstPadProbeReturn __mmcamcorder_audioque_dataprobe(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1648 _MMCamcorderMsgItem msg;
1649 guint64 trailer_size = 0;
1650 guint64 rec_pipe_time = 0;
1651 _MMCamcorderSubContext *sc = NULL;
1652 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1653 _MMCamcorderVideoInfo *videoinfo = NULL;
1654 unsigned int remained_time = 0;
1655 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1657 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1658 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1659 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1661 mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK);
1662 mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK);
1663 mmf_return_val_if_fail(sc->element, GST_PAD_PROBE_OK);
1665 videoinfo = sc->info_video;
1667 if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_PTS(buffer))) {
1668 _mmcam_dbg_err( "Buffer timestamp is invalid, check it");
1669 return GST_PAD_PROBE_OK;
1672 rec_pipe_time = GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer));
1674 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1675 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1680 /* calculate remained time can be recorded */
1681 if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) {
1682 remained_time = videoinfo->max_time - rec_pipe_time;
1683 } else if (videoinfo->max_size > 0) {
1684 long double max_size = (long double)videoinfo->max_size;
1685 long double current_size = (long double)(videoinfo->filesize + trailer_size);
1687 remained_time = (unsigned long long)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1690 if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) {
1691 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1692 rec_pipe_time, videoinfo->max_time);
1694 if (!sc->isMaxtimePausing) {
1695 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1697 sc->isMaxtimePausing = TRUE;
1699 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1700 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1701 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1702 msg.param.recording_status.remained_time = 0;
1703 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1705 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1706 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1709 return GST_PAD_PROBE_DROP;
1712 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1713 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1714 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1715 msg.param.recording_status.remained_time = remained_time;
1716 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1719 _mmcam_dbg_log("audio data probe :: time [%" GST_TIME_FORMAT "], size [%lld KB]",
1720 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1723 return GST_PAD_PROBE_OK;
1727 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_audio_mute(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1729 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1730 double volume = 0.0;
1733 int err = MM_ERROR_UNKNOWN;
1734 char *err_name = NULL;
1735 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1738 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1739 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_DROP);
1741 /*_mmcam_dbg_log("AUDIO SRC time stamp : [%" GST_TIME_FORMAT "] \n", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1742 err = mm_camcorder_get_attributes((MMHandleType)hcamcorder, &err_name,
1743 MMCAM_AUDIO_VOLUME, &volume,
1744 MMCAM_AUDIO_FORMAT, &format,
1745 MMCAM_AUDIO_CHANNEL, &channel,
1747 if (err != MM_ERROR_NONE) {
1748 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, err);
1749 SAFE_FREE(err_name);
1753 memset(&mapinfo, 0x0, sizeof(GstMapInfo));
1755 gst_buffer_map(buffer, &mapinfo, GST_MAP_READWRITE);
1757 /* Set audio stream NULL */
1758 if (volume == 0.0) {
1759 memset(mapinfo.data, 0, mapinfo.size);
1762 /* CALL audio stream callback */
1763 if (hcamcorder->astream_cb && buffer && mapinfo.data && mapinfo.size > 0) {
1764 MMCamcorderAudioStreamDataType stream;
1766 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) < MM_CAMCORDER_STATE_PREPARE) {
1767 _mmcam_dbg_warn("Not ready for stream callback");
1768 gst_buffer_unmap(buffer, &mapinfo);
1769 return GST_PAD_PROBE_OK;
1772 /*_mmcam_dbg_log("Call video steramCb, data[%p], Width[%d],Height[%d], Format[%d]",
1773 GST_BUFFER_DATA(buffer), width, height, format);*/
1775 stream.data = (void *)mapinfo.data;
1776 stream.format = format;
1777 stream.channel = channel;
1778 stream.length = mapinfo.size;
1779 stream.timestamp = (unsigned int)(GST_BUFFER_PTS(buffer)/1000000); /* nano -> milli second */
1781 _MMCAMCORDER_LOCK_ASTREAM_CALLBACK(hcamcorder);
1783 if (hcamcorder->astream_cb) {
1784 hcamcorder->astream_cb(&stream, hcamcorder->astream_cb_param);
1787 _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK(hcamcorder);
1790 gst_buffer_unmap(buffer, &mapinfo);
1791 return GST_PAD_PROBE_OK;
1795 static gboolean __mmcamcorder_add_metadata(MMHandleType handle, int fileformat)
1797 gboolean bret = FALSE;
1799 switch (fileformat) {
1800 case MM_FILE_FORMAT_3GP:
1801 case MM_FILE_FORMAT_MP4:
1802 bret = __mmcamcorder_add_metadata_mp4(handle);
1805 _mmcam_dbg_warn("Unsupported fileformat to insert location info (%d)", fileformat);
1813 static gboolean __mmcamcorder_add_metadata_mp4(MMHandleType handle)
1817 guint64 udta_size = 0;
1818 gint64 current_pos = 0;
1819 gint64 moov_pos = 0;
1820 gint64 udta_pos = 0;
1821 gdouble longitude = 0;
1822 gdouble latitude = 0;
1823 gdouble altitude = 0;
1825 int orientation = 0;
1827 char *err_name = NULL;
1828 char err_msg[MAX_ERROR_MESSAGE_LEN] = {'\0',};
1829 _MMCamcorderLocationInfo location_info = {0,0,0};
1830 _MMCamcorderLocationInfo geo_info = {0,0,0};
1832 _MMCamcorderVideoInfo *info = NULL;
1833 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1834 _MMCamcorderSubContext *sc = NULL;
1836 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1837 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1839 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1840 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1844 info = sc->info_video;
1846 f = fopen64(info->filename, "rb+");
1848 strerror_r(errno, err_msg, MAX_ERROR_MESSAGE_LEN);
1849 _mmcam_dbg_err("file open failed [%s]", err_msg);
1853 mm_camcorder_get_attributes(handle, &err_name,
1854 MMCAM_TAG_LATITUDE, &latitude,
1855 MMCAM_TAG_LONGITUDE, &longitude,
1856 MMCAM_TAG_ALTITUDE, &altitude,
1857 MMCAM_TAG_VIDEO_ORIENTATION, &orientation,
1858 MMCAM_TAG_GPS_ENABLE, &gps_enable,
1861 _mmcam_dbg_warn("Get tag attrs fail. (%s:%x)", err_name, err);
1862 SAFE_FREE (err_name);
1865 location_info.longitude = _mmcamcorder_double_to_fix(longitude);
1866 location_info.latitude = _mmcamcorder_double_to_fix(latitude);
1867 location_info.altitude = _mmcamcorder_double_to_fix(altitude);
1868 geo_info.longitude = longitude *10000;
1869 geo_info.latitude = latitude *10000;
1870 geo_info.altitude = altitude *10000;
1871 /* find udta container.
1872 if, there are udta container, write loci box after that
1873 else, make udta container and write loci box. */
1874 if (_mmcamcorder_find_fourcc(f, MMCAM_FOURCC('u','d','t','a'), TRUE)) {
1877 _mmcam_dbg_log("find udta container");
1880 if (fseek(f, -8L, SEEK_CUR) != 0) {
1884 udta_pos = ftello(f);
1889 nread = fread(&buf, sizeof(char), sizeof(buf), f);
1891 _mmcam_dbg_log("recorded file fread %d", nread);
1893 udta_size = _mmcamcorder_get_container_size(buf);
1895 /* goto end of udta and write 'loci' box */
1896 if (fseek(f, (udta_size-4L), SEEK_CUR) != 0) {
1901 if (!_mmcamcorder_write_loci(f, location_info)) {
1902 _mmcam_dbg_err("failed to write loci");
1906 if (!_mmcamcorder_write_geodata(f, geo_info)) {
1907 _mmcam_dbg_err("failed to write geodata");
1912 current_pos = ftello(f);
1913 if (current_pos < 0) {
1917 if (!_mmcamcorder_update_size(f, udta_pos, current_pos)) {
1921 _mmcam_dbg_log("No udta container");
1922 if (fseek(f, 0, SEEK_END) != 0) {
1926 if (!_mmcamcorder_write_udta(f, gps_enable, location_info, geo_info)) {
1927 _mmcam_dbg_err("failed to write udta");
1932 /* find moov container.
1933 update moov container size. */
1934 if((current_pos = ftello(f))<0)
1937 if (_mmcamcorder_find_tag(f, MMCAM_FOURCC('m','o','o','v'), TRUE)) {
1938 gint64 internal_pos = ftello(f);
1940 _mmcam_dbg_log("found moov container");
1941 if (fseek(f, -8L, SEEK_CUR) !=0) {
1945 moov_pos = ftello(f);
1950 if (!_mmcamcorder_update_size(f, moov_pos, current_pos)) {
1954 /* add orientation info */
1955 if (fseeko(f, internal_pos, SEEK_SET) < 0) {
1956 _mmcam_dbg_err("fseek failed : errno %d", errno);
1960 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t','r','a','k'), FALSE)) {
1961 _mmcam_dbg_err("failed to find [trak] tag");
1965 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t','k','h','d'), FALSE)) {
1966 _mmcam_dbg_err("failed to find [tkhd] tag");
1970 _mmcam_dbg_log("found [tkhd] tag");
1972 /* seek to start position of composition matrix */
1973 fseek(f, _OFFSET_COMPOSITION_MATRIX, SEEK_CUR);
1975 /* update composition matrix for orientation */
1976 _mmcamcorder_update_composition_matrix(f, orientation);
1978 _mmcam_dbg_err("No 'moov' container");
1990 _mmcam_dbg_err("ftell() returns negative value.");
1996 int _mmcamcorder_connect_video_stream_cb_signal(MMHandleType handle)
1998 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1999 _MMCamcorderSubContext *sc = NULL;
2001 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2003 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
2004 mmf_return_val_if_fail(sc && sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2006 /* check video source element */
2007 if (sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst) {
2008 _mmcam_dbg_warn("connect video stream cb signal to _MMCAMCORDER_VIDEOSRC_SRC");
2009 MMCAMCORDER_SIGNAL_CONNECT(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst,
2010 _MMCAMCORDER_HANDLER_VIDEOREC, "video-stream-cb",
2011 G_CALLBACK(__mmcamcorder_video_stream_cb),
2013 return MM_ERROR_NONE;
2015 _mmcam_dbg_err("videosrc element is not created yet");
2016 return MM_ERROR_CAMCORDER_NOT_INITIALIZED;
2021 int _mmcamcorder_video_prepare_record(MMHandleType handle)
2023 int ret = MM_ERROR_NONE;
2025 _MMCamcorderVideoInfo *info = NULL;
2026 _MMCamcorderSubContext *sc = NULL;
2027 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
2029 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2031 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
2032 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2033 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2035 info = sc->info_video;
2037 _mmcam_dbg_warn("start");
2039 /* create encoding pipeline */
2040 ret =_mmcamcorder_create_recorder_pipeline((MMHandleType)hcamcorder);
2041 if (ret != MM_ERROR_NONE) {
2042 goto _ERR_PREPARE_RECORD;
2045 if (info->filename == NULL) {
2046 char *temp_filename = NULL;
2049 mm_camcorder_get_attributes(handle, NULL,
2050 MMCAM_TARGET_FILENAME, &temp_filename, &size,
2052 if (temp_filename) {
2053 info->filename = strdup(temp_filename);
2056 if (!info->filename) {
2057 _mmcam_dbg_err("strdup[src:%p] was failed", temp_filename);
2058 goto _ERR_PREPARE_RECORD;
2062 _mmcam_dbg_log("Record file name [%s]", info->filename);
2064 MMCAMCORDER_G_OBJECT_SET_POINTER(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename);
2065 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", 0);
2067 /* Adjust display FPS */
2068 sc->display_interval = 0;
2069 sc->previous_slot_time = 0;
2071 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PAUSED);
2072 if (ret != MM_ERROR_NONE) {
2073 goto _ERR_PREPARE_RECORD;
2076 _mmcam_dbg_warn("done");
2080 _ERR_PREPARE_RECORD:
2081 /* Remove recorder pipeline and recording file which size maybe zero */
2082 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
2083 if (info && info->filename) {
2084 _mmcam_dbg_log("file delete(%s)", info->filename);
2085 unlink(info->filename);