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);
646 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
648 /* check decoder recreation */
649 if (!_mmcamcorder_recreate_decoder_for_encoded_preview(handle)) {
650 _mmcam_dbg_err("_mmcamcorder_recreate_decoder_for_encoded_preview failed");
651 ret = MM_ERROR_CAMCORDER_INTERNAL;
652 goto _ERR_CAMCORDER_VIDEO_COMMAND;
655 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
656 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
658 if (ret != MM_ERROR_NONE)
659 goto _ERR_CAMCORDER_VIDEO_COMMAND;
661 if (!_mmcamcorder_set_camera_resolution(handle, info->video_width, info->video_height)) {
662 ret = MM_ERROR_CAMCORDER_INTERNAL;
663 goto _ERR_CAMCORDER_VIDEO_COMMAND;
666 /* Start preview again with new setting */
667 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
668 if (ret != MM_ERROR_NONE)
669 goto _ERR_CAMCORDER_VIDEO_COMMAND;
671 if (motion_rate < 1.0) {
672 _mmcam_dbg_warn("wait for stabilization of frame");
676 _mmcam_dbg_log("no need to restart preview");
679 _mmcamcorder_conf_get_value_int(handle, hcamcorder->conf_main,
680 CONFIGURE_CATEGORY_MAIN_RECORD,
684 _mmcamcorder_conf_get_value_int(handle, hcamcorder->conf_main,
685 CONFIGURE_CATEGORY_MAIN_RECORD,
686 "PassFirstVideoFrame",
687 &(sc->pass_first_vframe));
689 _mmcam_dbg_log("Drop video frame count[%d], Pass fisrt video frame count[%d]",
690 sc->drop_vframe, sc->pass_first_vframe);
692 info->record_drop_count = (guint)motion_rate;
693 info->record_motion_rate = motion_rate;
694 if (sc->is_modified_rate)
695 info->record_timestamp_ratio = (_MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE/motion_rate);
697 info->record_timestamp_ratio = _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE;
699 _mmcam_dbg_warn("recording fps %d, motion rate %f, timestamp_ratio %f",
700 fps, info->record_motion_rate, info->record_timestamp_ratio);
702 /* set push buffer flag */
703 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_INIT;
704 info->base_video_ts = 0;
706 /* connect video stream cb signal */
707 /*130826 Connect video stream cb for handling fast record frame cb*/
708 if (info->record_dual_stream) {
709 if (_mmcamcorder_connect_video_stream_cb_signal((MMHandleType)hcamcorder) != MM_ERROR_NONE)
710 goto _ERR_CAMCORDER_VIDEO_COMMAND;
713 /* start video stream */
714 if (info->record_dual_stream) {
715 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
717 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
719 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_START");
720 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_START);
722 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
724 _mmcam_dbg_err("could not get camera control");
728 /* check pre-created encode pipeline */
729 g_mutex_lock(&hcamcorder->task_thread_lock);
730 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst == NULL &&
731 hcamcorder->task_thread_state == _MMCAMCORDER_TASK_THREAD_STATE_NONE) {
732 /* create encoding pipeline */
733 ret = _mmcamcorder_video_prepare_record((MMHandleType)hcamcorder);
734 if (ret != MM_ERROR_NONE) {
735 g_mutex_unlock(&hcamcorder->task_thread_lock);
736 goto _ERR_CAMCORDER_VIDEO_COMMAND;
739 g_mutex_unlock(&hcamcorder->task_thread_lock);
741 /* check recording start sound */
742 _mmcamcorder_sound_solo_play_wait(handle);
744 /**< To fix video recording hanging
745 1. use gst_element_set_start_time() instead of gst_pipeline_set_new_stream_time()
746 2. Set (GstClockTime)1 instead of (GstClockTime)0. Because of strict check in gstreamer 0.25,
747 basetime wouldn't change if you set (GstClockTime)0.
748 3. Move set start time position below PAUSED of pipeline.
751 gst_element_set_start_time(GST_ELEMENT(sc->element[_MMCAMCORDER_MAIN_PIPE].gst), (GstClockTime)1);
752 gst_element_set_start_time(GST_ELEMENT(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), (GstClockTime)1);
755 info->video_frame_count = 0;
756 info->is_firstframe = TRUE;
757 info->audio_frame_count = 0;
759 sc->ferror_send = FALSE;
760 sc->ferror_count = 0;
761 hcamcorder->error_occurs = FALSE;
762 sc->bget_eos = FALSE;
764 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PLAYING);
765 if (ret != MM_ERROR_NONE) {
766 /* stop video stream */
767 if (info->record_dual_stream) {
768 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
770 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
772 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
773 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
775 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
777 _mmcam_dbg_err("failed to get camera control");
781 /* Remove recorder pipeline and recording file which size maybe zero */
782 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
783 if (info->filename) {
784 _mmcam_dbg_log("file delete(%s)", info->filename);
785 unlink(info->filename);
787 goto _ERR_CAMCORDER_VIDEO_COMMAND;
790 /*set the camera control to create the GOP so that video record will get a new key frame*/
791 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
792 GST_IS_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst)) {
793 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
794 controls = gst_camera_control_list_channels(CameraControl);
795 if (controls != NULL) {
796 for (item = controls ; item && item->data ; item = item->next) {
797 CameraControlChannel = item->data;
798 _mmcam_dbg_log("CameraControlChannel->label %s", CameraControlChannel->label);
799 if (!strcmp(CameraControlChannel->label, "new-gop")) {
800 /* gst_camera_control_set_value(CameraControl, CameraControlChannel, 1); */
806 _mmcam_dbg_warn("failed to find new-gop control channel");
809 _mmcam_dbg_warn("Can't cast Video source into camera control or not H264 prevew format[%d]",
810 sc->info_image->preview_format);
815 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
816 GST_IS_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst)) {
817 /* generate and I-frame on resuming */
818 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
819 controls = gst_camera_control_list_channels(CameraControl);
820 if (controls != NULL) {
821 for (item = controls ; item && item->data ; item = item->next) {
822 CameraControlChannel = item->data;
823 _mmcam_dbg_log("CameraControlChannel->label %s", CameraControlChannel->label);
824 if (!strcmp(CameraControlChannel->label, "new-gop")) {
825 /* gst_camera_control_set_value(CameraControl, CameraControlChannel, 1); */
831 _mmcam_dbg_warn("failed to find new-gop control channel");
835 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", FALSE);
837 _mmcam_dbg_log("Object property settings done");
841 case _MMCamcorder_CMD_PAUSE:
843 if (info->b_commiting) {
844 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
845 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
848 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
849 if (sc->audio_disable) {
850 /* check only video frame */
851 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
853 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
854 _mmcam_dbg_err("Pause fail, frame count %llu", info->video_frame_count);
855 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
857 _mmcam_dbg_warn("Waiting for enough video frame, retrial[%d], frame %llu", count, info->video_frame_count);
860 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
862 /* check both of video and audio frame */
863 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
865 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
866 _mmcam_dbg_err("Pause fail, frame count VIDEO[%llu], AUDIO [%llu]",
867 info->video_frame_count, info->audio_frame_count);
868 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
870 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%llu], AUDIO [%llu]",
871 count, info->video_frame_count, info->audio_frame_count);
874 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
878 /* block encodebin */
879 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", TRUE);
882 case _MMCamcorder_CMD_CANCEL:
884 if (info->b_commiting) {
885 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
886 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
889 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
891 if (hcamcorder->capture_in_recording == FALSE) {
893 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
894 _mmcam_dbg_err("Failed to Wait capture data");
895 hcamcorder->capture_in_recording = FALSE;
898 _mmcam_dbg_warn("Waiting for capture data - retrial [%d]", count);
901 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
904 /* block push buffer */
905 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP;
907 ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
908 if (ret != MM_ERROR_NONE)
909 goto _ERR_CAMCORDER_VIDEO_COMMAND;
911 /* set recording hint */
912 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", FALSE);
914 /* stop video stream */
915 if (info->record_dual_stream) {
916 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
918 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
920 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
921 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
923 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
925 _mmcam_dbg_err("failed to get camera control");
929 if (info->restart_preview) {
930 /* restart preview */
931 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
932 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
934 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
936 /* check decoder recreation */
937 if (!_mmcamcorder_recreate_decoder_for_encoded_preview(handle)) {
938 _mmcam_dbg_err("_mmcamcorder_recreate_decoder_for_encoded_preview failed");
939 ret = MM_ERROR_CAMCORDER_INTERNAL;
942 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
943 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
945 if (ret != MM_ERROR_NONE)
946 goto _ERR_CAMCORDER_VIDEO_COMMAND;
948 /* reset restart_preview for inset window layout */
949 info->restart_preview = FALSE;
951 if (!_mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height)) {
952 ret = MM_ERROR_CAMCORDER_INTERNAL;
953 goto _ERR_CAMCORDER_VIDEO_COMMAND;
956 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
957 if (ret != MM_ERROR_NONE)
958 goto _ERR_CAMCORDER_VIDEO_COMMAND;
961 /* remove target file */
962 if (info->filename) {
963 _mmcam_dbg_log("file delete(%s)", info->filename);
964 unlink(info->filename);
967 sc->isMaxsizePausing = FALSE;
968 sc->isMaxtimePausing = FALSE;
970 sc->display_interval = 0;
971 sc->previous_slot_time = 0;
972 info->video_frame_count = 0;
973 info->audio_frame_count = 0;
975 hcamcorder->capture_in_recording = FALSE;
978 case _MMCamcorder_CMD_COMMIT:
980 if (info->b_commiting) {
981 _mmcam_dbg_err("now on commiting previous file!!(command : %d)", command);
982 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
984 _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT : start");
985 info->b_commiting = TRUE;
986 sc->bget_eos = FALSE;
989 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
990 if (sc->audio_disable) {
991 /* check only video frame */
992 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME &&
993 hcamcorder->capture_in_recording == FALSE) {
995 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
996 _mmcam_dbg_err("Commit fail, frame count is %llu, capturing %d",
997 info->video_frame_count, hcamcorder->capture_in_recording);
999 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
1000 _mmcam_dbg_warn("video frames are enough. keep going...");
1002 info->b_commiting = FALSE;
1003 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
1006 _mmcam_dbg_warn("Waiting for enough video frame, retrial [%d], frame %llu, capturing %d",
1007 count, info->video_frame_count, hcamcorder->capture_in_recording);
1010 /* check both of video and audio frame */
1011 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME &&
1012 info->audio_frame_count &&
1013 hcamcorder->capture_in_recording == FALSE) {
1015 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
1016 _mmcam_dbg_err("Commit fail, VIDEO[%llu], AUDIO [%llu], capturing %d",
1017 info->video_frame_count, info->audio_frame_count, hcamcorder->capture_in_recording);
1019 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
1020 _mmcam_dbg_warn("video/audio frames are enough. keep going...");
1022 info->b_commiting = FALSE;
1023 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
1026 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
1028 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%llu], AUDIO [%llu], capturing %d",
1029 count, info->video_frame_count, info->audio_frame_count, hcamcorder->capture_in_recording);
1033 if (hcamcorder->capture_in_recording) {
1034 gint64 end_time = g_get_monotonic_time() + (200 * G_TIME_SPAN_MILLISECOND);
1035 if (_MMCAMCORDER_CMD_WAIT_UNTIL(handle, end_time)) {
1036 _mmcam_dbg_warn("signal received");
1038 _mmcam_dbg_warn("timeout");
1041 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
1045 /* block push buffer */
1046 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP;
1047 _mmcam_dbg_log("block push buffer to appsrc");
1049 if (sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst != NULL) {
1050 if (gst_element_send_event(sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, gst_event_new_eos())) {
1051 _mmcam_dbg_warn("VIDEO: send eos to appsrc done");
1053 _mmcam_dbg_err("VIDEO: send EOS failed");
1054 info->b_commiting = FALSE;
1055 ret = MM_ERROR_CAMCORDER_INTERNAL;
1056 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1059 _mmcam_dbg_err("No video stream source");
1060 info->b_commiting = FALSE;
1061 ret = MM_ERROR_CAMCORDER_INTERNAL;
1062 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1065 if (sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst != NULL) {
1066 if (gst_element_send_event(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst, gst_event_new_eos())) {
1067 _mmcam_dbg_warn("AUDIO: send eos to audiosrc done");
1069 _mmcam_dbg_err("AUDIO: send EOS failed");
1070 info->b_commiting = FALSE;
1071 ret = MM_ERROR_CAMCORDER_INTERNAL;
1072 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1075 _mmcam_dbg_log("No audio stream");
1079 sc->display_interval = 0;
1080 sc->previous_slot_time = 0;
1083 _mmcam_dbg_log("Start to wait EOS");
1084 ret = _mmcamcorder_get_eos_message(handle);
1085 if (ret != MM_ERROR_NONE) {
1086 info->b_commiting = FALSE;
1087 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1091 hcamcorder->capture_in_recording = FALSE;
1095 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
1096 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1099 return MM_ERROR_NONE;
1101 _ERR_CAMCORDER_VIDEO_COMMAND:
1102 if (command == _MMCamcorder_CMD_RECORD)
1103 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
1109 int _mmcamcorder_video_handle_eos(MMHandleType handle)
1111 int ret = MM_ERROR_NONE;
1113 guint64 file_size = 0;
1115 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1116 _MMCamcorderSubContext *sc = NULL;
1117 _MMCamcorderVideoInfo *info = NULL;
1118 _MMCamcorderMsgItem msg;
1119 MMCamRecordingReport *report = NULL;
1121 mmf_return_val_if_fail(hcamcorder, FALSE);
1123 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1124 mmf_return_val_if_fail(sc, FALSE);
1125 mmf_return_val_if_fail(sc->info_video, FALSE);
1127 info = sc->info_video;
1131 if (hcamcorder->state_change_by_system != _MMCAMCORDER_STATE_CHANGE_BY_FOCUS) {
1132 /* Play record stop sound */
1133 _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_SAMPLE_SOUND_NAME_REC_STOP, FALSE);
1135 _mmcam_dbg_warn("Play stop sound through pulseaudio");
1137 _mmcamcorder_sound_init(handle);
1139 _mmcamcorder_sound_play((MMHandleType)hcamcorder, _MMCAMCORDER_SAMPLE_SOUND_NAME_REC_STOP, TRUE);
1141 _mmcamcorder_sound_finalize(handle);
1144 /* remove blocking part */
1145 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE);
1147 mm_camcorder_get_attributes(handle, NULL,
1148 MMCAM_RECORDER_TAG_ENABLE, &enabletag,
1151 ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
1152 if (ret != MM_ERROR_NONE)
1153 _mmcam_dbg_warn("_MMCamcorder_CMD_COMMIT:__mmcamcorder_remove_recorder_pipeline failed. error[%x]", ret);
1155 /* set recording hint */
1156 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", FALSE);
1158 /* stop video stream */
1159 if (info->record_dual_stream) {
1160 GstCameraControl *control = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
1162 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
1164 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
1165 gst_camera_control_set_record_command(control, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
1167 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
1169 _mmcam_dbg_err("failed to get camera control");
1173 if (enabletag && !(sc->ferror_send)) {
1174 ret = __mmcamcorder_add_metadata((MMHandleType)hcamcorder, info->fileformat);
1176 _mmcam_dbg_log("Writing location information SUCCEEDED !!");
1178 _mmcam_dbg_err("Writing location information FAILED !!");
1182 /* Check file size */
1183 if (info->max_size > 0) {
1184 _mmcamcorder_get_file_size(info->filename, &file_size);
1185 _mmcam_dbg_log("MAX size %lld byte - created filesize %lld byte",
1186 info->max_size, file_size);
1188 if (file_size > info->max_size) {
1189 _MMCamcorderMsgItem message;
1190 _mmcam_dbg_err("File size is greater than max size !!");
1191 message.id = MM_MESSAGE_CAMCORDER_ERROR;
1192 message.param.code = MM_ERROR_CAMCORDER_FILE_SIZE_OVER;
1193 _mmcamcorder_send_message((MMHandleType)hcamcorder, &message);
1197 if (info->restart_preview) {
1199 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
1200 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
1202 _mmcam_dbg_log("Set state of pipeline as READY");
1203 ret = _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_READY);
1205 /* check decoder recreation */
1206 if (!_mmcamcorder_recreate_decoder_for_encoded_preview(handle)) {
1207 _mmcam_dbg_err("_mmcamcorder_recreate_decoder_for_encoded_preview failed");
1208 ret = MM_ERROR_CAMCORDER_INTERNAL;
1212 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
1213 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
1214 if (ret != MM_ERROR_NONE) {
1215 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1216 msg.param.code = ret;
1217 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1218 _mmcam_dbg_err("Failed to set state READY[%x]", ret);
1221 /* reset restart_preview for inset window layout */
1222 info->restart_preview = FALSE;
1224 /* recover preview size */
1225 _mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height);
1227 ret = _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_PLAYING);
1228 /* Do not return when error is occurred.
1229 Recording file was created successfully, but starting pipeline failed */
1230 if (ret != MM_ERROR_NONE) {
1231 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1232 msg.param.code = ret;
1233 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1234 _mmcam_dbg_err("Failed to set state PLAYING[%x]", ret);
1237 _mmcam_dbg_log("No need to restart preview");
1240 /* Send recording report to application */
1241 msg.id = MM_MESSAGE_CAMCORDER_VIDEO_CAPTURED;
1242 report = (MMCamRecordingReport *)g_malloc(sizeof(MMCamRecordingReport));
1244 _mmcam_dbg_err("Recording report fail(%s). Out of memory.", info->filename);
1246 report->recording_filename = g_strdup(info->filename);
1247 msg.param.data = report;
1249 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1253 sc->pipeline_time = 0;
1255 sc->isMaxsizePausing = FALSE; /*In async function, this variable should set in callback function. */
1256 sc->isMaxtimePausing = FALSE;
1257 hcamcorder->error_occurs = FALSE;
1259 info->video_frame_count = 0;
1260 info->audio_frame_count = 0;
1262 info->b_commiting = FALSE;
1264 if (hcamcorder->state_change_by_system != _MMCAMCORDER_STATE_CHANGE_BY_FOCUS) {
1265 /* check recording stop sound */
1266 _mmcamcorder_sound_solo_play_wait(handle);
1269 _mmcam_dbg_err("_MMCamcorder_CMD_COMMIT : end");
1276 * This function is record video data probing function.
1277 * If this function is linked with certain pad by gst_pad_add_buffer_probe(),
1278 * this function will be called when data stream pass through the pad.
1280 * @param[in] pad probing pad which calls this function.
1281 * @param[in] buffer buffer which contains stream data.
1282 * @param[in] u_data user data.
1283 * @return This function returns true on success, or false value with error
1287 static GstPadProbeReturn __mmcamcorder_eventprobe_monitor(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1289 GstEvent *event = GST_PAD_PROBE_INFO_EVENT(info);
1290 switch (GST_EVENT_TYPE(event)) {
1291 case GST_EVENT_UNKNOWN:
1292 /* upstream events */
1294 case GST_EVENT_SEEK:
1295 case GST_EVENT_NAVIGATION:
1296 case GST_EVENT_LATENCY:
1297 /* downstream serialized events */
1298 case GST_EVENT_SEGMENT:
1300 case GST_EVENT_BUFFERSIZE:
1301 _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1304 _mmcam_dbg_warn("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1306 /* bidirectional events */
1307 case GST_EVENT_FLUSH_START:
1308 case GST_EVENT_FLUSH_STOP:
1309 _mmcam_dbg_err("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1312 _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event));
1316 return GST_PAD_PROBE_OK;
1320 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_check(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1322 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1323 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1325 _MMCamcorderSubContext *sc = NULL;
1326 _MMCamcorderVideoInfo *videoinfo = NULL;
1327 _MMCamcorderMsgItem msg;
1328 guint64 buffer_size = 0;
1329 guint64 trailer_size = 0;
1330 guint64 max_size = 0;
1332 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1333 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1334 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1336 mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK);
1337 videoinfo = sc->info_video;
1339 /* get buffer size */
1340 if (!gst_buffer_map(buffer, &mapinfo, GST_MAP_READ)) {
1341 _mmcam_dbg_warn("map failed : buffer %p", buffer);
1342 return GST_PAD_PROBE_OK;
1345 buffer_size = mapinfo.size;
1346 gst_buffer_unmap(buffer, &mapinfo);
1348 /*_mmcam_dbg_err("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1350 g_mutex_lock(&videoinfo->size_check_lock);
1352 if (videoinfo->audio_frame_count == 0) {
1353 videoinfo->filesize += buffer_size;
1354 videoinfo->audio_frame_count++;
1355 g_mutex_unlock(&videoinfo->size_check_lock);
1356 return GST_PAD_PROBE_OK;
1359 if (sc->ferror_send || sc->isMaxsizePausing) {
1360 _mmcam_dbg_warn("Recording is paused, drop frames");
1361 g_mutex_unlock(&videoinfo->size_check_lock);
1362 return GST_PAD_PROBE_DROP;
1365 /* get trailer size */
1366 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1367 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1372 /* check max size of recorded file */
1373 max_size = videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE;
1374 if (videoinfo->max_size > 0 && videoinfo->max_size < max_size) {
1375 GstState pipeline_state = GST_STATE_VOID_PENDING;
1376 GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1377 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1378 _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \
1379 " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]",
1380 videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size);
1382 if (!sc->isMaxsizePausing) {
1383 sc->isMaxsizePausing = TRUE;
1384 gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ;
1385 if (pipeline_state == GST_STATE_PLAYING)
1386 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1388 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1389 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1392 g_mutex_unlock(&videoinfo->size_check_lock);
1397 videoinfo->filesize += buffer_size;
1398 videoinfo->audio_frame_count++;
1400 g_mutex_unlock(&videoinfo->size_check_lock);
1402 return GST_PAD_PROBE_OK;
1406 static GstPadProbeReturn __mmcamcorder_video_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1411 guint64 free_space = 0;
1412 guint64 buffer_size = 0;
1413 guint64 trailer_size = 0;
1414 guint64 queued_buffer = 0;
1415 guint64 max_size = 0;
1416 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1418 storage_state_e storage_state = STORAGE_STATE_UNMOUNTABLE;
1420 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1421 _MMCamcorderMsgItem msg;
1422 _MMCamcorderSubContext *sc = NULL;
1423 _MMCamcorderVideoInfo *videoinfo = NULL;
1425 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1426 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1428 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1429 mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK);
1430 videoinfo = sc->info_video;
1432 /*_mmcam_dbg_log("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1433 if (sc->ferror_send) {
1434 _mmcam_dbg_warn("file write error, drop frames");
1435 return GST_PAD_PROBE_DROP;
1438 gst_buffer_map(buffer, &mapinfo, GST_MAP_READ);
1439 buffer_size = mapinfo.size;
1440 gst_buffer_unmap(buffer, &mapinfo);
1442 videoinfo->video_frame_count++;
1443 if (videoinfo->video_frame_count <= (guint64)_MMCAMCORDER_MINIMUM_FRAME) {
1444 /* _mmcam_dbg_log("Pass minimum frame: info->video_frame_count: %" G_GUINT64_FORMAT " ",
1445 info->video_frame_count); */
1446 g_mutex_lock(&videoinfo->size_check_lock);
1447 videoinfo->filesize += buffer_size;
1448 g_mutex_unlock(&videoinfo->size_check_lock);
1449 return GST_PAD_PROBE_OK;
1452 /* get trailer size */
1453 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1454 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1459 /* check free space */
1460 ret = _mmcamcorder_get_freespace(hcamcorder->storage_info.type, &free_space);
1462 _mmcam_dbg_err("Error occured. [%d]", ret);
1463 if (sc->ferror_count == 2 && sc->ferror_send == FALSE) {
1464 sc->ferror_send = TRUE;
1466 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1467 msg.param.code = MM_ERROR_FILE_READ;
1469 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1474 return GST_PAD_PROBE_DROP; /* skip this buffer */
1477 if (free_space == 0) {
1478 /* check storage state */
1479 storage_get_state(hcamcorder->storage_info.id, &storage_state);
1481 _mmcam_dbg_warn("storage state %d", storage_state);
1483 if (storage_state == STORAGE_STATE_REMOVED ||
1484 storage_state == STORAGE_STATE_UNMOUNTABLE) {
1485 _mmcam_dbg_err("storage was removed!");
1487 _MMCAMCORDER_LOCK(hcamcorder);
1489 if (sc->ferror_send == FALSE) {
1490 _mmcam_dbg_err("OUT_OF_STORAGE error");
1492 sc->ferror_send = TRUE;
1494 _MMCAMCORDER_UNLOCK(hcamcorder);
1496 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1497 msg.param.code = MM_ERROR_OUT_OF_STORAGE;
1499 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1501 _MMCAMCORDER_UNLOCK(hcamcorder);
1502 _mmcam_dbg_warn("error was already sent");
1505 return GST_PAD_PROBE_DROP;
1509 /* get queued buffer size */
1510 if (sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst) {
1511 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst, "current-level-bytes", &aq_size);
1514 if (sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst) {
1515 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst, "current-level-bytes", &vq_size);
1518 queued_buffer = aq_size + vq_size;
1520 if (free_space < (_MMCAMCORDER_MINIMUM_SPACE + buffer_size + trailer_size + queued_buffer)) {
1521 _mmcam_dbg_warn("No more space for recording!!! Recording is paused.");
1522 _mmcam_dbg_warn("Free Space : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]," \
1523 " buffer size : [%" G_GUINT64_FORMAT "], queued buffer size : [%" G_GUINT64_FORMAT "]", \
1524 free_space, trailer_size, buffer_size, queued_buffer);
1526 if (!sc->isMaxsizePausing) {
1527 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1528 sc->isMaxsizePausing = TRUE;
1530 msg.id = MM_MESSAGE_CAMCORDER_NO_FREE_SPACE;
1531 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1534 return GST_PAD_PROBE_DROP;
1537 g_mutex_lock(&videoinfo->size_check_lock);
1539 /* check max size of recorded file */
1540 max_size = videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE;
1541 if (videoinfo->max_size > 0 && videoinfo->max_size < max_size) {
1542 GstState pipeline_state = GST_STATE_VOID_PENDING;
1543 GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1544 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1545 _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \
1546 " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]",
1547 videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size);
1549 if (!sc->isMaxsizePausing) {
1550 sc->isMaxsizePausing = TRUE;
1551 gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ;
1552 if (pipeline_state == GST_STATE_PLAYING)
1553 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1555 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1556 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1559 g_mutex_unlock(&videoinfo->size_check_lock);
1561 return GST_PAD_PROBE_DROP;
1564 videoinfo->filesize += (guint64)buffer_size;
1567 _mmcam_dbg_log("filesize %lld Byte, ", videoinfo->filesize);
1570 g_mutex_unlock(&videoinfo->size_check_lock);
1572 return GST_PAD_PROBE_OK;
1576 static GstPadProbeReturn __mmcamcorder_video_dataprobe_audio_disable(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1578 guint64 trailer_size = 0;
1579 guint64 rec_pipe_time = 0;
1580 unsigned int remained_time = 0;
1582 GstClockTime b_time;
1584 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1585 _MMCamcorderMsgItem msg;
1586 _MMCamcorderSubContext *sc = NULL;
1587 _MMCamcorderVideoInfo *videoinfo = NULL;
1589 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1591 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1592 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1594 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1595 mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK);
1596 mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK);
1598 videoinfo = sc->info_video;
1600 b_time = GST_BUFFER_PTS(buffer);
1602 rec_pipe_time = GST_TIME_AS_MSECONDS(b_time);
1604 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1605 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1610 /* check max time */
1611 if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) {
1612 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1613 rec_pipe_time, videoinfo->max_time);
1615 if (!sc->isMaxtimePausing) {
1616 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1618 sc->isMaxtimePausing = TRUE;
1620 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1621 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1622 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1623 msg.param.recording_status.remained_time = 0;
1624 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1626 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1627 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1630 return GST_PAD_PROBE_DROP;
1633 /* calculate remained time can be recorded */
1634 if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) {
1635 remained_time = videoinfo->max_time - rec_pipe_time;
1636 } else if (videoinfo->max_size > 0) {
1637 long double max_size = (long double)videoinfo->max_size;
1638 long double current_size = (long double)(videoinfo->filesize + trailer_size);
1640 remained_time = (unsigned int)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1643 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1644 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1645 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1646 msg.param.recording_status.remained_time = remained_time;
1647 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1650 _mmcam_dbg_log("time [%" GST_TIME_FORMAT "], size [%d]",
1651 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1654 if (videoinfo->record_timestamp_ratio != _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) {
1655 guint record_motion_rate = (guint)videoinfo->record_motion_rate;
1658 _mmcam_dbg_log("record_motion_rate %d, videoinfo->record_drop_count %d",
1659 record_motion_rate, videoinfo->record_drop_count);
1662 /* drop some frame if fast motion */
1663 if (videoinfo->record_motion_rate > _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) {
1664 if (record_motion_rate != (videoinfo->record_drop_count++)) {
1666 _mmcam_dbg_warn("drop frame");
1668 return GST_PAD_PROBE_DROP;
1671 videoinfo->record_drop_count = 1;
1673 _mmcam_dbg_warn("pass frame");
1677 GST_BUFFER_PTS(buffer) = b_time * (videoinfo->record_timestamp_ratio);
1680 return GST_PAD_PROBE_OK;
1684 static GstPadProbeReturn __mmcamcorder_audioque_dataprobe(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1686 _MMCamcorderMsgItem msg;
1687 guint64 trailer_size = 0;
1688 guint64 rec_pipe_time = 0;
1689 _MMCamcorderSubContext *sc = NULL;
1690 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1691 _MMCamcorderVideoInfo *videoinfo = NULL;
1692 unsigned int remained_time = 0;
1693 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1695 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1696 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1697 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1699 mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK);
1700 mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK);
1701 mmf_return_val_if_fail(sc->element, GST_PAD_PROBE_OK);
1703 videoinfo = sc->info_video;
1705 if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_PTS(buffer))) {
1706 _mmcam_dbg_err("Buffer timestamp is invalid, check it");
1707 return GST_PAD_PROBE_OK;
1710 rec_pipe_time = GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer));
1712 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) {
1713 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1718 /* calculate remained time can be recorded */
1719 if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) {
1720 remained_time = videoinfo->max_time - rec_pipe_time;
1721 } else if (videoinfo->max_size > 0) {
1722 long double max_size = (long double)videoinfo->max_size;
1723 long double current_size = (long double)(videoinfo->filesize + trailer_size);
1725 remained_time = (unsigned long long)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1728 if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) {
1729 _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \
1730 rec_pipe_time, videoinfo->max_time);
1732 if (!sc->isMaxtimePausing) {
1733 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1735 sc->isMaxtimePausing = TRUE;
1737 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1738 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1739 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1740 msg.param.recording_status.remained_time = 0;
1741 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1743 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1744 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1747 return GST_PAD_PROBE_DROP;
1750 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1751 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1752 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1753 msg.param.recording_status.remained_time = remained_time;
1754 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1757 _mmcam_dbg_log("audio data probe :: time [%" GST_TIME_FORMAT "], size [%lld KB]",
1758 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1761 return GST_PAD_PROBE_OK;
1765 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_audio_mute(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1767 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1768 double volume = 0.0;
1771 int err = MM_ERROR_UNKNOWN;
1772 char *err_name = NULL;
1773 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1776 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1777 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_DROP);
1779 /*_mmcam_dbg_log("AUDIO SRC time stamp : [%" GST_TIME_FORMAT "] \n", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1780 err = mm_camcorder_get_attributes((MMHandleType)hcamcorder, &err_name,
1781 MMCAM_AUDIO_VOLUME, &volume,
1782 MMCAM_AUDIO_FORMAT, &format,
1783 MMCAM_AUDIO_CHANNEL, &channel,
1785 if (err != MM_ERROR_NONE) {
1786 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, err);
1787 SAFE_FREE(err_name);
1791 memset(&mapinfo, 0x0, sizeof(GstMapInfo));
1793 gst_buffer_map(buffer, &mapinfo, GST_MAP_READWRITE);
1795 /* Set audio stream NULL */
1797 memset(mapinfo.data, 0, mapinfo.size);
1799 /* CALL audio stream callback */
1800 if (hcamcorder->astream_cb && buffer && mapinfo.data && mapinfo.size > 0) {
1801 MMCamcorderAudioStreamDataType stream;
1803 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) < MM_CAMCORDER_STATE_PREPARE) {
1804 _mmcam_dbg_warn("Not ready for stream callback");
1805 gst_buffer_unmap(buffer, &mapinfo);
1806 return GST_PAD_PROBE_OK;
1809 /*_mmcam_dbg_log("Call video steramCb, data[%p], Width[%d],Height[%d], Format[%d]",
1810 GST_BUFFER_DATA(buffer), width, height, format);*/
1812 stream.data = (void *)mapinfo.data;
1813 stream.format = format;
1814 stream.channel = channel;
1815 stream.length = mapinfo.size;
1816 stream.timestamp = (unsigned int)(GST_BUFFER_PTS(buffer)/1000000); /* nano -> milli second */
1818 _MMCAMCORDER_LOCK_ASTREAM_CALLBACK(hcamcorder);
1820 if (hcamcorder->astream_cb)
1821 hcamcorder->astream_cb(&stream, hcamcorder->astream_cb_param);
1823 _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK(hcamcorder);
1826 gst_buffer_unmap(buffer, &mapinfo);
1827 return GST_PAD_PROBE_OK;
1831 static gboolean __mmcamcorder_add_metadata(MMHandleType handle, int fileformat)
1833 gboolean bret = FALSE;
1835 switch (fileformat) {
1836 case MM_FILE_FORMAT_3GP:
1837 case MM_FILE_FORMAT_MP4:
1838 bret = __mmcamcorder_add_metadata_mp4(handle);
1841 _mmcam_dbg_warn("Unsupported fileformat to insert location info (%d)", fileformat);
1849 static gboolean __mmcamcorder_add_metadata_mp4(MMHandleType handle)
1853 guint64 udta_size = 0;
1854 gint64 current_pos = 0;
1855 gint64 moov_pos = 0;
1856 gint64 udta_pos = 0;
1857 gdouble longitude = 0;
1858 gdouble latitude = 0;
1859 gdouble altitude = 0;
1861 int orientation = 0;
1863 char *err_name = NULL;
1864 char err_msg[MAX_ERROR_MESSAGE_LEN] = {'\0',};
1865 _MMCamcorderLocationInfo location_info = {0, 0, 0};
1866 _MMCamcorderLocationInfo geo_info = {0, 0, 0};
1868 _MMCamcorderVideoInfo *info = NULL;
1869 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1870 _MMCamcorderSubContext *sc = NULL;
1872 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1873 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1875 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1876 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1880 info = sc->info_video;
1882 f = fopen64(info->filename, "rb+");
1884 strerror_r(errno, err_msg, MAX_ERROR_MESSAGE_LEN);
1885 _mmcam_dbg_err("file open failed [%s]", err_msg);
1889 mm_camcorder_get_attributes(handle, &err_name,
1890 MMCAM_TAG_LATITUDE, &latitude,
1891 MMCAM_TAG_LONGITUDE, &longitude,
1892 MMCAM_TAG_ALTITUDE, &altitude,
1893 MMCAM_TAG_VIDEO_ORIENTATION, &orientation,
1894 MMCAM_TAG_GPS_ENABLE, &gps_enable,
1897 _mmcam_dbg_warn("Get tag attrs fail. (%s:%x)", err_name, err);
1898 SAFE_FREE(err_name);
1901 location_info.longitude = _mmcamcorder_double_to_fix(longitude);
1902 location_info.latitude = _mmcamcorder_double_to_fix(latitude);
1903 location_info.altitude = _mmcamcorder_double_to_fix(altitude);
1904 geo_info.longitude = longitude *10000;
1905 geo_info.latitude = latitude *10000;
1906 geo_info.altitude = altitude *10000;
1907 /* find udta container.
1908 if, there are udta container, write loci box after that
1909 else, make udta container and write loci box. */
1910 if (_mmcamcorder_find_fourcc(f, MMCAM_FOURCC('u', 'd', 't', 'a'), TRUE)) {
1913 _mmcam_dbg_log("find udta container");
1916 if (fseek(f, -8L, SEEK_CUR) != 0)
1919 udta_pos = ftello(f);
1923 nread = fread(&buf, sizeof(char), sizeof(buf), f);
1925 _mmcam_dbg_log("recorded file fread %d", nread);
1927 udta_size = _mmcamcorder_get_container_size(buf);
1929 /* goto end of udta and write 'loci' box */
1930 if (fseek(f, (udta_size-4L), SEEK_CUR) != 0)
1934 if (!_mmcamcorder_write_loci(f, location_info)) {
1935 _mmcam_dbg_err("failed to write loci");
1939 if (!_mmcamcorder_write_geodata(f, geo_info)) {
1940 _mmcam_dbg_err("failed to write geodata");
1945 current_pos = ftello(f);
1946 if (current_pos < 0)
1949 if (!_mmcamcorder_update_size(f, udta_pos, current_pos))
1952 _mmcam_dbg_log("No udta container");
1953 if (fseek(f, 0, SEEK_END) != 0)
1956 if (!_mmcamcorder_write_udta(f, gps_enable, location_info, geo_info)) {
1957 _mmcam_dbg_err("failed to write udta");
1962 /* find moov container.
1963 update moov container size. */
1964 if ((current_pos = ftello(f)) < 0)
1967 if (_mmcamcorder_find_tag(f, MMCAM_FOURCC('m', 'o', 'o', 'v'), TRUE)) {
1968 gint64 internal_pos = ftello(f);
1970 _mmcam_dbg_log("found moov container");
1971 if (fseek(f, -8L, SEEK_CUR) != 0)
1974 moov_pos = ftello(f);
1978 if (!_mmcamcorder_update_size(f, moov_pos, current_pos))
1981 /* add orientation info */
1982 if (fseeko(f, internal_pos, SEEK_SET) < 0) {
1983 _mmcam_dbg_err("fseek failed : errno %d", errno);
1987 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t', 'r', 'a', 'k'), FALSE)) {
1988 _mmcam_dbg_err("failed to find [trak] tag");
1992 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t', 'k', 'h', 'd'), FALSE)) {
1993 _mmcam_dbg_err("failed to find [tkhd] tag");
1997 _mmcam_dbg_log("found [tkhd] tag");
1999 /* seek to start position of composition matrix */
2000 fseek(f, _OFFSET_COMPOSITION_MATRIX, SEEK_CUR);
2002 /* update composition matrix for orientation */
2003 _mmcamcorder_update_composition_matrix(f, orientation);
2005 _mmcam_dbg_err("No 'moov' container");
2017 _mmcam_dbg_err("ftell() returns negative value.");
2023 int _mmcamcorder_connect_video_stream_cb_signal(MMHandleType handle)
2025 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
2026 _MMCamcorderSubContext *sc = NULL;
2028 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2030 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
2031 mmf_return_val_if_fail(sc && sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2033 /* check video source element */
2034 if (sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst) {
2035 _mmcam_dbg_warn("connect video stream cb signal to _MMCAMCORDER_VIDEOSRC_SRC");
2036 MMCAMCORDER_SIGNAL_CONNECT(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst,
2037 _MMCAMCORDER_HANDLER_VIDEOREC, "video-stream-cb",
2038 G_CALLBACK(__mmcamcorder_video_stream_cb),
2040 return MM_ERROR_NONE;
2042 _mmcam_dbg_err("videosrc element is not created yet");
2043 return MM_ERROR_CAMCORDER_NOT_INITIALIZED;
2048 int _mmcamcorder_video_prepare_record(MMHandleType handle)
2050 int ret = MM_ERROR_NONE;
2052 _MMCamcorderVideoInfo *info = NULL;
2053 _MMCamcorderSubContext *sc = NULL;
2054 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
2056 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2058 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
2059 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2060 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2062 info = sc->info_video;
2064 _mmcam_dbg_warn("start");
2066 /* create encoding pipeline */
2067 ret = _mmcamcorder_create_recorder_pipeline((MMHandleType)hcamcorder);
2068 if (ret != MM_ERROR_NONE)
2069 goto _ERR_PREPARE_RECORD;
2071 if (info->filename == NULL) {
2072 char *temp_filename = NULL;
2075 mm_camcorder_get_attributes(handle, NULL,
2076 MMCAM_TARGET_FILENAME, &temp_filename, &size,
2079 info->filename = g_strdup(temp_filename);
2081 if (!info->filename) {
2082 _mmcam_dbg_err("strdup[src:%p] was failed", temp_filename);
2083 goto _ERR_PREPARE_RECORD;
2087 _mmcam_dbg_log("Record file name [%s]", info->filename);
2089 MMCAMCORDER_G_OBJECT_SET_POINTER(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename);
2090 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", 0);
2092 /* Adjust display FPS */
2093 sc->display_interval = 0;
2094 sc->previous_slot_time = 0;
2096 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PAUSED);
2097 if (ret != MM_ERROR_NONE)
2098 goto _ERR_PREPARE_RECORD;
2100 _mmcam_dbg_warn("done");
2104 _ERR_PREPARE_RECORD:
2105 /* Remove recorder pipeline and recording file which size maybe zero */
2106 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
2107 if (info && info->filename) {
2108 _mmcam_dbg_log("file delete(%s)", info->filename);
2109 unlink(info->filename);