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 gboolean __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 gboolean __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_val_if_fail(buffer, FALSE);
72 mmf_return_val_if_fail(gst_buffer_n_memory(buffer), FALSE);
73 mmf_return_val_if_fail(hcamcorder, FALSE);
75 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
76 mmf_return_val_if_fail(sc, FALSE);
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 && ret != GST_FLOW_FLUSHING) {
111 _mmcam_dbg_err("gst_app_src_push_buffer failed [0x%x]", ret);
112 gst_buffer_unref(buffer);
116 /*_mmcam_dbg_log("push buffer result : 0x%x", ret);*/
118 _mmcam_dbg_warn("unref video buffer immediately - push encoding buffer %d",
119 sc->info_video->push_encoding_buffer);
121 gst_buffer_unref(buffer);
129 int _mmcamcorder_create_recorder_pipeline(MMHandleType handle)
132 int err = MM_ERROR_NONE;
133 int audio_disable = FALSE;
134 const char* gst_element_rsink_name = NULL;
137 GstPad *srcpad = NULL;
138 GstPad *sinkpad = NULL;
140 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
141 _MMCamcorderSubContext *sc = NULL;
143 type_element *RecordsinkElement = NULL;
145 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
147 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
148 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
149 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
151 _mmcam_dbg_warn("start");
153 err = _mmcamcorder_check_videocodec_fileformat_compatibility(handle);
154 if (err != MM_ERROR_NONE)
158 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst) {
159 _mmcam_dbg_log("pipeline is exist so need to remove pipeline _MMCAMCORDER_ENCODE_MAIN_PIPE = %p",
160 sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst);
161 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
164 _MMCAMCORDER_PIPELINE_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCODE_MAIN_PIPE, "recorder_pipeline", err);
166 /* get audio disable */
167 mm_camcorder_get_attributes(handle, NULL,
168 MMCAM_AUDIO_DISABLE, &audio_disable,
171 if (sc->is_modified_rate || audio_disable)
172 sc->audio_disable = TRUE;
174 sc->audio_disable = FALSE;
176 _mmcam_dbg_log("AUDIO DISABLE : %d (is_modified_rate %d, audio_disable %d)",
177 sc->audio_disable, sc->is_modified_rate, audio_disable);
179 if (sc->audio_disable == FALSE) {
180 /* create audiosrc bin */
181 err = _mmcamcorder_create_audiosrc_bin((MMHandleType)hcamcorder);
182 if (err != MM_ERROR_NONE)
186 err = _mmcamcorder_create_encodesink_bin((MMHandleType)hcamcorder, MM_CAMCORDER_ENCBIN_PROFILE_VIDEO);
187 if (err != MM_ERROR_NONE)
190 if (sc->audio_disable == FALSE) {
191 gst_bin_add(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst),
192 sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst);
195 /* add element and encodesink bin to encode main pipeline */
196 gst_bin_add_many(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst),
197 sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst,
198 sc->encode_element[_MMCAMCORDER_ENCSINK_FILT].gst,
199 sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst,
202 /* Link each element : appsrc - capsfilter - encodesink bin */
203 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, "src");
204 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_FILT].gst, "sink");
205 _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error);
207 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_FILT].gst, "src");
208 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst, "video_sink0");
209 _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error);
211 if (sc->audio_disable == FALSE) {
212 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst, "src");
213 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst, "audio_sink0");
214 _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error);
217 _mmcamcorder_conf_get_element(handle, hcamcorder->conf_main,
218 CONFIGURE_CATEGORY_MAIN_RECORD,
221 _mmcamcorder_conf_get_value_element_name(RecordsinkElement, &gst_element_rsink_name);
223 /* set data probe function */
225 /* register message cb */
227 /* set data probe function for audio */
229 if (sc->audio_disable == FALSE) {
230 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst, "sink");
231 MMCAMCORDER_ADD_BUFFER_PROBE(sinkpad, _MMCAMCORDER_HANDLER_VIDEOREC,
232 __mmcamcorder_audioque_dataprobe, hcamcorder);
233 gst_object_unref(sinkpad);
237 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst, "src");
238 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
239 __mmcamcorder_audio_dataprobe_audio_mute, hcamcorder);
240 gst_object_unref(srcpad);
243 if (sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst) {
244 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst, "src");
245 MMCAMCORDER_ADD_EVENT_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
246 __mmcamcorder_eventprobe_monitor, hcamcorder);
247 gst_object_unref(srcpad);
252 if (sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst) {
253 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst, "src");
254 MMCAMCORDER_ADD_EVENT_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
255 __mmcamcorder_eventprobe_monitor, hcamcorder);
256 gst_object_unref(srcpad);
260 if (sc->audio_disable) {
261 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC].gst, "sink");
262 MMCAMCORDER_ADD_BUFFER_PROBE(sinkpad, _MMCAMCORDER_HANDLER_VIDEOREC,
263 __mmcamcorder_video_dataprobe_audio_disable, hcamcorder);
264 gst_object_unref(sinkpad);
268 if (!strcmp(gst_element_rsink_name, "filesink")) {
269 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC].gst, "src");
270 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
271 __mmcamcorder_video_dataprobe_record, hcamcorder);
272 gst_object_unref(srcpad);
275 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst, "src");
276 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
277 __mmcamcorder_audio_dataprobe_check, hcamcorder);
278 gst_object_unref(srcpad);
282 bus = gst_pipeline_get_bus(GST_PIPELINE(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst));
284 /* register pipeline message callback */
285 hcamcorder->encode_pipeline_cb_event_id = gst_bus_add_watch(bus, (GstBusFunc)_mmcamcorder_pipeline_cb_message, hcamcorder);
287 /* set sync handler */
288 gst_bus_set_sync_handler(bus, _mmcamcorder_encode_pipeline_bus_sync_callback, (gpointer)hcamcorder, NULL);
290 gst_object_unref(bus);
293 return MM_ERROR_NONE;
295 pipeline_creation_error:
296 for (i = _MMCAMCORDER_AUDIOSRC_BIN ; i <= _MMCAMCORDER_ENCSINK_SINK ; i++)
297 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, i);
299 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCODE_MAIN_PIPE);
304 int _mmcamcorder_remove_audio_pipeline(MMHandleType handle)
306 GstPad *srcpad = NULL;
307 GstPad *sinkpad = NULL;
308 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
309 _MMCamcorderSubContext *sc = NULL;
311 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
313 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
314 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
315 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
319 if (sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst != NULL) {
320 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst, "src");
321 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst, "audio_sink0");
322 _MM_GST_PAD_UNLINK_UNREF(srcpad, sinkpad);
324 /* release audiosrc bin */
325 gst_bin_remove(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst),
326 sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst);
329 To avoid conflicting between old elements and newly created elements,
330 I clean element handles here. Real elements object will be finalized as the 'unref' process goes on.
331 This is a typical problem of unref. Even though I unref bin here, it takes much time to finalize each elements.
332 So I clean handles first, make them unref later. Audio recording, however, isn't needed this process.
333 It's because the pipeline of audio recording destroys at the same time,
334 and '_mmcamcorder_element_release_noti' will perfom removing handle.
336 _mmcamcorder_remove_element_handle(handle, (void *)sc->encode_element, _MMCAMCORDER_AUDIOSRC_BIN, _MMCAMCORDER_AUDIOSRC_VOL);
338 _mmcam_dbg_log("Audio pipeline removed");
341 return MM_ERROR_NONE;
345 int _mmcamcorder_remove_encode_pipeline(MMHandleType handle)
347 GstPad *reqpad = NULL;
348 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
349 _MMCamcorderSubContext *sc = NULL;
351 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
353 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
354 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
355 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
359 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst != NULL) {
360 /* release request pad */
361 reqpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "audio");
363 gst_element_release_request_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, reqpad);
364 gst_object_unref(reqpad);
368 reqpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "video");
370 gst_element_release_request_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, reqpad);
371 gst_object_unref(reqpad);
375 /* release encode main pipeline */
376 gst_object_unref(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst);
379 To avoid conflicting between old elements and newly created elements,
380 I clean element handles here. Real elements object will be finalized as the 'unref' process goes on.
381 This is a typical problem of unref. Even though I unref bin here, it takes much time to finalize each elements.
382 So I clean handles first, make them unref later. Audio recording, however, isn't needed this process.
383 It's because the pipeline of audio recording destroys at the same time,
384 and '_mmcamcorder_element_release_noti' will perfom removing handle.
386 /* _mmcamcorder_remove_element_handle(handle, (void *)sc->encode_element,
387 _MMCAMCORDER_ENCODE_MAIN_PIPE, _MMCAMCORDER_ENCSINK_SINK); */
389 _mmcam_dbg_log("Encoder pipeline removed");
392 return MM_ERROR_NONE;
396 int _mmcamcorder_remove_recorder_pipeline(MMHandleType handle)
398 int ret = MM_ERROR_NONE;
399 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
400 _MMCamcorderSubContext *sc = NULL;
404 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
405 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
406 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
408 _mmcam_dbg_log("start");
410 if (!sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst) {
411 _mmcam_dbg_warn("pipeline is not existed.");
412 return MM_ERROR_NONE;
415 _mmcamcorder_remove_all_handlers((MMHandleType)hcamcorder, _MMCAMCORDER_HANDLER_VIDEOREC);
417 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_NULL);
418 if (ret != MM_ERROR_NONE) {
419 _mmcam_dbg_err("Faile to change encode main pipeline [0x%x]", ret);
423 bus = gst_pipeline_get_bus(GST_PIPELINE(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst));
425 /* remove audio pipeline first */
426 ret = _mmcamcorder_remove_audio_pipeline(handle);
427 if (ret != MM_ERROR_NONE) {
428 _mmcam_dbg_err("Fail to remove audio pipeline");
432 ret = _mmcamcorder_remove_encode_pipeline(handle);
433 if (ret != MM_ERROR_NONE) {
434 _mmcam_dbg_err("Fail to remove encoder pipeline");
438 /* Remove pipeline message callback */
439 if (hcamcorder->encode_pipeline_cb_event_id != 0) {
440 g_source_remove(hcamcorder->encode_pipeline_cb_event_id);
441 hcamcorder->encode_pipeline_cb_event_id = 0;
444 /* Remove remained message */
446 GstMessage *gst_msg = NULL;
447 while ((gst_msg = gst_bus_pop(bus)) != NULL) {
448 _mmcamcorder_pipeline_cb_message(bus, gst_msg, (gpointer)hcamcorder);
449 gst_message_unref(gst_msg);
452 gst_object_unref(bus);
456 _mmcam_dbg_log("done");
462 int _mmcamcorder_video_command(MMHandleType handle, int command)
467 int ret = MM_ERROR_NONE;
468 double motion_rate = _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE;
469 char *err_name = NULL;
470 char *temp_filename = NULL;
471 GstCameraControl *CameraControl = NULL;
472 GstCameraControlChannel *CameraControlChannel = NULL;
473 const GList *controls = NULL;
474 const GList *item = NULL;
477 GstElement *pipeline = NULL;
479 _MMCamcorderVideoInfo *info = NULL;
480 _MMCamcorderSubContext *sc = NULL;
481 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
483 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
485 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
486 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
487 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
488 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
490 info = sc->info_video;
492 _mmcam_dbg_log("Command(%d)", command);
494 pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
497 case _MMCamcorder_CMD_RECORD:
499 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) != MM_CAMCORDER_STATE_PAUSED) {
505 int ret_free_space = 0;
506 char *dir_name = NULL;
507 guint64 free_space = 0;
508 int file_system_type = 0;
509 int root_directory_length = 0;
512 _mmcam_dbg_log("Record Start - dual stream %d", info->support_dual_stream);
514 /* init record_dual_stream */
515 info->record_dual_stream = FALSE;
517 ret = mm_camcorder_get_attributes(handle, &err_name,
518 MMCAM_CAMERA_FPS, &fps,
519 MMCAM_CAMERA_WIDTH, &(info->preview_width),
520 MMCAM_CAMERA_HEIGHT, &(info->preview_height),
521 MMCAM_VIDEO_WIDTH, &(info->video_width),
522 MMCAM_VIDEO_HEIGHT, &(info->video_height),
523 MMCAM_FILE_FORMAT, &fileformat,
524 MMCAM_TARGET_FILENAME, &temp_filename, &size,
525 MMCAM_TARGET_MAX_SIZE, &imax_size,
526 MMCAM_TARGET_TIME_LIMIT, &imax_time,
527 MMCAM_FILE_FORMAT, &(info->fileformat),
528 MMCAM_CAMERA_RECORDING_MOTION_RATE, &motion_rate,
529 MMCAM_ROOT_DIRECTORY, &hcamcorder->root_directory, &root_directory_length,
531 if (ret != MM_ERROR_NONE) {
532 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, ret);
534 goto _ERR_CAMCORDER_VIDEO_COMMAND;
537 if (temp_filename == NULL) {
538 _mmcam_dbg_err("filename is not set");
539 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
540 goto _ERR_CAMCORDER_VIDEO_COMMAND;
545 info->max_size = 0; /* do not check */
547 info->max_size = ((guint64)imax_size) << 10; /* to byte */
551 info->max_time = 0; /* do not check */
553 info->max_time = ((guint64)imax_time) * 1000; /* to millisecond */
555 dir_name = g_path_get_dirname(temp_filename);
557 ret = _mmcamcorder_get_storage_info(dir_name, hcamcorder->root_directory, &hcamcorder->storage_info);
559 _mmcam_dbg_err("get storage info failed");
562 return MM_ERROR_OUT_OF_STORAGE;
565 ret_free_space = _mmcamcorder_get_freespace(hcamcorder->storage_info.type, &free_space);
567 _mmcam_dbg_warn("current space - %s [%" G_GUINT64_FORMAT "]", dir_name, free_space);
569 if (_mmcamcorder_get_file_system_type(dir_name, &file_system_type) == 0) {
570 /* MSDOS_SUPER_MAGIC : 0x4d44 */
571 if (file_system_type == MSDOS_SUPER_MAGIC &&
572 (info->max_size == 0 || info->max_size > FAT32_FILE_SYSTEM_MAX_SIZE)) {
573 _mmcam_dbg_warn("FAT32 and too large max[%"G_GUINT64_FORMAT"], set max as %"G_GUINT64_FORMAT,
574 info->max_size, FAT32_FILE_SYSTEM_MAX_SIZE);
575 info->max_size = FAT32_FILE_SYSTEM_MAX_SIZE;
577 _mmcam_dbg_warn("file system 0x%x, max size %"G_GUINT64_FORMAT,
578 file_system_type, info->max_size);
581 _mmcam_dbg_warn("_mmcamcorder_get_file_system_type failed");
587 _mmcam_dbg_err("failed to get directory name");
591 if ((ret_free_space == -1) || free_space <= (_MMCAMCORDER_MINIMUM_SPACE<<1)) {
592 _mmcam_dbg_err("OUT of STORAGE [ret_free_space:%d or free space [%" G_GUINT64_FORMAT "] is smaller than [%d]",
593 ret_free_space, free_space, (_MMCAMCORDER_MINIMUM_SPACE<<1));
594 return MM_ERROR_OUT_OF_STORAGE;
597 g_mutex_lock(&hcamcorder->task_thread_lock);
598 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst == NULL &&
599 hcamcorder->task_thread_state == _MMCAMCORDER_TASK_THREAD_STATE_NONE) {
600 /* Play record start sound */
601 _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_SAMPLE_SOUND_NAME_REC_START, FALSE);
603 g_mutex_unlock(&hcamcorder->task_thread_lock);
605 _mmcam_dbg_warn("video size [%dx%d]", info->video_width, info->video_height);
607 if (info->video_width == 0 || info->video_height == 0) {
608 _mmcam_dbg_warn("video size is invalid [%dx%d] use preview size [%dx%d]",
609 info->video_width, info->video_height, info->preview_width, info->preview_height);
610 info->video_width = info->preview_width;
611 info->video_height = info->preview_height;
614 if (info->support_dual_stream) {
615 _mmcam_dbg_warn("DUAL STREAM MODE");
617 info->record_dual_stream = TRUE;
619 /* No need to restart preview */
620 info->restart_preview = FALSE;
622 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "video-width", info->video_width);
623 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "video-height", info->video_height);
624 } else if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
625 info->preview_width == info->video_width &&
626 info->preview_height == info->video_height) {
627 _mmcam_dbg_log("H264 preview mode and same resolution");
629 /* No need to restart preview */
630 info->restart_preview = FALSE;
632 /* always need to restart preview */
633 info->restart_preview = TRUE;
636 /* set recording hint */
637 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", TRUE);
639 if (info->restart_preview) {
640 /* stop preview and set new size */
641 _mmcam_dbg_log("restart preview");
643 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
644 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
645 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "keep-camera-preview", TRUE);
647 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
649 /* check decoder recreation */
650 if (!_mmcamcorder_recreate_decoder_for_encoded_preview(handle)) {
651 _mmcam_dbg_err("_mmcamcorder_recreate_decoder_for_encoded_preview failed");
652 ret = MM_ERROR_CAMCORDER_INTERNAL;
653 goto _ERR_CAMCORDER_VIDEO_COMMAND;
656 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
657 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
658 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "keep-camera-preview", FALSE);
660 if (ret != MM_ERROR_NONE)
661 goto _ERR_CAMCORDER_VIDEO_COMMAND;
663 if (!_mmcamcorder_set_camera_resolution(handle, info->video_width, info->video_height)) {
664 ret = MM_ERROR_CAMCORDER_INTERNAL;
665 goto _ERR_CAMCORDER_VIDEO_COMMAND;
668 /* Start preview again with new setting */
669 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
670 if (ret != MM_ERROR_NONE)
671 goto _ERR_CAMCORDER_VIDEO_COMMAND;
673 if (motion_rate < 1.0) {
674 _mmcam_dbg_warn("wait for stabilization of frame");
678 _mmcam_dbg_log("no need to restart preview");
681 _mmcamcorder_conf_get_value_int(handle, hcamcorder->conf_main,
682 CONFIGURE_CATEGORY_MAIN_RECORD,
686 _mmcamcorder_conf_get_value_int(handle, hcamcorder->conf_main,
687 CONFIGURE_CATEGORY_MAIN_RECORD,
688 "PassFirstVideoFrame",
689 &(sc->pass_first_vframe));
691 _mmcam_dbg_log("Drop video frame count[%d], Pass fisrt video frame count[%d]",
692 sc->drop_vframe, sc->pass_first_vframe);
694 info->record_drop_count = (guint)motion_rate;
695 info->record_motion_rate = motion_rate;
696 if (sc->is_modified_rate)
697 info->record_timestamp_ratio = (_MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE/motion_rate);
699 info->record_timestamp_ratio = _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE;
701 _mmcam_dbg_warn("recording fps %d, motion rate %f, timestamp_ratio %f",
702 fps, info->record_motion_rate, info->record_timestamp_ratio);
704 /* set push buffer flag */
705 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_INIT;
706 info->base_video_ts = 0;
708 /* connect video stream cb signal */
709 /*130826 Connect video stream cb for handling fast record frame cb*/
710 if (info->record_dual_stream) {
711 if (_mmcamcorder_connect_video_stream_cb_signal((MMHandleType)hcamcorder) != MM_ERROR_NONE)
712 goto _ERR_CAMCORDER_VIDEO_COMMAND;
715 /* start video stream */
716 if (info->record_dual_stream) {
717 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
719 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
721 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_START");
722 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_START);
724 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
726 _mmcam_dbg_err("could not get camera control");
730 /* check pre-created encode pipeline */
731 g_mutex_lock(&hcamcorder->task_thread_lock);
732 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst == NULL &&
733 hcamcorder->task_thread_state == _MMCAMCORDER_TASK_THREAD_STATE_NONE) {
734 /* create encoding pipeline */
735 ret = _mmcamcorder_video_prepare_record((MMHandleType)hcamcorder);
736 if (ret != MM_ERROR_NONE) {
737 g_mutex_unlock(&hcamcorder->task_thread_lock);
738 goto _ERR_CAMCORDER_VIDEO_COMMAND;
741 g_mutex_unlock(&hcamcorder->task_thread_lock);
743 /* check recording start sound */
744 _mmcamcorder_sound_solo_play_wait(handle);
746 /**< To fix video recording hanging
747 1. use gst_element_set_start_time() instead of gst_pipeline_set_new_stream_time()
748 2. Set (GstClockTime)1 instead of (GstClockTime)0. Because of strict check in gstreamer 0.25,
749 basetime wouldn't change if you set (GstClockTime)0.
750 3. Move set start time position below PAUSED of pipeline.
753 gst_element_set_start_time(GST_ELEMENT(sc->element[_MMCAMCORDER_MAIN_PIPE].gst), (GstClockTime)1);
754 gst_element_set_start_time(GST_ELEMENT(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), (GstClockTime)1);
757 info->video_frame_count = 0;
758 info->is_firstframe = TRUE;
759 info->audio_frame_count = 0;
761 sc->ferror_send = FALSE;
762 sc->ferror_count = 0;
763 hcamcorder->error_occurs = FALSE;
764 sc->bget_eos = FALSE;
766 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PLAYING);
767 if (ret != MM_ERROR_NONE) {
768 /* stop video stream */
769 if (info->record_dual_stream) {
770 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
772 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
774 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
775 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
777 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
779 _mmcam_dbg_err("failed to get camera control");
783 /* Remove recorder pipeline and recording file which size maybe zero */
784 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
785 if (info->filename) {
786 _mmcam_dbg_log("file delete(%s)", info->filename);
787 unlink(info->filename);
789 goto _ERR_CAMCORDER_VIDEO_COMMAND;
792 /*set the camera control to create the GOP so that video record will get a new key frame*/
793 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
794 GST_IS_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst)) {
795 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
796 controls = gst_camera_control_list_channels(CameraControl);
797 if (controls != NULL) {
798 for (item = controls ; item && item->data ; item = item->next) {
799 CameraControlChannel = item->data;
800 _mmcam_dbg_log("CameraControlChannel->label %s", CameraControlChannel->label);
801 if (!strcmp(CameraControlChannel->label, "new-gop")) {
802 /* gst_camera_control_set_value(CameraControl, CameraControlChannel, 1); */
808 _mmcam_dbg_warn("failed to find new-gop control channel");
811 _mmcam_dbg_warn("Can't cast Video source into camera control or not H264 prevew format[%d]",
812 sc->info_image->preview_format);
817 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
818 GST_IS_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst)) {
819 /* generate and I-frame on resuming */
820 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
821 controls = gst_camera_control_list_channels(CameraControl);
822 if (controls != NULL) {
823 for (item = controls ; item && item->data ; item = item->next) {
824 CameraControlChannel = item->data;
825 _mmcam_dbg_log("CameraControlChannel->label %s", CameraControlChannel->label);
826 if (!strcmp(CameraControlChannel->label, "new-gop")) {
827 /* gst_camera_control_set_value(CameraControl, CameraControlChannel, 1); */
833 _mmcam_dbg_warn("failed to find new-gop control channel");
837 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", FALSE);
839 _mmcam_dbg_log("Object property settings done");
843 case _MMCamcorder_CMD_PAUSE:
845 if (info->b_commiting) {
846 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
847 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
850 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
851 if (sc->audio_disable) {
852 /* check only video frame */
853 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
855 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
856 _mmcam_dbg_err("Pause fail, frame count %llu", info->video_frame_count);
857 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
859 _mmcam_dbg_warn("Waiting for enough video frame, retrial[%d], frame %llu", count, info->video_frame_count);
862 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
864 /* check both of video and audio frame */
865 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
867 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
868 _mmcam_dbg_err("Pause fail, frame count VIDEO[%llu], AUDIO [%llu]",
869 info->video_frame_count, info->audio_frame_count);
870 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
872 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%llu], AUDIO [%llu]",
873 count, info->video_frame_count, info->audio_frame_count);
876 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
880 /* block encodebin */
881 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", TRUE);
884 case _MMCamcorder_CMD_CANCEL:
886 if (info->b_commiting) {
887 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
888 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
891 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
893 if (hcamcorder->capture_in_recording == FALSE) {
895 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
896 _mmcam_dbg_err("Failed to Wait capture data");
897 hcamcorder->capture_in_recording = FALSE;
900 _mmcam_dbg_warn("Waiting for capture data - retrial [%d]", count);
903 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
906 /* block push buffer */
907 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP;
909 ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
910 if (ret != MM_ERROR_NONE)
911 goto _ERR_CAMCORDER_VIDEO_COMMAND;
913 /* set recording hint */
914 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", FALSE);
916 /* stop video stream */
917 if (info->record_dual_stream) {
918 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
920 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
922 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
923 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
925 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
927 _mmcam_dbg_err("failed to get camera control");
931 if (info->restart_preview) {
932 /* restart preview */
933 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
934 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
935 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "keep-camera-preview", TRUE);
937 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
939 /* check decoder recreation */
940 if (!_mmcamcorder_recreate_decoder_for_encoded_preview(handle)) {
941 _mmcam_dbg_err("_mmcamcorder_recreate_decoder_for_encoded_preview failed");
942 ret = MM_ERROR_CAMCORDER_INTERNAL;
945 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
946 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
947 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "keep-camera-preview", FALSE);
949 if (ret != MM_ERROR_NONE)
950 goto _ERR_CAMCORDER_VIDEO_COMMAND;
952 /* reset restart_preview for inset window layout */
953 info->restart_preview = FALSE;
955 if (!_mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height)) {
956 ret = MM_ERROR_CAMCORDER_INTERNAL;
957 goto _ERR_CAMCORDER_VIDEO_COMMAND;
960 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
961 if (ret != MM_ERROR_NONE)
962 goto _ERR_CAMCORDER_VIDEO_COMMAND;
965 /* remove target file */
966 if (info->filename) {
967 _mmcam_dbg_log("file delete(%s)", info->filename);
968 unlink(info->filename);
971 sc->isMaxsizePausing = FALSE;
972 sc->isMaxtimePausing = FALSE;
974 sc->display_interval = 0;
975 sc->previous_slot_time = 0;
976 info->video_frame_count = 0;
977 info->audio_frame_count = 0;
979 hcamcorder->capture_in_recording = FALSE;
982 case _MMCamcorder_CMD_COMMIT:
984 if (info->b_commiting) {
985 _mmcam_dbg_err("now on commiting previous file!!(command : %d)", command);
986 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
988 _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT : start");
989 info->b_commiting = TRUE;
990 sc->bget_eos = FALSE;
993 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
994 if (sc->audio_disable) {
995 /* check only video frame */
996 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME &&
997 hcamcorder->capture_in_recording == FALSE) {
999 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
1000 _mmcam_dbg_err("Commit fail, frame count is %llu, capturing %d",
1001 info->video_frame_count, hcamcorder->capture_in_recording);
1003 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
1004 _mmcam_dbg_warn("video frames are enough. keep going...");
1006 info->b_commiting = FALSE;
1007 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
1010 _mmcam_dbg_warn("Waiting for enough video frame, retrial [%d], frame %llu, capturing %d",
1011 count, info->video_frame_count, hcamcorder->capture_in_recording);
1014 /* check both of video and audio frame */
1015 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME &&
1016 info->audio_frame_count &&
1017 hcamcorder->capture_in_recording == FALSE) {
1019 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
1020 _mmcam_dbg_err("Commit fail, VIDEO[%llu], AUDIO [%llu], capturing %d",
1021 info->video_frame_count, info->audio_frame_count, hcamcorder->capture_in_recording);
1023 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
1024 _mmcam_dbg_warn("video/audio frames are enough. keep going...");
1026 info->b_commiting = FALSE;
1027 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
1030 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
1032 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%llu], AUDIO [%llu], capturing %d",
1033 count, info->video_frame_count, info->audio_frame_count, hcamcorder->capture_in_recording);
1037 if (hcamcorder->capture_in_recording) {
1038 gint64 end_time = g_get_monotonic_time() + (200 * G_TIME_SPAN_MILLISECOND);
1039 if (_MMCAMCORDER_CMD_WAIT_UNTIL(handle, end_time)) {
1040 _mmcam_dbg_warn("signal received");
1042 _mmcam_dbg_warn("timeout");
1045 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
1049 /* block push buffer */
1050 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP;
1051 _mmcam_dbg_log("block push buffer to appsrc");
1053 if (sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst != NULL) {
1054 if (gst_element_send_event(sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, gst_event_new_eos())) {
1055 _mmcam_dbg_warn("VIDEO: send eos to appsrc done");
1057 _mmcam_dbg_err("VIDEO: send EOS failed");
1058 info->b_commiting = FALSE;
1059 ret = MM_ERROR_CAMCORDER_INTERNAL;
1060 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1063 _mmcam_dbg_err("No video stream source");
1064 info->b_commiting = FALSE;
1065 ret = MM_ERROR_CAMCORDER_INTERNAL;
1066 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1069 if (sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst != NULL) {
1070 if (gst_element_send_event(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst, gst_event_new_eos())) {
1071 _mmcam_dbg_warn("AUDIO: send eos to audiosrc done");
1073 _mmcam_dbg_err("AUDIO: send EOS failed");
1074 info->b_commiting = FALSE;
1075 ret = MM_ERROR_CAMCORDER_INTERNAL;
1076 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1079 _mmcam_dbg_log("No audio stream");
1083 sc->display_interval = 0;
1084 sc->previous_slot_time = 0;
1087 _mmcam_dbg_log("Start to wait EOS");
1088 ret = _mmcamcorder_get_eos_message(handle);
1089 if (ret != MM_ERROR_NONE) {
1090 info->b_commiting = FALSE;
1091 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1095 hcamcorder->capture_in_recording = FALSE;
1099 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
1100 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1103 return MM_ERROR_NONE;
1105 _ERR_CAMCORDER_VIDEO_COMMAND:
1106 if (command == _MMCamcorder_CMD_RECORD)
1107 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
1113 int _mmcamcorder_video_handle_eos(MMHandleType handle)
1115 int ret = MM_ERROR_NONE;
1117 guint64 file_size = 0;
1119 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1120 _MMCamcorderSubContext *sc = NULL;
1121 _MMCamcorderVideoInfo *info = NULL;
1122 _MMCamcorderMsgItem msg;
1123 MMCamRecordingReport *report = NULL;
1125 mmf_return_val_if_fail(hcamcorder, FALSE);
1127 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1128 mmf_return_val_if_fail(sc, FALSE);
1129 mmf_return_val_if_fail(sc->info_video, FALSE);
1131 info = sc->info_video;
1135 if (hcamcorder->state_change_by_system != _MMCAMCORDER_STATE_CHANGE_BY_FOCUS) {
1136 /* Play record stop sound */
1137 _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_SAMPLE_SOUND_NAME_REC_STOP, FALSE);
1139 _mmcam_dbg_warn("Play stop sound through pulseaudio");
1141 _mmcamcorder_sound_init(handle);
1143 _mmcamcorder_sound_play((MMHandleType)hcamcorder, _MMCAMCORDER_SAMPLE_SOUND_NAME_REC_STOP, TRUE);
1145 _mmcamcorder_sound_finalize(handle);
1148 /* remove blocking part */
1149 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
1151 mm_camcorder_get_attributes(handle, NULL,
1152 MMCAM_RECORDER_TAG_ENABLE, &enabletag,
1155 ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
1156 if (ret != MM_ERROR_NONE)
1157 _mmcam_dbg_warn("_MMCamcorder_CMD_COMMIT:__mmcamcorder_remove_recorder_pipeline failed. error[%x]", ret);
1159 /* set recording hint */
1160 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", FALSE);
1162 /* stop video stream */
1163 if (info->record_dual_stream) {
1164 GstCameraControl *control = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
1166 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
1168 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
1169 gst_camera_control_set_record_command(control, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
1171 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
1173 _mmcam_dbg_err("failed to get camera control");
1177 if (enabletag && !(sc->ferror_send)) {
1178 ret = __mmcamcorder_add_metadata((MMHandleType)hcamcorder, info->fileformat);
1180 _mmcam_dbg_log("Writing location information SUCCEEDED !!");
1182 _mmcam_dbg_err("Writing location information FAILED !!");
1186 /* Check file size */
1187 if (info->max_size > 0) {
1188 _mmcamcorder_get_file_size(info->filename, &file_size);
1189 _mmcam_dbg_log("MAX size %lld byte - created filesize %lld byte",
1190 info->max_size, file_size);
1192 if (file_size > info->max_size) {
1193 _MMCamcorderMsgItem message;
1194 _mmcam_dbg_err("File size is greater than max size !!");
1195 message.id = MM_MESSAGE_CAMCORDER_ERROR;
1196 message.param.code = MM_ERROR_CAMCORDER_FILE_SIZE_OVER;
1197 _mmcamcorder_send_message((MMHandleType)hcamcorder, &message);
1201 if (info->restart_preview) {
1203 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
1204 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
1205 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "keep-camera-preview", TRUE);
1207 _mmcam_dbg_log("Set state of pipeline as READY");
1208 ret = _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_READY);
1210 /* check decoder recreation */
1211 if (!_mmcamcorder_recreate_decoder_for_encoded_preview(handle)) {
1212 _mmcam_dbg_err("_mmcamcorder_recreate_decoder_for_encoded_preview failed");
1213 ret = MM_ERROR_CAMCORDER_INTERNAL;
1217 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
1218 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
1219 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "keep-camera-preview", FALSE);
1221 if (ret != MM_ERROR_NONE) {
1222 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1223 msg.param.code = ret;
1224 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1225 _mmcam_dbg_err("Failed to set state READY[%x]", ret);
1228 /* reset restart_preview for inset window layout */
1229 info->restart_preview = FALSE;
1231 /* recover preview size */
1232 _mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height);
1234 ret = _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_PLAYING);
1235 /* Do not return when error is occurred.
1236 Recording file was created successfully, but starting pipeline failed */
1237 if (ret != MM_ERROR_NONE) {
1238 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1239 msg.param.code = ret;
1240 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1241 _mmcam_dbg_err("Failed to set state PLAYING[%x]", ret);
1244 _mmcam_dbg_log("No need to restart preview");
1247 /* Send recording report to application */
1248 msg.id = MM_MESSAGE_CAMCORDER_VIDEO_CAPTURED;
1249 report = (MMCamRecordingReport *)g_malloc(sizeof(MMCamRecordingReport));
1251 _mmcam_dbg_err("Recording report fail(%s). Out of memory.", info->filename);
1253 report->recording_filename = g_strdup(info->filename);
1254 msg.param.data = report;
1256 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1260 sc->pipeline_time = 0;
1262 sc->isMaxsizePausing = FALSE; /*In async function, this variable should set in callback function. */
1263 sc->isMaxtimePausing = FALSE;
1264 hcamcorder->error_occurs = FALSE;
1266 info->video_frame_count = 0;
1267 info->audio_frame_count = 0;
1269 info->b_commiting = FALSE;
1271 if (hcamcorder->state_change_by_system != _MMCAMCORDER_STATE_CHANGE_BY_FOCUS) {
1272 /* check recording stop sound */
1273 _mmcamcorder_sound_solo_play_wait(handle);
1276 _mmcam_dbg_err("_MMCamcorder_CMD_COMMIT : end");
1283 * This function is record video data probing function.
1284 * If this function is linked with certain pad by gst_pad_add_buffer_probe(),
1285 * this function will be called when data stream pass through the pad.
1287 * @param[in] pad probing pad which calls this function.
1288 * @param[in] buffer buffer which contains stream data.
1289 * @param[in] u_data user data.
1290 * @return This function returns true on success, or false value with error
1294 static GstPadProbeReturn __mmcamcorder_eventprobe_monitor(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1296 GstEvent *event = GST_PAD_PROBE_INFO_EVENT(info);
1297 switch (GST_EVENT_TYPE(event)) {
1298 case GST_EVENT_UNKNOWN:
1299 /* upstream events */
1301 case GST_EVENT_SEEK:
1302 case GST_EVENT_NAVIGATION:
1303 case GST_EVENT_LATENCY:
1304 /* downstream serialized events */
1305 case GST_EVENT_SEGMENT:
1307 case GST_EVENT_BUFFERSIZE:
1308 _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1311 _mmcam_dbg_warn("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1313 /* bidirectional events */
1314 case GST_EVENT_FLUSH_START:
1315 case GST_EVENT_FLUSH_STOP:
1316 _mmcam_dbg_err("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1319 _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1323 return GST_PAD_PROBE_OK;
1327 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_check(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1329 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1330 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1332 _MMCamcorderSubContext *sc = NULL;
1333 _MMCamcorderVideoInfo *videoinfo = NULL;
1334 _MMCamcorderMsgItem msg;
1335 guint64 buffer_size = 0;
1336 guint64 trailer_size = 0;
1337 guint64 max_size = 0;
1339 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1340 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1341 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1343 mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK);
1344 videoinfo = sc->info_video;
1346 /* get buffer size */
1347 if (!gst_buffer_map(buffer, &mapinfo, GST_MAP_READ)) {
1348 _mmcam_dbg_warn("map failed : buffer %p", buffer);
1349 return GST_PAD_PROBE_OK;
1352 buffer_size = mapinfo.size;
1353 gst_buffer_unmap(buffer, &mapinfo);
1355 /*_mmcam_dbg_err("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1357 g_mutex_lock(&videoinfo->size_check_lock);
1359 if (videoinfo->audio_frame_count == 0) {
1360 videoinfo->filesize += buffer_size;
1361 videoinfo->audio_frame_count++;
1362 g_mutex_unlock(&videoinfo->size_check_lock);
1363 return GST_PAD_PROBE_OK;
1366 if (sc->ferror_send || sc->isMaxsizePausing) {
1367 _mmcam_dbg_warn("Recording is paused, drop frames");
1368 g_mutex_unlock(&videoinfo->size_check_lock);
1369 return GST_PAD_PROBE_DROP;
1372 /* get trailer size */
1373 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1374 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1379 /* check max size of recorded file */
1380 max_size = videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE;
1381 if (videoinfo->max_size > 0 && videoinfo->max_size < max_size) {
1382 GstState pipeline_state = GST_STATE_VOID_PENDING;
1383 GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1384 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1385 _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \
1386 " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]",
1387 videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size);
1389 if (!sc->isMaxsizePausing) {
1390 sc->isMaxsizePausing = TRUE;
1391 gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ;
1392 if (pipeline_state == GST_STATE_PLAYING)
1393 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1395 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1396 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1399 g_mutex_unlock(&videoinfo->size_check_lock);
1404 videoinfo->filesize += buffer_size;
1405 videoinfo->audio_frame_count++;
1407 g_mutex_unlock(&videoinfo->size_check_lock);
1409 return GST_PAD_PROBE_OK;
1413 static GstPadProbeReturn __mmcamcorder_video_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1418 guint64 free_space = 0;
1419 guint64 buffer_size = 0;
1420 guint64 trailer_size = 0;
1421 guint64 queued_buffer = 0;
1422 guint64 max_size = 0;
1423 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1425 storage_state_e storage_state = STORAGE_STATE_UNMOUNTABLE;
1427 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1428 _MMCamcorderMsgItem msg;
1429 _MMCamcorderSubContext *sc = NULL;
1430 _MMCamcorderVideoInfo *videoinfo = NULL;
1432 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1433 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1435 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1436 mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK);
1437 videoinfo = sc->info_video;
1439 /*_mmcam_dbg_log("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1440 if (sc->ferror_send) {
1441 _mmcam_dbg_warn("file write error, drop frames");
1442 return GST_PAD_PROBE_DROP;
1445 gst_buffer_map(buffer, &mapinfo, GST_MAP_READ);
1446 buffer_size = mapinfo.size;
1447 gst_buffer_unmap(buffer, &mapinfo);
1449 videoinfo->video_frame_count++;
1450 if (videoinfo->video_frame_count <= (guint64)_MMCAMCORDER_MINIMUM_FRAME) {
1451 /* _mmcam_dbg_log("Pass minimum frame: info->video_frame_count: %" G_GUINT64_FORMAT " ",
1452 info->video_frame_count); */
1453 g_mutex_lock(&videoinfo->size_check_lock);
1454 videoinfo->filesize += buffer_size;
1455 g_mutex_unlock(&videoinfo->size_check_lock);
1456 return GST_PAD_PROBE_OK;
1459 /* get trailer size */
1460 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1461 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1466 /* check free space */
1467 ret = _mmcamcorder_get_freespace(hcamcorder->storage_info.type, &free_space);
1469 _mmcam_dbg_err("Error occured. [%d]", ret);
1470 if (sc->ferror_count == 2 && sc->ferror_send == FALSE) {
1471 sc->ferror_send = TRUE;
1473 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1474 msg.param.code = MM_ERROR_FILE_READ;
1476 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1481 return GST_PAD_PROBE_DROP; /* skip this buffer */
1484 if (free_space == 0) {
1485 /* check storage state */
1486 storage_get_state(hcamcorder->storage_info.id, &storage_state);
1488 _mmcam_dbg_warn("storage state %d", storage_state);
1490 if (storage_state == STORAGE_STATE_REMOVED ||
1491 storage_state == STORAGE_STATE_UNMOUNTABLE) {
1492 _mmcam_dbg_err("storage was removed!");
1494 _MMCAMCORDER_LOCK(hcamcorder);
1496 if (sc->ferror_send == FALSE) {
1497 _mmcam_dbg_err("OUT_OF_STORAGE error");
1499 sc->ferror_send = TRUE;
1501 _MMCAMCORDER_UNLOCK(hcamcorder);
1503 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1504 msg.param.code = MM_ERROR_OUT_OF_STORAGE;
1506 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1508 _MMCAMCORDER_UNLOCK(hcamcorder);
1509 _mmcam_dbg_warn("error was already sent");
1512 return GST_PAD_PROBE_DROP;
1516 /* get queued buffer size */
1517 if (sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst) {
1518 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst, "current-level-bytes", &aq_size);
1521 if (sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst) {
1522 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst, "current-level-bytes", &vq_size);
1525 queued_buffer = aq_size + vq_size;
1527 if (free_space < (_MMCAMCORDER_MINIMUM_SPACE + buffer_size + trailer_size + queued_buffer)) {
1528 _mmcam_dbg_warn("No more space for recording!!! Recording is paused.");
1529 _mmcam_dbg_warn("Free Space : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]," \
1530 " buffer size : [%" G_GUINT64_FORMAT "], queued buffer size : [%" G_GUINT64_FORMAT "]", \
1531 free_space, trailer_size, buffer_size, queued_buffer);
1533 if (!sc->isMaxsizePausing) {
1534 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1535 sc->isMaxsizePausing = TRUE;
1537 msg.id = MM_MESSAGE_CAMCORDER_NO_FREE_SPACE;
1538 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1541 return GST_PAD_PROBE_DROP;
1544 g_mutex_lock(&videoinfo->size_check_lock);
1546 /* check max size of recorded file */
1547 max_size = videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE;
1548 if (videoinfo->max_size > 0 && videoinfo->max_size < max_size) {
1549 GstState pipeline_state = GST_STATE_VOID_PENDING;
1550 GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1551 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1552 _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \
1553 " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]",
1554 videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size);
1556 if (!sc->isMaxsizePausing) {
1557 sc->isMaxsizePausing = TRUE;
1558 gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ;
1559 if (pipeline_state == GST_STATE_PLAYING)
1560 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1562 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1563 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1566 g_mutex_unlock(&videoinfo->size_check_lock);
1568 return GST_PAD_PROBE_DROP;
1571 videoinfo->filesize += (guint64)buffer_size;
1574 _mmcam_dbg_log("filesize %lld Byte, ", videoinfo->filesize);
1577 g_mutex_unlock(&videoinfo->size_check_lock);
1579 return GST_PAD_PROBE_OK;
1583 static GstPadProbeReturn __mmcamcorder_video_dataprobe_audio_disable(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1585 guint64 trailer_size = 0;
1586 guint64 rec_pipe_time = 0;
1587 unsigned int remained_time = 0;
1589 GstClockTime b_time;
1591 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1592 _MMCamcorderMsgItem msg;
1593 _MMCamcorderSubContext *sc = NULL;
1594 _MMCamcorderVideoInfo *videoinfo = NULL;
1596 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1598 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1599 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1601 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1602 mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK);
1603 mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK);
1605 videoinfo = sc->info_video;
1607 b_time = GST_BUFFER_PTS(buffer);
1609 rec_pipe_time = GST_TIME_AS_MSECONDS(b_time);
1611 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1612 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1617 /* check max time */
1618 if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) {
1619 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1620 rec_pipe_time, videoinfo->max_time);
1622 if (!sc->isMaxtimePausing) {
1623 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1625 sc->isMaxtimePausing = TRUE;
1627 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1628 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1629 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1630 msg.param.recording_status.remained_time = 0;
1631 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1633 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1634 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1637 return GST_PAD_PROBE_DROP;
1640 /* calculate remained time can be recorded */
1641 if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) {
1642 remained_time = videoinfo->max_time - rec_pipe_time;
1643 } else if (videoinfo->max_size > 0) {
1644 long double max_size = (long double)videoinfo->max_size;
1645 long double current_size = (long double)(videoinfo->filesize + trailer_size);
1647 remained_time = (unsigned int)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1650 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1651 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1652 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1653 msg.param.recording_status.remained_time = remained_time;
1654 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1657 _mmcam_dbg_log("time [%" GST_TIME_FORMAT "], size [%d]",
1658 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1661 if (videoinfo->record_timestamp_ratio != _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) {
1662 guint record_motion_rate = (guint)videoinfo->record_motion_rate;
1665 _mmcam_dbg_log("record_motion_rate %d, videoinfo->record_drop_count %d",
1666 record_motion_rate, videoinfo->record_drop_count);
1669 /* drop some frame if fast motion */
1670 if (videoinfo->record_motion_rate > _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) {
1671 if (record_motion_rate != (videoinfo->record_drop_count++)) {
1673 _mmcam_dbg_warn("drop frame");
1675 return GST_PAD_PROBE_DROP;
1678 videoinfo->record_drop_count = 1;
1680 _mmcam_dbg_warn("pass frame");
1684 GST_BUFFER_PTS(buffer) = b_time * (videoinfo->record_timestamp_ratio);
1687 return GST_PAD_PROBE_OK;
1691 static GstPadProbeReturn __mmcamcorder_audioque_dataprobe(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1693 _MMCamcorderMsgItem msg;
1694 guint64 trailer_size = 0;
1695 guint64 rec_pipe_time = 0;
1696 _MMCamcorderSubContext *sc = NULL;
1697 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1698 _MMCamcorderVideoInfo *videoinfo = NULL;
1699 unsigned int remained_time = 0;
1700 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1702 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1703 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1704 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1706 mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK);
1707 mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK);
1708 mmf_return_val_if_fail(sc->element, GST_PAD_PROBE_OK);
1710 videoinfo = sc->info_video;
1712 if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_PTS(buffer))) {
1713 _mmcam_dbg_err("Buffer timestamp is invalid, check it");
1714 return GST_PAD_PROBE_OK;
1717 rec_pipe_time = GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer));
1719 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1720 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1725 /* calculate remained time can be recorded */
1726 if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) {
1727 remained_time = videoinfo->max_time - rec_pipe_time;
1728 } else if (videoinfo->max_size > 0) {
1729 long double max_size = (long double)videoinfo->max_size;
1730 long double current_size = (long double)(videoinfo->filesize + trailer_size);
1732 remained_time = (unsigned long long)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1735 if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) {
1736 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1737 rec_pipe_time, videoinfo->max_time);
1739 if (!sc->isMaxtimePausing) {
1740 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1742 sc->isMaxtimePausing = TRUE;
1744 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1745 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1746 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1747 msg.param.recording_status.remained_time = 0;
1748 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1750 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1751 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1754 return GST_PAD_PROBE_DROP;
1757 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1758 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1759 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1760 msg.param.recording_status.remained_time = remained_time;
1761 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1764 _mmcam_dbg_log("audio data probe :: time [%" GST_TIME_FORMAT "], size [%lld KB]",
1765 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1768 return GST_PAD_PROBE_OK;
1772 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_audio_mute(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1774 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1775 double volume = 0.0;
1778 int err = MM_ERROR_UNKNOWN;
1779 char *err_name = NULL;
1780 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1783 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1784 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_DROP);
1786 /*_mmcam_dbg_log("AUDIO SRC time stamp : [%" GST_TIME_FORMAT "] \n", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1787 err = mm_camcorder_get_attributes((MMHandleType)hcamcorder, &err_name,
1788 MMCAM_AUDIO_VOLUME, &volume,
1789 MMCAM_AUDIO_FORMAT, &format,
1790 MMCAM_AUDIO_CHANNEL, &channel,
1792 if (err != MM_ERROR_NONE) {
1793 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, err);
1794 SAFE_FREE(err_name);
1798 memset(&mapinfo, 0x0, sizeof(GstMapInfo));
1800 gst_buffer_map(buffer, &mapinfo, GST_MAP_READWRITE);
1802 /* Set audio stream NULL */
1804 memset(mapinfo.data, 0, mapinfo.size);
1806 /* CALL audio stream callback */
1807 if (hcamcorder->astream_cb && buffer && mapinfo.data && mapinfo.size > 0) {
1808 MMCamcorderAudioStreamDataType stream;
1810 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) < MM_CAMCORDER_STATE_PREPARE) {
1811 _mmcam_dbg_warn("Not ready for stream callback");
1812 gst_buffer_unmap(buffer, &mapinfo);
1813 return GST_PAD_PROBE_OK;
1816 /*_mmcam_dbg_log("Call video steramCb, data[%p], Width[%d],Height[%d], Format[%d]",
1817 GST_BUFFER_DATA(buffer), width, height, format);*/
1819 stream.data = (void *)mapinfo.data;
1820 stream.format = format;
1821 stream.channel = channel;
1822 stream.length = mapinfo.size;
1823 stream.timestamp = (unsigned int)(GST_BUFFER_PTS(buffer)/1000000); /* nano -> milli second */
1825 _MMCAMCORDER_LOCK_ASTREAM_CALLBACK(hcamcorder);
1827 if (hcamcorder->astream_cb)
1828 hcamcorder->astream_cb(&stream, hcamcorder->astream_cb_param);
1830 _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK(hcamcorder);
1833 gst_buffer_unmap(buffer, &mapinfo);
1834 return GST_PAD_PROBE_OK;
1838 static gboolean __mmcamcorder_add_metadata(MMHandleType handle, int fileformat)
1840 gboolean bret = FALSE;
1842 switch (fileformat) {
1843 case MM_FILE_FORMAT_3GP:
1844 case MM_FILE_FORMAT_MP4:
1845 bret = __mmcamcorder_add_metadata_mp4(handle);
1848 _mmcam_dbg_warn("Unsupported fileformat to insert location info (%d)", fileformat);
1856 static gboolean __mmcamcorder_add_metadata_mp4(MMHandleType handle)
1860 guint64 udta_size = 0;
1861 gint64 current_pos = 0;
1862 gint64 moov_pos = 0;
1863 gint64 udta_pos = 0;
1864 gdouble longitude = 0;
1865 gdouble latitude = 0;
1866 gdouble altitude = 0;
1868 int orientation = 0;
1870 char *err_name = NULL;
1871 char err_msg[MAX_ERROR_MESSAGE_LEN] = {'\0',};
1872 _MMCamcorderLocationInfo location_info = {0, 0, 0};
1873 _MMCamcorderLocationInfo geo_info = {0, 0, 0};
1875 _MMCamcorderVideoInfo *info = NULL;
1876 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1877 _MMCamcorderSubContext *sc = NULL;
1879 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1880 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1882 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1883 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1887 info = sc->info_video;
1889 f = fopen64(info->filename, "rb+");
1891 strerror_r(errno, err_msg, MAX_ERROR_MESSAGE_LEN);
1892 _mmcam_dbg_err("file open failed [%s]", err_msg);
1896 mm_camcorder_get_attributes(handle, &err_name,
1897 MMCAM_TAG_LATITUDE, &latitude,
1898 MMCAM_TAG_LONGITUDE, &longitude,
1899 MMCAM_TAG_ALTITUDE, &altitude,
1900 MMCAM_TAG_VIDEO_ORIENTATION, &orientation,
1901 MMCAM_TAG_GPS_ENABLE, &gps_enable,
1904 _mmcam_dbg_warn("Get tag attrs fail. (%s:%x)", err_name, err);
1905 SAFE_FREE(err_name);
1908 location_info.longitude = _mmcamcorder_double_to_fix(longitude);
1909 location_info.latitude = _mmcamcorder_double_to_fix(latitude);
1910 location_info.altitude = _mmcamcorder_double_to_fix(altitude);
1911 geo_info.longitude = longitude *10000;
1912 geo_info.latitude = latitude *10000;
1913 geo_info.altitude = altitude *10000;
1914 /* find udta container.
1915 if, there are udta container, write loci box after that
1916 else, make udta container and write loci box. */
1917 if (_mmcamcorder_find_fourcc(f, MMCAM_FOURCC('u', 'd', 't', 'a'), TRUE)) {
1920 _mmcam_dbg_log("find udta container");
1923 if (fseek(f, -8L, SEEK_CUR) != 0)
1926 udta_pos = ftello(f);
1930 nread = fread(&buf, sizeof(char), sizeof(buf), f);
1932 _mmcam_dbg_log("recorded file fread %d", nread);
1934 udta_size = _mmcamcorder_get_container_size(buf);
1936 /* goto end of udta and write 'loci' box */
1937 if (fseek(f, (udta_size-4L), SEEK_CUR) != 0)
1941 if (!_mmcamcorder_write_loci(f, location_info)) {
1942 _mmcam_dbg_err("failed to write loci");
1946 if (!_mmcamcorder_write_geodata(f, geo_info)) {
1947 _mmcam_dbg_err("failed to write geodata");
1952 current_pos = ftello(f);
1953 if (current_pos < 0)
1956 if (!_mmcamcorder_update_size(f, udta_pos, current_pos))
1959 _mmcam_dbg_log("No udta container");
1960 if (fseek(f, 0, SEEK_END) != 0)
1963 if (!_mmcamcorder_write_udta(f, gps_enable, location_info, geo_info)) {
1964 _mmcam_dbg_err("failed to write udta");
1969 /* find moov container.
1970 update moov container size. */
1971 if ((current_pos = ftello(f)) < 0)
1974 if (_mmcamcorder_find_tag(f, MMCAM_FOURCC('m', 'o', 'o', 'v'), TRUE)) {
1975 gint64 internal_pos = ftello(f);
1977 _mmcam_dbg_log("found moov container");
1978 if (fseek(f, -8L, SEEK_CUR) != 0)
1981 moov_pos = ftello(f);
1985 if (!_mmcamcorder_update_size(f, moov_pos, current_pos))
1988 /* add orientation info */
1989 if (fseeko(f, internal_pos, SEEK_SET) < 0) {
1990 _mmcam_dbg_err("fseek failed : errno %d", errno);
1994 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t', 'r', 'a', 'k'), FALSE)) {
1995 _mmcam_dbg_err("failed to find [trak] tag");
1999 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t', 'k', 'h', 'd'), FALSE)) {
2000 _mmcam_dbg_err("failed to find [tkhd] tag");
2004 _mmcam_dbg_log("found [tkhd] tag");
2006 /* seek to start position of composition matrix */
2007 fseek(f, _OFFSET_COMPOSITION_MATRIX, SEEK_CUR);
2009 /* update composition matrix for orientation */
2010 _mmcamcorder_update_composition_matrix(f, orientation);
2012 _mmcam_dbg_err("No 'moov' container");
2024 _mmcam_dbg_err("ftell() returns negative value.");
2030 int _mmcamcorder_connect_video_stream_cb_signal(MMHandleType handle)
2032 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
2033 _MMCamcorderSubContext *sc = NULL;
2035 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2037 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
2038 mmf_return_val_if_fail(sc && sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2040 /* check video source element */
2041 if (sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst) {
2042 _mmcam_dbg_warn("connect video stream cb signal to _MMCAMCORDER_VIDEOSRC_SRC");
2043 MMCAMCORDER_SIGNAL_CONNECT(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst,
2044 _MMCAMCORDER_HANDLER_VIDEOREC, "video-stream-cb",
2045 G_CALLBACK(__mmcamcorder_video_stream_cb),
2047 return MM_ERROR_NONE;
2049 _mmcam_dbg_err("videosrc element is not created yet");
2050 return MM_ERROR_CAMCORDER_NOT_INITIALIZED;
2055 int _mmcamcorder_video_prepare_record(MMHandleType handle)
2057 int ret = MM_ERROR_NONE;
2059 _MMCamcorderVideoInfo *info = NULL;
2060 _MMCamcorderSubContext *sc = NULL;
2061 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
2063 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2065 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
2066 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2067 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2069 info = sc->info_video;
2071 _mmcam_dbg_warn("start");
2073 /* create encoding pipeline */
2074 ret = _mmcamcorder_create_recorder_pipeline((MMHandleType)hcamcorder);
2075 if (ret != MM_ERROR_NONE)
2076 goto _ERR_PREPARE_RECORD;
2078 if (info->filename == NULL) {
2079 char *temp_filename = NULL;
2082 mm_camcorder_get_attributes(handle, NULL,
2083 MMCAM_TARGET_FILENAME, &temp_filename, &size,
2086 info->filename = g_strdup(temp_filename);
2088 if (!info->filename) {
2089 _mmcam_dbg_err("strdup[src:%p] was failed", temp_filename);
2090 goto _ERR_PREPARE_RECORD;
2094 _mmcam_dbg_log("Record file name [%s]", info->filename);
2096 MMCAMCORDER_G_OBJECT_SET_POINTER(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename);
2097 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", 0);
2099 /* Adjust display FPS */
2100 sc->display_interval = 0;
2101 sc->previous_slot_time = 0;
2103 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PAUSED);
2104 if (ret != MM_ERROR_NONE)
2105 goto _ERR_PREPARE_RECORD;
2107 _mmcam_dbg_warn("done");
2111 _ERR_PREPARE_RECORD:
2112 /* Remove recorder pipeline and recording file which size maybe zero */
2113 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
2114 if (info && info->filename) {
2115 _mmcam_dbg_log("file delete(%s)", info->filename);
2116 unlink(info->filename);