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*/
44 #define _MMCAMCORDER_VIDEO_MINIMUM_SPACE (_MMCAMCORDER_MINIMUM_SPACE << 1) /* byte */
46 /*---------------------------------------------------------------------------------------
47 | LOCAL FUNCTION PROTOTYPES: |
48 ---------------------------------------------------------------------------------------*/
49 /* STATIC INTERNAL FUNCTION */
50 static gboolean __mmcamcorder_video_stream_cb(GstElement *element, GstSample *sample, gpointer u_data);
51 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_check(GstPad *pad, GstPadProbeInfo *info, gpointer u_data);
52 static GstPadProbeReturn __mmcamcorder_video_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data);
53 static GstPadProbeReturn __mmcamcorder_audioque_dataprobe(GstPad *pad, GstPadProbeInfo *info, gpointer u_data);
54 static GstPadProbeReturn __mmcamcorder_video_dataprobe_audio_disable(GstPad *pad, GstPadProbeInfo *info, gpointer u_data);
55 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_audio_mute(GstPad *pad, GstPadProbeInfo *info, gpointer u_data);
56 static gboolean __mmcamcorder_add_metadata(MMHandleType handle, int fileformat);
57 static gboolean __mmcamcorder_add_metadata_mp4(MMHandleType handle);
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;
108 GST_BUFFER_DTS(buffer) = GST_BUFFER_PTS(buffer);
110 ret = gst_app_src_push_buffer((GstAppSrc *)sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, buffer);
111 if (ret != GST_FLOW_OK && ret != GST_FLOW_FLUSHING) {
112 _mmcam_dbg_err("gst_app_src_push_buffer failed [0x%x]", ret);
113 gst_buffer_unref(buffer);
117 /*_mmcam_dbg_log("push buffer result : 0x%x", ret);*/
119 _mmcam_dbg_warn("unref video buffer immediately - push encoding buffer %d",
120 sc->info_video->push_encoding_buffer);
122 gst_buffer_unref(buffer);
130 int _mmcamcorder_create_recorder_pipeline(MMHandleType handle)
133 int err = MM_ERROR_NONE;
134 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, &sc->audio_disable,
171 _mmcam_dbg_log("MMCAM_AUDIO_DISABLE %d, is_modified_rate %d",
172 sc->audio_disable, sc->is_modified_rate);
174 sc->audio_disable |= sc->is_modified_rate;
176 if (sc->audio_disable == FALSE) {
177 /* create audiosrc bin */
178 err = _mmcamcorder_create_audiosrc_bin((MMHandleType)hcamcorder);
179 if (err != MM_ERROR_NONE)
183 err = _mmcamcorder_create_encodesink_bin((MMHandleType)hcamcorder, MM_CAMCORDER_ENCBIN_PROFILE_VIDEO);
184 if (err != MM_ERROR_NONE)
187 if (sc->audio_disable == FALSE) {
188 gst_bin_add(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst),
189 sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst);
192 /* add element and encodesink bin to encode main pipeline */
193 gst_bin_add_many(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst),
194 sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst,
195 sc->encode_element[_MMCAMCORDER_ENCSINK_FILT].gst,
196 sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst,
199 /* Link each element : appsrc - capsfilter - encodesink bin */
200 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, "src");
201 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_FILT].gst, "sink");
202 _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error);
204 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_FILT].gst, "src");
205 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst, "video_sink0");
206 _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error);
208 if (sc->audio_disable == FALSE) {
209 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst, "src");
210 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst, "audio_sink0");
211 _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error);
214 _mmcamcorder_conf_get_element(handle, hcamcorder->conf_main,
215 CONFIGURE_CATEGORY_MAIN_RECORD,
218 _mmcamcorder_conf_get_value_element_name(RecordsinkElement, &gst_element_rsink_name);
220 if (!gst_element_rsink_name) {
221 _mmcam_dbg_err("failed to get recordsink name");
222 err = MM_ERROR_CAMCORDER_INTERNAL;
223 goto pipeline_creation_error;
226 /* set data probe function */
228 /* register message cb */
230 /* set data probe functions */
231 if (sc->audio_disable == FALSE) {
232 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst, "sink");
233 MMCAMCORDER_ADD_BUFFER_PROBE(sinkpad, _MMCAMCORDER_HANDLER_VIDEOREC,
234 __mmcamcorder_audioque_dataprobe, hcamcorder);
235 gst_object_unref(sinkpad);
239 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst, "src");
240 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
241 __mmcamcorder_audio_dataprobe_audio_mute, hcamcorder);
242 gst_object_unref(srcpad);
245 if (sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst) {
246 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst, "src");
247 MMCAMCORDER_ADD_EVENT_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
248 __mmcamcorder_eventprobe_monitor, hcamcorder);
249 gst_object_unref(srcpad);
254 if (sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst) {
255 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst, "src");
256 MMCAMCORDER_ADD_EVENT_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
257 __mmcamcorder_eventprobe_monitor, hcamcorder);
258 gst_object_unref(srcpad);
262 if (sc->audio_disable) {
263 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC].gst, "sink");
264 MMCAMCORDER_ADD_BUFFER_PROBE(sinkpad, _MMCAMCORDER_HANDLER_VIDEOREC,
265 __mmcamcorder_video_dataprobe_audio_disable, hcamcorder);
266 gst_object_unref(sinkpad);
270 if (!strcmp(gst_element_rsink_name, "filesink")) {
271 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC].gst, "src");
272 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
273 __mmcamcorder_video_dataprobe_record, hcamcorder);
274 gst_object_unref(srcpad);
277 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst, "src");
278 MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC,
279 __mmcamcorder_audio_dataprobe_check, hcamcorder);
280 gst_object_unref(srcpad);
284 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "sink");
285 MMCAMCORDER_ADD_BUFFER_PROBE(sinkpad, _MMCAMCORDER_HANDLER_VIDEOREC,
286 __mmcamcorder_muxed_dataprobe, hcamcorder);
287 MMCAMCORDER_ADD_EVENT_PROBE(sinkpad, _MMCAMCORDER_HANDLER_VIDEOREC,
288 __mmcamcorder_eventprobe_monitor, hcamcorder);
289 gst_object_unref(sinkpad);
292 bus = gst_pipeline_get_bus(GST_PIPELINE(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst));
294 /* register pipeline message callback */
295 hcamcorder->encode_pipeline_cb_event_id = gst_bus_add_watch(bus, (GstBusFunc)_mmcamcorder_pipeline_cb_message, hcamcorder);
297 /* set sync handler */
298 gst_bus_set_sync_handler(bus, _mmcamcorder_encode_pipeline_bus_sync_callback, (gpointer)hcamcorder, NULL);
300 gst_object_unref(bus);
303 return MM_ERROR_NONE;
305 pipeline_creation_error:
306 for (i = _MMCAMCORDER_AUDIOSRC_BIN ; i <= _MMCAMCORDER_ENCSINK_SINK ; i++)
307 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, i);
309 _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCODE_MAIN_PIPE);
314 int _mmcamcorder_remove_audio_pipeline(MMHandleType handle)
316 GstPad *srcpad = NULL;
317 GstPad *sinkpad = NULL;
318 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
319 _MMCamcorderSubContext *sc = NULL;
321 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
323 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
324 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
325 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
329 if (sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst != NULL) {
330 srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst, "src");
331 sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst, "audio_sink0");
332 _MM_GST_PAD_UNLINK_UNREF(srcpad, sinkpad);
334 /* release audiosrc bin */
335 gst_bin_remove(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst),
336 sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst);
339 To avoid conflicting between old elements and newly created elements,
340 I clean element handles here. Real elements object will be finalized as the 'unref' process goes on.
341 This is a typical problem of unref. Even though I unref bin here, it takes much time to finalize each elements.
342 So I clean handles first, make them unref later. Audio recording, however, isn't needed this process.
343 It's because the pipeline of audio recording destroys at the same time,
344 and '_mmcamcorder_element_release_noti' will perfom removing handle.
346 _mmcamcorder_remove_element_handle(handle, (void *)sc->encode_element, _MMCAMCORDER_AUDIOSRC_BIN, _MMCAMCORDER_AUDIOSRC_VOL);
348 _mmcam_dbg_log("Audio pipeline removed");
351 return MM_ERROR_NONE;
355 int _mmcamcorder_remove_encode_pipeline(MMHandleType handle)
357 GstPad *reqpad = NULL;
358 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
359 _MMCamcorderSubContext *sc = NULL;
360 #ifdef _MMCAMCORDER_MM_RM_SUPPORT
361 int ret = MM_ERROR_NONE;
362 #endif /* _MMCAMCORDER_MM_RM_SUPPORT */
364 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
366 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
367 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
368 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
372 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst != NULL) {
373 /* release request pad */
374 reqpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "audio");
376 gst_element_release_request_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, reqpad);
377 gst_object_unref(reqpad);
381 reqpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "video");
383 gst_element_release_request_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, reqpad);
384 gst_object_unref(reqpad);
388 /* release encode main pipeline */
389 gst_object_unref(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst);
392 To avoid conflicting between old elements and newly created elements,
393 I clean element handles here. Real elements object will be finalized as the 'unref' process goes on.
394 This is a typical problem of unref. Even though I unref bin here, it takes much time to finalize each elements.
395 So I clean handles first, make them unref later. Audio recording, however, isn't needed this process.
396 It's because the pipeline of audio recording destroys at the same time,
397 and '_mmcamcorder_element_release_noti' will perfom removing handle.
399 /* _mmcamcorder_remove_element_handle(handle, (void *)sc->encode_element,
400 _MMCAMCORDER_ENCODE_MAIN_PIPE, _MMCAMCORDER_ENCSINK_SINK); */
402 _mmcam_dbg_warn("Encoder pipeline removed");
404 #ifdef _MMCAMCORDER_MM_RM_SUPPORT
405 _MMCAMCORDER_LOCK_RESOURCE(hcamcorder);
407 _mmcam_dbg_warn("lock resource - cb calling %d", hcamcorder->is_release_cb_calling);
409 if (hcamcorder->is_release_cb_calling == FALSE) {
410 /* release resource */
411 ret = mm_resource_manager_mark_for_release(hcamcorder->resource_manager,
412 hcamcorder->video_encoder_resource);
413 if (ret == MM_RESOURCE_MANAGER_ERROR_NONE)
414 hcamcorder->video_encoder_resource = NULL;
416 _mmcam_dbg_warn("mark resource for release 0x%x", ret);
418 ret = mm_resource_manager_commit(hcamcorder->resource_manager);
420 _mmcam_dbg_warn("commit resource release 0x%x", ret);
423 _MMCAMCORDER_UNLOCK_RESOURCE(hcamcorder);
425 _mmcam_dbg_warn("unlock resource");
426 #endif /* _MMCAMCORDER_MM_RM_SUPPORT */
429 return MM_ERROR_NONE;
433 int _mmcamcorder_remove_recorder_pipeline(MMHandleType handle)
435 int ret = MM_ERROR_NONE;
436 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
437 _MMCamcorderSubContext *sc = NULL;
441 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
442 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
443 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
445 _mmcam_dbg_log("start");
447 if (!sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst) {
448 _mmcam_dbg_warn("pipeline is not existed.");
449 return MM_ERROR_NONE;
452 _mmcamcorder_remove_all_handlers((MMHandleType)hcamcorder, _MMCAMCORDER_HANDLER_VIDEOREC);
454 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_NULL);
455 if (ret != MM_ERROR_NONE) {
456 _mmcam_dbg_err("Faile to change encode main pipeline [0x%x]", ret);
460 bus = gst_pipeline_get_bus(GST_PIPELINE(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst));
462 /* remove audio pipeline first */
463 ret = _mmcamcorder_remove_audio_pipeline(handle);
464 if (ret != MM_ERROR_NONE) {
465 _mmcam_dbg_err("Fail to remove audio pipeline");
469 ret = _mmcamcorder_remove_encode_pipeline(handle);
470 if (ret != MM_ERROR_NONE) {
471 _mmcam_dbg_err("Fail to remove encoder pipeline");
475 /* Remove pipeline message callback */
476 if (hcamcorder->encode_pipeline_cb_event_id != 0) {
477 g_source_remove(hcamcorder->encode_pipeline_cb_event_id);
478 hcamcorder->encode_pipeline_cb_event_id = 0;
481 /* Remove remained message */
483 GstMessage *gst_msg = NULL;
484 while ((gst_msg = gst_bus_pop(bus)) != NULL) {
485 _mmcamcorder_pipeline_cb_message(bus, gst_msg, (gpointer)hcamcorder);
486 gst_message_unref(gst_msg);
489 gst_object_unref(bus);
493 _mmcam_dbg_log("done");
499 int _mmcamcorder_video_command(MMHandleType handle, int command)
504 int gop_interval = 0;
505 int ret = MM_ERROR_NONE;
506 double motion_rate = _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE;
507 char *err_name = NULL;
508 char *target_filename = NULL;
509 GstCameraControl *CameraControl = NULL;
512 GstElement *pipeline = NULL;
514 _MMCamcorderVideoInfo *info = NULL;
515 _MMCamcorderSubContext *sc = NULL;
516 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
518 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
520 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
521 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
522 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
523 mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
525 info = sc->info_video;
527 _mmcam_dbg_log("Command(%d)", command);
529 pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
532 case _MMCamcorder_CMD_RECORD:
534 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) != MM_CAMCORDER_STATE_PAUSED) {
538 gboolean storage_validity = FALSE;
541 int root_directory_length = 0;
544 _mmcam_dbg_log("Record Start - dual stream %d", info->support_dual_stream);
546 #ifdef _MMCAMCORDER_MM_RM_SUPPORT
547 _MMCAMCORDER_LOCK_RESOURCE(hcamcorder);
549 /* prepare resource manager for H/W encoder */
550 if (hcamcorder->video_encoder_resource == NULL) {
551 ret = mm_resource_manager_mark_for_acquire(hcamcorder->resource_manager,
552 MM_RESOURCE_MANAGER_RES_TYPE_VIDEO_ENCODER,
553 MM_RESOURCE_MANAGER_RES_VOLUME_FULL,
554 &hcamcorder->video_encoder_resource);
555 if (ret != MM_RESOURCE_MANAGER_ERROR_NONE) {
556 _mmcam_dbg_err("could not prepare for encoder resource");
557 ret = MM_ERROR_RESOURCE_INTERNAL;
558 _MMCAMCORDER_UNLOCK_RESOURCE(hcamcorder);
559 goto _ERR_CAMCORDER_VIDEO_COMMAND;
562 _mmcam_dbg_log("encoder already acquired");
565 /* acquire resources */
566 ret = mm_resource_manager_commit(hcamcorder->resource_manager);
567 if (ret != MM_RESOURCE_MANAGER_ERROR_NONE) {
568 _mmcam_dbg_err("could not acquire resources");
569 ret = MM_ERROR_RESOURCE_INTERNAL;
570 _MMCAMCORDER_UNLOCK_RESOURCE(hcamcorder);
571 goto _ERR_CAMCORDER_VIDEO_COMMAND;
574 _MMCAMCORDER_UNLOCK_RESOURCE(hcamcorder);
575 #endif /* _MMCAMCORDER_MM_RM_SUPPORT */
577 /* init record_dual_stream */
578 info->record_dual_stream = FALSE;
580 ret = mm_camcorder_get_attributes(handle, &err_name,
581 MMCAM_CAMERA_FPS, &fps,
582 MMCAM_CAMERA_WIDTH, &(info->preview_width),
583 MMCAM_CAMERA_HEIGHT, &(info->preview_height),
584 MMCAM_VIDEO_WIDTH, &(info->video_width),
585 MMCAM_VIDEO_HEIGHT, &(info->video_height),
586 MMCAM_FILE_FORMAT, &fileformat,
587 MMCAM_TARGET_FILENAME, &target_filename, &size,
588 MMCAM_TARGET_MAX_SIZE, &imax_size,
589 MMCAM_TARGET_TIME_LIMIT, &imax_time,
590 MMCAM_FILE_FORMAT, &(info->fileformat),
591 MMCAM_CAMERA_RECORDING_MOTION_RATE, &motion_rate,
592 MMCAM_ROOT_DIRECTORY, &hcamcorder->root_directory, &root_directory_length,
594 if (ret != MM_ERROR_NONE) {
595 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, ret);
597 goto _ERR_CAMCORDER_VIDEO_COMMAND;
600 if (!target_filename && !hcamcorder->mstream_cb) {
601 _mmcam_dbg_err("filename is not set and muxed stream cb is NULL");
602 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
603 goto _ERR_CAMCORDER_VIDEO_COMMAND;
608 info->max_size = 0; /* do not check */
610 info->max_size = ((guint64)imax_size) << 10; /* to byte */
614 info->max_time = 0; /* do not check */
616 info->max_time = (guint64)((double)imax_time * (double)1000 * motion_rate); /* to millisecond */
618 ret = _mmcamcorder_get_storage_validity(hcamcorder, target_filename,
619 _MMCAMCORDER_VIDEO_MINIMUM_SPACE, &storage_validity);
620 if (ret != MM_ERROR_NONE) {
621 _mmcam_dbg_err("storage validation failed[0x%x]:%d", ret, storage_validity);
625 _mmcamcorder_adjust_recording_max_size(target_filename, &info->max_size);
627 g_mutex_lock(&hcamcorder->task_thread_lock);
628 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst == NULL &&
629 hcamcorder->task_thread_state == _MMCAMCORDER_TASK_THREAD_STATE_NONE) {
630 /* Play record start sound */
631 _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_SAMPLE_SOUND_NAME_REC_START, FALSE);
633 g_mutex_unlock(&hcamcorder->task_thread_lock);
635 _mmcam_dbg_warn("video size [%dx%d]", info->video_width, info->video_height);
637 if (info->video_width == 0 || info->video_height == 0) {
638 _mmcam_dbg_warn("video size is invalid [%dx%d] use preview size [%dx%d]",
639 info->video_width, info->video_height, info->preview_width, info->preview_height);
640 info->video_width = info->preview_width;
641 info->video_height = info->preview_height;
644 if (info->support_dual_stream) {
645 _mmcam_dbg_warn("DUAL STREAM MODE");
647 info->record_dual_stream = TRUE;
649 /* No need to restart preview */
650 info->restart_preview = FALSE;
652 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "video-width", info->video_width);
653 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "video-height", info->video_height);
654 } else if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
655 info->preview_width == info->video_width &&
656 info->preview_height == info->video_height) {
657 _mmcam_dbg_log("H264 preview mode and same resolution");
659 /* No need to restart preview */
660 info->restart_preview = FALSE;
662 /* always need to restart preview */
663 info->restart_preview = TRUE;
666 /* set recording hint */
667 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", TRUE);
669 if (info->restart_preview) {
670 /* stop preview and set new size */
671 _mmcam_dbg_log("restart preview");
673 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
674 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
675 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "keep-camera-preview", TRUE);
677 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
679 /* check decoder recreation */
680 if (!_mmcamcorder_recreate_decoder_for_encoded_preview(handle)) {
681 _mmcam_dbg_err("_mmcamcorder_recreate_decoder_for_encoded_preview failed");
682 ret = MM_ERROR_CAMCORDER_INTERNAL;
683 goto _ERR_CAMCORDER_VIDEO_COMMAND;
686 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
687 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
688 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "keep-camera-preview", FALSE);
690 if (ret != MM_ERROR_NONE)
691 goto _ERR_CAMCORDER_VIDEO_COMMAND;
693 if (!_mmcamcorder_set_camera_resolution(handle, info->video_width, info->video_height)) {
694 ret = MM_ERROR_CAMCORDER_INTERNAL;
695 goto _ERR_CAMCORDER_VIDEO_COMMAND;
698 /* Start preview again with new setting */
699 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
700 if (ret != MM_ERROR_NONE)
701 goto _ERR_CAMCORDER_VIDEO_COMMAND;
703 if (motion_rate < 1.0) {
704 _mmcam_dbg_warn("wait for stabilization of frame");
708 _mmcam_dbg_log("no need to restart preview");
711 _mmcamcorder_conf_get_value_int(handle, hcamcorder->conf_main,
712 CONFIGURE_CATEGORY_MAIN_RECORD,
716 _mmcamcorder_conf_get_value_int(handle, hcamcorder->conf_main,
717 CONFIGURE_CATEGORY_MAIN_RECORD,
718 "PassFirstVideoFrame",
719 &(sc->pass_first_vframe));
721 _mmcam_dbg_log("Drop video frame count[%d], Pass fisrt video frame count[%d]",
722 sc->drop_vframe, sc->pass_first_vframe);
724 info->record_drop_count = (guint)motion_rate;
725 info->record_motion_rate = motion_rate;
726 if (sc->is_modified_rate)
727 info->record_timestamp_ratio = (_MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE/motion_rate);
729 info->record_timestamp_ratio = _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE;
731 _mmcam_dbg_warn("recording fps %d, motion rate %f, timestamp_ratio %f",
732 fps, info->record_motion_rate, info->record_timestamp_ratio);
734 /* set push buffer flag */
735 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_INIT;
736 info->base_video_ts = 0;
738 /* connect video stream cb signal */
739 /*130826 Connect video stream cb for handling fast record frame cb*/
740 if (info->record_dual_stream) {
741 if (_mmcamcorder_connect_video_stream_cb_signal((MMHandleType)hcamcorder) != MM_ERROR_NONE)
742 goto _ERR_CAMCORDER_VIDEO_COMMAND;
745 /* start video stream */
746 if (info->record_dual_stream) {
747 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
749 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
751 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_START");
752 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_START);
754 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
756 _mmcam_dbg_err("could not get camera control");
760 /* check pre-created encode pipeline */
761 g_mutex_lock(&hcamcorder->task_thread_lock);
762 if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst == NULL &&
763 hcamcorder->task_thread_state == _MMCAMCORDER_TASK_THREAD_STATE_NONE) {
764 /* create encoding pipeline */
765 ret = _mmcamcorder_video_prepare_record((MMHandleType)hcamcorder);
766 if (ret != MM_ERROR_NONE) {
767 g_mutex_unlock(&hcamcorder->task_thread_lock);
768 goto _ERR_CAMCORDER_VIDEO_COMMAND;
771 g_mutex_unlock(&hcamcorder->task_thread_lock);
773 /* check recording start sound */
774 _mmcamcorder_sound_solo_play_wait(handle);
776 /**< To fix video recording hanging
777 1. use gst_element_set_start_time() instead of gst_pipeline_set_new_stream_time()
778 2. Set (GstClockTime)1 instead of (GstClockTime)0. Because of strict check in gstreamer 0.25,
779 basetime wouldn't change if you set (GstClockTime)0.
780 3. Move set start time position below PAUSED of pipeline.
783 gst_element_set_start_time(GST_ELEMENT(sc->element[_MMCAMCORDER_MAIN_PIPE].gst), (GstClockTime)1);
784 gst_element_set_start_time(GST_ELEMENT(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), (GstClockTime)1);
787 info->video_frame_count = 0;
788 info->is_firstframe = TRUE;
789 info->audio_frame_count = 0;
791 sc->ferror_send = FALSE;
792 sc->ferror_count = 0;
793 hcamcorder->error_occurs = FALSE;
794 sc->bget_eos = FALSE;
795 sc->muxed_stream_offset = 0;
797 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PLAYING);
798 if (ret != MM_ERROR_NONE) {
799 /* stop video stream */
800 if (info->record_dual_stream) {
801 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
803 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
805 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
806 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
808 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
810 _mmcam_dbg_err("failed to get camera control");
814 /* Remove recorder pipeline and recording file which size maybe zero */
815 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
816 if (info->filename) {
817 _mmcam_dbg_log("file delete(%s)", info->filename);
818 unlink(info->filename);
820 goto _ERR_CAMCORDER_VIDEO_COMMAND;
823 /*set the GOP so that video record will get a new key frame*/
824 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264) {
825 if (mm_camcorder_get_attributes(handle, NULL,
826 MMCAM_ENCODED_PREVIEW_GOP_INTERVAL, &gop_interval, NULL) == MM_ERROR_NONE)
827 _mmcamcorder_set_encoded_preview_gop_interval(handle, gop_interval);
829 _mmcam_dbg_err("get gop interval failed");
833 if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264) {
834 if (mm_camcorder_get_attributes(handle, NULL,
835 MMCAM_ENCODED_PREVIEW_GOP_INTERVAL, &gop_interval, NULL) == MM_ERROR_NONE)
836 _mmcamcorder_set_encoded_preview_gop_interval(handle, gop_interval);
838 _mmcam_dbg_err("get gop interval failed");
841 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", FALSE);
843 _mmcam_dbg_log("Object property settings done");
847 case _MMCamcorder_CMD_PAUSE:
849 if (info->b_commiting) {
850 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
851 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
854 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
855 if (sc->audio_disable) {
856 /* check only video frame */
857 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
859 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
860 _mmcam_dbg_err("Pause fail, frame count %"G_GUINT64_FORMAT, info->video_frame_count);
861 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
863 _mmcam_dbg_warn("Waiting for enough video frame, retrial[%d], frame %"G_GUINT64_FORMAT, count, info->video_frame_count);
866 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
868 /* check both of video and audio frame */
869 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
871 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
872 _mmcam_dbg_err("Pause fail, frame count VIDEO[%"G_GUINT64_FORMAT"], AUDIO [%"G_GUINT64_FORMAT"]",
873 info->video_frame_count, info->audio_frame_count);
874 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
876 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%"G_GUINT64_FORMAT"], AUDIO [%"G_GUINT64_FORMAT"]",
877 count, info->video_frame_count, info->audio_frame_count);
880 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
884 /* block encodebin */
885 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", TRUE);
888 case _MMCamcorder_CMD_CANCEL:
890 if (info->b_commiting) {
891 _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command);
892 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
895 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
897 if (hcamcorder->capture_in_recording == FALSE) {
899 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
900 _mmcam_dbg_err("Failed to Wait capture data");
901 hcamcorder->capture_in_recording = FALSE;
904 _mmcam_dbg_warn("Waiting for capture data - retrial [%d]", count);
907 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
910 /* block push buffer */
911 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP;
913 ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
914 if (ret != MM_ERROR_NONE)
915 goto _ERR_CAMCORDER_VIDEO_COMMAND;
917 /* set recording hint */
918 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", FALSE);
920 /* stop video stream */
921 if (info->record_dual_stream) {
922 CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst);
924 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE);
926 _mmcam_dbg_log("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP");
927 gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP);
929 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE);
931 _mmcam_dbg_err("failed to get camera control");
935 if (info->restart_preview) {
936 /* restart preview */
937 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE);
938 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE);
939 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "keep-camera-preview", TRUE);
941 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY);
943 /* check decoder recreation */
944 if (!_mmcamcorder_recreate_decoder_for_encoded_preview(handle)) {
945 _mmcam_dbg_err("_mmcamcorder_recreate_decoder_for_encoded_preview failed");
946 ret = MM_ERROR_CAMCORDER_INTERNAL;
949 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
950 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
951 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "keep-camera-preview", FALSE);
953 if (ret != MM_ERROR_NONE)
954 goto _ERR_CAMCORDER_VIDEO_COMMAND;
956 /* reset restart_preview for inset window layout */
957 info->restart_preview = FALSE;
959 if (!_mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height)) {
960 ret = MM_ERROR_CAMCORDER_INTERNAL;
961 goto _ERR_CAMCORDER_VIDEO_COMMAND;
964 ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING);
965 if (ret != MM_ERROR_NONE)
966 goto _ERR_CAMCORDER_VIDEO_COMMAND;
969 /* remove target file */
970 if (info->filename) {
971 _mmcam_dbg_log("file delete(%s)", info->filename);
972 unlink(info->filename);
975 sc->isMaxsizePausing = FALSE;
976 sc->isMaxtimePausing = FALSE;
978 sc->display_interval = 0;
979 sc->previous_slot_time = 0;
980 info->video_frame_count = 0;
981 info->audio_frame_count = 0;
983 hcamcorder->capture_in_recording = FALSE;
986 case _MMCamcorder_CMD_COMMIT:
990 if (info->b_commiting) {
991 _mmcam_dbg_err("now on commiting previous file!!(command : %d)", command);
992 return MM_ERROR_CAMCORDER_CMD_IS_RUNNING;
994 _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT : start");
995 info->b_commiting = TRUE;
996 sc->bget_eos = FALSE;
999 for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) {
1000 if (sc->audio_disable) {
1001 /* check only video frame */
1002 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME &&
1003 hcamcorder->capture_in_recording == FALSE) {
1005 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
1006 _mmcam_dbg_err("Commit fail, frame count is %"G_GUINT64_FORMAT", capturing %d",
1007 info->video_frame_count, hcamcorder->capture_in_recording);
1009 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) {
1010 _mmcam_dbg_warn("video frames are enough. keep going...");
1012 info->b_commiting = FALSE;
1013 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
1016 _mmcam_dbg_warn("Waiting for enough video frame, retrial [%d], frame %"G_GUINT64_FORMAT", capturing %d",
1017 count, info->video_frame_count, hcamcorder->capture_in_recording);
1020 /* check both of video and audio frame */
1021 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME &&
1022 info->audio_frame_count &&
1023 hcamcorder->capture_in_recording == FALSE) {
1025 } else if (count == _MMCAMCORDER_RETRIAL_COUNT) {
1026 _mmcam_dbg_err("Commit fail, VIDEO[%"G_GUINT64_FORMAT"], AUDIO [%"G_GUINT64_FORMAT"], capturing %d",
1027 info->video_frame_count, info->audio_frame_count, hcamcorder->capture_in_recording);
1029 if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) {
1030 _mmcam_dbg_warn("video/audio frames are enough. keep going...");
1032 info->b_commiting = FALSE;
1033 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
1036 return MM_ERROR_CAMCORDER_INVALID_CONDITION;
1038 _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%"G_GUINT64_FORMAT"], AUDIO [%"G_GUINT64_FORMAT"], capturing %d",
1039 count, info->video_frame_count, info->audio_frame_count, hcamcorder->capture_in_recording);
1043 if (hcamcorder->capture_in_recording) {
1044 gint64 end_time = g_get_monotonic_time() + (200 * G_TIME_SPAN_MILLISECOND);
1045 if (!_MMCAMCORDER_CMD_WAIT_UNTIL(handle, end_time))
1046 _mmcam_dbg_warn("timeout");
1048 usleep(_MMCAMCORDER_FRAME_WAIT_TIME);
1052 /* block push buffer */
1053 info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP;
1054 _mmcam_dbg_log("block push buffer to appsrc");
1056 _mmcamcorder_get_freespace(hcamcorder->storage_info.type, &free_space);
1057 if (free_space < _MMCAMCORDER_MINIMUM_SPACE) {
1058 _mmcam_dbg_warn("_MMCamcorder_CMD_COMMIT out of storage [%" G_GUINT64_FORMAT "]", free_space);
1059 ret = MM_ERROR_OUT_OF_STORAGE;
1060 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1063 if (sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst != NULL) {
1064 if (gst_element_send_event(sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, gst_event_new_eos())) {
1065 _mmcam_dbg_warn("VIDEO: send eos to appsrc done");
1067 _mmcam_dbg_err("VIDEO: send EOS failed");
1068 info->b_commiting = FALSE;
1069 ret = MM_ERROR_CAMCORDER_INTERNAL;
1070 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1073 _mmcam_dbg_err("No video stream source");
1074 info->b_commiting = FALSE;
1075 ret = MM_ERROR_CAMCORDER_INTERNAL;
1076 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1079 if (sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst != NULL) {
1080 if (gst_element_send_event(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst, gst_event_new_eos())) {
1081 _mmcam_dbg_warn("AUDIO: send eos to audiosrc done");
1083 _mmcam_dbg_err("AUDIO: send EOS failed");
1084 info->b_commiting = FALSE;
1085 ret = MM_ERROR_CAMCORDER_INTERNAL;
1086 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1089 _mmcam_dbg_log("No audio stream");
1093 sc->display_interval = 0;
1094 sc->previous_slot_time = 0;
1097 _mmcam_dbg_log("Start to wait EOS");
1098 ret = _mmcamcorder_get_eos_message(handle);
1099 if (ret != MM_ERROR_NONE) {
1100 info->b_commiting = FALSE;
1101 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1105 hcamcorder->capture_in_recording = FALSE;
1109 ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT;
1110 goto _ERR_CAMCORDER_VIDEO_COMMAND;
1113 return MM_ERROR_NONE;
1115 _ERR_CAMCORDER_VIDEO_COMMAND:
1116 if (command == _MMCamcorder_CMD_RECORD)
1117 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
1123 int _mmcamcorder_video_handle_eos(MMHandleType handle)
1125 int ret = MM_ERROR_NONE;
1127 guint64 file_size = 0;
1129 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1130 _MMCamcorderSubContext *sc = NULL;
1131 _MMCamcorderVideoInfo *info = NULL;
1132 _MMCamcorderMsgItem msg;
1133 MMCamRecordingReport *report = NULL;
1135 mmf_return_val_if_fail(hcamcorder, FALSE);
1137 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1138 mmf_return_val_if_fail(sc, FALSE);
1139 mmf_return_val_if_fail(sc->info_video, FALSE);
1141 info = sc->info_video;
1145 /* Play record stop sound */
1146 _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_SAMPLE_SOUND_NAME_REC_STOP, FALSE);
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);
1179 _mmcam_dbg_log("Writing location information [%s] !!", ret ? "SUCCEEDED" : "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 %"G_GUINT64_FORMAT" byte - created filesize %"G_GUINT64_FORMAT" 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);
1201 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "keep-camera-preview", TRUE);
1203 _mmcam_dbg_log("Set state of pipeline as READY");
1204 ret = _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_READY);
1206 /* check decoder recreation */
1207 if (!_mmcamcorder_recreate_decoder_for_encoded_preview(handle)) {
1208 _mmcam_dbg_err("_mmcamcorder_recreate_decoder_for_encoded_preview failed");
1209 ret = MM_ERROR_CAMCORDER_INTERNAL;
1213 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE);
1214 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE);
1215 MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "keep-camera-preview", FALSE);
1217 if (ret != MM_ERROR_NONE) {
1218 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1219 msg.param.code = ret;
1220 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1221 _mmcam_dbg_err("Failed to set state READY[%x]", ret);
1224 /* reset restart_preview for inset window layout */
1225 info->restart_preview = FALSE;
1227 /* recover preview size */
1228 if (!_mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height)) {
1229 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1230 msg.param.code = MM_ERROR_CAMCORDER_INTERNAL;
1231 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1232 _mmcam_dbg_err("Failed to set camera resolution %dx%d",
1233 info->preview_width, info->preview_height);
1236 ret = _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_PLAYING);
1237 /* Do not return when error is occurred.
1238 Recording file was created successfully, but starting pipeline failed */
1239 if (ret != MM_ERROR_NONE) {
1240 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1241 msg.param.code = ret;
1242 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1243 _mmcam_dbg_err("Failed to set state PLAYING[%x]", ret);
1246 _mmcam_dbg_log("No need to restart preview");
1249 /* Send recording report to application */
1250 msg.id = MM_MESSAGE_CAMCORDER_VIDEO_CAPTURED;
1251 report = (MMCamRecordingReport *)g_malloc(sizeof(MMCamRecordingReport));
1253 _mmcam_dbg_err("Recording report fail(%s). Out of memory.", info->filename);
1255 report->recording_filename = g_strdup(info->filename);
1256 msg.param.data = report;
1258 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1262 sc->pipeline_time = 0;
1264 sc->isMaxsizePausing = FALSE; /*In async function, this variable should set in callback function. */
1265 sc->isMaxtimePausing = FALSE;
1266 hcamcorder->error_occurs = FALSE;
1268 info->video_frame_count = 0;
1269 info->audio_frame_count = 0;
1271 info->b_commiting = FALSE;
1273 /* check recording stop sound */
1274 _mmcamcorder_sound_solo_play_wait(handle);
1276 _mmcam_dbg_err("_MMCamcorder_CMD_COMMIT : end");
1282 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_check(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1284 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1285 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1287 _MMCamcorderSubContext *sc = NULL;
1288 _MMCamcorderVideoInfo *videoinfo = NULL;
1289 _MMCamcorderMsgItem msg;
1290 guint64 buffer_size = 0;
1291 guint64 trailer_size = 0;
1292 guint64 max_size = 0;
1294 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1295 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1296 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1298 mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK);
1299 videoinfo = sc->info_video;
1301 /* get buffer size */
1302 if (!gst_buffer_map(buffer, &mapinfo, GST_MAP_READ)) {
1303 _mmcam_dbg_warn("map failed : buffer %p", buffer);
1304 return GST_PAD_PROBE_OK;
1307 buffer_size = mapinfo.size;
1308 gst_buffer_unmap(buffer, &mapinfo);
1310 /*_mmcam_dbg_err("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1312 g_mutex_lock(&videoinfo->size_check_lock);
1314 if (videoinfo->audio_frame_count == 0) {
1315 videoinfo->filesize += buffer_size;
1316 videoinfo->audio_frame_count++;
1317 g_mutex_unlock(&videoinfo->size_check_lock);
1318 return GST_PAD_PROBE_OK;
1321 if (sc->ferror_send || sc->isMaxsizePausing) {
1322 _mmcam_dbg_warn("Recording is paused, drop frames");
1323 g_mutex_unlock(&videoinfo->size_check_lock);
1324 return GST_PAD_PROBE_DROP;
1327 /* get trailer size */
1328 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4)
1329 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1333 /* check max size of recorded file */
1334 max_size = videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE;
1335 if (videoinfo->max_size > 0 && videoinfo->max_size < max_size) {
1336 GstState pipeline_state = GST_STATE_VOID_PENDING;
1337 GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1338 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1339 _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \
1340 " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]",
1341 videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size);
1343 if (!sc->isMaxsizePausing) {
1344 sc->isMaxsizePausing = TRUE;
1345 gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ;
1346 if (pipeline_state == GST_STATE_PLAYING)
1347 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1349 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1350 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1353 g_mutex_unlock(&videoinfo->size_check_lock);
1358 videoinfo->filesize += buffer_size;
1359 videoinfo->audio_frame_count++;
1361 g_mutex_unlock(&videoinfo->size_check_lock);
1363 return GST_PAD_PROBE_OK;
1367 static GstPadProbeReturn __mmcamcorder_video_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1372 guint64 free_space = 0;
1373 guint64 buffer_size = 0;
1374 guint64 trailer_size = 0;
1375 guint64 queued_buffer = 0;
1376 guint64 max_size = 0;
1377 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1379 storage_state_e storage_state = STORAGE_STATE_UNMOUNTABLE;
1381 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1382 _MMCamcorderMsgItem msg;
1383 _MMCamcorderSubContext *sc = NULL;
1384 _MMCamcorderVideoInfo *videoinfo = NULL;
1386 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1387 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1389 sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
1390 mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK);
1391 videoinfo = sc->info_video;
1393 /*_mmcam_dbg_log("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1394 if (sc->ferror_send) {
1395 _mmcam_dbg_warn("file write error, drop frames");
1396 return GST_PAD_PROBE_DROP;
1399 gst_buffer_map(buffer, &mapinfo, GST_MAP_READ);
1400 buffer_size = mapinfo.size;
1401 gst_buffer_unmap(buffer, &mapinfo);
1403 videoinfo->video_frame_count++;
1404 if (videoinfo->video_frame_count <= (guint64)_MMCAMCORDER_MINIMUM_FRAME) {
1405 /* _mmcam_dbg_log("Pass minimum frame: info->video_frame_count: %" G_GUINT64_FORMAT " ",
1406 info->video_frame_count); */
1407 g_mutex_lock(&videoinfo->size_check_lock);
1408 videoinfo->filesize += buffer_size;
1409 g_mutex_unlock(&videoinfo->size_check_lock);
1410 return GST_PAD_PROBE_OK;
1413 /* get trailer size */
1414 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4)
1415 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1419 /* check free space */
1420 ret = _mmcamcorder_get_freespace(hcamcorder->storage_info.type, &free_space);
1422 _mmcam_dbg_err("Error occured. [%d]", ret);
1423 if (sc->ferror_count == 2 && sc->ferror_send == FALSE) {
1424 sc->ferror_send = TRUE;
1426 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1427 msg.param.code = MM_ERROR_FILE_READ;
1429 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1434 return GST_PAD_PROBE_DROP; /* skip this buffer */
1437 if (free_space == 0) {
1438 /* check storage state */
1439 storage_get_state(hcamcorder->storage_info.id, &storage_state);
1441 _mmcam_dbg_warn("storage state %d", storage_state);
1443 if (storage_state == STORAGE_STATE_REMOVED ||
1444 storage_state == STORAGE_STATE_UNMOUNTABLE) {
1445 _mmcam_dbg_err("storage was removed!");
1447 _MMCAMCORDER_LOCK(hcamcorder);
1449 if (sc->ferror_send == FALSE) {
1450 _mmcam_dbg_err("OUT_OF_STORAGE error");
1452 sc->ferror_send = TRUE;
1454 _MMCAMCORDER_UNLOCK(hcamcorder);
1456 msg.id = MM_MESSAGE_CAMCORDER_ERROR;
1457 msg.param.code = MM_ERROR_OUT_OF_STORAGE;
1459 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1461 _MMCAMCORDER_UNLOCK(hcamcorder);
1462 _mmcam_dbg_warn("error was already sent");
1465 return GST_PAD_PROBE_DROP;
1469 /* get queued buffer size */
1470 if (sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst)
1471 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst, "current-level-bytes", &aq_size);
1473 if (sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst)
1474 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst, "current-level-bytes", &vq_size);
1476 queued_buffer = aq_size + vq_size;
1478 if (free_space < (_MMCAMCORDER_MINIMUM_SPACE + buffer_size + trailer_size + queued_buffer)) {
1479 _mmcam_dbg_warn("No more space for recording!!! Recording is paused.");
1480 _mmcam_dbg_warn("Free Space : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]," \
1481 " buffer size : [%" G_GUINT64_FORMAT "], queued buffer size : [%" G_GUINT64_FORMAT "]", \
1482 free_space, trailer_size, buffer_size, queued_buffer);
1484 if (!sc->isMaxsizePausing) {
1485 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1486 sc->isMaxsizePausing = TRUE;
1488 msg.id = MM_MESSAGE_CAMCORDER_NO_FREE_SPACE;
1489 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1492 return GST_PAD_PROBE_DROP;
1495 g_mutex_lock(&videoinfo->size_check_lock);
1497 /* check max size of recorded file */
1498 max_size = videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE;
1499 if (videoinfo->max_size > 0 && videoinfo->max_size < max_size) {
1500 GstState pipeline_state = GST_STATE_VOID_PENDING;
1501 GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst;
1502 _mmcam_dbg_warn("Max size!!! Recording is paused.");
1503 _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \
1504 " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]",
1505 videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size);
1507 if (!sc->isMaxsizePausing) {
1508 sc->isMaxsizePausing = TRUE;
1509 gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ;
1510 if (pipeline_state == GST_STATE_PLAYING)
1511 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1513 msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE;
1514 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1517 g_mutex_unlock(&videoinfo->size_check_lock);
1519 return GST_PAD_PROBE_DROP;
1522 videoinfo->filesize += (guint64)buffer_size;
1525 _mmcam_dbg_log("filesize %lld Byte, ", videoinfo->filesize);
1528 g_mutex_unlock(&videoinfo->size_check_lock);
1530 return GST_PAD_PROBE_OK;
1534 static GstPadProbeReturn __mmcamcorder_video_dataprobe_audio_disable(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1536 guint64 trailer_size = 0;
1537 guint64 rec_pipe_time = 0;
1538 unsigned int remained_time = 0;
1540 GstClockTime b_time;
1542 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1543 _MMCamcorderMsgItem msg;
1544 _MMCamcorderSubContext *sc = NULL;
1545 _MMCamcorderVideoInfo *videoinfo = NULL;
1547 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1549 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1550 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1552 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1553 mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK);
1554 mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK);
1556 videoinfo = sc->info_video;
1558 b_time = GST_BUFFER_PTS(buffer);
1560 rec_pipe_time = GST_TIME_AS_MSECONDS(b_time);
1562 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4)
1563 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1567 /* check max time */
1568 if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) {
1569 _mmcam_dbg_warn("Time current [%" G_GUINT64_FORMAT "], Max [%" G_GUINT64_FORMAT "], motion rate [%lf]", \
1570 rec_pipe_time, videoinfo->max_time, videoinfo->record_motion_rate);
1572 if (!sc->isMaxtimePausing) {
1573 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1575 sc->isMaxtimePausing = TRUE;
1577 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1578 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1579 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1580 msg.param.recording_status.remained_time = 0;
1581 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1583 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1584 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1587 return GST_PAD_PROBE_DROP;
1590 /* calculate remained time can be recorded */
1591 if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) {
1592 remained_time = videoinfo->max_time - rec_pipe_time;
1593 } else if (videoinfo->max_size > 0) {
1594 long double max_size = (long double)videoinfo->max_size;
1595 long double current_size = (long double)(videoinfo->filesize + trailer_size);
1597 remained_time = (unsigned int)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1600 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1601 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1602 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1603 msg.param.recording_status.remained_time = remained_time;
1604 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1607 _mmcam_dbg_log("time [%" GST_TIME_FORMAT "], size [%d]",
1608 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1611 if (videoinfo->record_timestamp_ratio != _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) {
1612 guint record_motion_rate = (guint)videoinfo->record_motion_rate;
1615 _mmcam_dbg_log("record_motion_rate %d, videoinfo->record_drop_count %d",
1616 record_motion_rate, videoinfo->record_drop_count);
1619 /* drop some frame if fast motion */
1620 if (videoinfo->record_motion_rate > _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) {
1621 if (record_motion_rate != (videoinfo->record_drop_count++)) {
1623 _mmcam_dbg_warn("drop frame");
1625 return GST_PAD_PROBE_DROP;
1628 videoinfo->record_drop_count = 1;
1630 _mmcam_dbg_warn("pass frame");
1634 GST_BUFFER_PTS(buffer) = b_time * (videoinfo->record_timestamp_ratio);
1635 GST_BUFFER_DTS(buffer) = GST_BUFFER_PTS(buffer);
1638 return GST_PAD_PROBE_OK;
1642 static GstPadProbeReturn __mmcamcorder_audioque_dataprobe(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1644 _MMCamcorderMsgItem msg;
1645 guint64 trailer_size = 0;
1646 guint64 rec_pipe_time = 0;
1647 _MMCamcorderSubContext *sc = NULL;
1648 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1649 _MMCamcorderVideoInfo *videoinfo = NULL;
1650 unsigned int remained_time = 0;
1651 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1653 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1654 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK);
1655 sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
1657 mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK);
1658 mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK);
1659 mmf_return_val_if_fail(sc->element, GST_PAD_PROBE_OK);
1661 videoinfo = sc->info_video;
1663 if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_PTS(buffer))) {
1664 _mmcam_dbg_err("Buffer timestamp is invalid, check it");
1665 return GST_PAD_PROBE_OK;
1668 rec_pipe_time = GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer));
1670 if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4)
1671 MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size);
1675 /* calculate remained time can be recorded */
1676 if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) {
1677 remained_time = videoinfo->max_time - rec_pipe_time;
1678 } else if (videoinfo->max_size > 0) {
1679 long double max_size = (long double)videoinfo->max_size;
1680 long double current_size = (long double)(videoinfo->filesize + trailer_size);
1682 remained_time = (unsigned long long)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time;
1685 if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) {
1686 _mmcam_dbg_warn("Time current [%" G_GUINT64_FORMAT "], Max [%" G_GUINT64_FORMAT "], motion rate [%lf]", \
1687 rec_pipe_time, videoinfo->max_time, videoinfo->record_motion_rate);
1689 if (!sc->isMaxtimePausing) {
1690 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE);
1692 sc->isMaxtimePausing = TRUE;
1694 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1695 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1696 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1697 msg.param.recording_status.remained_time = 0;
1698 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1700 msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT;
1701 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1704 return GST_PAD_PROBE_DROP;
1707 msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS;
1708 msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time;
1709 msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10);
1710 msg.param.recording_status.remained_time = remained_time;
1711 _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg);
1714 _mmcam_dbg_log("audio data probe :: time [%" GST_TIME_FORMAT "], size [%lld KB]",
1715 GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize);
1718 return GST_PAD_PROBE_OK;
1722 static GstPadProbeReturn __mmcamcorder_audio_dataprobe_audio_mute(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
1724 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
1725 double volume = 0.0;
1728 int err = MM_ERROR_UNKNOWN;
1729 char *err_name = NULL;
1730 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
1733 mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP);
1734 mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_DROP);
1736 /*_mmcam_dbg_log("AUDIO SRC time stamp : [%" GST_TIME_FORMAT "] \n", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
1737 err = mm_camcorder_get_attributes((MMHandleType)hcamcorder, &err_name,
1738 MMCAM_AUDIO_VOLUME, &volume,
1739 MMCAM_AUDIO_FORMAT, &format,
1740 MMCAM_AUDIO_CHANNEL, &channel,
1742 if (err != MM_ERROR_NONE) {
1743 _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, err);
1744 SAFE_FREE(err_name);
1748 memset(&mapinfo, 0x0, sizeof(GstMapInfo));
1750 gst_buffer_map(buffer, &mapinfo, GST_MAP_READWRITE);
1752 /* Set audio stream NULL */
1754 memset(mapinfo.data, 0, mapinfo.size);
1756 /* CALL audio stream callback */
1757 if (hcamcorder->astream_cb && buffer && mapinfo.data && mapinfo.size > 0) {
1758 MMCamcorderAudioStreamDataType stream;
1760 if (_mmcamcorder_get_state((MMHandleType)hcamcorder) < MM_CAMCORDER_STATE_PREPARE) {
1761 _mmcam_dbg_warn("Not ready for stream callback");
1762 gst_buffer_unmap(buffer, &mapinfo);
1763 return GST_PAD_PROBE_OK;
1766 /*_mmcam_dbg_log("Call video steramCb, data[%p], Width[%d],Height[%d], Format[%d]",
1767 GST_BUFFER_DATA(buffer), width, height, format);*/
1769 stream.data = (void *)mapinfo.data;
1770 stream.format = format;
1771 stream.channel = channel;
1772 stream.length = mapinfo.size;
1773 stream.timestamp = (unsigned int)(GST_BUFFER_PTS(buffer)/1000000); /* nano -> milli second */
1775 _MMCAMCORDER_LOCK_ASTREAM_CALLBACK(hcamcorder);
1777 if (hcamcorder->astream_cb)
1778 hcamcorder->astream_cb(&stream, hcamcorder->astream_cb_param);
1780 _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK(hcamcorder);
1783 gst_buffer_unmap(buffer, &mapinfo);
1784 return GST_PAD_PROBE_OK;
1788 static gboolean __mmcamcorder_add_metadata(MMHandleType handle, int fileformat)
1790 gboolean bret = FALSE;
1792 switch (fileformat) {
1793 case MM_FILE_FORMAT_3GP:
1794 case MM_FILE_FORMAT_MP4:
1795 bret = __mmcamcorder_add_metadata_mp4(handle);
1798 _mmcam_dbg_warn("Unsupported fileformat to insert location info (%d)", fileformat);
1806 static gboolean __mmcamcorder_add_metadata_mp4(MMHandleType handle)
1810 guint64 udta_size = 0;
1811 gint64 current_pos = 0;
1812 gint64 moov_pos = 0;
1813 gint64 udta_pos = 0;
1814 gdouble longitude = 0;
1815 gdouble latitude = 0;
1816 gdouble altitude = 0;
1818 int orientation = 0;
1820 char *err_name = NULL;
1821 char err_msg[MAX_ERROR_MESSAGE_LEN] = {'\0',};
1822 _MMCamcorderLocationInfo location_info = {0, 0, 0};
1823 _MMCamcorderLocationInfo geo_info = {0, 0, 0};
1825 _MMCamcorderVideoInfo *info = NULL;
1826 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1827 _MMCamcorderSubContext *sc = NULL;
1829 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1830 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1832 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1833 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1837 info = sc->info_video;
1839 f = fopen64(info->filename, "rb+");
1841 strerror_r(errno, err_msg, MAX_ERROR_MESSAGE_LEN);
1842 _mmcam_dbg_err("file open failed [%s]", err_msg);
1846 mm_camcorder_get_attributes(handle, &err_name,
1847 MMCAM_TAG_LATITUDE, &latitude,
1848 MMCAM_TAG_LONGITUDE, &longitude,
1849 MMCAM_TAG_ALTITUDE, &altitude,
1850 MMCAM_TAG_VIDEO_ORIENTATION, &orientation,
1851 MMCAM_TAG_GPS_ENABLE, &gps_enable,
1854 _mmcam_dbg_warn("Get tag attrs fail. (%s:%x)", err_name, err);
1855 SAFE_FREE(err_name);
1858 location_info.longitude = _mmcamcorder_double_to_fix(longitude);
1859 location_info.latitude = _mmcamcorder_double_to_fix(latitude);
1860 location_info.altitude = _mmcamcorder_double_to_fix(altitude);
1861 geo_info.longitude = longitude *10000;
1862 geo_info.latitude = latitude *10000;
1863 geo_info.altitude = altitude *10000;
1864 /* find udta container.
1865 if, there are udta container, write loci box after that
1866 else, make udta container and write loci box. */
1867 if (_mmcamcorder_find_fourcc(f, MMCAM_FOURCC('u', 'd', 't', 'a'), TRUE)) {
1870 _mmcam_dbg_log("find udta container");
1873 if (fseek(f, -8L, SEEK_CUR) != 0)
1876 udta_pos = ftello(f);
1880 nread = fread(&buf, sizeof(char), sizeof(buf), f);
1882 _mmcam_dbg_log("recorded file fread %zu", nread);
1884 udta_size = _mmcamcorder_get_container_size(buf);
1886 /* goto end of udta and write 'loci' box */
1887 if (fseek(f, (udta_size-4L), SEEK_CUR) != 0)
1891 if (!_mmcamcorder_write_loci(f, location_info)) {
1892 _mmcam_dbg_err("failed to write loci");
1896 if (!_mmcamcorder_write_geodata(f, geo_info)) {
1897 _mmcam_dbg_err("failed to write geodata");
1902 current_pos = ftello(f);
1903 if (current_pos < 0)
1906 if (!_mmcamcorder_update_size(f, udta_pos, current_pos))
1909 _mmcam_dbg_log("No udta container");
1910 if (fseek(f, 0, SEEK_END) != 0)
1913 if (!_mmcamcorder_write_udta(f, gps_enable, location_info, geo_info)) {
1914 _mmcam_dbg_err("failed to write udta");
1919 /* find moov container.
1920 update moov container size. */
1921 if ((current_pos = ftello(f)) < 0)
1924 if (_mmcamcorder_find_tag(f, MMCAM_FOURCC('m', 'o', 'o', 'v'), TRUE)) {
1925 gint64 internal_pos = ftello(f);
1927 _mmcam_dbg_log("found moov container");
1928 if (fseek(f, -8L, SEEK_CUR) != 0)
1931 moov_pos = ftello(f);
1935 if (!_mmcamcorder_update_size(f, moov_pos, current_pos))
1938 /* add orientation info */
1939 if (fseeko(f, internal_pos, SEEK_SET) < 0) {
1940 _mmcam_dbg_err("fseeko failed : errno %d", errno);
1944 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t', 'r', 'a', 'k'), FALSE)) {
1945 _mmcam_dbg_err("failed to find [trak] tag");
1949 if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t', 'k', 'h', 'd'), FALSE)) {
1950 _mmcam_dbg_err("failed to find [tkhd] tag");
1954 _mmcam_dbg_log("found [tkhd] tag");
1956 /* seek to start position of composition matrix */
1957 if (fseek(f, _OFFSET_COMPOSITION_MATRIX, SEEK_CUR) == 0) {
1958 /* update composition matrix for orientation */
1959 _mmcamcorder_update_composition_matrix(f, orientation);
1961 _mmcam_dbg_err("fseek failed : errno %d", errno);
1965 _mmcam_dbg_err("No 'moov' container");
1977 _mmcam_dbg_err("ftell() returns negative value.");
1983 int _mmcamcorder_connect_video_stream_cb_signal(MMHandleType handle)
1985 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
1986 _MMCamcorderSubContext *sc = NULL;
1988 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1990 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
1991 mmf_return_val_if_fail(sc && sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
1993 /* check video source element */
1994 if (sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst) {
1995 _mmcam_dbg_warn("connect video stream cb signal to _MMCAMCORDER_VIDEOSRC_SRC");
1996 MMCAMCORDER_SIGNAL_CONNECT(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst,
1997 _MMCAMCORDER_HANDLER_VIDEOREC, "video-stream-cb",
1998 G_CALLBACK(__mmcamcorder_video_stream_cb),
2000 return MM_ERROR_NONE;
2002 _mmcam_dbg_err("videosrc element is not created yet");
2003 return MM_ERROR_CAMCORDER_NOT_INITIALIZED;
2008 int _mmcamcorder_video_prepare_record(MMHandleType handle)
2010 int ret = MM_ERROR_NONE;
2012 char *temp_filename = NULL;
2014 _MMCamcorderVideoInfo *info = NULL;
2015 _MMCamcorderSubContext *sc = NULL;
2016 mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
2018 mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2020 sc = MMF_CAMCORDER_SUBCONTEXT(handle);
2021 mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2022 mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED);
2024 info = sc->info_video;
2026 _mmcam_dbg_warn("start");
2028 /* create encoding pipeline */
2029 ret = _mmcamcorder_create_recorder_pipeline((MMHandleType)hcamcorder);
2030 if (ret != MM_ERROR_NONE)
2031 goto _ERR_PREPARE_RECORD;
2033 SAFE_G_FREE(info->filename);
2035 mm_camcorder_get_attributes(handle, NULL,
2036 MMCAM_TARGET_FILENAME, &temp_filename, &size,
2038 if (temp_filename) {
2039 info->filename = g_strdup(temp_filename);
2040 if (!info->filename) {
2041 _mmcam_dbg_err("strdup[src:%p] was failed", temp_filename);
2042 goto _ERR_PREPARE_RECORD;
2045 _mmcam_dbg_log("Record file name [%s]", info->filename);
2046 MMCAMCORDER_G_OBJECT_SET_POINTER(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename);
2048 _mmcam_dbg_log("Recorded data will be written in [%s]", _MMCamcorder_FILENAME_NULL);
2049 MMCAMCORDER_G_OBJECT_SET_POINTER(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", _MMCamcorder_FILENAME_NULL);
2052 MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", 0);
2054 /* Adjust display FPS */
2055 sc->display_interval = 0;
2056 sc->previous_slot_time = 0;
2058 ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PAUSED);
2059 if (ret != MM_ERROR_NONE)
2060 goto _ERR_PREPARE_RECORD;
2062 _mmcam_dbg_warn("done");
2066 _ERR_PREPARE_RECORD:
2067 /* Remove recorder pipeline and recording file which size maybe zero */
2068 _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder);
2069 if (info && info->filename) {
2070 _mmcam_dbg_log("file delete(%s)", info->filename);
2071 unlink(info->filename);