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 if (info->restart_preview) {
638 /* stop preview and set new size */
639 _mmcam_dbg_log("restart preview");
641 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
642 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
644 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
646 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
647 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
649 if (ret != MM_ERROR_NONE) {
650 goto _ERR_CAMCORDER_VIDEO_COMMAND;
653 if (!_mmcamcorder_set_camera_resolution(handle, info->video_width, info->video_height)) {
654 ret = MM_ERROR_CAMCORDER_INTERNAL;
655 goto _ERR_CAMCORDER_VIDEO_COMMAND;
658 /* Start preview again with new setting */
659 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
660 if (ret != MM_ERROR_NONE) {
661 goto _ERR_CAMCORDER_VIDEO_COMMAND;
664 if (motion_rate < 1.0) {
665 _mmcam_dbg_warn("wait for stabilization of frame");
669 _mmcam_dbg_log("no need to restart preview");
672 _mmcamcorder_conf_get_value_int(handle, hcamcorder->conf_main,
673 CONFIGURE_CATEGORY_MAIN_RECORD,
677 _mmcamcorder_conf_get_value_int(handle, hcamcorder->conf_main,
678 CONFIGURE_CATEGORY_MAIN_RECORD,
679 "PassFirstVideoFrame",
680 &(sc->pass_first_vframe));
682 _mmcam_dbg_log("Drop video frame count[%d], Pass fisrt video frame count[%d]",
683 sc->drop_vframe, sc->pass_first_vframe);
685 info->record_drop_count = (guint)motion_rate;
686 info->record_motion_rate = motion_rate;
687 if (sc->is_modified_rate) {
688 info->record_timestamp_ratio = (_MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE/motion_rate);
690 info->record_timestamp_ratio = _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE;
693 _mmcam_dbg_warn("recording fps %d, motion rate %f, timestamp_ratio %f",
694 fps, info->record_motion_rate, info->record_timestamp_ratio);
696 /* set push buffer flag */
697 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_INIT;
698 info->base_video_ts = 0;
700 /* connect video stream cb signal */
701 /*130826 Connect video stream cb for handling fast record frame cb*/
702 if (info->record_dual_stream) {
703 if (_mmcamcorder_connect_video_stream_cb_signal((MMHandleType)hcamcorder) != MM_ERROR_NONE) {
704 goto _ERR_CAMCORDER_VIDEO_COMMAND;
708 /* start video stream */
709 if (info->record_dual_stream) {
710 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
712 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
713 #ifdef LATEST_CAMERA_CONTROL
714 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_START");
715 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_START);
716 #endif /* LATEST_CAMERA_CONTROL */
717 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
719 _mmcam_dbg_err("could not get camera control");
723 /* check pre-created encode pipeline */
724 pthread_mutex_lock(&(hcamcorder->task_thread_lock));
725 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst == NULL &&
726 hcamcorder->task_thread_state == _MMCAMCORDER_TASK_THREAD_STATE_NONE) {
727 /* create encoding pipeline */
728 ret =_mmcamcorder_video_prepare_record((MMHandleType)hcamcorder);
729 if (ret != MM_ERROR_NONE) {
730 pthread_mutex_unlock(&(hcamcorder->task_thread_lock));
731 goto _ERR_CAMCORDER_VIDEO_COMMAND;
734 pthread_mutex_unlock(&(hcamcorder->task_thread_lock));
736 /* check recording start sound */
737 _mmcamcorder_sound_solo_play_wait(handle);
739 /**< To fix video recording hanging
740 1. use gst_element_set_start_time() instead of gst_pipeline_set_new_stream_time()
741 2. Set (GstClockTime)1 instead of (GstClockTime)0. Because of strict check in gstreamer 0.25,
742 basetime wouldn't change if you set (GstClockTime)0.
743 3. Move set start time position below PAUSED of pipeline.
745 //gst_element_set_start_time(GST_ELEMENT(sc->element[_MMCAMCORDER_MAIN_PIPE].gst), (GstClockTime)1);
746 //gst_element_set_start_time(GST_ELEMENT(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), (GstClockTime)1);
748 info->video_frame_count = 0;
749 info->is_firstframe = TRUE;
750 info->audio_frame_count = 0;
752 sc->ferror_send = FALSE;
753 sc->ferror_count = 0;
754 hcamcorder->error_occurs = FALSE;
755 sc->bget_eos = FALSE;
757 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PLAYING);
758 if (ret != MM_ERROR_NONE) {
759 /* stop video stream */
760 if (info->record_dual_stream) {
761 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
763 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
764 #ifdef LATEST_CAMERA_CONTROL
765 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
766 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
767 #endif /* LATEST_CAMERA_CONTROL */
768 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
770 _mmcam_dbg_err("failed to get camera control");
774 /* Remove recorder pipeline and recording file which size maybe zero */
775 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
776 if (info->filename) {
777 _mmcam_dbg_log("file delete(%s)", info->filename);
778 unlink(info->filename);
780 goto _ERR_CAMCORDER_VIDEO_COMMAND;
783 /*set the camera control to create the GOP so that video record will get a new key frame*/
784 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
785 GST_IS_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst)) {
786 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
787 controls = gst_camera_control_list_channels(CameraControl);
788 if (controls != NULL) {
789 for (item = controls ; item && item->data ; item = item->next) {
790 CameraControlChannel = item->data;
791 _mmcam_dbg_log("CameraControlChannel->label %s", CameraControlChannel->label);
792 if (!strcmp(CameraControlChannel->label, "new-gop")) {
793 //gst_camera_control_set_value(CameraControl, CameraControlChannel, 1);
799 _mmcam_dbg_warn("failed to find new-gop control channel");
803 _mmcam_dbg_warn("Can't cast Video source into camera control or not H264 prevew format[%d]",
804 sc->info_image->preview_format);
809 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
810 GST_IS_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst)) {
811 /* generate and I-frame on resuming */
812 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
813 controls = gst_camera_control_list_channels(CameraControl);
814 if (controls != NULL) {
815 for (item = controls ; item && item->data ; item = item->next) {
816 CameraControlChannel = item->data;
817 _mmcam_dbg_log("CameraControlChannel->label %s", CameraControlChannel->label);
818 if (!strcmp(CameraControlChannel->label, "new-gop")) {
819 //gst_camera_control_set_value(CameraControl, CameraControlChannel, 1);
825 _mmcam_dbg_warn("failed to find new-gop control channel");
830 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", FALSE);
832 _mmcam_dbg_log("Object property settings done");
836 case _MMCamcorder_CMD_PAUSE:
840 if (info->b_commiting) {
841 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
842 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
845 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
846 if (sc->audio_disable) {
847 /* check only video frame */
848 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
850 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
851 _mmcam_dbg_err("Pause fail, frame count %" G_GUINT64_FORMAT "",
852 info->video_frame_count);
853 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
855 _mmcam_dbg_warn("Waiting for enough video frame, retrial[%d], frame %" G_GUINT64_FORMAT "",
856 count, info->video_frame_count);
859 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
861 /* check both of video and audio frame */
862 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
864 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
865 _mmcam_dbg_err("Pause fail, frame count VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]",
866 info->video_frame_count, info->audio_frame_count);
867 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
869 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]",
870 count, info->video_frame_count, info->audio_frame_count);
873 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
877 /* block encodebin */
878 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", TRUE);
881 case _MMCamcorder_CMD_CANCEL:
883 if (info->b_commiting) {
884 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
885 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
888 /* block push buffer */
889 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP;
891 ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
892 if (ret != MM_ERROR_NONE) {
893 goto _ERR_CAMCORDER_VIDEO_COMMAND;
896 /* stop video stream */
897 if (info->record_dual_stream) {
898 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
900 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
901 #ifdef LATEST_CAMERA_CONTROL
902 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
903 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
904 #endif /* LATEST_CAMERA_CONTROL */
905 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
907 _mmcam_dbg_err("failed to get camera control");
911 if (info->restart_preview) {
912 /* restart preview */
913 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
914 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
916 ret =_mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
918 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
919 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
920 if (ret != MM_ERROR_NONE) {
921 goto _ERR_CAMCORDER_VIDEO_COMMAND;
924 /* reset restart_preview for inset window layout */
925 info->restart_preview = FALSE;
927 if (!_mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height)) {
928 ret = MM_ERROR_CAMCORDER_INTERNAL;
929 goto _ERR_CAMCORDER_VIDEO_COMMAND;
932 ret =_mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
933 if (ret != MM_ERROR_NONE) {
934 goto _ERR_CAMCORDER_VIDEO_COMMAND;
938 /* remove target file */
939 if (info->filename) {
940 _mmcam_dbg_log("file delete(%s)", info->filename);
941 unlink(info->filename);
944 sc->isMaxsizePausing = FALSE;
945 sc->isMaxtimePausing = FALSE;
947 sc->display_interval = 0;
948 sc->previous_slot_time = 0;
949 info->video_frame_count = 0;
950 info->audio_frame_count = 0;
954 case _MMCamcorder_CMD_COMMIT:
958 if (info->b_commiting) {
959 _mmcam_dbg_err("now on commiting previous file!!(command : %d)", command);
960 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
962 _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT : start");
963 info->b_commiting = TRUE;
964 sc->bget_eos = FALSE;
967 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
968 if (sc->audio_disable) {
969 /* check only video frame */
970 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
972 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
973 _mmcam_dbg_err("Commit fail, frame count is %" G_GUINT64_FORMAT "",
974 info->video_frame_count);
975 info->b_commiting = FALSE;
976 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
978 _mmcam_dbg_warn("Waiting for enough video frame, retrial [%d], frame %" G_GUINT64_FORMAT "",
979 count, info->video_frame_count);
982 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
984 /* check both of video and audio frame */
985 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
987 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
988 _mmcam_dbg_err("Commit fail, VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]",
989 info->video_frame_count, info->audio_frame_count);
991 info->b_commiting = FALSE;
992 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
994 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]",
995 count, info->video_frame_count, info->audio_frame_count);
998 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
1002 /* block push buffer */
1003 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP;
1004 _mmcam_dbg_log("block push buffer to appsrc");
1006 if (sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst != NULL) {
1007 ret = gst_element_send_event(sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, gst_event_new_eos());
1008 _mmcam_dbg_warn("send eos to appsrc result : %d", ret);
1011 if (sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst != NULL) {
1012 ret = gst_element_send_event(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst, gst_event_new_eos());
1013 _mmcam_dbg_warn("send eos to audiosrc result : %d", ret);
1017 sc->display_interval = 0;
1018 sc->previous_slot_time = 0;
1020 /* init system memory size */
1021 hcamcorder->system_memory = 0;
1024 _mmcam_dbg_log("Start to wait EOS");
1025 ret =_mmcamcorder_get_eos_message(handle);
1026 if (ret != MM_ERROR_NONE) {
1027 info->b_commiting = FALSE;
1028 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1033 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
1034 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1037 return MM_ERROR_NONE;
1039 _ERR_CAMCORDER_VIDEO_COMMAND:
1040 if (command == _MMCamcorder_CMD_RECORD) {
1041 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
1048 int _mmcamcorder_video_handle_eos(MMHandleType handle)
1050 int ret = MM_ERROR_NONE;
1052 guint64 file_size = 0;
1054 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1055 _MMCamcorderSubContext *sc = NULL;
1056 _MMCamcorderVideoInfo *info = NULL;
1057 _MMCamcorderMsgItem msg;
1058 MMCamRecordingReport *report = NULL;
1060 mmf_return_val_if_fail(hcamcorder, FALSE);
1062 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1063 mmf_return_val_if_fail(sc, FALSE);
1064 mmf_return_val_if_fail(sc->info_video, FALSE);
1066 info = sc->info_video;
1070 if (hcamcorder->state_change_by_system != _MMCAMCORDER_STATE_CHANGE_BY_ASM) {
1071 /* Play record stop sound */
1072 _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_FILEPATH_REC_STOP_SND, FALSE);
1074 _mmcam_dbg_warn("Play stop sound through pulseaudio");
1076 #ifdef _MMCAMCORDER_UPLOAD_SAMPLE
1077 _mmcamcorder_sound_init(handle, _MMCAMCORDER_FILEPATH_REC_STOP_SND);
1078 #else /* _MMCAMCORDER_UPLOAD_SAMPLE */
1079 _mmcamcorder_sound_init(handle);
1080 #endif /* _MMCAMCORDER_UPLOAD_SAMPLE */
1082 _mmcamcorder_sound_play((MMHandleType)hcamcorder, _MMCAMCORDER_SAMPLE_SOUND_NAME_REC_STOP, TRUE);
1084 _mmcamcorder_sound_finalize(handle);
1087 /* remove blocking part */
1088 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
1090 mm_camcorder_get_attributes(handle, NULL,
1091 MMCAM_RECORDER_TAG_ENABLE, &enabletag,
1094 ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
1095 if (ret != MM_ERROR_NONE) {
1096 _mmcam_dbg_warn("_MMCamcorder_CMD_COMMIT:__mmcamcorder_remove_recorder_pipeline failed. error[%x]", ret);
1099 /* stop video stream */
1100 if (info->record_dual_stream) {
1101 GstCameraControl *control = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
1103 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
1104 #ifdef LATEST_CAMERA_CONTROL
1105 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
1106 gst_camera_control_set_record_command(control, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
1107 #endif /* LATEST_CAMERA_CONTROL */
1108 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
1110 _mmcam_dbg_err("failed to get camera control");
1114 if (enabletag && !(sc->ferror_send)) {
1115 ret = __mmcamcorder_add_metadata((MMHandleType)hcamcorder, info->fileformat);
1117 _mmcam_dbg_log("Writing location information SUCCEEDED !!");
1119 _mmcam_dbg_err("Writing location information FAILED !!");
1123 /* Check file size */
1124 if (info->max_size > 0) {
1125 _mmcamcorder_get_file_size(info->filename, &file_size);
1126 _mmcam_dbg_log("MAX size %lld byte - created filesize %lld byte",
1127 info->max_size, file_size);
1129 if (file_size > info->max_size) {
1130 _MMCamcorderMsgItem message;
1131 _mmcam_dbg_err("File size is greater than max size !!");
1132 message.id = MM_MESSAGE_CAMCORDER_ERROR;
1133 message.param.code = MM_ERROR_CAMCORDER_FILE_SIZE_OVER;
1134 _mmcamcroder_send_message((MMHandleType)hcamcorder, &message);
1138 if (info->restart_preview) {
1140 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
1141 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
1143 _mmcam_dbg_log("Set state of pipeline as READY");
1144 ret = _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_READY);
1147 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
1148 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
1149 if (ret != MM_ERROR_NONE) {
1150 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1151 msg.param.code = ret;
1152 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1153 _mmcam_dbg_err("Failed to set state READY[%x]", ret);
1156 /* reset restart_preview for inset window layout */
1157 info->restart_preview = FALSE;
1159 /* recover preview size */
1160 _mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height);
1162 ret =_mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_PLAYING);
1163 /* Do not return when error is occurred.
1164 Recording file was created successfully, but starting pipeline failed */
1165 if (ret != MM_ERROR_NONE) {
1166 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1167 msg.param.code = ret;
1168 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1169 _mmcam_dbg_err("Failed to set state PLAYING[%x]", ret);
1172 _mmcam_dbg_log("No need to restart preview");
1175 /* Send recording report to application */
1176 msg.id = MM_MESSAGE_CAMCORDER_VIDEO_CAPTURED;
1177 report = (MMCamRecordingReport *)malloc(sizeof(MMCamRecordingReport));
1179 _mmcam_dbg_err("Recording report fail(%s). Out of memory.", info->filename);
1181 report->recording_filename = strdup(info->filename);
1182 msg.param.data= report;
1184 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1188 sc->pipeline_time = 0;
1190 sc->isMaxsizePausing = FALSE; /*In async function, this variable should set in callback function. */
1191 sc->isMaxtimePausing = FALSE;
1192 hcamcorder->error_occurs = FALSE;
1194 info->video_frame_count = 0;
1195 info->audio_frame_count = 0;
1197 info->b_commiting = FALSE;
1199 if (hcamcorder->state_change_by_system != _MMCAMCORDER_STATE_CHANGE_BY_ASM) {
1200 /* check recording stop sound */
1201 _mmcamcorder_sound_solo_play_wait(handle);
1204 _mmcam_dbg_err("_MMCamcorder_CMD_COMMIT : end");
1211 * This function is record video data probing function.
1212 * If this function is linked with certain pad by gst_pad_add_buffer_probe(),
1213 * this function will be called when data stream pass through the pad.
1215 * @param[in] pad probing pad which calls this function.
1216 * @param[in] buffer buffer which contains stream data.
1217 * @param[in] u_data user data.
1218 * @return This function returns true on success, or false value with error
1222 static GstPadProbeReturn __mmcamcorder_eventprobe_monitor(GstPad *pad, GstPadProbeInfo *info, gpointer u_data){
1223 GstEvent *event = GST_PAD_PROBE_INFO_EVENT(info);
1224 switch (GST_EVENT_TYPE(event)) {
1225 case GST_EVENT_UNKNOWN:
1226 /* upstream events */
1228 case GST_EVENT_SEEK:
1229 case GST_EVENT_NAVIGATION:
1230 case GST_EVENT_LATENCY:
1231 /* downstream serialized events */
1232 case GST_EVENT_SEGMENT :
1234 case GST_EVENT_BUFFERSIZE:
1235 _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1238 _mmcam_dbg_warn("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1240 /* bidirectional events */
1241 case GST_EVENT_FLUSH_START:
1242 case GST_EVENT_FLUSH_STOP:
1243 _mmcam_dbg_err("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1246 _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1250 return GST_PAD_PROBE_OK;
1254 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_check(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1256 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1257 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1259 _MMCamcorderSubContext *sc = NULL;
1260 _MMCamcorderVideoInfo *videoinfo = NULL;
1261 _MMCamcorderMsgItem msg;
1262 guint64 buffer_size = 0;
1263 guint64 trailer_size = 0;
1265 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1266 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1267 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1269 mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK);
1270 videoinfo = sc->info_video;
1272 /* get buffer size */
1273 if (!gst_buffer_map(buffer, &mapinfo, GST_MAP_READ)) {
1274 _mmcam_dbg_warn("map failed : buffer %p", buffer);
1275 return GST_PAD_PROBE_OK;
1278 buffer_size = mapinfo.size;
1279 gst_buffer_unmap(buffer, &mapinfo);
1281 /*_mmcam_dbg_err("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1283 pthread_mutex_lock(&(videoinfo->size_check_lock));
1285 if (videoinfo->audio_frame_count == 0) {
1286 videoinfo->filesize += buffer_size;
1287 videoinfo->audio_frame_count++;
1288 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1289 return GST_PAD_PROBE_OK;
1292 if (sc->ferror_send || sc->isMaxsizePausing) {
1293 _mmcam_dbg_warn("Recording is paused, drop frames");
1294 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1295 return GST_PAD_PROBE_DROP;
1298 /* get trailer size */
1299 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1300 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1305 /* check max size of recorded file */
1306 if (videoinfo->max_size > 0 &&
1307 videoinfo->max_size < videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) {
1308 GstState pipeline_state = GST_STATE_VOID_PENDING;
1309 GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1310 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1311 _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \
1312 " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]",
1313 videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size);
1315 if (!sc->isMaxsizePausing) {
1316 sc->isMaxsizePausing = TRUE;
1317 gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ;
1318 if (pipeline_state == GST_STATE_PLAYING) {
1319 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1322 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1323 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1326 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1331 videoinfo->filesize += buffer_size;
1332 videoinfo->audio_frame_count++;
1334 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1336 return GST_PAD_PROBE_OK;
1340 static GstPadProbeReturn __mmcamcorder_video_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1345 guint64 free_space = 0;
1346 guint64 buffer_size = 0;
1347 guint64 trailer_size = 0;
1348 guint64 queued_buffer = 0;
1349 char *filename = NULL;
1350 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1353 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1354 _MMCamcorderMsgItem msg;
1355 _MMCamcorderSubContext *sc = NULL;
1356 _MMCamcorderVideoInfo *videoinfo = NULL;
1358 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1359 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1361 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1362 mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK);
1363 videoinfo = sc->info_video;
1365 /*_mmcam_dbg_log("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1366 if (sc->ferror_send) {
1367 _mmcam_dbg_warn("file write error, drop frames");
1368 return GST_PAD_PROBE_DROP;
1371 gst_buffer_map(buffer, &mapinfo, GST_MAP_READ);
1372 buffer_size = mapinfo.size;
1373 gst_buffer_unmap(buffer, &mapinfo);
1375 videoinfo->video_frame_count++;
1376 if (videoinfo->video_frame_count <= (guint64)_MMCAMCORDER_MINIMUM_FRAME) {
1377 /* _mmcam_dbg_log("Pass minimum frame: info->video_frame_count: %" G_GUINT64_FORMAT " ",
1378 info->video_frame_count); */
1379 pthread_mutex_lock(&(videoinfo->size_check_lock));
1380 videoinfo->filesize += buffer_size;
1381 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1382 return GST_PAD_PROBE_OK;
1385 /* get trailer size */
1386 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1387 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1392 filename = videoinfo->filename;
1393 ret = _mmcamcorder_get_freespace(filename, &free_space);
1395 if(_mmcamcorder_check_file_path(filename) && hcamcorder->system_memory) {
1396 free_space = free_space - hcamcorder->system_memory;
1399 /*_mmcam_dbg_log("check free space for recording");*/
1402 case -2: /* file not exist */
1403 case -1: /* failed to get free space */
1404 _mmcam_dbg_err("Error occured. [%d]", ret);
1405 if (sc->ferror_count == 2 && sc->ferror_send == FALSE) {
1406 sc->ferror_send = TRUE;
1407 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1409 msg.param.code = MM_ERROR_FILE_NOT_FOUND;
1411 msg.param.code = MM_ERROR_FILE_READ;
1413 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1418 return GST_PAD_PROBE_DROP; /* skip this buffer */
1420 default: /* succeeded to get free space */
1421 /* check free space for recording */
1422 /* get queued buffer size */
1423 if (sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst) {
1424 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst, "current-level-bytes", &aq_size);
1426 if (sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst) {
1427 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst, "current-level-bytes", &vq_size);
1430 queued_buffer = aq_size + vq_size;
1432 /* check free space */
1433 if (free_space < (_MMCAMCORDER_MINIMUM_SPACE + buffer_size + trailer_size + queued_buffer)) {
1434 _mmcam_dbg_warn("No more space for recording!!! Recording is paused.");
1435 _mmcam_dbg_warn("Free Space : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]," \
1436 " buffer size : [%" G_GUINT64_FORMAT "], queued buffer size : [%" G_GUINT64_FORMAT "]", \
1437 free_space, trailer_size, buffer_size, queued_buffer);
1439 if (!sc->isMaxsizePausing) {
1440 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1441 sc->isMaxsizePausing = TRUE;
1443 msg.id = MM_MESSAGE_CAMCORDER_NO_FREE_SPACE;
1444 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1447 return GST_PAD_PROBE_DROP;
1452 pthread_mutex_lock(&(videoinfo->size_check_lock));
1454 /* check max size of recorded file */
1455 if (videoinfo->max_size > 0 &&
1456 videoinfo->max_size < videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) {
1457 GstState pipeline_state = GST_STATE_VOID_PENDING;
1458 GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1459 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1460 _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \
1461 " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]",
1462 videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size);
1464 if (!sc->isMaxsizePausing) {
1465 sc->isMaxsizePausing = TRUE;
1466 gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ;
1467 if (pipeline_state == GST_STATE_PLAYING) {
1468 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1471 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1472 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1475 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1477 return GST_PAD_PROBE_DROP;
1480 videoinfo->filesize += (guint64)buffer_size;
1483 _mmcam_dbg_log("filesize %lld Byte, ", videoinfo->filesize);
1486 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1488 return GST_PAD_PROBE_OK;
1492 static GstPadProbeReturn __mmcamcorder_video_dataprobe_audio_disable(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1494 guint64 trailer_size = 0;
1495 guint64 rec_pipe_time = 0;
1496 unsigned int remained_time = 0;
1498 GstClockTime b_time;
1500 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1501 _MMCamcorderMsgItem msg;
1502 _MMCamcorderSubContext *sc = NULL;
1503 _MMCamcorderVideoInfo *videoinfo = NULL;
1505 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1507 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1508 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1510 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1511 mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK);
1512 mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK);
1514 videoinfo = sc->info_video;
1516 b_time = GST_BUFFER_PTS(buffer);
1518 rec_pipe_time = GST_TIME_AS_MSECONDS(b_time);
1520 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1521 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1526 /* check max time */
1527 if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) {
1528 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1529 rec_pipe_time, videoinfo->max_time);
1531 if (!sc->isMaxtimePausing) {
1532 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1534 sc->isMaxtimePausing = TRUE;
1536 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1537 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1538 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1539 msg.param.recording_status.remained_time = 0;
1540 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1542 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1543 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1546 return GST_PAD_PROBE_DROP;
1549 /* calculate remained time can be recorded */
1550 if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) {
1551 remained_time = videoinfo->max_time - rec_pipe_time;
1552 } else if (videoinfo->max_size > 0) {
1553 long double max_size = (long double)videoinfo->max_size;
1554 long double current_size = (long double)(videoinfo->filesize + trailer_size);
1556 remained_time = (unsigned int)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1559 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1560 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1561 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1562 msg.param.recording_status.remained_time = remained_time;
1563 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1566 _mmcam_dbg_log("time [%" GST_TIME_FORMAT "], size [%d]",
1567 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1570 if (videoinfo->record_timestamp_ratio != _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) {
1571 guint record_motion_rate = (guint)videoinfo->record_motion_rate;
1574 _mmcam_dbg_log("record_motion_rate %d, videoinfo->record_drop_count %d",
1575 record_motion_rate, videoinfo->record_drop_count);
1578 /* drop some frame if fast motion */
1579 if (videoinfo->record_motion_rate > _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) {
1580 if (record_motion_rate != (videoinfo->record_drop_count++)) {
1582 _mmcam_dbg_warn("drop frame");
1584 return GST_PAD_PROBE_DROP;
1587 videoinfo->record_drop_count = 1;
1589 _mmcam_dbg_warn("pass frame");
1593 GST_BUFFER_PTS(buffer) = b_time * (videoinfo->record_timestamp_ratio);
1596 return GST_PAD_PROBE_OK;
1600 static GstPadProbeReturn __mmcamcorder_audioque_dataprobe(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1602 _MMCamcorderMsgItem msg;
1603 guint64 trailer_size = 0;
1604 guint64 rec_pipe_time = 0;
1605 _MMCamcorderSubContext *sc = NULL;
1606 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1607 _MMCamcorderVideoInfo *videoinfo = NULL;
1608 unsigned int remained_time = 0;
1609 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1611 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1612 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1613 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1615 mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK);
1616 mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK);
1617 mmf_return_val_if_fail(sc->element, GST_PAD_PROBE_OK);
1619 videoinfo = sc->info_video;
1621 if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_PTS(buffer))) {
1622 _mmcam_dbg_err( "Buffer timestamp is invalid, check it");
1623 return GST_PAD_PROBE_OK;
1626 rec_pipe_time = GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer));
1628 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1629 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1634 /* calculate remained time can be recorded */
1635 if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) {
1636 remained_time = videoinfo->max_time - rec_pipe_time;
1637 } else if (videoinfo->max_size > 0) {
1638 long double max_size = (long double)videoinfo->max_size;
1639 long double current_size = (long double)(videoinfo->filesize + trailer_size);
1641 remained_time = (unsigned long long)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1644 if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) {
1645 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1646 rec_pipe_time, videoinfo->max_time);
1648 if (!sc->isMaxtimePausing) {
1649 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1651 sc->isMaxtimePausing = TRUE;
1653 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1654 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1655 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1656 msg.param.recording_status.remained_time = 0;
1657 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1659 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1660 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1663 return GST_PAD_PROBE_DROP;
1666 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1667 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1668 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1669 msg.param.recording_status.remained_time = remained_time;
1670 _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg);
1673 _mmcam_dbg_log("audio data probe :: time [%" GST_TIME_FORMAT "], size [%lld KB]",
1674 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1677 return GST_PAD_PROBE_OK;
1681 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_audio_mute(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1683 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1684 double volume = 0.0;
1687 int err = MM_ERROR_UNKNOWN;
1688 char *err_name = NULL;
1689 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1692 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1693 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_DROP);
1695 /*_mmcam_dbg_log("AUDIO SRC time stamp : [%" GST_TIME_FORMAT "] \n", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1696 err = mm_camcorder_get_attributes((MMHandleType)hcamcorder, &err_name,
1697 MMCAM_AUDIO_VOLUME, &volume,
1698 MMCAM_AUDIO_FORMAT, &format,
1699 MMCAM_AUDIO_CHANNEL, &channel,
1701 if (err != MM_ERROR_NONE) {
1702 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, err);
1703 SAFE_FREE(err_name);
1707 memset(&mapinfo, 0x0, sizeof(GstMapInfo));
1709 gst_buffer_map(buffer, &mapinfo, GST_MAP_READWRITE);
1711 /* Set audio stream NULL */
1712 if (volume == 0.0) {
1713 memset(mapinfo.data, 0, mapinfo.size);
1716 /* CALL audio stream callback */
1717 if (hcamcorder->astream_cb && buffer && mapinfo.data && mapinfo.size > 0) {
1718 MMCamcorderAudioStreamDataType stream;
1720 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) < MM_CAMCORDER_STATE_PREPARE) {
1721 _mmcam_dbg_warn("Not ready for stream callback");
1722 gst_buffer_unmap(buffer, &mapinfo);
1723 return GST_PAD_PROBE_OK;
1726 /*_mmcam_dbg_log("Call video steramCb, data[%p], Width[%d],Height[%d], Format[%d]",
1727 GST_BUFFER_DATA(buffer), width, height, format);*/
1729 stream.data = (void *)mapinfo.data;
1730 stream.format = format;
1731 stream.channel = channel;
1732 stream.length = mapinfo.size;
1733 stream.timestamp = (unsigned int)(GST_BUFFER_PTS(buffer)/1000000); /* nano -> milli second */
1735 _MMCAMCORDER_LOCK_ASTREAM_CALLBACK(hcamcorder);
1737 if (hcamcorder->astream_cb) {
1738 hcamcorder->astream_cb(&stream, hcamcorder->astream_cb_param);
1741 _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK(hcamcorder);
1744 gst_buffer_unmap(buffer, &mapinfo);
1745 return GST_PAD_PROBE_OK;
1749 static gboolean __mmcamcorder_add_metadata(MMHandleType handle, int fileformat)
1751 gboolean bret = FALSE;
1753 switch (fileformat) {
1754 case MM_FILE_FORMAT_3GP:
1755 case MM_FILE_FORMAT_MP4:
1756 bret = __mmcamcorder_add_metadata_mp4(handle);
1759 _mmcam_dbg_warn("Unsupported fileformat to insert location info (%d)", fileformat);
1767 static gboolean __mmcamcorder_add_metadata_mp4(MMHandleType handle)
1771 guint64 udta_size = 0;
1772 gint64 current_pos = 0;
1773 gint64 moov_pos = 0;
1774 gint64 udta_pos = 0;
1775 gdouble longitude = 0;
1776 gdouble latitude = 0;
1777 gdouble altitude = 0;
1779 int orientation = 0;
1781 char *err_name = NULL;
1782 char err_msg[MAX_ERROR_MESSAGE_LEN] = {'\0',};
1783 _MMCamcorderLocationInfo location_info = {0,0,0};
1784 _MMCamcorderLocationInfo geo_info = {0,0,0};
1786 _MMCamcorderVideoInfo *info = NULL;
1787 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1788 _MMCamcorderSubContext *sc = NULL;
1790 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1791 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1793 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1794 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1798 info = sc->info_video;
1800 f = fopen(info->filename, "rb+");
1802 strerror_r(errno, err_msg, MAX_ERROR_MESSAGE_LEN);
1803 _mmcam_dbg_err("file open failed [%s]", err_msg);
1807 mm_camcorder_get_attributes(handle, &err_name,
1808 MMCAM_TAG_LATITUDE, &latitude,
1809 MMCAM_TAG_LONGITUDE, &longitude,
1810 MMCAM_TAG_ALTITUDE, &altitude,
1811 MMCAM_TAG_VIDEO_ORIENTATION, &orientation,
1812 MMCAM_TAG_GPS_ENABLE, &gps_enable,
1815 _mmcam_dbg_warn("Get tag attrs fail. (%s:%x)", err_name, err);
1816 SAFE_FREE (err_name);
1819 location_info.longitude = _mmcamcorder_double_to_fix(longitude);
1820 location_info.latitude = _mmcamcorder_double_to_fix(latitude);
1821 location_info.altitude = _mmcamcorder_double_to_fix(altitude);
1822 geo_info.longitude = longitude *10000;
1823 geo_info.latitude = latitude *10000;
1824 geo_info.altitude = altitude *10000;
1825 /* find udta container.
1826 if, there are udta container, write loci box after that
1827 else, make udta container and write loci box. */
1828 if (_mmcamcorder_find_fourcc(f, MMCAM_FOURCC('u','d','t','a'), TRUE)) {
1831 _mmcam_dbg_log("find udta container");
1834 if (fseek(f, -8L, SEEK_CUR) != 0) {
1838 udta_pos = ftell(f);
1843 nread = fread(&buf, sizeof(char), sizeof(buf), f);
1845 _mmcam_dbg_log("recorded file fread %d", nread);
1847 udta_size = _mmcamcorder_get_container_size(buf);
1849 /* goto end of udta and write 'loci' box */
1850 if (fseek(f, (udta_size-4L), SEEK_CUR) != 0) {
1855 if (!_mmcamcorder_write_loci(f, location_info)) {
1856 _mmcam_dbg_err("failed to write loci");
1860 if (!_mmcamcorder_write_geodata(f, geo_info)) {
1861 _mmcam_dbg_err("failed to write geodata");
1866 current_pos = ftell(f);
1867 if (current_pos < 0) {
1871 if (!_mmcamcorder_update_size(f, udta_pos, current_pos)) {
1875 _mmcam_dbg_log("No udta container");
1876 if (fseek(f, 0, SEEK_END) != 0) {
1880 if (!_mmcamcorder_write_udta(f, gps_enable, location_info, geo_info)) {
1881 _mmcam_dbg_err("failed to write udta");
1886 /* find moov container.
1887 update moov container size. */
1888 if((current_pos = ftell(f))<0)
1891 if (_mmcamcorder_find_tag(f, MMCAM_FOURCC('m','o','o','v'), TRUE)) {
1892 gint64 internal_pos = ftell(f);
1894 _mmcam_dbg_log("found moov container");
1895 if (fseek(f, -8L, SEEK_CUR) !=0) {
1899 moov_pos = ftell(f);
1904 if (!_mmcamcorder_update_size(f, moov_pos, current_pos)) {
1908 /* add orientation info */
1909 if (fseek(f, internal_pos, SEEK_SET) < 0) {
1910 _mmcam_dbg_err("fseek failed : errno %d", errno);
1914 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t','r','a','k'), FALSE)) {
1915 _mmcam_dbg_err("failed to find [trak] tag");
1919 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t','k','h','d'), FALSE)) {
1920 _mmcam_dbg_err("failed to find [tkhd] tag");
1924 _mmcam_dbg_log("found [tkhd] tag");
1926 /* seek to start position of composition matrix */
1927 fseek(f, _OFFSET_COMPOSITION_MATRIX, SEEK_CUR);
1929 /* update composition matrix for orientation */
1930 _mmcamcorder_update_composition_matrix(f, orientation);
1932 _mmcam_dbg_err("No 'moov' container");
1944 _mmcam_dbg_err("ftell() returns negative value.");
1950 int _mmcamcorder_connect_video_stream_cb_signal(MMHandleType handle)
1952 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1953 _MMCamcorderSubContext *sc = NULL;
1955 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1957 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1958 mmf_return_val_if_fail(sc && sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1960 /* check video source element */
1961 if (sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst) {
1962 _mmcam_dbg_warn("connect video stream cb signal to _MMCAMCORDER_VIDEOSRC_SRC");
1963 MMCAMCORDER_SIGNAL_CONNECT(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst,
1964 _MMCAMCORDER_HANDLER_VIDEOREC, "video-stream-cb",
1965 G_CALLBACK(__mmcamcorder_video_stream_cb),
1967 return MM_ERROR_NONE;
1969 _mmcam_dbg_err("videosrc element is not created yet");
1970 return MM_ERROR_CAMCORDER_NOT_INITIALIZED;
1975 int _mmcamcorder_video_prepare_record(MMHandleType handle)
1977 int ret = MM_ERROR_NONE;
1979 _MMCamcorderVideoInfo *info = NULL;
1980 _MMCamcorderSubContext *sc = NULL;
1981 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1983 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1985 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1986 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1987 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1989 info = sc->info_video;
1991 _mmcam_dbg_warn("start");
1993 /* create encoding pipeline */
1994 ret =_mmcamcorder_create_recorder_pipeline((MMHandleType)hcamcorder);
1995 if (ret != MM_ERROR_NONE) {
1996 goto _ERR_PREPARE_RECORD;
1999 if (info->filename == NULL) {
2000 char *temp_filename = NULL;
2003 mm_camcorder_get_attributes(handle, NULL,
2004 MMCAM_TARGET_FILENAME, &temp_filename, &size,
2006 if (temp_filename) {
2007 info->filename = strdup(temp_filename);
2010 if (!info->filename) {
2011 _mmcam_dbg_err("strdup[src:%p] was failed", temp_filename);
2012 goto _ERR_PREPARE_RECORD;
2016 _mmcam_dbg_log("Record file name [%s]", info->filename);
2018 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename);
2019 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", 0);
2021 /* Adjust display FPS */
2022 sc->display_interval = 0;
2023 sc->previous_slot_time = 0;
2025 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PAUSED);
2026 if (ret != MM_ERROR_NONE) {
2027 goto _ERR_PREPARE_RECORD;
2030 _mmcam_dbg_warn("done");
2034 _ERR_PREPARE_RECORD:
2035 /* Remove recorder pipeline and recording file which size maybe zero */
2036 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
2037 if (info && info->filename) {
2038 _mmcam_dbg_log("file delete(%s)", info->filename);
2039 unlink(info->filename);