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)
466 int ret = MM_ERROR_NONE;
467 double motion_rate = _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE;
468 char *err_name = NULL;
469 char *temp_filename = NULL;
470 GstCameraControl *CameraControl = NULL;
471 GstCameraControlChannel *CameraControlChannel = NULL;
472 const GList *controls = NULL;
473 const GList *item = NULL;
476 GstElement *pipeline = NULL;
478 _MMCamcorderVideoInfo *info = NULL;
479 _MMCamcorderSubContext *sc = NULL;
480 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
482 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
484 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
485 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
486 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
487 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
489 info = sc->info_video;
491 _mmcam_dbg_log("Command(%d)", command);
493 pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
496 case _MMCamcorder_CMD_RECORD:
498 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) != MM_CAMCORDER_STATE_PAUSED) {
504 int ret_free_space = 0;
505 char *dir_name = NULL;
506 guint64 free_space = 0;
507 guint64 free_space_exceptsystem = 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, &free_space);
558 if(_mmcamcorder_check_file_path(dir_name)) {
559 if (_mmcamcorder_get_freespace_except_system(&free_space_exceptsystem) == MM_ERROR_NONE) {
560 hcamcorder->system_memory = free_space - free_space_exceptsystem;
561 free_space = free_space - hcamcorder->system_memory;
563 hcamcorder->system_memory = 0;
567 _mmcam_dbg_warn("current space - %s [%" G_GUINT64_FORMAT "], system [%" G_GUINT64_FORMAT "]",
568 dir_name, free_space, hcamcorder->system_memory);
570 if (_mmcamcorder_get_file_system_type(dir_name, &file_system_type) == 0) {
571 /* MSDOS_SUPER_MAGIC : 0x4d44 */
572 if (file_system_type == MSDOS_SUPER_MAGIC &&
573 (info->max_size == 0 || info->max_size > FAT32_FILE_SYSTEM_MAX_SIZE)) {
574 _mmcam_dbg_warn("FAT32 and too large max[%"G_GUINT64_FORMAT"], set max as %"G_GUINT64_FORMAT,
575 info->max_size, FAT32_FILE_SYSTEM_MAX_SIZE);
576 info->max_size = FAT32_FILE_SYSTEM_MAX_SIZE;
578 _mmcam_dbg_warn("file system 0x%x, max size %"G_GUINT64_FORMAT,
579 file_system_type, info->max_size);
582 _mmcam_dbg_warn("_mmcamcorder_get_file_system_type failed");
588 _mmcam_dbg_err("failed to get directory name");
592 if ((ret_free_space == -1) || free_space <= (_MMCAMCORDER_MINIMUM_SPACE<<1)) {
593 _mmcam_dbg_err("OUT of STORAGE [ret_free_space:%d or free space [%" G_GUINT64_FORMAT "] is smaller than [%d]",
594 ret_free_space, free_space, (_MMCAMCORDER_MINIMUM_SPACE<<1));
595 return MM_ERROR_OUT_OF_STORAGE;
598 pthread_mutex_lock(&(hcamcorder->task_thread_lock));
599 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst == NULL &&
600 hcamcorder->task_thread_state == _MMCAMCORDER_TASK_THREAD_STATE_NONE) {
601 /* Play record start sound */
602 _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_FILEPATH_REC_START_SND, FALSE);
604 pthread_mutex_unlock(&(hcamcorder->task_thread_lock));
606 _mmcam_dbg_warn("video size [%dx%d]", info->video_width, info->video_height);
608 if (info->video_width == 0 || info->video_height == 0) {
609 _mmcam_dbg_warn("video size is invalid [%dx%d] use preview size [%dx%d]",
610 info->video_width, info->video_height, info->preview_width, info->preview_height);
611 info->video_width = info->preview_width;
612 info->video_height = info->preview_height;
615 if (info->support_dual_stream) {
616 _mmcam_dbg_warn("DUAL STREAM MODE");
618 info->record_dual_stream = TRUE;
620 /* No need to restart preview */
621 info->restart_preview = FALSE;
623 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "video-width", info->video_width);
624 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "video-height", info->video_height);
625 } else if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
626 info->preview_width == info->video_width &&
627 info->preview_height == info->video_height) {
628 _mmcam_dbg_log("H264 preview mode and same resolution");
630 /* No need to restart preview */
631 info->restart_preview = FALSE;
633 /* always need to restart preview */
634 info->restart_preview = TRUE;
637 /* set recording hint */
638 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", TRUE);
640 if (info->restart_preview) {
641 /* stop preview and set new size */
642 _mmcam_dbg_log("restart preview");
644 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
645 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
647 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
649 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
650 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
652 if (ret != MM_ERROR_NONE) {
653 goto _ERR_CAMCORDER_VIDEO_COMMAND;
656 if (!_mmcamcorder_set_camera_resolution(handle, info->video_width, info->video_height)) {
657 ret = MM_ERROR_CAMCORDER_INTERNAL;
658 goto _ERR_CAMCORDER_VIDEO_COMMAND;
661 /* Start preview again with new setting */
662 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
663 if (ret != MM_ERROR_NONE) {
664 goto _ERR_CAMCORDER_VIDEO_COMMAND;
667 if (motion_rate < 1.0) {
668 _mmcam_dbg_warn("wait for stabilization of frame");
672 _mmcam_dbg_log("no need to restart preview");
675 _mmcamcorder_conf_get_value_int(handle, hcamcorder->conf_main,
676 CONFIGURE_CATEGORY_MAIN_RECORD,
680 _mmcamcorder_conf_get_value_int(handle, hcamcorder->conf_main,
681 CONFIGURE_CATEGORY_MAIN_RECORD,
682 "PassFirstVideoFrame",
683 &(sc->pass_first_vframe));
685 _mmcam_dbg_log("Drop video frame count[%d], Pass fisrt video frame count[%d]",
686 sc->drop_vframe, sc->pass_first_vframe);
688 info->record_drop_count = (guint)motion_rate;
689 info->record_motion_rate = motion_rate;
690 if (sc->is_modified_rate) {
691 info->record_timestamp_ratio = (_MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE/motion_rate);
693 info->record_timestamp_ratio = _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE;
696 _mmcam_dbg_warn("recording fps %d, motion rate %f, timestamp_ratio %f",
697 fps, info->record_motion_rate, info->record_timestamp_ratio);
699 /* set push buffer flag */
700 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_INIT;
701 info->base_video_ts = 0;
703 /* connect video stream cb signal */
704 /*130826 Connect video stream cb for handling fast record frame cb*/
705 if (info->record_dual_stream) {
706 if (_mmcamcorder_connect_video_stream_cb_signal((MMHandleType)hcamcorder) != MM_ERROR_NONE) {
707 goto _ERR_CAMCORDER_VIDEO_COMMAND;
711 /* start video stream */
712 if (info->record_dual_stream) {
713 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
715 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
717 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_START");
718 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_START);
720 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
722 _mmcam_dbg_err("could not get camera control");
726 /* check pre-created encode pipeline */
727 pthread_mutex_lock(&(hcamcorder->task_thread_lock));
728 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst == NULL &&
729 hcamcorder->task_thread_state == _MMCAMCORDER_TASK_THREAD_STATE_NONE) {
730 /* create encoding pipeline */
731 ret =_mmcamcorder_video_prepare_record((MMHandleType)hcamcorder);
732 if (ret != MM_ERROR_NONE) {
733 pthread_mutex_unlock(&(hcamcorder->task_thread_lock));
734 goto _ERR_CAMCORDER_VIDEO_COMMAND;
737 pthread_mutex_unlock(&(hcamcorder->task_thread_lock));
739 /* check recording start sound */
740 _mmcamcorder_sound_solo_play_wait(handle);
742 /**< To fix video recording hanging
743 1. use gst_element_set_start_time() instead of gst_pipeline_set_new_stream_time()
744 2. Set (GstClockTime)1 instead of (GstClockTime)0. Because of strict check in gstreamer 0.25,
745 basetime wouldn't change if you set (GstClockTime)0.
746 3. Move set start time position below PAUSED of pipeline.
748 //gst_element_set_start_time(GST_ELEMENT(sc->element[_MMCAMCORDER_MAIN_PIPE].gst), (GstClockTime)1);
749 //gst_element_set_start_time(GST_ELEMENT(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), (GstClockTime)1);
751 info->video_frame_count = 0;
752 info->is_firstframe = TRUE;
753 info->audio_frame_count = 0;
755 sc->ferror_send = FALSE;
756 sc->ferror_count = 0;
757 hcamcorder->error_occurs = FALSE;
758 sc->bget_eos = FALSE;
760 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PLAYING);
761 if (ret != MM_ERROR_NONE) {
762 /* stop video stream */
763 if (info->record_dual_stream) {
764 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
766 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
768 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
769 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
771 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
773 _mmcam_dbg_err("failed to get camera control");
777 /* Remove recorder pipeline and recording file which size maybe zero */
778 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
779 if (info->filename) {
780 _mmcam_dbg_log("file delete(%s)", info->filename);
781 unlink(info->filename);
783 goto _ERR_CAMCORDER_VIDEO_COMMAND;
786 /*set the camera control to create the GOP so that video record will get a new key frame*/
787 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
788 GST_IS_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst)) {
789 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
790 controls = gst_camera_control_list_channels(CameraControl);
791 if (controls != NULL) {
792 for (item = controls ; item && item->data ; item = item->next) {
793 CameraControlChannel = item->data;
794 _mmcam_dbg_log("CameraControlChannel->label %s", CameraControlChannel->label);
795 if (!strcmp(CameraControlChannel->label, "new-gop")) {
796 //gst_camera_control_set_value(CameraControl, CameraControlChannel, 1);
802 _mmcam_dbg_warn("failed to find new-gop control channel");
806 _mmcam_dbg_warn("Can't cast Video source into camera control or not H264 prevew format[%d]",
807 sc->info_image->preview_format);
812 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
813 GST_IS_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst)) {
814 /* generate and I-frame on resuming */
815 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
816 controls = gst_camera_control_list_channels(CameraControl);
817 if (controls != NULL) {
818 for (item = controls ; item && item->data ; item = item->next) {
819 CameraControlChannel = item->data;
820 _mmcam_dbg_log("CameraControlChannel->label %s", CameraControlChannel->label);
821 if (!strcmp(CameraControlChannel->label, "new-gop")) {
822 //gst_camera_control_set_value(CameraControl, CameraControlChannel, 1);
828 _mmcam_dbg_warn("failed to find new-gop control channel");
833 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", FALSE);
835 _mmcam_dbg_log("Object property settings done");
839 case _MMCamcorder_CMD_PAUSE:
843 if (info->b_commiting) {
844 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
845 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
848 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
849 if (sc->audio_disable) {
850 /* check only video frame */
851 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
853 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
854 _mmcam_dbg_err("Pause fail, frame count %" G_GUINT64_FORMAT "",
855 info->video_frame_count);
856 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
858 _mmcam_dbg_warn("Waiting for enough video frame, retrial[%d], frame %" G_GUINT64_FORMAT "",
859 count, info->video_frame_count);
862 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
864 /* check both of video and audio frame */
865 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
867 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
868 _mmcam_dbg_err("Pause fail, frame count VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]",
869 info->video_frame_count, info->audio_frame_count);
870 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
872 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]",
873 count, info->video_frame_count, info->audio_frame_count);
876 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
880 /* block encodebin */
881 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", TRUE);
884 case _MMCamcorder_CMD_CANCEL:
886 if (info->b_commiting) {
887 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
888 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
891 /* block push buffer */
892 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP;
894 ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
895 if (ret != MM_ERROR_NONE) {
896 goto _ERR_CAMCORDER_VIDEO_COMMAND;
899 /* set recording hint */
900 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", FALSE);
902 /* stop video stream */
903 if (info->record_dual_stream) {
904 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
906 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
908 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
909 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
911 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
913 _mmcam_dbg_err("failed to get camera control");
917 if (info->restart_preview) {
918 /* restart preview */
919 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
920 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
922 ret =_mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
924 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
925 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
926 if (ret != MM_ERROR_NONE) {
927 goto _ERR_CAMCORDER_VIDEO_COMMAND;
930 /* reset restart_preview for inset window layout */
931 info->restart_preview = FALSE;
933 if (!_mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height)) {
934 ret = MM_ERROR_CAMCORDER_INTERNAL;
935 goto _ERR_CAMCORDER_VIDEO_COMMAND;
938 ret =_mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
939 if (ret != MM_ERROR_NONE) {
940 goto _ERR_CAMCORDER_VIDEO_COMMAND;
944 /* remove target file */
945 if (info->filename) {
946 _mmcam_dbg_log("file delete(%s)", info->filename);
947 unlink(info->filename);
950 sc->isMaxsizePausing = FALSE;
951 sc->isMaxtimePausing = FALSE;
953 sc->display_interval = 0;
954 sc->previous_slot_time = 0;
955 info->video_frame_count = 0;
956 info->audio_frame_count = 0;
960 case _MMCamcorder_CMD_COMMIT:
964 if (info->b_commiting) {
965 _mmcam_dbg_err("now on commiting previous file!!(command : %d)", command);
966 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
968 _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT : start");
969 info->b_commiting = TRUE;
970 sc->bget_eos = FALSE;
973 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
974 if (sc->audio_disable) {
975 /* check only video frame */
976 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
978 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
979 _mmcam_dbg_err("Commit fail, frame count is %" G_GUINT64_FORMAT "",
980 info->video_frame_count);
981 info->b_commiting = FALSE;
982 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
984 _mmcam_dbg_warn("Waiting for enough video frame, retrial [%d], frame %" G_GUINT64_FORMAT "",
985 count, info->video_frame_count);
988 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
990 /* check both of video and audio frame */
991 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
993 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
994 _mmcam_dbg_err("Commit fail, VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]",
995 info->video_frame_count, info->audio_frame_count);
997 info->b_commiting = FALSE;
998 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
1000 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]",
1001 count, info->video_frame_count, info->audio_frame_count);
1004 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
1008 /* block push buffer */
1009 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP;
1010 _mmcam_dbg_log("block push buffer to appsrc");
1012 if (sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst != NULL) {
1013 ret = gst_element_send_event(sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, gst_event_new_eos());
1014 _mmcam_dbg_warn("send eos to appsrc result : %d", ret);
1017 if (sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst != NULL) {
1018 ret = gst_element_send_event(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst, gst_event_new_eos());
1019 _mmcam_dbg_warn("send eos to audiosrc result : %d", ret);
1023 sc->display_interval = 0;
1024 sc->previous_slot_time = 0;
1026 /* init system memory size */
1027 hcamcorder->system_memory = 0;
1030 _mmcam_dbg_log("Start to wait EOS");
1031 ret =_mmcamcorder_get_eos_message(handle);
1032 if (ret != MM_ERROR_NONE) {
1033 info->b_commiting = FALSE;
1034 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1039 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
1040 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1043 return MM_ERROR_NONE;
1045 _ERR_CAMCORDER_VIDEO_COMMAND:
1046 if (command == _MMCamcorder_CMD_RECORD) {
1047 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
1054 int _mmcamcorder_video_handle_eos(MMHandleType handle)
1056 int ret = MM_ERROR_NONE;
1058 guint64 file_size = 0;
1060 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1061 _MMCamcorderSubContext *sc = NULL;
1062 _MMCamcorderVideoInfo *info = NULL;
1063 _MMCamcorderMsgItem msg;
1064 MMCamRecordingReport *report = NULL;
1066 mmf_return_val_if_fail(hcamcorder, FALSE);
1068 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1069 mmf_return_val_if_fail(sc, FALSE);
1070 mmf_return_val_if_fail(sc->info_video, FALSE);
1072 info = sc->info_video;
1076 if (hcamcorder->state_change_by_system != _MMCAMCORDER_STATE_CHANGE_BY_ASM) {
1077 /* Play record stop sound */
1078 _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_FILEPATH_REC_STOP_SND, FALSE);
1080 _mmcam_dbg_warn("Play stop sound through pulseaudio");
1082 #ifdef _MMCAMCORDER_UPLOAD_SAMPLE
1083 _mmcamcorder_sound_init(handle, _MMCAMCORDER_FILEPATH_REC_STOP_SND);
1084 #else /* _MMCAMCORDER_UPLOAD_SAMPLE */
1085 _mmcamcorder_sound_init(handle);
1086 #endif /* _MMCAMCORDER_UPLOAD_SAMPLE */
1088 _mmcamcorder_sound_play((MMHandleType)hcamcorder, _MMCAMCORDER_SAMPLE_SOUND_NAME_REC_STOP, TRUE);
1090 _mmcamcorder_sound_finalize(handle);
1093 /* remove blocking part */
1094 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
1096 mm_camcorder_get_attributes(handle, NULL,
1097 MMCAM_RECORDER_TAG_ENABLE, &enabletag,
1100 ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
1101 if (ret != MM_ERROR_NONE) {
1102 _mmcam_dbg_warn("_MMCamcorder_CMD_COMMIT:__mmcamcorder_remove_recorder_pipeline failed. error[%x]", ret);
1105 /* set recording hint */
1106 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", FALSE);
1108 /* stop video stream */
1109 if (info->record_dual_stream) {
1110 GstCameraControl *control = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
1112 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
1114 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
1115 gst_camera_control_set_record_command(control, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
1117 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
1119 _mmcam_dbg_err("failed to get camera control");
1123 if (enabletag && !(sc->ferror_send)) {
1124 ret = __mmcamcorder_add_metadata((MMHandleType)hcamcorder, info->fileformat);
1126 _mmcam_dbg_log("Writing location information SUCCEEDED !!");
1128 _mmcam_dbg_err("Writing location information FAILED !!");
1132 /* Check file size */
1133 if (info->max_size > 0) {
1134 _mmcamcorder_get_file_size(info->filename, &file_size);
1135 _mmcam_dbg_log("MAX size %lld byte - created filesize %lld byte",
1136 info->max_size, file_size);
1138 if (file_size > info->max_size) {
1139 _MMCamcorderMsgItem message;
1140 _mmcam_dbg_err("File size is greater than max size !!");
1141 message.id = MM_MESSAGE_CAMCORDER_ERROR;
1142 message.param.code = MM_ERROR_CAMCORDER_FILE_SIZE_OVER;
1143 _mmcamcorder_send_message((MMHandleType)hcamcorder, &message);
1147 if (info->restart_preview) {
1149 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
1150 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
1152 _mmcam_dbg_log("Set state of pipeline as READY");
1153 ret = _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_READY);
1156 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
1157 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
1158 if (ret != MM_ERROR_NONE) {
1159 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1160 msg.param.code = ret;
1161 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1162 _mmcam_dbg_err("Failed to set state READY[%x]", ret);
1165 /* reset restart_preview for inset window layout */
1166 info->restart_preview = FALSE;
1168 /* recover preview size */
1169 _mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height);
1171 ret =_mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_PLAYING);
1172 /* Do not return when error is occurred.
1173 Recording file was created successfully, but starting pipeline failed */
1174 if (ret != MM_ERROR_NONE) {
1175 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1176 msg.param.code = ret;
1177 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1178 _mmcam_dbg_err("Failed to set state PLAYING[%x]", ret);
1181 _mmcam_dbg_log("No need to restart preview");
1184 /* Send recording report to application */
1185 msg.id = MM_MESSAGE_CAMCORDER_VIDEO_CAPTURED;
1186 report = (MMCamRecordingReport *)malloc(sizeof(MMCamRecordingReport));
1188 _mmcam_dbg_err("Recording report fail(%s). Out of memory.", info->filename);
1190 report->recording_filename = strdup(info->filename);
1191 msg.param.data= report;
1193 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1197 sc->pipeline_time = 0;
1199 sc->isMaxsizePausing = FALSE; /*In async function, this variable should set in callback function. */
1200 sc->isMaxtimePausing = FALSE;
1201 hcamcorder->error_occurs = FALSE;
1203 info->video_frame_count = 0;
1204 info->audio_frame_count = 0;
1206 info->b_commiting = FALSE;
1208 if (hcamcorder->state_change_by_system != _MMCAMCORDER_STATE_CHANGE_BY_ASM) {
1209 /* check recording stop sound */
1210 _mmcamcorder_sound_solo_play_wait(handle);
1213 _mmcam_dbg_err("_MMCamcorder_CMD_COMMIT : end");
1220 * This function is record video data probing function.
1221 * If this function is linked with certain pad by gst_pad_add_buffer_probe(),
1222 * this function will be called when data stream pass through the pad.
1224 * @param[in] pad probing pad which calls this function.
1225 * @param[in] buffer buffer which contains stream data.
1226 * @param[in] u_data user data.
1227 * @return This function returns true on success, or false value with error
1231 static GstPadProbeReturn __mmcamcorder_eventprobe_monitor(GstPad *pad, GstPadProbeInfo *info, gpointer u_data){
1232 GstEvent *event = GST_PAD_PROBE_INFO_EVENT(info);
1233 switch (GST_EVENT_TYPE(event)) {
1234 case GST_EVENT_UNKNOWN:
1235 /* upstream events */
1237 case GST_EVENT_SEEK:
1238 case GST_EVENT_NAVIGATION:
1239 case GST_EVENT_LATENCY:
1240 /* downstream serialized events */
1241 case GST_EVENT_SEGMENT :
1243 case GST_EVENT_BUFFERSIZE:
1244 _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1247 _mmcam_dbg_warn("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1249 /* bidirectional events */
1250 case GST_EVENT_FLUSH_START:
1251 case GST_EVENT_FLUSH_STOP:
1252 _mmcam_dbg_err("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1255 _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1259 return GST_PAD_PROBE_OK;
1263 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_check(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1265 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1266 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1268 _MMCamcorderSubContext *sc = NULL;
1269 _MMCamcorderVideoInfo *videoinfo = NULL;
1270 _MMCamcorderMsgItem msg;
1271 guint64 buffer_size = 0;
1272 guint64 trailer_size = 0;
1274 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1275 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1276 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1278 mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK);
1279 videoinfo = sc->info_video;
1281 /* get buffer size */
1282 if (!gst_buffer_map(buffer, &mapinfo, GST_MAP_READ)) {
1283 _mmcam_dbg_warn("map failed : buffer %p", buffer);
1284 return GST_PAD_PROBE_OK;
1287 buffer_size = mapinfo.size;
1288 gst_buffer_unmap(buffer, &mapinfo);
1290 /*_mmcam_dbg_err("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1292 pthread_mutex_lock(&(videoinfo->size_check_lock));
1294 if (videoinfo->audio_frame_count == 0) {
1295 videoinfo->filesize += buffer_size;
1296 videoinfo->audio_frame_count++;
1297 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1298 return GST_PAD_PROBE_OK;
1301 if (sc->ferror_send || sc->isMaxsizePausing) {
1302 _mmcam_dbg_warn("Recording is paused, drop frames");
1303 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1304 return GST_PAD_PROBE_DROP;
1307 /* get trailer size */
1308 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1309 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1314 /* check max size of recorded file */
1315 if (videoinfo->max_size > 0 &&
1316 videoinfo->max_size < videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) {
1317 GstState pipeline_state = GST_STATE_VOID_PENDING;
1318 GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1319 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1320 _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \
1321 " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]",
1322 videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size);
1324 if (!sc->isMaxsizePausing) {
1325 sc->isMaxsizePausing = TRUE;
1326 gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ;
1327 if (pipeline_state == GST_STATE_PLAYING) {
1328 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1331 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1332 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1335 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1340 videoinfo->filesize += buffer_size;
1341 videoinfo->audio_frame_count++;
1343 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1345 return GST_PAD_PROBE_OK;
1349 static GstPadProbeReturn __mmcamcorder_video_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1354 guint64 free_space = 0;
1355 guint64 buffer_size = 0;
1356 guint64 trailer_size = 0;
1357 guint64 queued_buffer = 0;
1358 char *filename = NULL;
1359 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1362 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1363 _MMCamcorderMsgItem msg;
1364 _MMCamcorderSubContext *sc = NULL;
1365 _MMCamcorderVideoInfo *videoinfo = NULL;
1367 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1368 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1370 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1371 mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK);
1372 videoinfo = sc->info_video;
1374 /*_mmcam_dbg_log("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1375 if (sc->ferror_send) {
1376 _mmcam_dbg_warn("file write error, drop frames");
1377 return GST_PAD_PROBE_DROP;
1380 gst_buffer_map(buffer, &mapinfo, GST_MAP_READ);
1381 buffer_size = mapinfo.size;
1382 gst_buffer_unmap(buffer, &mapinfo);
1384 videoinfo->video_frame_count++;
1385 if (videoinfo->video_frame_count <= (guint64)_MMCAMCORDER_MINIMUM_FRAME) {
1386 /* _mmcam_dbg_log("Pass minimum frame: info->video_frame_count: %" G_GUINT64_FORMAT " ",
1387 info->video_frame_count); */
1388 pthread_mutex_lock(&(videoinfo->size_check_lock));
1389 videoinfo->filesize += buffer_size;
1390 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1391 return GST_PAD_PROBE_OK;
1394 /* get trailer size */
1395 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1396 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1401 filename = videoinfo->filename;
1402 ret = _mmcamcorder_get_freespace(filename, &free_space);
1404 if(_mmcamcorder_check_file_path(filename) && hcamcorder->system_memory) {
1405 free_space = free_space - hcamcorder->system_memory;
1408 /*_mmcam_dbg_log("check free space for recording");*/
1411 case -2: /* file not exist */
1412 case -1: /* failed to get free space */
1413 _mmcam_dbg_err("Error occured. [%d]", ret);
1414 if (sc->ferror_count == 2 && sc->ferror_send == FALSE) {
1415 sc->ferror_send = TRUE;
1416 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1418 msg.param.code = MM_ERROR_FILE_NOT_FOUND;
1420 msg.param.code = MM_ERROR_FILE_READ;
1422 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1427 return GST_PAD_PROBE_DROP; /* skip this buffer */
1429 default: /* succeeded to get free space */
1430 /* check free space for recording */
1431 /* get queued buffer size */
1432 if (sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst) {
1433 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst, "current-level-bytes", &aq_size);
1435 if (sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst) {
1436 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst, "current-level-bytes", &vq_size);
1439 queued_buffer = aq_size + vq_size;
1441 /* check free space */
1442 if (free_space < (_MMCAMCORDER_MINIMUM_SPACE + buffer_size + trailer_size + queued_buffer)) {
1443 _mmcam_dbg_warn("No more space for recording!!! Recording is paused.");
1444 _mmcam_dbg_warn("Free Space : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]," \
1445 " buffer size : [%" G_GUINT64_FORMAT "], queued buffer size : [%" G_GUINT64_FORMAT "]", \
1446 free_space, trailer_size, buffer_size, queued_buffer);
1448 if (!sc->isMaxsizePausing) {
1449 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1450 sc->isMaxsizePausing = TRUE;
1452 msg.id = MM_MESSAGE_CAMCORDER_NO_FREE_SPACE;
1453 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1456 return GST_PAD_PROBE_DROP;
1461 pthread_mutex_lock(&(videoinfo->size_check_lock));
1463 /* check max size of recorded file */
1464 if (videoinfo->max_size > 0 &&
1465 videoinfo->max_size < videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) {
1466 GstState pipeline_state = GST_STATE_VOID_PENDING;
1467 GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1468 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1469 _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \
1470 " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]",
1471 videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size);
1473 if (!sc->isMaxsizePausing) {
1474 sc->isMaxsizePausing = TRUE;
1475 gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ;
1476 if (pipeline_state == GST_STATE_PLAYING) {
1477 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1480 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1481 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1484 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1486 return GST_PAD_PROBE_DROP;
1489 videoinfo->filesize += (guint64)buffer_size;
1492 _mmcam_dbg_log("filesize %lld Byte, ", videoinfo->filesize);
1495 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1497 return GST_PAD_PROBE_OK;
1501 static GstPadProbeReturn __mmcamcorder_video_dataprobe_audio_disable(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1503 guint64 trailer_size = 0;
1504 guint64 rec_pipe_time = 0;
1505 unsigned int remained_time = 0;
1507 GstClockTime b_time;
1509 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1510 _MMCamcorderMsgItem msg;
1511 _MMCamcorderSubContext *sc = NULL;
1512 _MMCamcorderVideoInfo *videoinfo = NULL;
1514 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1516 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1517 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1519 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1520 mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK);
1521 mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK);
1523 videoinfo = sc->info_video;
1525 b_time = GST_BUFFER_PTS(buffer);
1527 rec_pipe_time = GST_TIME_AS_MSECONDS(b_time);
1529 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1530 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1535 /* check max time */
1536 if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) {
1537 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1538 rec_pipe_time, videoinfo->max_time);
1540 if (!sc->isMaxtimePausing) {
1541 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1543 sc->isMaxtimePausing = TRUE;
1545 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1546 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1547 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1548 msg.param.recording_status.remained_time = 0;
1549 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1551 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1552 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1555 return GST_PAD_PROBE_DROP;
1558 /* calculate remained time can be recorded */
1559 if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) {
1560 remained_time = videoinfo->max_time - rec_pipe_time;
1561 } else if (videoinfo->max_size > 0) {
1562 long double max_size = (long double)videoinfo->max_size;
1563 long double current_size = (long double)(videoinfo->filesize + trailer_size);
1565 remained_time = (unsigned int)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1568 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1569 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1570 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1571 msg.param.recording_status.remained_time = remained_time;
1572 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1575 _mmcam_dbg_log("time [%" GST_TIME_FORMAT "], size [%d]",
1576 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1579 if (videoinfo->record_timestamp_ratio != _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) {
1580 guint record_motion_rate = (guint)videoinfo->record_motion_rate;
1583 _mmcam_dbg_log("record_motion_rate %d, videoinfo->record_drop_count %d",
1584 record_motion_rate, videoinfo->record_drop_count);
1587 /* drop some frame if fast motion */
1588 if (videoinfo->record_motion_rate > _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) {
1589 if (record_motion_rate != (videoinfo->record_drop_count++)) {
1591 _mmcam_dbg_warn("drop frame");
1593 return GST_PAD_PROBE_DROP;
1596 videoinfo->record_drop_count = 1;
1598 _mmcam_dbg_warn("pass frame");
1602 GST_BUFFER_PTS(buffer) = b_time * (videoinfo->record_timestamp_ratio);
1605 return GST_PAD_PROBE_OK;
1609 static GstPadProbeReturn __mmcamcorder_audioque_dataprobe(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1611 _MMCamcorderMsgItem msg;
1612 guint64 trailer_size = 0;
1613 guint64 rec_pipe_time = 0;
1614 _MMCamcorderSubContext *sc = NULL;
1615 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1616 _MMCamcorderVideoInfo *videoinfo = NULL;
1617 unsigned int remained_time = 0;
1618 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1620 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1621 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1622 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1624 mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK);
1625 mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK);
1626 mmf_return_val_if_fail(sc->element, GST_PAD_PROBE_OK);
1628 videoinfo = sc->info_video;
1630 if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_PTS(buffer))) {
1631 _mmcam_dbg_err( "Buffer timestamp is invalid, check it");
1632 return GST_PAD_PROBE_OK;
1635 rec_pipe_time = GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer));
1637 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1638 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1643 /* calculate remained time can be recorded */
1644 if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) {
1645 remained_time = videoinfo->max_time - rec_pipe_time;
1646 } else if (videoinfo->max_size > 0) {
1647 long double max_size = (long double)videoinfo->max_size;
1648 long double current_size = (long double)(videoinfo->filesize + trailer_size);
1650 remained_time = (unsigned long long)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1653 if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) {
1654 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1655 rec_pipe_time, videoinfo->max_time);
1657 if (!sc->isMaxtimePausing) {
1658 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1660 sc->isMaxtimePausing = TRUE;
1662 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1663 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1664 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1665 msg.param.recording_status.remained_time = 0;
1666 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1668 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1669 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1672 return GST_PAD_PROBE_DROP;
1675 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1676 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1677 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1678 msg.param.recording_status.remained_time = remained_time;
1679 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1682 _mmcam_dbg_log("audio data probe :: time [%" GST_TIME_FORMAT "], size [%lld KB]",
1683 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1686 return GST_PAD_PROBE_OK;
1690 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_audio_mute(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1692 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1693 double volume = 0.0;
1696 int err = MM_ERROR_UNKNOWN;
1697 char *err_name = NULL;
1698 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1701 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1702 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_DROP);
1704 /*_mmcam_dbg_log("AUDIO SRC time stamp : [%" GST_TIME_FORMAT "] \n", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1705 err = mm_camcorder_get_attributes((MMHandleType)hcamcorder, &err_name,
1706 MMCAM_AUDIO_VOLUME, &volume,
1707 MMCAM_AUDIO_FORMAT, &format,
1708 MMCAM_AUDIO_CHANNEL, &channel,
1710 if (err != MM_ERROR_NONE) {
1711 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, err);
1712 SAFE_FREE(err_name);
1716 memset(&mapinfo, 0x0, sizeof(GstMapInfo));
1718 gst_buffer_map(buffer, &mapinfo, GST_MAP_READWRITE);
1720 /* Set audio stream NULL */
1721 if (volume == 0.0) {
1722 memset(mapinfo.data, 0, mapinfo.size);
1725 /* CALL audio stream callback */
1726 if (hcamcorder->astream_cb && buffer && mapinfo.data && mapinfo.size > 0) {
1727 MMCamcorderAudioStreamDataType stream;
1729 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) < MM_CAMCORDER_STATE_PREPARE) {
1730 _mmcam_dbg_warn("Not ready for stream callback");
1731 gst_buffer_unmap(buffer, &mapinfo);
1732 return GST_PAD_PROBE_OK;
1735 /*_mmcam_dbg_log("Call video steramCb, data[%p], Width[%d],Height[%d], Format[%d]",
1736 GST_BUFFER_DATA(buffer), width, height, format);*/
1738 stream.data = (void *)mapinfo.data;
1739 stream.format = format;
1740 stream.channel = channel;
1741 stream.length = mapinfo.size;
1742 stream.timestamp = (unsigned int)(GST_BUFFER_PTS(buffer)/1000000); /* nano -> milli second */
1744 _MMCAMCORDER_LOCK_ASTREAM_CALLBACK(hcamcorder);
1746 if (hcamcorder->astream_cb) {
1747 hcamcorder->astream_cb(&stream, hcamcorder->astream_cb_param);
1750 _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK(hcamcorder);
1753 gst_buffer_unmap(buffer, &mapinfo);
1754 return GST_PAD_PROBE_OK;
1758 static gboolean __mmcamcorder_add_metadata(MMHandleType handle, int fileformat)
1760 gboolean bret = FALSE;
1762 switch (fileformat) {
1763 case MM_FILE_FORMAT_3GP:
1764 case MM_FILE_FORMAT_MP4:
1765 bret = __mmcamcorder_add_metadata_mp4(handle);
1768 _mmcam_dbg_warn("Unsupported fileformat to insert location info (%d)", fileformat);
1776 static gboolean __mmcamcorder_add_metadata_mp4(MMHandleType handle)
1780 guint64 udta_size = 0;
1781 gint64 current_pos = 0;
1782 gint64 moov_pos = 0;
1783 gint64 udta_pos = 0;
1784 gdouble longitude = 0;
1785 gdouble latitude = 0;
1786 gdouble altitude = 0;
1788 int orientation = 0;
1790 char *err_name = NULL;
1791 char err_msg[MAX_ERROR_MESSAGE_LEN] = {'\0',};
1792 _MMCamcorderLocationInfo location_info = {0,0,0};
1793 _MMCamcorderLocationInfo geo_info = {0,0,0};
1795 _MMCamcorderVideoInfo *info = NULL;
1796 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1797 _MMCamcorderSubContext *sc = NULL;
1799 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1800 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1802 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1803 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1807 info = sc->info_video;
1809 f = fopen64(info->filename, "rb+");
1811 strerror_r(errno, err_msg, MAX_ERROR_MESSAGE_LEN);
1812 _mmcam_dbg_err("file open failed [%s]", err_msg);
1816 mm_camcorder_get_attributes(handle, &err_name,
1817 MMCAM_TAG_LATITUDE, &latitude,
1818 MMCAM_TAG_LONGITUDE, &longitude,
1819 MMCAM_TAG_ALTITUDE, &altitude,
1820 MMCAM_TAG_VIDEO_ORIENTATION, &orientation,
1821 MMCAM_TAG_GPS_ENABLE, &gps_enable,
1824 _mmcam_dbg_warn("Get tag attrs fail. (%s:%x)", err_name, err);
1825 SAFE_FREE (err_name);
1828 location_info.longitude = _mmcamcorder_double_to_fix(longitude);
1829 location_info.latitude = _mmcamcorder_double_to_fix(latitude);
1830 location_info.altitude = _mmcamcorder_double_to_fix(altitude);
1831 geo_info.longitude = longitude *10000;
1832 geo_info.latitude = latitude *10000;
1833 geo_info.altitude = altitude *10000;
1834 /* find udta container.
1835 if, there are udta container, write loci box after that
1836 else, make udta container and write loci box. */
1837 if (_mmcamcorder_find_fourcc(f, MMCAM_FOURCC('u','d','t','a'), TRUE)) {
1840 _mmcam_dbg_log("find udta container");
1843 if (fseek(f, -8L, SEEK_CUR) != 0) {
1847 udta_pos = ftello(f);
1852 nread = fread(&buf, sizeof(char), sizeof(buf), f);
1854 _mmcam_dbg_log("recorded file fread %d", nread);
1856 udta_size = _mmcamcorder_get_container_size(buf);
1858 /* goto end of udta and write 'loci' box */
1859 if (fseek(f, (udta_size-4L), SEEK_CUR) != 0) {
1864 if (!_mmcamcorder_write_loci(f, location_info)) {
1865 _mmcam_dbg_err("failed to write loci");
1869 if (!_mmcamcorder_write_geodata(f, geo_info)) {
1870 _mmcam_dbg_err("failed to write geodata");
1875 current_pos = ftello(f);
1876 if (current_pos < 0) {
1880 if (!_mmcamcorder_update_size(f, udta_pos, current_pos)) {
1884 _mmcam_dbg_log("No udta container");
1885 if (fseek(f, 0, SEEK_END) != 0) {
1889 if (!_mmcamcorder_write_udta(f, gps_enable, location_info, geo_info)) {
1890 _mmcam_dbg_err("failed to write udta");
1895 /* find moov container.
1896 update moov container size. */
1897 if((current_pos = ftello(f))<0)
1900 if (_mmcamcorder_find_tag(f, MMCAM_FOURCC('m','o','o','v'), TRUE)) {
1901 gint64 internal_pos = ftello(f);
1903 _mmcam_dbg_log("found moov container");
1904 if (fseek(f, -8L, SEEK_CUR) !=0) {
1908 moov_pos = ftello(f);
1913 if (!_mmcamcorder_update_size(f, moov_pos, current_pos)) {
1917 /* add orientation info */
1918 if (fseeko(f, internal_pos, SEEK_SET) < 0) {
1919 _mmcam_dbg_err("fseek failed : errno %d", errno);
1923 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t','r','a','k'), FALSE)) {
1924 _mmcam_dbg_err("failed to find [trak] tag");
1928 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t','k','h','d'), FALSE)) {
1929 _mmcam_dbg_err("failed to find [tkhd] tag");
1933 _mmcam_dbg_log("found [tkhd] tag");
1935 /* seek to start position of composition matrix */
1936 fseek(f, _OFFSET_COMPOSITION_MATRIX, SEEK_CUR);
1938 /* update composition matrix for orientation */
1939 _mmcamcorder_update_composition_matrix(f, orientation);
1941 _mmcam_dbg_err("No 'moov' container");
1953 _mmcam_dbg_err("ftell() returns negative value.");
1959 int _mmcamcorder_connect_video_stream_cb_signal(MMHandleType handle)
1961 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1962 _MMCamcorderSubContext *sc = NULL;
1964 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1966 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1967 mmf_return_val_if_fail(sc && sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1969 /* check video source element */
1970 if (sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst) {
1971 _mmcam_dbg_warn("connect video stream cb signal to _MMCAMCORDER_VIDEOSRC_SRC");
1972 MMCAMCORDER_SIGNAL_CONNECT(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst,
1973 _MMCAMCORDER_HANDLER_VIDEOREC, "video-stream-cb",
1974 G_CALLBACK(__mmcamcorder_video_stream_cb),
1976 return MM_ERROR_NONE;
1978 _mmcam_dbg_err("videosrc element is not created yet");
1979 return MM_ERROR_CAMCORDER_NOT_INITIALIZED;
1984 int _mmcamcorder_video_prepare_record(MMHandleType handle)
1986 int ret = MM_ERROR_NONE;
1988 _MMCamcorderVideoInfo *info = NULL;
1989 _MMCamcorderSubContext *sc = NULL;
1990 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1992 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1994 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1995 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1996 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1998 info = sc->info_video;
2000 _mmcam_dbg_warn("start");
2002 /* create encoding pipeline */
2003 ret =_mmcamcorder_create_recorder_pipeline((MMHandleType)hcamcorder);
2004 if (ret != MM_ERROR_NONE) {
2005 goto _ERR_PREPARE_RECORD;
2008 if (info->filename == NULL) {
2009 char *temp_filename = NULL;
2012 mm_camcorder_get_attributes(handle, NULL,
2013 MMCAM_TARGET_FILENAME, &temp_filename, &size,
2015 if (temp_filename) {
2016 info->filename = strdup(temp_filename);
2019 if (!info->filename) {
2020 _mmcam_dbg_err("strdup[src:%p] was failed", temp_filename);
2021 goto _ERR_PREPARE_RECORD;
2025 _mmcam_dbg_log("Record file name [%s]", info->filename);
2027 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename);
2028 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", 0);
2030 /* Adjust display FPS */
2031 sc->display_interval = 0;
2032 sc->previous_slot_time = 0;
2034 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PAUSED);
2035 if (ret != MM_ERROR_NONE) {
2036 goto _ERR_PREPARE_RECORD;
2039 _mmcam_dbg_warn("done");
2043 _ERR_PREPARE_RECORD:
2044 /* Remove recorder pipeline and recording file which size maybe zero */
2045 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
2046 if (info && info->filename) {
2047 _mmcam_dbg_log("file delete(%s)", info->filename);
2048 unlink(info->filename);