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;
509 int root_directory_length = 0;
512 _mmcam_dbg_log("Record Start - dual stream %d", info->support_dual_stream);
514 /* init record_dual_stream */
515 info->record_dual_stream = FALSE;
517 ret = mm_camcorder_get_attributes(handle, &err_name,
518 MMCAM_CAMERA_FPS, &fps,
519 MMCAM_CAMERA_WIDTH, &(info->preview_width),
520 MMCAM_CAMERA_HEIGHT, &(info->preview_height),
521 MMCAM_VIDEO_WIDTH, &(info->video_width),
522 MMCAM_VIDEO_HEIGHT, &(info->video_height),
523 MMCAM_FILE_FORMAT, &fileformat,
524 MMCAM_TARGET_FILENAME, &temp_filename, &size,
525 MMCAM_TARGET_MAX_SIZE, &imax_size,
526 MMCAM_TARGET_TIME_LIMIT, &imax_time,
527 MMCAM_FILE_FORMAT, &(info->fileformat),
528 MMCAM_CAMERA_RECORDING_MOTION_RATE, &motion_rate,
529 MMCAM_ROOT_DIRECTORY, &hcamcorder->root_directory, &root_directory_length,
531 if (ret != MM_ERROR_NONE) {
532 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, ret);
533 SAFE_FREE (err_name);
534 goto _ERR_CAMCORDER_VIDEO_COMMAND;
537 if (temp_filename == NULL) {
538 _mmcam_dbg_err("filename is not set");
539 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
540 goto _ERR_CAMCORDER_VIDEO_COMMAND;
544 if (imax_size <= 0) {
545 info->max_size = 0; /* do not check */
547 info->max_size = ((guint64)imax_size) << 10; /* to byte */
551 if (imax_time <= 0) {
552 info->max_time = 0; /* do not check */
554 info->max_time = ((guint64)imax_time) * 1000; /* to millisecond */
557 dir_name = g_path_get_dirname(temp_filename);
559 ret_free_space = _mmcamcorder_get_freespace(dir_name, hcamcorder->root_directory, &free_space);
561 _mmcam_dbg_warn("current space - %s [%" G_GUINT64_FORMAT "]", dir_name, free_space);
563 if (_mmcamcorder_get_file_system_type(dir_name, &file_system_type) == 0) {
564 /* MSDOS_SUPER_MAGIC : 0x4d44 */
565 if (file_system_type == MSDOS_SUPER_MAGIC &&
566 (info->max_size == 0 || info->max_size > FAT32_FILE_SYSTEM_MAX_SIZE)) {
567 _mmcam_dbg_warn("FAT32 and too large max[%"G_GUINT64_FORMAT"], set max as %"G_GUINT64_FORMAT,
568 info->max_size, FAT32_FILE_SYSTEM_MAX_SIZE);
569 info->max_size = FAT32_FILE_SYSTEM_MAX_SIZE;
571 _mmcam_dbg_warn("file system 0x%x, max size %"G_GUINT64_FORMAT,
572 file_system_type, info->max_size);
575 _mmcam_dbg_warn("_mmcamcorder_get_file_system_type failed");
581 _mmcam_dbg_err("failed to get directory name");
585 if ((ret_free_space == -1) || free_space <= (_MMCAMCORDER_MINIMUM_SPACE<<1)) {
586 _mmcam_dbg_err("OUT of STORAGE [ret_free_space:%d or free space [%" G_GUINT64_FORMAT "] is smaller than [%d]",
587 ret_free_space, free_space, (_MMCAMCORDER_MINIMUM_SPACE<<1));
588 return MM_ERROR_OUT_OF_STORAGE;
591 pthread_mutex_lock(&(hcamcorder->task_thread_lock));
592 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst == NULL &&
593 hcamcorder->task_thread_state == _MMCAMCORDER_TASK_THREAD_STATE_NONE) {
594 /* Play record start sound */
595 _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_FILEPATH_REC_START_SND, FALSE);
597 pthread_mutex_unlock(&(hcamcorder->task_thread_lock));
599 _mmcam_dbg_warn("video size [%dx%d]", info->video_width, info->video_height);
601 if (info->video_width == 0 || info->video_height == 0) {
602 _mmcam_dbg_warn("video size is invalid [%dx%d] use preview size [%dx%d]",
603 info->video_width, info->video_height, info->preview_width, info->preview_height);
604 info->video_width = info->preview_width;
605 info->video_height = info->preview_height;
608 if (info->support_dual_stream) {
609 _mmcam_dbg_warn("DUAL STREAM MODE");
611 info->record_dual_stream = TRUE;
613 /* No need to restart preview */
614 info->restart_preview = FALSE;
616 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "video-width", info->video_width);
617 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "video-height", info->video_height);
618 } else if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
619 info->preview_width == info->video_width &&
620 info->preview_height == info->video_height) {
621 _mmcam_dbg_log("H264 preview mode and same resolution");
623 /* No need to restart preview */
624 info->restart_preview = FALSE;
626 /* always need to restart preview */
627 info->restart_preview = TRUE;
630 /* set recording hint */
631 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", TRUE);
633 if (info->restart_preview) {
634 /* stop preview and set new size */
635 _mmcam_dbg_log("restart preview");
637 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
638 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
640 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
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(handle, hcamcorder->conf_main,
669 CONFIGURE_CATEGORY_MAIN_RECORD,
673 _mmcamcorder_conf_get_value_int(handle, 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 CameraControl = 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(CameraControl, 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 CameraControl = 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(CameraControl, 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;
779 /*set the camera control to create the GOP so that video record will get a new key frame*/
780 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
781 GST_IS_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst)) {
782 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
783 controls = gst_camera_control_list_channels(CameraControl);
784 if (controls != NULL) {
785 for (item = controls ; item && item->data ; item = item->next) {
786 CameraControlChannel = item->data;
787 _mmcam_dbg_log("CameraControlChannel->label %s", CameraControlChannel->label);
788 if (!strcmp(CameraControlChannel->label, "new-gop")) {
789 //gst_camera_control_set_value(CameraControl, CameraControlChannel, 1);
795 _mmcam_dbg_warn("failed to find new-gop control channel");
799 _mmcam_dbg_warn("Can't cast Video source into camera control or not H264 prevew format[%d]",
800 sc->info_image->preview_format);
805 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
806 GST_IS_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst)) {
807 /* generate and I-frame on resuming */
808 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
809 controls = gst_camera_control_list_channels(CameraControl);
810 if (controls != NULL) {
811 for (item = controls ; item && item->data ; item = item->next) {
812 CameraControlChannel = item->data;
813 _mmcam_dbg_log("CameraControlChannel->label %s", CameraControlChannel->label);
814 if (!strcmp(CameraControlChannel->label, "new-gop")) {
815 //gst_camera_control_set_value(CameraControl, CameraControlChannel, 1);
821 _mmcam_dbg_warn("failed to find new-gop control channel");
826 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", FALSE);
828 _mmcam_dbg_log("Object property settings done");
832 case _MMCamcorder_CMD_PAUSE:
834 if (info->b_commiting) {
835 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
836 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
839 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
840 if (sc->audio_disable) {
841 /* check only video frame */
842 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
844 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
845 _mmcam_dbg_err("Pause fail, frame count %llu",
846 info->video_frame_count);
847 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
849 _mmcam_dbg_warn("Waiting for enough video frame, retrial[%d], frame %llu",
850 count, info->video_frame_count);
853 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
855 /* check both of video and audio frame */
856 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
858 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
859 _mmcam_dbg_err("Pause fail, frame count VIDEO[%llu], AUDIO [%llu]",
860 info->video_frame_count, info->audio_frame_count);
861 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
863 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%llu], AUDIO [%llu]",
864 count, info->video_frame_count, info->audio_frame_count);
867 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
871 /* block encodebin */
872 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", TRUE);
875 case _MMCamcorder_CMD_CANCEL:
877 if (info->b_commiting) {
878 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
879 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
882 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
884 if (hcamcorder->capture_in_recording == FALSE) {
886 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
887 _mmcam_dbg_err("Failed to Wait capture data");
888 hcamcorder->capture_in_recording = FALSE;
891 _mmcam_dbg_warn("Waiting for capture data - retrial [%d]", count);
894 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
897 /* block push buffer */
898 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP;
900 ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
901 if (ret != MM_ERROR_NONE) {
902 goto _ERR_CAMCORDER_VIDEO_COMMAND;
905 /* set recording hint */
906 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", FALSE);
908 /* stop video stream */
909 if (info->record_dual_stream) {
910 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
912 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
914 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
915 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
917 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
919 _mmcam_dbg_err("failed to get camera control");
923 if (info->restart_preview) {
924 /* restart preview */
925 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
926 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
928 ret =_mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
930 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
931 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
932 if (ret != MM_ERROR_NONE) {
933 goto _ERR_CAMCORDER_VIDEO_COMMAND;
936 /* reset restart_preview for inset window layout */
937 info->restart_preview = FALSE;
939 if (!_mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height)) {
940 ret = MM_ERROR_CAMCORDER_INTERNAL;
941 goto _ERR_CAMCORDER_VIDEO_COMMAND;
944 ret =_mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
945 if (ret != MM_ERROR_NONE) {
946 goto _ERR_CAMCORDER_VIDEO_COMMAND;
950 /* remove target file */
951 if (info->filename) {
952 _mmcam_dbg_log("file delete(%s)", info->filename);
953 unlink(info->filename);
956 sc->isMaxsizePausing = FALSE;
957 sc->isMaxtimePausing = FALSE;
959 sc->display_interval = 0;
960 sc->previous_slot_time = 0;
961 info->video_frame_count = 0;
962 info->audio_frame_count = 0;
964 hcamcorder->capture_in_recording = FALSE;
967 case _MMCamcorder_CMD_COMMIT:
969 if (info->b_commiting) {
970 _mmcam_dbg_err("now on commiting previous file!!(command : %d)", command);
971 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
973 _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT : start");
974 info->b_commiting = TRUE;
975 sc->bget_eos = FALSE;
978 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
979 if (sc->audio_disable) {
980 /* check only video frame */
981 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME &&
982 hcamcorder->capture_in_recording == FALSE) {
984 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
985 _mmcam_dbg_err("Commit fail, frame count is %llu, capturing %d",
986 info->video_frame_count, hcamcorder->capture_in_recording);
988 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
989 _mmcam_dbg_warn("video frames are enough. keep going...");
991 info->b_commiting = FALSE;
992 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
995 _mmcam_dbg_warn("Waiting for enough video frame, retrial [%d], frame %llu, capturing %d",
996 count, info->video_frame_count, hcamcorder->capture_in_recording);
999 /* check both of video and audio frame */
1000 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME &&
1001 info->audio_frame_count &&
1002 hcamcorder->capture_in_recording == FALSE) {
1004 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
1005 _mmcam_dbg_err("Commit fail, VIDEO[%llu], AUDIO [%llu], capturing %d",
1006 info->video_frame_count, info->audio_frame_count, hcamcorder->capture_in_recording);
1008 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
1009 _mmcam_dbg_warn("video/audio frames are enough. keep going...");
1011 info->b_commiting = FALSE;
1012 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
1015 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
1017 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%llu], AUDIO [%llu], capturing %d",
1018 count, info->video_frame_count, info->audio_frame_count, hcamcorder->capture_in_recording);
1022 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
1025 /* block push buffer */
1026 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP;
1027 _mmcam_dbg_log("block push buffer to appsrc");
1029 if (sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst != NULL) {
1030 if (gst_element_send_event(sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, gst_event_new_eos())) {
1031 _mmcam_dbg_warn("VIDEO: send eos to appsrc done");
1033 _mmcam_dbg_err("VIDEO: send EOS failed");
1034 info->b_commiting = FALSE;
1035 ret = MM_ERROR_CAMCORDER_INTERNAL;
1036 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1039 _mmcam_dbg_err("No video stream source");
1040 info->b_commiting = FALSE;
1041 ret = MM_ERROR_CAMCORDER_INTERNAL;
1042 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1045 if (sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst != NULL) {
1046 if (gst_element_send_event(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst, gst_event_new_eos())) {
1047 _mmcam_dbg_warn("AUDIO: send eos to audiosrc done");
1049 _mmcam_dbg_err("AUDIO: send EOS failed");
1050 info->b_commiting = FALSE;
1051 ret = MM_ERROR_CAMCORDER_INTERNAL;
1052 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1055 _mmcam_dbg_log("No audio stream");
1059 sc->display_interval = 0;
1060 sc->previous_slot_time = 0;
1063 _mmcam_dbg_log("Start to wait EOS");
1064 ret =_mmcamcorder_get_eos_message(handle);
1065 if (ret != MM_ERROR_NONE) {
1066 info->b_commiting = FALSE;
1067 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1071 hcamcorder->capture_in_recording = FALSE;
1075 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
1076 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1079 return MM_ERROR_NONE;
1081 _ERR_CAMCORDER_VIDEO_COMMAND:
1082 if (command == _MMCamcorder_CMD_RECORD) {
1083 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
1090 int _mmcamcorder_video_handle_eos(MMHandleType handle)
1092 int ret = MM_ERROR_NONE;
1094 guint64 file_size = 0;
1096 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1097 _MMCamcorderSubContext *sc = NULL;
1098 _MMCamcorderVideoInfo *info = NULL;
1099 _MMCamcorderMsgItem msg;
1100 MMCamRecordingReport *report = NULL;
1102 mmf_return_val_if_fail(hcamcorder, FALSE);
1104 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1105 mmf_return_val_if_fail(sc, FALSE);
1106 mmf_return_val_if_fail(sc->info_video, FALSE);
1108 info = sc->info_video;
1112 if (hcamcorder->state_change_by_system != _MMCAMCORDER_STATE_CHANGE_BY_ASM) {
1113 /* Play record stop sound */
1114 _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_FILEPATH_REC_STOP_SND, FALSE);
1116 _mmcam_dbg_warn("Play stop sound through pulseaudio");
1118 #ifdef _MMCAMCORDER_UPLOAD_SAMPLE
1119 _mmcamcorder_sound_init(handle, _MMCAMCORDER_FILEPATH_REC_STOP_SND);
1120 #else /* _MMCAMCORDER_UPLOAD_SAMPLE */
1121 _mmcamcorder_sound_init(handle);
1122 #endif /* _MMCAMCORDER_UPLOAD_SAMPLE */
1124 _mmcamcorder_sound_play((MMHandleType)hcamcorder, _MMCAMCORDER_SAMPLE_SOUND_NAME_REC_STOP, TRUE);
1126 _mmcamcorder_sound_finalize(handle);
1129 /* remove blocking part */
1130 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
1132 mm_camcorder_get_attributes(handle, NULL,
1133 MMCAM_RECORDER_TAG_ENABLE, &enabletag,
1136 ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
1137 if (ret != MM_ERROR_NONE) {
1138 _mmcam_dbg_warn("_MMCamcorder_CMD_COMMIT:__mmcamcorder_remove_recorder_pipeline failed. error[%x]", ret);
1141 /* set recording hint */
1142 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", FALSE);
1144 /* stop video stream */
1145 if (info->record_dual_stream) {
1146 GstCameraControl *control = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
1148 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
1150 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
1151 gst_camera_control_set_record_command(control, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
1153 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
1155 _mmcam_dbg_err("failed to get camera control");
1159 if (enabletag && !(sc->ferror_send)) {
1160 ret = __mmcamcorder_add_metadata((MMHandleType)hcamcorder, info->fileformat);
1162 _mmcam_dbg_log("Writing location information SUCCEEDED !!");
1164 _mmcam_dbg_err("Writing location information FAILED !!");
1168 /* Check file size */
1169 if (info->max_size > 0) {
1170 _mmcamcorder_get_file_size(info->filename, &file_size);
1171 _mmcam_dbg_log("MAX size %lld byte - created filesize %lld byte",
1172 info->max_size, file_size);
1174 if (file_size > info->max_size) {
1175 _MMCamcorderMsgItem message;
1176 _mmcam_dbg_err("File size is greater than max size !!");
1177 message.id = MM_MESSAGE_CAMCORDER_ERROR;
1178 message.param.code = MM_ERROR_CAMCORDER_FILE_SIZE_OVER;
1179 _mmcamcorder_send_message((MMHandleType)hcamcorder, &message);
1183 if (info->restart_preview) {
1185 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
1186 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
1188 _mmcam_dbg_log("Set state of pipeline as READY");
1189 ret = _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_READY);
1192 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
1193 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
1194 if (ret != MM_ERROR_NONE) {
1195 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1196 msg.param.code = ret;
1197 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1198 _mmcam_dbg_err("Failed to set state READY[%x]", ret);
1201 /* reset restart_preview for inset window layout */
1202 info->restart_preview = FALSE;
1204 /* recover preview size */
1205 _mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height);
1207 ret =_mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_PLAYING);
1208 /* Do not return when error is occurred.
1209 Recording file was created successfully, but starting pipeline failed */
1210 if (ret != MM_ERROR_NONE) {
1211 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1212 msg.param.code = ret;
1213 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1214 _mmcam_dbg_err("Failed to set state PLAYING[%x]", ret);
1217 _mmcam_dbg_log("No need to restart preview");
1220 /* Send recording report to application */
1221 msg.id = MM_MESSAGE_CAMCORDER_VIDEO_CAPTURED;
1222 report = (MMCamRecordingReport *)malloc(sizeof(MMCamRecordingReport));
1224 _mmcam_dbg_err("Recording report fail(%s). Out of memory.", info->filename);
1226 report->recording_filename = strdup(info->filename);
1227 msg.param.data= report;
1229 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1233 sc->pipeline_time = 0;
1235 sc->isMaxsizePausing = FALSE; /*In async function, this variable should set in callback function. */
1236 sc->isMaxtimePausing = FALSE;
1237 hcamcorder->error_occurs = FALSE;
1239 info->video_frame_count = 0;
1240 info->audio_frame_count = 0;
1242 info->b_commiting = FALSE;
1244 if (hcamcorder->state_change_by_system != _MMCAMCORDER_STATE_CHANGE_BY_ASM) {
1245 /* check recording stop sound */
1246 _mmcamcorder_sound_solo_play_wait(handle);
1249 _mmcam_dbg_err("_MMCamcorder_CMD_COMMIT : end");
1256 * This function is record video data probing function.
1257 * If this function is linked with certain pad by gst_pad_add_buffer_probe(),
1258 * this function will be called when data stream pass through the pad.
1260 * @param[in] pad probing pad which calls this function.
1261 * @param[in] buffer buffer which contains stream data.
1262 * @param[in] u_data user data.
1263 * @return This function returns true on success, or false value with error
1267 static GstPadProbeReturn __mmcamcorder_eventprobe_monitor(GstPad *pad, GstPadProbeInfo *info, gpointer u_data){
1268 GstEvent *event = GST_PAD_PROBE_INFO_EVENT(info);
1269 switch (GST_EVENT_TYPE(event)) {
1270 case GST_EVENT_UNKNOWN:
1271 /* upstream events */
1273 case GST_EVENT_SEEK:
1274 case GST_EVENT_NAVIGATION:
1275 case GST_EVENT_LATENCY:
1276 /* downstream serialized events */
1277 case GST_EVENT_SEGMENT :
1279 case GST_EVENT_BUFFERSIZE:
1280 _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1283 _mmcam_dbg_warn("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1285 /* bidirectional events */
1286 case GST_EVENT_FLUSH_START:
1287 case GST_EVENT_FLUSH_STOP:
1288 _mmcam_dbg_err("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1291 _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1295 return GST_PAD_PROBE_OK;
1299 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_check(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1301 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1302 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1304 _MMCamcorderSubContext *sc = NULL;
1305 _MMCamcorderVideoInfo *videoinfo = NULL;
1306 _MMCamcorderMsgItem msg;
1307 guint64 buffer_size = 0;
1308 guint64 trailer_size = 0;
1310 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1311 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1312 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1314 mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK);
1315 videoinfo = sc->info_video;
1317 /* get buffer size */
1318 if (!gst_buffer_map(buffer, &mapinfo, GST_MAP_READ)) {
1319 _mmcam_dbg_warn("map failed : buffer %p", buffer);
1320 return GST_PAD_PROBE_OK;
1323 buffer_size = mapinfo.size;
1324 gst_buffer_unmap(buffer, &mapinfo);
1326 /*_mmcam_dbg_err("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1328 pthread_mutex_lock(&(videoinfo->size_check_lock));
1330 if (videoinfo->audio_frame_count == 0) {
1331 videoinfo->filesize += buffer_size;
1332 videoinfo->audio_frame_count++;
1333 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1334 return GST_PAD_PROBE_OK;
1337 if (sc->ferror_send || sc->isMaxsizePausing) {
1338 _mmcam_dbg_warn("Recording is paused, drop frames");
1339 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1340 return GST_PAD_PROBE_DROP;
1343 /* get trailer size */
1344 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1345 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1350 /* check max size of recorded file */
1351 if (videoinfo->max_size > 0 &&
1352 videoinfo->max_size < videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) {
1353 GstState pipeline_state = GST_STATE_VOID_PENDING;
1354 GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1355 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1356 _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \
1357 " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]",
1358 videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size);
1360 if (!sc->isMaxsizePausing) {
1361 sc->isMaxsizePausing = TRUE;
1362 gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ;
1363 if (pipeline_state == GST_STATE_PLAYING) {
1364 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1367 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1368 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1371 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1376 videoinfo->filesize += buffer_size;
1377 videoinfo->audio_frame_count++;
1379 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1381 return GST_PAD_PROBE_OK;
1385 static GstPadProbeReturn __mmcamcorder_video_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1390 guint64 free_space = 0;
1391 guint64 buffer_size = 0;
1392 guint64 trailer_size = 0;
1393 guint64 queued_buffer = 0;
1394 char *dir_name = NULL;
1395 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1398 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1399 _MMCamcorderMsgItem msg;
1400 _MMCamcorderSubContext *sc = NULL;
1401 _MMCamcorderVideoInfo *videoinfo = NULL;
1403 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1404 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1406 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1407 mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK);
1408 videoinfo = sc->info_video;
1410 /*_mmcam_dbg_log("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1411 if (sc->ferror_send) {
1412 _mmcam_dbg_warn("file write error, drop frames");
1413 return GST_PAD_PROBE_DROP;
1416 gst_buffer_map(buffer, &mapinfo, GST_MAP_READ);
1417 buffer_size = mapinfo.size;
1418 gst_buffer_unmap(buffer, &mapinfo);
1420 videoinfo->video_frame_count++;
1421 if (videoinfo->video_frame_count <= (guint64)_MMCAMCORDER_MINIMUM_FRAME) {
1422 /* _mmcam_dbg_log("Pass minimum frame: info->video_frame_count: %" G_GUINT64_FORMAT " ",
1423 info->video_frame_count); */
1424 pthread_mutex_lock(&(videoinfo->size_check_lock));
1425 videoinfo->filesize += buffer_size;
1426 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1427 return GST_PAD_PROBE_OK;
1430 /* get trailer size */
1431 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1432 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1437 dir_name = g_path_get_dirname(videoinfo->filename);
1439 ret = _mmcamcorder_get_freespace(dir_name, hcamcorder->root_directory, &free_space);
1443 _mmcam_dbg_err("failed to get dir name from [%s]", videoinfo->filename);
1447 /*_mmcam_dbg_log("check free space for recording");*/
1450 case -2: /* file not exist */
1451 case -1: /* failed to get free space */
1452 _mmcam_dbg_err("Error occured. [%d]", ret);
1453 if (sc->ferror_count == 2 && sc->ferror_send == FALSE) {
1454 sc->ferror_send = TRUE;
1455 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1457 msg.param.code = MM_ERROR_FILE_NOT_FOUND;
1459 msg.param.code = MM_ERROR_FILE_READ;
1461 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1466 return GST_PAD_PROBE_DROP; /* skip this buffer */
1468 default: /* succeeded to get free space */
1469 /* check free space for recording */
1470 /* get queued buffer size */
1471 if (sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst) {
1472 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst, "current-level-bytes", &aq_size);
1474 if (sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst) {
1475 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst, "current-level-bytes", &vq_size);
1478 queued_buffer = aq_size + vq_size;
1480 /* check free space */
1481 if (free_space < (_MMCAMCORDER_MINIMUM_SPACE + buffer_size + trailer_size + queued_buffer)) {
1482 _mmcam_dbg_warn("No more space for recording!!! Recording is paused.");
1483 _mmcam_dbg_warn("Free Space : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]," \
1484 " buffer size : [%" G_GUINT64_FORMAT "], queued buffer size : [%" G_GUINT64_FORMAT "]", \
1485 free_space, trailer_size, buffer_size, queued_buffer);
1487 if (!sc->isMaxsizePausing) {
1488 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1489 sc->isMaxsizePausing = TRUE;
1491 msg.id = MM_MESSAGE_CAMCORDER_NO_FREE_SPACE;
1492 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1495 return GST_PAD_PROBE_DROP;
1500 pthread_mutex_lock(&(videoinfo->size_check_lock));
1502 /* check max size of recorded file */
1503 if (videoinfo->max_size > 0 &&
1504 videoinfo->max_size < videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) {
1505 GstState pipeline_state = GST_STATE_VOID_PENDING;
1506 GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1507 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1508 _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \
1509 " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]",
1510 videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size);
1512 if (!sc->isMaxsizePausing) {
1513 sc->isMaxsizePausing = TRUE;
1514 gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ;
1515 if (pipeline_state == GST_STATE_PLAYING) {
1516 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1519 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1520 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1523 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1525 return GST_PAD_PROBE_DROP;
1528 videoinfo->filesize += (guint64)buffer_size;
1531 _mmcam_dbg_log("filesize %lld Byte, ", videoinfo->filesize);
1534 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1536 return GST_PAD_PROBE_OK;
1540 static GstPadProbeReturn __mmcamcorder_video_dataprobe_audio_disable(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1542 guint64 trailer_size = 0;
1543 guint64 rec_pipe_time = 0;
1544 unsigned int remained_time = 0;
1546 GstClockTime b_time;
1548 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1549 _MMCamcorderMsgItem msg;
1550 _MMCamcorderSubContext *sc = NULL;
1551 _MMCamcorderVideoInfo *videoinfo = NULL;
1553 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1555 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1556 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1558 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1559 mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK);
1560 mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK);
1562 videoinfo = sc->info_video;
1564 b_time = GST_BUFFER_PTS(buffer);
1566 rec_pipe_time = GST_TIME_AS_MSECONDS(b_time);
1568 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1569 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1574 /* check max time */
1575 if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) {
1576 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1577 rec_pipe_time, videoinfo->max_time);
1579 if (!sc->isMaxtimePausing) {
1580 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1582 sc->isMaxtimePausing = TRUE;
1584 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1585 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1586 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1587 msg.param.recording_status.remained_time = 0;
1588 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1590 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1591 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1594 return GST_PAD_PROBE_DROP;
1597 /* calculate remained time can be recorded */
1598 if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) {
1599 remained_time = videoinfo->max_time - rec_pipe_time;
1600 } else if (videoinfo->max_size > 0) {
1601 long double max_size = (long double)videoinfo->max_size;
1602 long double current_size = (long double)(videoinfo->filesize + trailer_size);
1604 remained_time = (unsigned int)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1607 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1608 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1609 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1610 msg.param.recording_status.remained_time = remained_time;
1611 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1614 _mmcam_dbg_log("time [%" GST_TIME_FORMAT "], size [%d]",
1615 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1618 if (videoinfo->record_timestamp_ratio != _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) {
1619 guint record_motion_rate = (guint)videoinfo->record_motion_rate;
1622 _mmcam_dbg_log("record_motion_rate %d, videoinfo->record_drop_count %d",
1623 record_motion_rate, videoinfo->record_drop_count);
1626 /* drop some frame if fast motion */
1627 if (videoinfo->record_motion_rate > _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) {
1628 if (record_motion_rate != (videoinfo->record_drop_count++)) {
1630 _mmcam_dbg_warn("drop frame");
1632 return GST_PAD_PROBE_DROP;
1635 videoinfo->record_drop_count = 1;
1637 _mmcam_dbg_warn("pass frame");
1641 GST_BUFFER_PTS(buffer) = b_time * (videoinfo->record_timestamp_ratio);
1644 return GST_PAD_PROBE_OK;
1648 static GstPadProbeReturn __mmcamcorder_audioque_dataprobe(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1650 _MMCamcorderMsgItem msg;
1651 guint64 trailer_size = 0;
1652 guint64 rec_pipe_time = 0;
1653 _MMCamcorderSubContext *sc = NULL;
1654 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1655 _MMCamcorderVideoInfo *videoinfo = NULL;
1656 unsigned int remained_time = 0;
1657 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1659 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1660 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1661 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1663 mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK);
1664 mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK);
1665 mmf_return_val_if_fail(sc->element, GST_PAD_PROBE_OK);
1667 videoinfo = sc->info_video;
1669 if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_PTS(buffer))) {
1670 _mmcam_dbg_err( "Buffer timestamp is invalid, check it");
1671 return GST_PAD_PROBE_OK;
1674 rec_pipe_time = GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer));
1676 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1677 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1682 /* calculate remained time can be recorded */
1683 if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) {
1684 remained_time = videoinfo->max_time - rec_pipe_time;
1685 } else if (videoinfo->max_size > 0) {
1686 long double max_size = (long double)videoinfo->max_size;
1687 long double current_size = (long double)(videoinfo->filesize + trailer_size);
1689 remained_time = (unsigned long long)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1692 if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) {
1693 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1694 rec_pipe_time, videoinfo->max_time);
1696 if (!sc->isMaxtimePausing) {
1697 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1699 sc->isMaxtimePausing = TRUE;
1701 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1702 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1703 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1704 msg.param.recording_status.remained_time = 0;
1705 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1707 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1708 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1711 return GST_PAD_PROBE_DROP;
1714 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1715 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1716 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1717 msg.param.recording_status.remained_time = remained_time;
1718 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1721 _mmcam_dbg_log("audio data probe :: time [%" GST_TIME_FORMAT "], size [%lld KB]",
1722 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1725 return GST_PAD_PROBE_OK;
1729 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_audio_mute(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1731 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1732 double volume = 0.0;
1735 int err = MM_ERROR_UNKNOWN;
1736 char *err_name = NULL;
1737 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1740 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1741 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_DROP);
1743 /*_mmcam_dbg_log("AUDIO SRC time stamp : [%" GST_TIME_FORMAT "] \n", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1744 err = mm_camcorder_get_attributes((MMHandleType)hcamcorder, &err_name,
1745 MMCAM_AUDIO_VOLUME, &volume,
1746 MMCAM_AUDIO_FORMAT, &format,
1747 MMCAM_AUDIO_CHANNEL, &channel,
1749 if (err != MM_ERROR_NONE) {
1750 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, err);
1751 SAFE_FREE(err_name);
1755 memset(&mapinfo, 0x0, sizeof(GstMapInfo));
1757 gst_buffer_map(buffer, &mapinfo, GST_MAP_READWRITE);
1759 /* Set audio stream NULL */
1760 if (volume == 0.0) {
1761 memset(mapinfo.data, 0, mapinfo.size);
1764 /* CALL audio stream callback */
1765 if (hcamcorder->astream_cb && buffer && mapinfo.data && mapinfo.size > 0) {
1766 MMCamcorderAudioStreamDataType stream;
1768 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) < MM_CAMCORDER_STATE_PREPARE) {
1769 _mmcam_dbg_warn("Not ready for stream callback");
1770 gst_buffer_unmap(buffer, &mapinfo);
1771 return GST_PAD_PROBE_OK;
1774 /*_mmcam_dbg_log("Call video steramCb, data[%p], Width[%d],Height[%d], Format[%d]",
1775 GST_BUFFER_DATA(buffer), width, height, format);*/
1777 stream.data = (void *)mapinfo.data;
1778 stream.format = format;
1779 stream.channel = channel;
1780 stream.length = mapinfo.size;
1781 stream.timestamp = (unsigned int)(GST_BUFFER_PTS(buffer)/1000000); /* nano -> milli second */
1783 _MMCAMCORDER_LOCK_ASTREAM_CALLBACK(hcamcorder);
1785 if (hcamcorder->astream_cb) {
1786 hcamcorder->astream_cb(&stream, hcamcorder->astream_cb_param);
1789 _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK(hcamcorder);
1792 gst_buffer_unmap(buffer, &mapinfo);
1793 return GST_PAD_PROBE_OK;
1797 static gboolean __mmcamcorder_add_metadata(MMHandleType handle, int fileformat)
1799 gboolean bret = FALSE;
1801 switch (fileformat) {
1802 case MM_FILE_FORMAT_3GP:
1803 case MM_FILE_FORMAT_MP4:
1804 bret = __mmcamcorder_add_metadata_mp4(handle);
1807 _mmcam_dbg_warn("Unsupported fileformat to insert location info (%d)", fileformat);
1815 static gboolean __mmcamcorder_add_metadata_mp4(MMHandleType handle)
1819 guint64 udta_size = 0;
1820 gint64 current_pos = 0;
1821 gint64 moov_pos = 0;
1822 gint64 udta_pos = 0;
1823 gdouble longitude = 0;
1824 gdouble latitude = 0;
1825 gdouble altitude = 0;
1827 int orientation = 0;
1829 char *err_name = NULL;
1830 char err_msg[MAX_ERROR_MESSAGE_LEN] = {'\0',};
1831 _MMCamcorderLocationInfo location_info = {0,0,0};
1832 _MMCamcorderLocationInfo geo_info = {0,0,0};
1834 _MMCamcorderVideoInfo *info = NULL;
1835 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1836 _MMCamcorderSubContext *sc = NULL;
1838 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1839 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1841 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1842 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1846 info = sc->info_video;
1848 f = fopen64(info->filename, "rb+");
1850 strerror_r(errno, err_msg, MAX_ERROR_MESSAGE_LEN);
1851 _mmcam_dbg_err("file open failed [%s]", err_msg);
1855 mm_camcorder_get_attributes(handle, &err_name,
1856 MMCAM_TAG_LATITUDE, &latitude,
1857 MMCAM_TAG_LONGITUDE, &longitude,
1858 MMCAM_TAG_ALTITUDE, &altitude,
1859 MMCAM_TAG_VIDEO_ORIENTATION, &orientation,
1860 MMCAM_TAG_GPS_ENABLE, &gps_enable,
1863 _mmcam_dbg_warn("Get tag attrs fail. (%s:%x)", err_name, err);
1864 SAFE_FREE (err_name);
1867 location_info.longitude = _mmcamcorder_double_to_fix(longitude);
1868 location_info.latitude = _mmcamcorder_double_to_fix(latitude);
1869 location_info.altitude = _mmcamcorder_double_to_fix(altitude);
1870 geo_info.longitude = longitude *10000;
1871 geo_info.latitude = latitude *10000;
1872 geo_info.altitude = altitude *10000;
1873 /* find udta container.
1874 if, there are udta container, write loci box after that
1875 else, make udta container and write loci box. */
1876 if (_mmcamcorder_find_fourcc(f, MMCAM_FOURCC('u','d','t','a'), TRUE)) {
1879 _mmcam_dbg_log("find udta container");
1882 if (fseek(f, -8L, SEEK_CUR) != 0) {
1886 udta_pos = ftello(f);
1891 nread = fread(&buf, sizeof(char), sizeof(buf), f);
1893 _mmcam_dbg_log("recorded file fread %d", nread);
1895 udta_size = _mmcamcorder_get_container_size(buf);
1897 /* goto end of udta and write 'loci' box */
1898 if (fseek(f, (udta_size-4L), SEEK_CUR) != 0) {
1903 if (!_mmcamcorder_write_loci(f, location_info)) {
1904 _mmcam_dbg_err("failed to write loci");
1908 if (!_mmcamcorder_write_geodata(f, geo_info)) {
1909 _mmcam_dbg_err("failed to write geodata");
1914 current_pos = ftello(f);
1915 if (current_pos < 0) {
1919 if (!_mmcamcorder_update_size(f, udta_pos, current_pos)) {
1923 _mmcam_dbg_log("No udta container");
1924 if (fseek(f, 0, SEEK_END) != 0) {
1928 if (!_mmcamcorder_write_udta(f, gps_enable, location_info, geo_info)) {
1929 _mmcam_dbg_err("failed to write udta");
1934 /* find moov container.
1935 update moov container size. */
1936 if((current_pos = ftello(f))<0)
1939 if (_mmcamcorder_find_tag(f, MMCAM_FOURCC('m','o','o','v'), TRUE)) {
1940 gint64 internal_pos = ftello(f);
1942 _mmcam_dbg_log("found moov container");
1943 if (fseek(f, -8L, SEEK_CUR) !=0) {
1947 moov_pos = ftello(f);
1952 if (!_mmcamcorder_update_size(f, moov_pos, current_pos)) {
1956 /* add orientation info */
1957 if (fseeko(f, internal_pos, SEEK_SET) < 0) {
1958 _mmcam_dbg_err("fseek failed : errno %d", errno);
1962 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t','r','a','k'), FALSE)) {
1963 _mmcam_dbg_err("failed to find [trak] tag");
1967 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t','k','h','d'), FALSE)) {
1968 _mmcam_dbg_err("failed to find [tkhd] tag");
1972 _mmcam_dbg_log("found [tkhd] tag");
1974 /* seek to start position of composition matrix */
1975 fseek(f, _OFFSET_COMPOSITION_MATRIX, SEEK_CUR);
1977 /* update composition matrix for orientation */
1978 _mmcamcorder_update_composition_matrix(f, orientation);
1980 _mmcam_dbg_err("No 'moov' container");
1992 _mmcam_dbg_err("ftell() returns negative value.");
1998 int _mmcamcorder_connect_video_stream_cb_signal(MMHandleType handle)
2000 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
2001 _MMCamcorderSubContext *sc = NULL;
2003 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2005 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
2006 mmf_return_val_if_fail(sc && sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2008 /* check video source element */
2009 if (sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst) {
2010 _mmcam_dbg_warn("connect video stream cb signal to _MMCAMCORDER_VIDEOSRC_SRC");
2011 MMCAMCORDER_SIGNAL_CONNECT(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst,
2012 _MMCAMCORDER_HANDLER_VIDEOREC, "video-stream-cb",
2013 G_CALLBACK(__mmcamcorder_video_stream_cb),
2015 return MM_ERROR_NONE;
2017 _mmcam_dbg_err("videosrc element is not created yet");
2018 return MM_ERROR_CAMCORDER_NOT_INITIALIZED;
2023 int _mmcamcorder_video_prepare_record(MMHandleType handle)
2025 int ret = MM_ERROR_NONE;
2027 _MMCamcorderVideoInfo *info = NULL;
2028 _MMCamcorderSubContext *sc = NULL;
2029 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
2031 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2033 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
2034 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2035 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2037 info = sc->info_video;
2039 _mmcam_dbg_warn("start");
2041 /* create encoding pipeline */
2042 ret =_mmcamcorder_create_recorder_pipeline((MMHandleType)hcamcorder);
2043 if (ret != MM_ERROR_NONE) {
2044 goto _ERR_PREPARE_RECORD;
2047 if (info->filename == NULL) {
2048 char *temp_filename = NULL;
2051 mm_camcorder_get_attributes(handle, NULL,
2052 MMCAM_TARGET_FILENAME, &temp_filename, &size,
2054 if (temp_filename) {
2055 info->filename = strdup(temp_filename);
2058 if (!info->filename) {
2059 _mmcam_dbg_err("strdup[src:%p] was failed", temp_filename);
2060 goto _ERR_PREPARE_RECORD;
2064 _mmcam_dbg_log("Record file name [%s]", info->filename);
2066 MMCAMCORDER_G_OBJECT_SET_POINTER(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename);
2067 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", 0);
2069 /* Adjust display FPS */
2070 sc->display_interval = 0;
2071 sc->previous_slot_time = 0;
2073 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PAUSED);
2074 if (ret != MM_ERROR_NONE) {
2075 goto _ERR_PREPARE_RECORD;
2078 _mmcam_dbg_warn("done");
2082 _ERR_PREPARE_RECORD:
2083 /* Remove recorder pipeline and recording file which size maybe zero */
2084 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
2085 if (info && info->filename) {
2086 _mmcam_dbg_log("file delete(%s)", info->filename);
2087 unlink(info->filename);