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 int file_system_type = 0;
510 _mmcam_dbg_log("Record Start - dual stream %d", info->support_dual_stream);
512 /* init record_dual_stream */
513 info->record_dual_stream = FALSE;
515 ret = mm_camcorder_get_attributes(handle, &err_name,
516 MMCAM_CAMERA_FPS, &fps,
517 MMCAM_CAMERA_WIDTH, &(info->preview_width),
518 MMCAM_CAMERA_HEIGHT, &(info->preview_height),
519 MMCAM_VIDEO_WIDTH, &(info->video_width),
520 MMCAM_VIDEO_HEIGHT, &(info->video_height),
521 MMCAM_FILE_FORMAT, &fileformat,
522 MMCAM_TARGET_FILENAME, &temp_filename, &size,
523 MMCAM_TARGET_MAX_SIZE, &imax_size,
524 MMCAM_TARGET_TIME_LIMIT, &imax_time,
525 MMCAM_FILE_FORMAT, &(info->fileformat),
526 MMCAM_CAMERA_RECORDING_MOTION_RATE, &motion_rate,
528 if (ret != MM_ERROR_NONE) {
529 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, ret);
530 SAFE_FREE (err_name);
531 goto _ERR_CAMCORDER_VIDEO_COMMAND;
534 if (temp_filename == NULL) {
535 _mmcam_dbg_err("filename is not set");
536 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
537 goto _ERR_CAMCORDER_VIDEO_COMMAND;
541 if (imax_size <= 0) {
542 info->max_size = 0; /* do not check */
544 info->max_size = ((guint64)imax_size) << 10; /* to byte */
548 if (imax_time <= 0) {
549 info->max_time = 0; /* do not check */
551 info->max_time = ((guint64)imax_time) * 1000; /* to millisecond */
554 dir_name = g_path_get_dirname(temp_filename);
556 ret_free_space = _mmcamcorder_get_freespace(dir_name, hcamcorder->root_directory, &free_space);
558 _mmcam_dbg_warn("current space - %s [%" G_GUINT64_FORMAT "]", dir_name, free_space);
560 if (_mmcamcorder_get_file_system_type(dir_name, &file_system_type) == 0) {
561 /* MSDOS_SUPER_MAGIC : 0x4d44 */
562 if (file_system_type == MSDOS_SUPER_MAGIC &&
563 (info->max_size == 0 || info->max_size > FAT32_FILE_SYSTEM_MAX_SIZE)) {
564 _mmcam_dbg_warn("FAT32 and too large max[%"G_GUINT64_FORMAT"], set max as %"G_GUINT64_FORMAT,
565 info->max_size, FAT32_FILE_SYSTEM_MAX_SIZE);
566 info->max_size = FAT32_FILE_SYSTEM_MAX_SIZE;
568 _mmcam_dbg_warn("file system 0x%x, max size %"G_GUINT64_FORMAT,
569 file_system_type, info->max_size);
572 _mmcam_dbg_warn("_mmcamcorder_get_file_system_type failed");
578 _mmcam_dbg_err("failed to get directory name");
582 if ((ret_free_space == -1) || free_space <= (_MMCAMCORDER_MINIMUM_SPACE<<1)) {
583 _mmcam_dbg_err("OUT of STORAGE [ret_free_space:%d or free space [%" G_GUINT64_FORMAT "] is smaller than [%d]",
584 ret_free_space, free_space, (_MMCAMCORDER_MINIMUM_SPACE<<1));
585 return MM_ERROR_OUT_OF_STORAGE;
588 pthread_mutex_lock(&(hcamcorder->task_thread_lock));
589 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst == NULL &&
590 hcamcorder->task_thread_state == _MMCAMCORDER_TASK_THREAD_STATE_NONE) {
591 /* Play record start sound */
592 _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_FILEPATH_REC_START_SND, FALSE);
594 pthread_mutex_unlock(&(hcamcorder->task_thread_lock));
596 _mmcam_dbg_warn("video size [%dx%d]", info->video_width, info->video_height);
598 if (info->video_width == 0 || info->video_height == 0) {
599 _mmcam_dbg_warn("video size is invalid [%dx%d] use preview size [%dx%d]",
600 info->video_width, info->video_height, info->preview_width, info->preview_height);
601 info->video_width = info->preview_width;
602 info->video_height = info->preview_height;
605 if (info->support_dual_stream) {
606 _mmcam_dbg_warn("DUAL STREAM MODE");
608 info->record_dual_stream = TRUE;
610 /* No need to restart preview */
611 info->restart_preview = FALSE;
613 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "video-width", info->video_width);
614 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "video-height", info->video_height);
615 } else if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
616 info->preview_width == info->video_width &&
617 info->preview_height == info->video_height) {
618 _mmcam_dbg_log("H264 preview mode and same resolution");
620 /* No need to restart preview */
621 info->restart_preview = FALSE;
623 /* always need to restart preview */
624 info->restart_preview = TRUE;
627 /* set recording hint */
628 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", TRUE);
630 if (info->restart_preview) {
631 /* stop preview and set new size */
632 _mmcam_dbg_log("restart preview");
634 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
635 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
637 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
639 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
640 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
642 if (ret != MM_ERROR_NONE) {
643 goto _ERR_CAMCORDER_VIDEO_COMMAND;
646 if (!_mmcamcorder_set_camera_resolution(handle, info->video_width, info->video_height)) {
647 ret = MM_ERROR_CAMCORDER_INTERNAL;
648 goto _ERR_CAMCORDER_VIDEO_COMMAND;
651 /* Start preview again with new setting */
652 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
653 if (ret != MM_ERROR_NONE) {
654 goto _ERR_CAMCORDER_VIDEO_COMMAND;
657 if (motion_rate < 1.0) {
658 _mmcam_dbg_warn("wait for stabilization of frame");
662 _mmcam_dbg_log("no need to restart preview");
665 _mmcamcorder_conf_get_value_int(handle, hcamcorder->conf_main,
666 CONFIGURE_CATEGORY_MAIN_RECORD,
670 _mmcamcorder_conf_get_value_int(handle, hcamcorder->conf_main,
671 CONFIGURE_CATEGORY_MAIN_RECORD,
672 "PassFirstVideoFrame",
673 &(sc->pass_first_vframe));
675 _mmcam_dbg_log("Drop video frame count[%d], Pass fisrt video frame count[%d]",
676 sc->drop_vframe, sc->pass_first_vframe);
678 info->record_drop_count = (guint)motion_rate;
679 info->record_motion_rate = motion_rate;
680 if (sc->is_modified_rate) {
681 info->record_timestamp_ratio = (_MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE/motion_rate);
683 info->record_timestamp_ratio = _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE;
686 _mmcam_dbg_warn("recording fps %d, motion rate %f, timestamp_ratio %f",
687 fps, info->record_motion_rate, info->record_timestamp_ratio);
689 /* set push buffer flag */
690 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_INIT;
691 info->base_video_ts = 0;
693 /* connect video stream cb signal */
694 /*130826 Connect video stream cb for handling fast record frame cb*/
695 if (info->record_dual_stream) {
696 if (_mmcamcorder_connect_video_stream_cb_signal((MMHandleType)hcamcorder) != MM_ERROR_NONE) {
697 goto _ERR_CAMCORDER_VIDEO_COMMAND;
701 /* start video stream */
702 if (info->record_dual_stream) {
703 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
705 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
707 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_START");
708 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_START);
710 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
712 _mmcam_dbg_err("could not get camera control");
716 /* check pre-created encode pipeline */
717 pthread_mutex_lock(&(hcamcorder->task_thread_lock));
718 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst == NULL &&
719 hcamcorder->task_thread_state == _MMCAMCORDER_TASK_THREAD_STATE_NONE) {
720 /* create encoding pipeline */
721 ret =_mmcamcorder_video_prepare_record((MMHandleType)hcamcorder);
722 if (ret != MM_ERROR_NONE) {
723 pthread_mutex_unlock(&(hcamcorder->task_thread_lock));
724 goto _ERR_CAMCORDER_VIDEO_COMMAND;
727 pthread_mutex_unlock(&(hcamcorder->task_thread_lock));
729 /* check recording start sound */
730 _mmcamcorder_sound_solo_play_wait(handle);
732 /**< To fix video recording hanging
733 1. use gst_element_set_start_time() instead of gst_pipeline_set_new_stream_time()
734 2. Set (GstClockTime)1 instead of (GstClockTime)0. Because of strict check in gstreamer 0.25,
735 basetime wouldn't change if you set (GstClockTime)0.
736 3. Move set start time position below PAUSED of pipeline.
738 //gst_element_set_start_time(GST_ELEMENT(sc->element[_MMCAMCORDER_MAIN_PIPE].gst), (GstClockTime)1);
739 //gst_element_set_start_time(GST_ELEMENT(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), (GstClockTime)1);
741 info->video_frame_count = 0;
742 info->is_firstframe = TRUE;
743 info->audio_frame_count = 0;
745 sc->ferror_send = FALSE;
746 sc->ferror_count = 0;
747 hcamcorder->error_occurs = FALSE;
748 sc->bget_eos = FALSE;
750 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PLAYING);
751 if (ret != MM_ERROR_NONE) {
752 /* stop video stream */
753 if (info->record_dual_stream) {
754 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
756 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
758 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
759 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
761 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
763 _mmcam_dbg_err("failed to get camera control");
767 /* Remove recorder pipeline and recording file which size maybe zero */
768 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
769 if (info->filename) {
770 _mmcam_dbg_log("file delete(%s)", info->filename);
771 unlink(info->filename);
773 goto _ERR_CAMCORDER_VIDEO_COMMAND;
776 /*set the camera control to create the GOP so that video record will get a new key frame*/
777 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
778 GST_IS_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst)) {
779 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
780 controls = gst_camera_control_list_channels(CameraControl);
781 if (controls != NULL) {
782 for (item = controls ; item && item->data ; item = item->next) {
783 CameraControlChannel = item->data;
784 _mmcam_dbg_log("CameraControlChannel->label %s", CameraControlChannel->label);
785 if (!strcmp(CameraControlChannel->label, "new-gop")) {
786 //gst_camera_control_set_value(CameraControl, CameraControlChannel, 1);
792 _mmcam_dbg_warn("failed to find new-gop control channel");
796 _mmcam_dbg_warn("Can't cast Video source into camera control or not H264 prevew format[%d]",
797 sc->info_image->preview_format);
802 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
803 GST_IS_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst)) {
804 /* generate and I-frame on resuming */
805 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
806 controls = gst_camera_control_list_channels(CameraControl);
807 if (controls != NULL) {
808 for (item = controls ; item && item->data ; item = item->next) {
809 CameraControlChannel = item->data;
810 _mmcam_dbg_log("CameraControlChannel->label %s", CameraControlChannel->label);
811 if (!strcmp(CameraControlChannel->label, "new-gop")) {
812 //gst_camera_control_set_value(CameraControl, CameraControlChannel, 1);
818 _mmcam_dbg_warn("failed to find new-gop control channel");
823 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", FALSE);
825 _mmcam_dbg_log("Object property settings done");
829 case _MMCamcorder_CMD_PAUSE:
833 if (info->b_commiting) {
834 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
835 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
838 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
839 if (sc->audio_disable) {
840 /* check only video frame */
841 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
843 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
844 _mmcam_dbg_err("Pause fail, frame count %" G_GUINT64_FORMAT "",
845 info->video_frame_count);
846 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
848 _mmcam_dbg_warn("Waiting for enough video frame, retrial[%d], frame %" G_GUINT64_FORMAT "",
849 count, info->video_frame_count);
852 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
854 /* check both of video and audio frame */
855 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
857 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
858 _mmcam_dbg_err("Pause fail, frame count VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]",
859 info->video_frame_count, info->audio_frame_count);
860 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
862 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]",
863 count, info->video_frame_count, info->audio_frame_count);
866 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
870 /* block encodebin */
871 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", TRUE);
874 case _MMCamcorder_CMD_CANCEL:
876 if (info->b_commiting) {
877 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
878 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
881 /* block push buffer */
882 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP;
884 ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
885 if (ret != MM_ERROR_NONE) {
886 goto _ERR_CAMCORDER_VIDEO_COMMAND;
889 /* set recording hint */
890 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", FALSE);
892 /* stop video stream */
893 if (info->record_dual_stream) {
894 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
896 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
898 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
899 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
901 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
903 _mmcam_dbg_err("failed to get camera control");
907 if (info->restart_preview) {
908 /* restart preview */
909 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
910 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
912 ret =_mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
914 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
915 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
916 if (ret != MM_ERROR_NONE) {
917 goto _ERR_CAMCORDER_VIDEO_COMMAND;
920 /* reset restart_preview for inset window layout */
921 info->restart_preview = FALSE;
923 if (!_mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height)) {
924 ret = MM_ERROR_CAMCORDER_INTERNAL;
925 goto _ERR_CAMCORDER_VIDEO_COMMAND;
928 ret =_mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
929 if (ret != MM_ERROR_NONE) {
930 goto _ERR_CAMCORDER_VIDEO_COMMAND;
934 /* remove target file */
935 if (info->filename) {
936 _mmcam_dbg_log("file delete(%s)", info->filename);
937 unlink(info->filename);
940 sc->isMaxsizePausing = FALSE;
941 sc->isMaxtimePausing = FALSE;
943 sc->display_interval = 0;
944 sc->previous_slot_time = 0;
945 info->video_frame_count = 0;
946 info->audio_frame_count = 0;
950 case _MMCamcorder_CMD_COMMIT:
954 if (info->b_commiting) {
955 _mmcam_dbg_err("now on commiting previous file!!(command : %d)", command);
956 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
958 _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT : start");
959 info->b_commiting = TRUE;
960 sc->bget_eos = FALSE;
963 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
964 if (sc->audio_disable) {
965 /* check only video frame */
966 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
968 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
969 _mmcam_dbg_err("Commit fail, frame count is %" G_GUINT64_FORMAT "",
970 info->video_frame_count);
971 info->b_commiting = FALSE;
972 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
974 _mmcam_dbg_warn("Waiting for enough video frame, retrial [%d], frame %" G_GUINT64_FORMAT "",
975 count, info->video_frame_count);
978 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
980 /* check both of video and audio frame */
981 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
983 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
984 _mmcam_dbg_err("Commit fail, VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]",
985 info->video_frame_count, info->audio_frame_count);
987 info->b_commiting = FALSE;
988 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
990 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]",
991 count, info->video_frame_count, info->audio_frame_count);
994 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
998 /* block push buffer */
999 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP;
1000 _mmcam_dbg_log("block push buffer to appsrc");
1002 if (sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst != NULL) {
1003 if (gst_element_send_event(sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, gst_event_new_eos())) {
1004 _mmcam_dbg_warn("VIDEO: send eos to appsrc done");
1006 _mmcam_dbg_err("VIDEO: send EOS failed");
1007 info->b_commiting = FALSE;
1008 ret = MM_ERROR_CAMCORDER_INTERNAL;
1009 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1012 _mmcam_dbg_err("No video stream source");
1013 info->b_commiting = FALSE;
1014 ret = MM_ERROR_CAMCORDER_INTERNAL;
1015 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1018 if (sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst != NULL) {
1019 if (gst_element_send_event(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst, gst_event_new_eos())) {
1020 _mmcam_dbg_warn("AUDIO: send eos to audiosrc done");
1022 _mmcam_dbg_err("AUDIO: send EOS failed");
1023 info->b_commiting = FALSE;
1024 ret = MM_ERROR_CAMCORDER_INTERNAL;
1025 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1028 _mmcam_dbg_log("No audio stream");
1032 sc->display_interval = 0;
1033 sc->previous_slot_time = 0;
1036 _mmcam_dbg_log("Start to wait EOS");
1037 ret =_mmcamcorder_get_eos_message(handle);
1038 if (ret != MM_ERROR_NONE) {
1039 info->b_commiting = FALSE;
1040 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1045 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
1046 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1049 return MM_ERROR_NONE;
1051 _ERR_CAMCORDER_VIDEO_COMMAND:
1052 if (command == _MMCamcorder_CMD_RECORD) {
1053 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
1060 int _mmcamcorder_video_handle_eos(MMHandleType handle)
1062 int ret = MM_ERROR_NONE;
1064 guint64 file_size = 0;
1066 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1067 _MMCamcorderSubContext *sc = NULL;
1068 _MMCamcorderVideoInfo *info = NULL;
1069 _MMCamcorderMsgItem msg;
1070 MMCamRecordingReport *report = NULL;
1072 mmf_return_val_if_fail(hcamcorder, FALSE);
1074 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1075 mmf_return_val_if_fail(sc, FALSE);
1076 mmf_return_val_if_fail(sc->info_video, FALSE);
1078 info = sc->info_video;
1082 if (hcamcorder->state_change_by_system != _MMCAMCORDER_STATE_CHANGE_BY_ASM) {
1083 /* Play record stop sound */
1084 _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_FILEPATH_REC_STOP_SND, FALSE);
1086 _mmcam_dbg_warn("Play stop sound through pulseaudio");
1088 #ifdef _MMCAMCORDER_UPLOAD_SAMPLE
1089 _mmcamcorder_sound_init(handle, _MMCAMCORDER_FILEPATH_REC_STOP_SND);
1090 #else /* _MMCAMCORDER_UPLOAD_SAMPLE */
1091 _mmcamcorder_sound_init(handle);
1092 #endif /* _MMCAMCORDER_UPLOAD_SAMPLE */
1094 _mmcamcorder_sound_play((MMHandleType)hcamcorder, _MMCAMCORDER_SAMPLE_SOUND_NAME_REC_STOP, TRUE);
1096 _mmcamcorder_sound_finalize(handle);
1099 /* remove blocking part */
1100 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
1102 mm_camcorder_get_attributes(handle, NULL,
1103 MMCAM_RECORDER_TAG_ENABLE, &enabletag,
1106 ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
1107 if (ret != MM_ERROR_NONE) {
1108 _mmcam_dbg_warn("_MMCamcorder_CMD_COMMIT:__mmcamcorder_remove_recorder_pipeline failed. error[%x]", ret);
1111 /* set recording hint */
1112 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", FALSE);
1114 /* stop video stream */
1115 if (info->record_dual_stream) {
1116 GstCameraControl *control = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
1118 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
1120 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
1121 gst_camera_control_set_record_command(control, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
1123 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
1125 _mmcam_dbg_err("failed to get camera control");
1129 if (enabletag && !(sc->ferror_send)) {
1130 ret = __mmcamcorder_add_metadata((MMHandleType)hcamcorder, info->fileformat);
1132 _mmcam_dbg_log("Writing location information SUCCEEDED !!");
1134 _mmcam_dbg_err("Writing location information FAILED !!");
1138 /* Check file size */
1139 if (info->max_size > 0) {
1140 _mmcamcorder_get_file_size(info->filename, &file_size);
1141 _mmcam_dbg_log("MAX size %lld byte - created filesize %lld byte",
1142 info->max_size, file_size);
1144 if (file_size > info->max_size) {
1145 _MMCamcorderMsgItem message;
1146 _mmcam_dbg_err("File size is greater than max size !!");
1147 message.id = MM_MESSAGE_CAMCORDER_ERROR;
1148 message.param.code = MM_ERROR_CAMCORDER_FILE_SIZE_OVER;
1149 _mmcamcorder_send_message((MMHandleType)hcamcorder, &message);
1153 if (info->restart_preview) {
1155 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
1156 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
1158 _mmcam_dbg_log("Set state of pipeline as READY");
1159 ret = _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_READY);
1162 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
1163 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
1164 if (ret != MM_ERROR_NONE) {
1165 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1166 msg.param.code = ret;
1167 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1168 _mmcam_dbg_err("Failed to set state READY[%x]", ret);
1171 /* reset restart_preview for inset window layout */
1172 info->restart_preview = FALSE;
1174 /* recover preview size */
1175 _mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height);
1177 ret =_mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_PLAYING);
1178 /* Do not return when error is occurred.
1179 Recording file was created successfully, but starting pipeline failed */
1180 if (ret != MM_ERROR_NONE) {
1181 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1182 msg.param.code = ret;
1183 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1184 _mmcam_dbg_err("Failed to set state PLAYING[%x]", ret);
1187 _mmcam_dbg_log("No need to restart preview");
1190 /* Send recording report to application */
1191 msg.id = MM_MESSAGE_CAMCORDER_VIDEO_CAPTURED;
1192 report = (MMCamRecordingReport *)malloc(sizeof(MMCamRecordingReport));
1194 _mmcam_dbg_err("Recording report fail(%s). Out of memory.", info->filename);
1196 report->recording_filename = strdup(info->filename);
1197 msg.param.data= report;
1199 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1203 sc->pipeline_time = 0;
1205 sc->isMaxsizePausing = FALSE; /*In async function, this variable should set in callback function. */
1206 sc->isMaxtimePausing = FALSE;
1207 hcamcorder->error_occurs = FALSE;
1209 info->video_frame_count = 0;
1210 info->audio_frame_count = 0;
1212 info->b_commiting = FALSE;
1214 if (hcamcorder->state_change_by_system != _MMCAMCORDER_STATE_CHANGE_BY_ASM) {
1215 /* check recording stop sound */
1216 _mmcamcorder_sound_solo_play_wait(handle);
1219 _mmcam_dbg_err("_MMCamcorder_CMD_COMMIT : end");
1226 * This function is record video data probing function.
1227 * If this function is linked with certain pad by gst_pad_add_buffer_probe(),
1228 * this function will be called when data stream pass through the pad.
1230 * @param[in] pad probing pad which calls this function.
1231 * @param[in] buffer buffer which contains stream data.
1232 * @param[in] u_data user data.
1233 * @return This function returns true on success, or false value with error
1237 static GstPadProbeReturn __mmcamcorder_eventprobe_monitor(GstPad *pad, GstPadProbeInfo *info, gpointer u_data){
1238 GstEvent *event = GST_PAD_PROBE_INFO_EVENT(info);
1239 switch (GST_EVENT_TYPE(event)) {
1240 case GST_EVENT_UNKNOWN:
1241 /* upstream events */
1243 case GST_EVENT_SEEK:
1244 case GST_EVENT_NAVIGATION:
1245 case GST_EVENT_LATENCY:
1246 /* downstream serialized events */
1247 case GST_EVENT_SEGMENT :
1249 case GST_EVENT_BUFFERSIZE:
1250 _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1253 _mmcam_dbg_warn("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1255 /* bidirectional events */
1256 case GST_EVENT_FLUSH_START:
1257 case GST_EVENT_FLUSH_STOP:
1258 _mmcam_dbg_err("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1261 _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1265 return GST_PAD_PROBE_OK;
1269 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_check(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1271 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1272 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1274 _MMCamcorderSubContext *sc = NULL;
1275 _MMCamcorderVideoInfo *videoinfo = NULL;
1276 _MMCamcorderMsgItem msg;
1277 guint64 buffer_size = 0;
1278 guint64 trailer_size = 0;
1280 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1281 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1282 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1284 mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK);
1285 videoinfo = sc->info_video;
1287 /* get buffer size */
1288 if (!gst_buffer_map(buffer, &mapinfo, GST_MAP_READ)) {
1289 _mmcam_dbg_warn("map failed : buffer %p", buffer);
1290 return GST_PAD_PROBE_OK;
1293 buffer_size = mapinfo.size;
1294 gst_buffer_unmap(buffer, &mapinfo);
1296 /*_mmcam_dbg_err("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1298 pthread_mutex_lock(&(videoinfo->size_check_lock));
1300 if (videoinfo->audio_frame_count == 0) {
1301 videoinfo->filesize += buffer_size;
1302 videoinfo->audio_frame_count++;
1303 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1304 return GST_PAD_PROBE_OK;
1307 if (sc->ferror_send || sc->isMaxsizePausing) {
1308 _mmcam_dbg_warn("Recording is paused, drop frames");
1309 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1310 return GST_PAD_PROBE_DROP;
1313 /* get trailer size */
1314 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1315 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1320 /* check max size of recorded file */
1321 if (videoinfo->max_size > 0 &&
1322 videoinfo->max_size < videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) {
1323 GstState pipeline_state = GST_STATE_VOID_PENDING;
1324 GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1325 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1326 _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \
1327 " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]",
1328 videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size);
1330 if (!sc->isMaxsizePausing) {
1331 sc->isMaxsizePausing = TRUE;
1332 gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ;
1333 if (pipeline_state == GST_STATE_PLAYING) {
1334 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1337 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1338 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1341 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1346 videoinfo->filesize += buffer_size;
1347 videoinfo->audio_frame_count++;
1349 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1351 return GST_PAD_PROBE_OK;
1355 static GstPadProbeReturn __mmcamcorder_video_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1360 guint64 free_space = 0;
1361 guint64 buffer_size = 0;
1362 guint64 trailer_size = 0;
1363 guint64 queued_buffer = 0;
1364 char *dir_name = NULL;
1365 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1368 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1369 _MMCamcorderMsgItem msg;
1370 _MMCamcorderSubContext *sc = NULL;
1371 _MMCamcorderVideoInfo *videoinfo = NULL;
1373 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1374 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1376 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1377 mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK);
1378 videoinfo = sc->info_video;
1380 /*_mmcam_dbg_log("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1381 if (sc->ferror_send) {
1382 _mmcam_dbg_warn("file write error, drop frames");
1383 return GST_PAD_PROBE_DROP;
1386 gst_buffer_map(buffer, &mapinfo, GST_MAP_READ);
1387 buffer_size = mapinfo.size;
1388 gst_buffer_unmap(buffer, &mapinfo);
1390 videoinfo->video_frame_count++;
1391 if (videoinfo->video_frame_count <= (guint64)_MMCAMCORDER_MINIMUM_FRAME) {
1392 /* _mmcam_dbg_log("Pass minimum frame: info->video_frame_count: %" G_GUINT64_FORMAT " ",
1393 info->video_frame_count); */
1394 pthread_mutex_lock(&(videoinfo->size_check_lock));
1395 videoinfo->filesize += buffer_size;
1396 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1397 return GST_PAD_PROBE_OK;
1400 /* get trailer size */
1401 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1402 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1407 dir_name = g_path_get_dirname(videoinfo->filename);
1409 ret = _mmcamcorder_get_freespace(dir_name, hcamcorder->root_directory, &free_space);
1413 _mmcam_dbg_err("failed to get dir name from [%s]", videoinfo->filename);
1417 /*_mmcam_dbg_log("check free space for recording");*/
1420 case -2: /* file not exist */
1421 case -1: /* failed to get free space */
1422 _mmcam_dbg_err("Error occured. [%d]", ret);
1423 if (sc->ferror_count == 2 && sc->ferror_send == FALSE) {
1424 sc->ferror_send = TRUE;
1425 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1427 msg.param.code = MM_ERROR_FILE_NOT_FOUND;
1429 msg.param.code = MM_ERROR_FILE_READ;
1431 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1436 return GST_PAD_PROBE_DROP; /* skip this buffer */
1438 default: /* succeeded to get free space */
1439 /* check free space for recording */
1440 /* get queued buffer size */
1441 if (sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst) {
1442 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst, "current-level-bytes", &aq_size);
1444 if (sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst) {
1445 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst, "current-level-bytes", &vq_size);
1448 queued_buffer = aq_size + vq_size;
1450 /* check free space */
1451 if (free_space < (_MMCAMCORDER_MINIMUM_SPACE + buffer_size + trailer_size + queued_buffer)) {
1452 _mmcam_dbg_warn("No more space for recording!!! Recording is paused.");
1453 _mmcam_dbg_warn("Free Space : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]," \
1454 " buffer size : [%" G_GUINT64_FORMAT "], queued buffer size : [%" G_GUINT64_FORMAT "]", \
1455 free_space, trailer_size, buffer_size, queued_buffer);
1457 if (!sc->isMaxsizePausing) {
1458 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1459 sc->isMaxsizePausing = TRUE;
1461 msg.id = MM_MESSAGE_CAMCORDER_NO_FREE_SPACE;
1462 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1465 return GST_PAD_PROBE_DROP;
1470 pthread_mutex_lock(&(videoinfo->size_check_lock));
1472 /* check max size of recorded file */
1473 if (videoinfo->max_size > 0 &&
1474 videoinfo->max_size < videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) {
1475 GstState pipeline_state = GST_STATE_VOID_PENDING;
1476 GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1477 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1478 _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \
1479 " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]",
1480 videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size);
1482 if (!sc->isMaxsizePausing) {
1483 sc->isMaxsizePausing = TRUE;
1484 gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ;
1485 if (pipeline_state == GST_STATE_PLAYING) {
1486 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1489 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1490 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1493 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1495 return GST_PAD_PROBE_DROP;
1498 videoinfo->filesize += (guint64)buffer_size;
1501 _mmcam_dbg_log("filesize %lld Byte, ", videoinfo->filesize);
1504 pthread_mutex_unlock(&(videoinfo->size_check_lock));
1506 return GST_PAD_PROBE_OK;
1510 static GstPadProbeReturn __mmcamcorder_video_dataprobe_audio_disable(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1512 guint64 trailer_size = 0;
1513 guint64 rec_pipe_time = 0;
1514 unsigned int remained_time = 0;
1516 GstClockTime b_time;
1518 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1519 _MMCamcorderMsgItem msg;
1520 _MMCamcorderSubContext *sc = NULL;
1521 _MMCamcorderVideoInfo *videoinfo = NULL;
1523 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1525 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1526 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1528 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1529 mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK);
1530 mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK);
1532 videoinfo = sc->info_video;
1534 b_time = GST_BUFFER_PTS(buffer);
1536 rec_pipe_time = GST_TIME_AS_MSECONDS(b_time);
1538 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1539 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1544 /* check max time */
1545 if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) {
1546 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1547 rec_pipe_time, videoinfo->max_time);
1549 if (!sc->isMaxtimePausing) {
1550 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1552 sc->isMaxtimePausing = TRUE;
1554 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1555 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1556 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1557 msg.param.recording_status.remained_time = 0;
1558 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1560 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1561 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1564 return GST_PAD_PROBE_DROP;
1567 /* calculate remained time can be recorded */
1568 if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) {
1569 remained_time = videoinfo->max_time - rec_pipe_time;
1570 } else if (videoinfo->max_size > 0) {
1571 long double max_size = (long double)videoinfo->max_size;
1572 long double current_size = (long double)(videoinfo->filesize + trailer_size);
1574 remained_time = (unsigned int)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1577 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1578 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1579 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1580 msg.param.recording_status.remained_time = remained_time;
1581 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1584 _mmcam_dbg_log("time [%" GST_TIME_FORMAT "], size [%d]",
1585 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1588 if (videoinfo->record_timestamp_ratio != _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) {
1589 guint record_motion_rate = (guint)videoinfo->record_motion_rate;
1592 _mmcam_dbg_log("record_motion_rate %d, videoinfo->record_drop_count %d",
1593 record_motion_rate, videoinfo->record_drop_count);
1596 /* drop some frame if fast motion */
1597 if (videoinfo->record_motion_rate > _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) {
1598 if (record_motion_rate != (videoinfo->record_drop_count++)) {
1600 _mmcam_dbg_warn("drop frame");
1602 return GST_PAD_PROBE_DROP;
1605 videoinfo->record_drop_count = 1;
1607 _mmcam_dbg_warn("pass frame");
1611 GST_BUFFER_PTS(buffer) = b_time * (videoinfo->record_timestamp_ratio);
1614 return GST_PAD_PROBE_OK;
1618 static GstPadProbeReturn __mmcamcorder_audioque_dataprobe(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1620 _MMCamcorderMsgItem msg;
1621 guint64 trailer_size = 0;
1622 guint64 rec_pipe_time = 0;
1623 _MMCamcorderSubContext *sc = NULL;
1624 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1625 _MMCamcorderVideoInfo *videoinfo = NULL;
1626 unsigned int remained_time = 0;
1627 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1629 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1630 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1631 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1633 mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK);
1634 mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK);
1635 mmf_return_val_if_fail(sc->element, GST_PAD_PROBE_OK);
1637 videoinfo = sc->info_video;
1639 if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_PTS(buffer))) {
1640 _mmcam_dbg_err( "Buffer timestamp is invalid, check it");
1641 return GST_PAD_PROBE_OK;
1644 rec_pipe_time = GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer));
1646 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1647 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1652 /* calculate remained time can be recorded */
1653 if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) {
1654 remained_time = videoinfo->max_time - rec_pipe_time;
1655 } else if (videoinfo->max_size > 0) {
1656 long double max_size = (long double)videoinfo->max_size;
1657 long double current_size = (long double)(videoinfo->filesize + trailer_size);
1659 remained_time = (unsigned long long)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1662 if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) {
1663 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1664 rec_pipe_time, videoinfo->max_time);
1666 if (!sc->isMaxtimePausing) {
1667 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1669 sc->isMaxtimePausing = TRUE;
1671 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1672 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1673 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1674 msg.param.recording_status.remained_time = 0;
1675 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1677 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1678 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1681 return GST_PAD_PROBE_DROP;
1684 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1685 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1686 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1687 msg.param.recording_status.remained_time = remained_time;
1688 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1691 _mmcam_dbg_log("audio data probe :: time [%" GST_TIME_FORMAT "], size [%lld KB]",
1692 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1695 return GST_PAD_PROBE_OK;
1699 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_audio_mute(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1701 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1702 double volume = 0.0;
1705 int err = MM_ERROR_UNKNOWN;
1706 char *err_name = NULL;
1707 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1710 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1711 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_DROP);
1713 /*_mmcam_dbg_log("AUDIO SRC time stamp : [%" GST_TIME_FORMAT "] \n", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1714 err = mm_camcorder_get_attributes((MMHandleType)hcamcorder, &err_name,
1715 MMCAM_AUDIO_VOLUME, &volume,
1716 MMCAM_AUDIO_FORMAT, &format,
1717 MMCAM_AUDIO_CHANNEL, &channel,
1719 if (err != MM_ERROR_NONE) {
1720 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, err);
1721 SAFE_FREE(err_name);
1725 memset(&mapinfo, 0x0, sizeof(GstMapInfo));
1727 gst_buffer_map(buffer, &mapinfo, GST_MAP_READWRITE);
1729 /* Set audio stream NULL */
1730 if (volume == 0.0) {
1731 memset(mapinfo.data, 0, mapinfo.size);
1734 /* CALL audio stream callback */
1735 if (hcamcorder->astream_cb && buffer && mapinfo.data && mapinfo.size > 0) {
1736 MMCamcorderAudioStreamDataType stream;
1738 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) < MM_CAMCORDER_STATE_PREPARE) {
1739 _mmcam_dbg_warn("Not ready for stream callback");
1740 gst_buffer_unmap(buffer, &mapinfo);
1741 return GST_PAD_PROBE_OK;
1744 /*_mmcam_dbg_log("Call video steramCb, data[%p], Width[%d],Height[%d], Format[%d]",
1745 GST_BUFFER_DATA(buffer), width, height, format);*/
1747 stream.data = (void *)mapinfo.data;
1748 stream.format = format;
1749 stream.channel = channel;
1750 stream.length = mapinfo.size;
1751 stream.timestamp = (unsigned int)(GST_BUFFER_PTS(buffer)/1000000); /* nano -> milli second */
1753 _MMCAMCORDER_LOCK_ASTREAM_CALLBACK(hcamcorder);
1755 if (hcamcorder->astream_cb) {
1756 hcamcorder->astream_cb(&stream, hcamcorder->astream_cb_param);
1759 _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK(hcamcorder);
1762 gst_buffer_unmap(buffer, &mapinfo);
1763 return GST_PAD_PROBE_OK;
1767 static gboolean __mmcamcorder_add_metadata(MMHandleType handle, int fileformat)
1769 gboolean bret = FALSE;
1771 switch (fileformat) {
1772 case MM_FILE_FORMAT_3GP:
1773 case MM_FILE_FORMAT_MP4:
1774 bret = __mmcamcorder_add_metadata_mp4(handle);
1777 _mmcam_dbg_warn("Unsupported fileformat to insert location info (%d)", fileformat);
1785 static gboolean __mmcamcorder_add_metadata_mp4(MMHandleType handle)
1789 guint64 udta_size = 0;
1790 gint64 current_pos = 0;
1791 gint64 moov_pos = 0;
1792 gint64 udta_pos = 0;
1793 gdouble longitude = 0;
1794 gdouble latitude = 0;
1795 gdouble altitude = 0;
1797 int orientation = 0;
1799 char *err_name = NULL;
1800 char err_msg[MAX_ERROR_MESSAGE_LEN] = {'\0',};
1801 _MMCamcorderLocationInfo location_info = {0,0,0};
1802 _MMCamcorderLocationInfo geo_info = {0,0,0};
1804 _MMCamcorderVideoInfo *info = NULL;
1805 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1806 _MMCamcorderSubContext *sc = NULL;
1808 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1809 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1811 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1812 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1816 info = sc->info_video;
1818 f = fopen64(info->filename, "rb+");
1820 strerror_r(errno, err_msg, MAX_ERROR_MESSAGE_LEN);
1821 _mmcam_dbg_err("file open failed [%s]", err_msg);
1825 mm_camcorder_get_attributes(handle, &err_name,
1826 MMCAM_TAG_LATITUDE, &latitude,
1827 MMCAM_TAG_LONGITUDE, &longitude,
1828 MMCAM_TAG_ALTITUDE, &altitude,
1829 MMCAM_TAG_VIDEO_ORIENTATION, &orientation,
1830 MMCAM_TAG_GPS_ENABLE, &gps_enable,
1833 _mmcam_dbg_warn("Get tag attrs fail. (%s:%x)", err_name, err);
1834 SAFE_FREE (err_name);
1837 location_info.longitude = _mmcamcorder_double_to_fix(longitude);
1838 location_info.latitude = _mmcamcorder_double_to_fix(latitude);
1839 location_info.altitude = _mmcamcorder_double_to_fix(altitude);
1840 geo_info.longitude = longitude *10000;
1841 geo_info.latitude = latitude *10000;
1842 geo_info.altitude = altitude *10000;
1843 /* find udta container.
1844 if, there are udta container, write loci box after that
1845 else, make udta container and write loci box. */
1846 if (_mmcamcorder_find_fourcc(f, MMCAM_FOURCC('u','d','t','a'), TRUE)) {
1849 _mmcam_dbg_log("find udta container");
1852 if (fseek(f, -8L, SEEK_CUR) != 0) {
1856 udta_pos = ftello(f);
1861 nread = fread(&buf, sizeof(char), sizeof(buf), f);
1863 _mmcam_dbg_log("recorded file fread %d", nread);
1865 udta_size = _mmcamcorder_get_container_size(buf);
1867 /* goto end of udta and write 'loci' box */
1868 if (fseek(f, (udta_size-4L), SEEK_CUR) != 0) {
1873 if (!_mmcamcorder_write_loci(f, location_info)) {
1874 _mmcam_dbg_err("failed to write loci");
1878 if (!_mmcamcorder_write_geodata(f, geo_info)) {
1879 _mmcam_dbg_err("failed to write geodata");
1884 current_pos = ftello(f);
1885 if (current_pos < 0) {
1889 if (!_mmcamcorder_update_size(f, udta_pos, current_pos)) {
1893 _mmcam_dbg_log("No udta container");
1894 if (fseek(f, 0, SEEK_END) != 0) {
1898 if (!_mmcamcorder_write_udta(f, gps_enable, location_info, geo_info)) {
1899 _mmcam_dbg_err("failed to write udta");
1904 /* find moov container.
1905 update moov container size. */
1906 if((current_pos = ftello(f))<0)
1909 if (_mmcamcorder_find_tag(f, MMCAM_FOURCC('m','o','o','v'), TRUE)) {
1910 gint64 internal_pos = ftello(f);
1912 _mmcam_dbg_log("found moov container");
1913 if (fseek(f, -8L, SEEK_CUR) !=0) {
1917 moov_pos = ftello(f);
1922 if (!_mmcamcorder_update_size(f, moov_pos, current_pos)) {
1926 /* add orientation info */
1927 if (fseeko(f, internal_pos, SEEK_SET) < 0) {
1928 _mmcam_dbg_err("fseek failed : errno %d", errno);
1932 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t','r','a','k'), FALSE)) {
1933 _mmcam_dbg_err("failed to find [trak] tag");
1937 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t','k','h','d'), FALSE)) {
1938 _mmcam_dbg_err("failed to find [tkhd] tag");
1942 _mmcam_dbg_log("found [tkhd] tag");
1944 /* seek to start position of composition matrix */
1945 fseek(f, _OFFSET_COMPOSITION_MATRIX, SEEK_CUR);
1947 /* update composition matrix for orientation */
1948 _mmcamcorder_update_composition_matrix(f, orientation);
1950 _mmcam_dbg_err("No 'moov' container");
1962 _mmcam_dbg_err("ftell() returns negative value.");
1968 int _mmcamcorder_connect_video_stream_cb_signal(MMHandleType handle)
1970 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1971 _MMCamcorderSubContext *sc = NULL;
1973 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1975 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1976 mmf_return_val_if_fail(sc && sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1978 /* check video source element */
1979 if (sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst) {
1980 _mmcam_dbg_warn("connect video stream cb signal to _MMCAMCORDER_VIDEOSRC_SRC");
1981 MMCAMCORDER_SIGNAL_CONNECT(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst,
1982 _MMCAMCORDER_HANDLER_VIDEOREC, "video-stream-cb",
1983 G_CALLBACK(__mmcamcorder_video_stream_cb),
1985 return MM_ERROR_NONE;
1987 _mmcam_dbg_err("videosrc element is not created yet");
1988 return MM_ERROR_CAMCORDER_NOT_INITIALIZED;
1993 int _mmcamcorder_video_prepare_record(MMHandleType handle)
1995 int ret = MM_ERROR_NONE;
1997 _MMCamcorderVideoInfo *info = NULL;
1998 _MMCamcorderSubContext *sc = NULL;
1999 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
2001 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2003 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
2004 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2005 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2007 info = sc->info_video;
2009 _mmcam_dbg_warn("start");
2011 /* create encoding pipeline */
2012 ret =_mmcamcorder_create_recorder_pipeline((MMHandleType)hcamcorder);
2013 if (ret != MM_ERROR_NONE) {
2014 goto _ERR_PREPARE_RECORD;
2017 if (info->filename == NULL) {
2018 char *temp_filename = NULL;
2021 mm_camcorder_get_attributes(handle, NULL,
2022 MMCAM_TARGET_FILENAME, &temp_filename, &size,
2024 if (temp_filename) {
2025 info->filename = strdup(temp_filename);
2028 if (!info->filename) {
2029 _mmcam_dbg_err("strdup[src:%p] was failed", temp_filename);
2030 goto _ERR_PREPARE_RECORD;
2034 _mmcam_dbg_log("Record file name [%s]", info->filename);
2036 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename);
2037 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", 0);
2039 /* Adjust display FPS */
2040 sc->display_interval = 0;
2041 sc->previous_slot_time = 0;
2043 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PAUSED);
2044 if (ret != MM_ERROR_NONE) {
2045 goto _ERR_PREPARE_RECORD;
2048 _mmcam_dbg_warn("done");
2052 _ERR_PREPARE_RECORD:
2053 /* Remove recorder pipeline and recording file which size maybe zero */
2054 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
2055 if (info && info->filename) {
2056 _mmcam_dbg_log("file delete(%s)", info->filename);
2057 unlink(info->filename);