X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fmm_camcorder_videorec.c;h=687da3041573105a22c9a73f91402e69a9eaecad;hb=HEAD;hp=0a04eab0208c416db147248c1563605437758ab7;hpb=9633a958271081da2a660fdc047c23904f40dc07;p=platform%2Fcore%2Fmultimedia%2Flibmm-camcorder.git diff --git a/src/mm_camcorder_videorec.c b/src/mm_camcorder_videorec.c index 0a04eab..32cb955 100644 --- a/src/mm_camcorder_videorec.c +++ b/src/mm_camcorder_videorec.c @@ -20,39 +20,37 @@ */ /*======================================================================================= -| INCLUDE FILES | +| INCLUDE FILES | =======================================================================================*/ -#include +#include +#include #include "mm_camcorder_internal.h" #include "mm_camcorder_videorec.h" /*--------------------------------------------------------------------------------------- -| GLOBAL VARIABLE DEFINITIONS for internal | +| LOCAL VARIABLE DEFINITIONS for internal | ---------------------------------------------------------------------------------------*/ -#define _MMCAMCORDER_LOCATION_INFO // for add gps information -#define MAX_ERROR_MESSAGE_LEN 128 +#define _MMCAMCORDER_MINIMUM_FRAME 5 +#define _MMCAMCORDER_RETRIAL_COUNT 15 +#define _MMCAMCORDER_FRAME_WAIT_TIME 200000 /* us */ +#define _MMCAMCORDER_FRAME_PASS_MIN_FPS 30 +#define _MMCAMCORDER_MIN_TIME_TO_PASS_FRAME 30000000 /* ns */ +#define _MMCAMCORDER_VIDEO_MINIMUM_SPACE (_MMCAMCORDER_MINIMUM_SPACE << 1) /* byte */ +#define OFFSET_COMPOSITION_MATRIX 40L +#define MAX_ERROR_MESSAGE_LEN 128 /*--------------------------------------------------------------------------------------- -| LOCAL VARIABLE DEFINITIONS for internal | ----------------------------------------------------------------------------------------*/ -#define _MMCAMCORDER_MINIMUM_FRAME 3 -#define _MMCAMCORDER_RETRIAL_COUNT 10 -#define _MMCAMCORDER_FRAME_WAIT_TIME 200000 /* ms */ -#define _MMCAMCORDER_FREE_SPACE_CHECK_INTERVAL 5 -#define _OFFSET_COMPOSITION_MATRIX 40L - -/*--------------------------------------------------------------------------------------- -| LOCAL FUNCTION PROTOTYPES: | +| LOCAL FUNCTION PROTOTYPES: | ---------------------------------------------------------------------------------------*/ /* STATIC INTERNAL FUNCTION */ -static gboolean __mmcamcorder_audio_dataprobe_check(GstPad *pad, GstBuffer *buffer, gpointer u_data); -static gboolean __mmcamcorder_video_dataprobe_record(GstPad *pad, GstBuffer *buffer, gpointer u_data); -static gboolean __mmcamcorder_audioque_dataprobe(GstPad *pad, GstBuffer *buffer, gpointer u_data); -static gboolean __mmcamcorder_video_dataprobe_audio_disable(GstPad *pad, GstBuffer *buffer, gpointer u_data); -static gboolean __mmcamcorder_audio_dataprobe_audio_mute(GstPad *pad, GstBuffer *buffer, gpointer u_data); -static gboolean __mmcamcorder_add_locationinfo(MMHandleType handle, int fileformat); -static gboolean __mmcamcorder_add_locationinfo_mp4(MMHandleType handle); -static gboolean __mmcamcorder_eventprobe_monitor(GstPad *pad, GstEvent *event, gpointer u_data); +static void __mmcamcorder_video_stream_cb(GstElement *element, GstSample *sample, gpointer u_data); +static GstPadProbeReturn __mmcamcorder_audio_dataprobe_check(GstPad *pad, GstPadProbeInfo *info, gpointer u_data); +static GstPadProbeReturn __mmcamcorder_video_dataprobe_encoded(GstPad *pad, GstPadProbeInfo *info, gpointer u_data); +static GstPadProbeReturn __mmcamcorder_audioque_dataprobe(GstPad *pad, GstPadProbeInfo *info, gpointer u_data); +static GstPadProbeReturn __mmcamcorder_video_dataprobe_audio_disable(GstPad *pad, GstPadProbeInfo *info, gpointer u_data); +static GstPadProbeReturn __mmcamcorder_audio_dataprobe_audio_mute(GstPad *pad, GstPadProbeInfo *info, gpointer u_data); +static gboolean __mmcamcorder_add_metadata(MMHandleType handle, int fileformat); +static gboolean __mmcamcorder_add_metadata_mp4(MMHandleType handle); /*======================================================================================= | FUNCTION DEFINITIONS | @@ -60,12 +58,146 @@ static gboolean __mmcamcorder_eventprobe_monitor(GstPad *pad, GstEvent *event, g /*--------------------------------------------------------------------------------------- | GLOBAL FUNCTION DEFINITIONS: | ---------------------------------------------------------------------------------------*/ -int _mmcamcorder_add_recorder_pipeline(MMHandleType handle) +gboolean _mmcamcorder_video_push_buffer(void *handle, GstSample *sample) { + mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle); + _MMCamcorderSubContext *sc = NULL; + _MMCamcorderImageInfo *info_image = NULL; + _MMCamcorderVideoInfo *info_video = NULL; + _MMCamcorderGstElement *element = NULL; + GstClockTime current_ts = 0; /* nsec */ + GstBuffer *buffer = NULL; + + mmf_return_val_if_fail(hcamcorder, FALSE); + mmf_return_val_if_fail(MMF_CAMCORDER_SUBCONTEXT(hcamcorder), FALSE); + + buffer = gst_sample_get_buffer(sample); + + mmf_return_val_if_fail(buffer, FALSE); + mmf_return_val_if_fail(gst_buffer_n_memory(buffer), FALSE); + + sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder); + + mmf_return_val_if_fail(sc->info_image, FALSE); + mmf_return_val_if_fail(sc->info_video, FALSE); + mmf_return_val_if_fail(sc->encode_element, FALSE); + + info_image = sc->info_image; + info_video = sc->info_video; + element = sc->encode_element; + + if (info_video->push_encoding_buffer == PUSH_ENCODING_BUFFER_RUN && + element[_MMCAMCORDER_ENCSINK_SRC].gst) { + int ret = 0; + GstClock *clock = NULL; + + MMCAM_LOG_VERBOSE("buffer[%p] - delta flag[%d]", + buffer, GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT)); + + current_ts = GST_BUFFER_PTS(buffer); + + if (info_video->is_first_frame) { + /* check first I frame for H.264 stream */ + if (_mmcamcorder_is_encoded_preview_pixel_format(info_image->preview_format)) { + if (!GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_HEADER)) { + MMCAM_LOG_WARNING("No header in buffer"); + return TRUE; + } + + MMCAM_LOG_WARNING("Got buffer with header"); + } + + /* set base timestamp */ + if (element[_MMCAMCORDER_AUDIOSRC_SRC].gst) { + clock = GST_ELEMENT_CLOCK(element[_MMCAMCORDER_AUDIOSRC_SRC].gst); + if (clock) { + gst_object_ref(clock); + info_video->base_video_ts = current_ts - (gst_clock_get_time(clock) - \ + GST_ELEMENT(element[_MMCAMCORDER_ENCSINK_SRC].gst)->base_time); + gst_object_unref(clock); + } + } else { + /* for image capture with encodebin and v4l2src */ + if (hcamcorder->capture_mode == MM_CAMCORDER_CAPTURE_MODE_ENCODEBIN && info_image->capturing) { + g_mutex_lock(&hcamcorder->task_thread_lock); + MMCAM_LOG_INFO("send signal for sound play"); + hcamcorder->task_thread_state = _MMCAMCORDER_TASK_THREAD_STATE_SOUND_SOLO_PLAY_START; + g_cond_signal(&hcamcorder->task_thread_cond); + g_mutex_unlock(&hcamcorder->task_thread_lock); + } + info_video->base_video_ts = current_ts; + } + } else { + if (_mmcamcorder_invoke_video_stream_cb(handle, sample, FALSE, -1) == FALSE) { + /* increase base video timestamp by frame duration, + it will remove delay of dropped buffer when play recorded file. */ + info_video->base_video_ts += current_ts - info_video->last_video_ts; + MMCAM_LOG_DEBUG("do not push buffer to encode by app's return value"); + goto _VIDEO_PUSH_BUFFER_DONE; + } + } + + GST_BUFFER_DTS(buffer) = GST_BUFFER_PTS(buffer) = current_ts - info_video->base_video_ts; + + MMCAM_LOG_DEBUG("buffer[%p] - timestamp[%"GST_TIME_FORMAT"]", + buffer, GST_TIME_ARGS(GST_BUFFER_PTS(buffer))); + + /* It will INCREASE reference count of buffer */ + g_signal_emit_by_name(element[_MMCAMCORDER_ENCSINK_SRC].gst, "push-buffer", buffer, &ret); + + MMCAM_LOG_VERBOSE("push buffer result : 0x%x", ret); + +_VIDEO_PUSH_BUFFER_DONE: + info_video->last_video_ts = current_ts; + + if (info_video->is_first_frame) { + info_video->is_first_frame = FALSE; + + /* drop buffer if it's from tizen allocator */ + if (gst_is_tizen_memory(gst_buffer_peek_memory(buffer, 0))) { + MMCAM_LOG_WARNING("drop first buffer from tizen allocator to avoid copy in basesrc"); + return FALSE; + } + } + } + + /* skip display if too fast FPS */ + if (info_video->record_dual_stream == FALSE && + info_video->fps > _MMCAMCORDER_FRAME_PASS_MIN_FPS) { + if (info_video->prev_preview_ts != 0) { + if (GST_BUFFER_PTS(buffer) - info_video->prev_preview_ts < _MMCAMCORDER_MIN_TIME_TO_PASS_FRAME) { + MMCAM_LOG_VERBOSE("it's too fast. drop frame..."); + return FALSE; + } + } + + MMCAM_LOG_VERBOSE("display buffer [%p]", buffer); + + info_video->prev_preview_ts = GST_BUFFER_PTS(buffer); + } + + return TRUE; +} + + +static void __mmcamcorder_video_stream_cb(GstElement *element, GstSample *sample, gpointer u_data) +{ + mmf_return_if_fail(sample); + + /* no need to check return value here */ + _mmcamcorder_video_push_buffer(u_data, sample); + + gst_sample_unref(sample); +} + + +int _mmcamcorder_create_recorder_pipeline(MMHandleType handle) +{ + int i = 0; int err = MM_ERROR_NONE; - int audio_disable = FALSE; - char* gst_element_rsink_name = NULL; + const char* gst_element_rsink_name = NULL; + GstBus *bus = NULL; GstPad *srcpad = NULL; GstPad *sinkpad = NULL; @@ -79,144 +211,185 @@ int _mmcamcorder_add_recorder_pipeline(MMHandleType handle) sc = MMF_CAMCORDER_SUBCONTEXT(handle); mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED); mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED); - - _mmcam_dbg_log(""); - err = _mmcamcorder_check_videocodec_fileformat_compatibility( handle ); - if( err != MM_ERROR_NONE ) - { + MMCAM_LOG_WARNING("start"); + + err = _mmcamcorder_check_videocodec_fileformat_compatibility(handle); + if (err != MM_ERROR_NONE) return err; - } - /* Create gstreamer element */ - /* Check main pipeline */ - if (!sc->element[_MMCAMCORDER_MAIN_PIPE].gst) { - err = MM_ERROR_CAMCORDER_RESOURCE_CREATION; - goto pipeline_creation_error; + /* Main pipeline */ + if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst) { + MMCAM_LOG_INFO("pipeline is exist so need to remove pipeline _MMCAMCORDER_ENCODE_MAIN_PIPE = %p", + sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst); + _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder); } + _MMCAMCORDER_PIPELINE_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCODE_MAIN_PIPE, "recorder_pipeline", err); + /* get audio disable */ mm_camcorder_get_attributes(handle, NULL, - MMCAM_AUDIO_DISABLE, &audio_disable, - NULL); + MMCAM_AUDIO_DISABLE, &sc->audio_disable, + NULL); + + MMCAM_LOG_INFO("MMCAM_AUDIO_DISABLE %d, is_modified_rate %d, ved_cb %p", + sc->audio_disable, sc->is_modified_rate, hcamcorder->vedecision_cb); - if (sc->is_modified_rate || audio_disable) { + if (sc->is_modified_rate || hcamcorder->vedecision_cb) { + MMCAM_LOG_INFO("audio disabled[prev:%d]", sc->audio_disable); sc->audio_disable = TRUE; - } else { - sc->audio_disable = FALSE; } - _mmcam_dbg_log("AUDIO DISABLE : %d (is_modified_rate %d, audio_disable %d)", - sc->audio_disable, sc->is_modified_rate, audio_disable); if (sc->audio_disable == FALSE) { - /* Sub pipeline */ - __ta__(" __mmcamcorder_create_audiosrc_bin", + /* create audiosrc bin */ err = _mmcamcorder_create_audiosrc_bin((MMHandleType)hcamcorder); - ); - if (err != MM_ERROR_NONE) { + if (err != MM_ERROR_NONE) return err; - } - - gst_bin_add(GST_BIN(sc->element[_MMCAMCORDER_MAIN_PIPE].gst), - sc->element[_MMCAMCORDER_AUDIOSRC_BIN].gst); } - __ta__(" _mmcamcorder_create_encodesink_bin", err = _mmcamcorder_create_encodesink_bin((MMHandleType)hcamcorder, MM_CAMCORDER_ENCBIN_PROFILE_VIDEO); - ); - if (err != MM_ERROR_NONE) { + if (err != MM_ERROR_NONE) return err; + + if (sc->audio_disable == FALSE) { + gst_bin_add(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), + sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst); } - gst_bin_add(GST_BIN(sc->element[_MMCAMCORDER_MAIN_PIPE].gst), - sc->element[_MMCAMCORDER_ENCSINK_BIN].gst); + /* add elements and encodesink bin to encode main pipeline */ + if (sc->info_video->use_videoscale) { + gst_bin_add_many(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), + sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, + sc->encode_element[_MMCAMCORDER_ENCSINK_CAPS].gst, + sc->encode_element[_MMCAMCORDER_ENCSINK_VSCALE].gst, + sc->encode_element[_MMCAMCORDER_ENCSINK_VSCALE_CAPS].gst, + sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst, + NULL); + } else { + gst_bin_add_many(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), + sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, + sc->encode_element[_MMCAMCORDER_ENCSINK_CAPS].gst, + sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst, + NULL); + } - /* Link each element */ - srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_VIDEOSRC_BIN].gst, "src1"); - sinkpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_BIN].gst, "video_sink0"); + /* Link each element : appsrc - capsfilter - encodesink bin */ + srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, "src"); + sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_CAPS].gst, "sink"); + _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error); + + srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_CAPS].gst, "src"); + if (sc->info_video->use_videoscale) { + sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_VSCALE].gst, "sink"); + _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error); + + srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_VSCALE].gst, "src"); + sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_VSCALE_CAPS].gst, "sink"); + _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error); + + srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_VSCALE_CAPS].gst, "src"); + } + sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst, "video_sink0"); _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error); if (sc->audio_disable == FALSE) { - srcpad = gst_element_get_static_pad (sc->element[_MMCAMCORDER_AUDIOSRC_BIN].gst, "src"); - sinkpad = gst_element_get_static_pad (sc->element[_MMCAMCORDER_ENCSINK_BIN].gst, "audio_sink0"); + srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst, "src"); + sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst, "audio_sink0"); _MM_GST_PAD_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error); } - _mmcamcorder_conf_get_element(hcamcorder->conf_main, - CONFIGURE_CATEGORY_MAIN_RECORD, - "RecordsinkElement", - &RecordsinkElement); + _mmcamcorder_conf_get_element(handle, hcamcorder->conf_main, + CONFIGURE_CATEGORY_MAIN_RECORD, + "RecordsinkElement", + &RecordsinkElement); _mmcamcorder_conf_get_value_element_name(RecordsinkElement, &gst_element_rsink_name); + if (!gst_element_rsink_name) { + MMCAM_LOG_ERROR("failed to get recordsink name"); + err = MM_ERROR_CAMCORDER_INTERNAL; + goto pipeline_creation_error; + } + /* set data probe function */ /* register message cb */ - /* set data probe function for audio */ - + /* set data probe functions */ if (sc->audio_disable == FALSE) { - sinkpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_AENC].gst, "sink"); + sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst, "sink"); MMCAMCORDER_ADD_BUFFER_PROBE(sinkpad, _MMCAMCORDER_HANDLER_VIDEOREC, - __mmcamcorder_audioque_dataprobe, hcamcorder); + __mmcamcorder_audioque_dataprobe, hcamcorder); gst_object_unref(sinkpad); - sinkpad = NULL; - + /* for voice mute */ - srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_AUDIOSRC_SRC].gst, "src"); + srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst, "src"); MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC, - __mmcamcorder_audio_dataprobe_audio_mute, hcamcorder); + __mmcamcorder_audio_dataprobe_audio_mute, hcamcorder); gst_object_unref(srcpad); - srcpad = NULL; - if (sc->element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst) { - srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst, "src"); + if (sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst) { + srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst, "src"); MMCAMCORDER_ADD_EVENT_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC, - __mmcamcorder_eventprobe_monitor, hcamcorder); + __mmcamcorder_eventprobe_monitor, hcamcorder); gst_object_unref(srcpad); - srcpad = NULL; } } - if (sc->element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst) { - srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst, "src"); + if (sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst) { + srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst, "src"); MMCAMCORDER_ADD_EVENT_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC, - __mmcamcorder_eventprobe_monitor, hcamcorder); + __mmcamcorder_eventprobe_monitor, hcamcorder); gst_object_unref(srcpad); - srcpad = NULL; } if (sc->audio_disable) { - sinkpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_VENC].gst, "sink"); + sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC].gst, "sink"); MMCAMCORDER_ADD_BUFFER_PROBE(sinkpad, _MMCAMCORDER_HANDLER_VIDEOREC, - __mmcamcorder_video_dataprobe_audio_disable, hcamcorder); + __mmcamcorder_video_dataprobe_audio_disable, hcamcorder); gst_object_unref(sinkpad); - sinkpad = NULL; } if (!strcmp(gst_element_rsink_name, "filesink")) { - srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_VENC].gst, "src"); + srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC].gst, "src"); MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC, - __mmcamcorder_video_dataprobe_record, hcamcorder); + __mmcamcorder_video_dataprobe_encoded, hcamcorder); gst_object_unref(srcpad); - srcpad = NULL; - srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_AENC].gst, "src"); - MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC, - __mmcamcorder_audio_dataprobe_check, hcamcorder); - gst_object_unref(srcpad); - srcpad = NULL; + if (sc->audio_disable == FALSE) { + srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst, "src"); + MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_VIDEOREC, + __mmcamcorder_audio_dataprobe_check, hcamcorder); + gst_object_unref(srcpad); + } } + sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "sink"); + MMCAMCORDER_ADD_BUFFER_PROBE(sinkpad, _MMCAMCORDER_HANDLER_VIDEOREC, + __mmcamcorder_muxed_dataprobe, hcamcorder); + MMCAMCORDER_ADD_EVENT_PROBE(sinkpad, _MMCAMCORDER_HANDLER_VIDEOREC, + __mmcamcorder_eventprobe_monitor, hcamcorder); + gst_object_unref(sinkpad); + + bus = gst_pipeline_get_bus(GST_PIPELINE(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst)); + + /* set sync handler */ + gst_bus_set_sync_handler(bus, _mmcamcorder_encode_pipeline_bus_sync_callback, (gpointer)hcamcorder, NULL); + + gst_object_unref(bus); + return MM_ERROR_NONE; pipeline_creation_error: + for (i = _MMCAMCORDER_AUDIOSRC_BIN ; i <= _MMCAMCORDER_ENCSINK_SINK ; i++) + _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, i); + + _MMCAMCORDER_ELEMENT_REMOVE(sc->encode_element, _MMCAMCORDER_ENCODE_MAIN_PIPE); return err; } int _mmcamcorder_remove_audio_pipeline(MMHandleType handle) { - int ret = MM_ERROR_NONE; GstPad *srcpad = NULL; GstPad *sinkpad = NULL; mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle); @@ -228,23 +401,16 @@ int _mmcamcorder_remove_audio_pipeline(MMHandleType handle) mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED); mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED); - _mmcam_dbg_log(""); - - if (sc->element[_MMCAMCORDER_AUDIOSRC_BIN].gst != NULL) { - __ta__( " AudiosrcBin Set NULL", - ret = _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_AUDIOSRC_BIN].gst, GST_STATE_NULL); - ); - if (ret != MM_ERROR_NONE) { - _mmcam_dbg_err("Faile to change audio source state[%d]", ret); - return ret; - } + MMCAM_LOG_INFO(""); - srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_AUDIOSRC_BIN].gst, "src"); - sinkpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_BIN].gst, "audio_sink0"); + if (sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst != NULL) { + srcpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst, "src"); + sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst, "audio_sink0"); _MM_GST_PAD_UNLINK_UNREF(srcpad, sinkpad); - gst_bin_remove(GST_BIN(sc->element[_MMCAMCORDER_MAIN_PIPE].gst), - sc->element[_MMCAMCORDER_AUDIOSRC_BIN].gst); + /* release audiosrc bin */ + gst_bin_remove(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), + sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst); /* To avoid conflicting between old elements and newly created elements, @@ -252,64 +418,47 @@ int _mmcamcorder_remove_audio_pipeline(MMHandleType handle) This is a typical problem of unref. Even though I unref bin here, it takes much time to finalize each elements. So I clean handles first, make them unref later. Audio recording, however, isn't needed this process. It's because the pipeline of audio recording destroys at the same time, - and '_mmcamcorder_element_release_noti' will perfom removing handle. + and '_mmcamcorder_element_release_noti' will perform removing handle. */ - _mmcamcorder_remove_element_handle(handle, _MMCAMCORDER_AUDIOSRC_BIN, _MMCAMCORDER_AUDIOSRC_NS); + _mmcamcorder_remove_element_handle(handle, (void *)sc->encode_element, _MMCAMCORDER_AUDIOSRC_BIN, _MMCAMCORDER_AUDIOSRC_VOL); - _mmcam_dbg_log("Audio pipeline removed"); + MMCAM_LOG_INFO("Audio pipeline removed"); } return MM_ERROR_NONE; } -int _mmcamcorder_remove_encoder_pipeline(MMHandleType handle) +int _mmcamcorder_remove_encode_pipeline(MMHandleType handle) { - int ret = MM_ERROR_NONE; - GstPad *srcpad = NULL; - GstPad *sinkpad = NULL; GstPad *reqpad = NULL; mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle); _MMCamcorderSubContext *sc = NULL; - + mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED); sc = MMF_CAMCORDER_SUBCONTEXT(handle); mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED); mmf_return_val_if_fail(sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED); - _mmcam_dbg_log(""); - - if (sc->element[_MMCAMCORDER_ENCSINK_BIN].gst != NULL) { - __ta__( " EncodeBin Set NULL", - ret = _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_ENCSINK_BIN].gst, GST_STATE_NULL); - ); - if (ret != MM_ERROR_NONE) { - _mmcam_dbg_err("Faile to change encode bin state[%d]", ret); - return ret; - } - - srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_VIDEOSRC_BIN].gst, "src1"); - sinkpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_BIN].gst, "video_sink0"); - _MM_GST_PAD_UNLINK_UNREF(srcpad, sinkpad); + MMCAM_LOG_INFO(""); + if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst != NULL) { /* release request pad */ - reqpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "audio"); + reqpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "audio"); if (reqpad) { - gst_element_release_request_pad(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, reqpad); + gst_element_release_request_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, reqpad); gst_object_unref(reqpad); - reqpad = NULL; } - reqpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "video"); + reqpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "video"); if (reqpad) { - gst_element_release_request_pad(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, reqpad); + gst_element_release_request_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, reqpad); gst_object_unref(reqpad); - reqpad = NULL; } - gst_bin_remove(GST_BIN(sc->element[_MMCAMCORDER_MAIN_PIPE].gst), - sc->element[_MMCAMCORDER_ENCSINK_BIN].gst); + /* release encode main pipeline */ + gst_object_unref(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst); /* To avoid conflicting between old elements and newly created elements, @@ -317,12 +466,12 @@ int _mmcamcorder_remove_encoder_pipeline(MMHandleType handle) This is a typical problem of unref. Even though I unref bin here, it takes much time to finalize each elements. So I clean handles first, make them unref later. Audio recording, however, isn't needed this process. It's because the pipeline of audio recording destroys at the same time, - and '_mmcamcorder_element_release_noti' will perfom removing handle. + and '_mmcamcorder_element_release_noti' will perform removing handle. */ - _mmcamcorder_remove_element_handle(handle, _MMCAMCORDER_AUDIOSRC_QUE, _MMCAMCORDER_AUDIOSRC_ENC); /* Encode bin has audio encoder too. */ - _mmcamcorder_remove_element_handle(handle, _MMCAMCORDER_ENCSINK_BIN, _MMCAMCORDER_ENCSINK_SINK); + /* _mmcamcorder_remove_element_handle(handle, (void *)sc->encode_element, + _MMCAMCORDER_ENCODE_MAIN_PIPE, _MMCAMCORDER_ENCSINK_SINK); */ - _mmcam_dbg_log("Encoder pipeline removed"); + MMCAM_LOG_WARNING("Encoder pipeline removed"); } return MM_ERROR_NONE; @@ -335,68 +484,55 @@ int _mmcamcorder_remove_recorder_pipeline(MMHandleType handle) mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle); _MMCamcorderSubContext *sc = NULL; + GstBus *bus = NULL; + mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED); sc = MMF_CAMCORDER_SUBCONTEXT(handle); mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED); - _mmcam_dbg_log(""); + MMCAM_LOG_INFO("start"); - if (!sc->element[_MMCAMCORDER_MAIN_PIPE].gst) { - _mmcam_dbg_warn("pipeline is not existed."); + if (!sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst) { + MMCAM_LOG_WARNING("pipeline is not existed."); return MM_ERROR_NONE; } _mmcamcorder_remove_all_handlers((MMHandleType)hcamcorder, _MMCAMCORDER_HANDLER_VIDEOREC); + ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_NULL); + if (ret != MM_ERROR_NONE) { + MMCAM_LOG_ERROR("Failed to change encode main pipeline [0x%x]", ret); + return ret; + } + + bus = gst_pipeline_get_bus(GST_PIPELINE(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst)); + /* remove audio pipeline first */ ret = _mmcamcorder_remove_audio_pipeline(handle); if (ret != MM_ERROR_NONE) { - _mmcam_dbg_err("Fail to remove audio pipeline"); + MMCAM_LOG_ERROR("Fail to remove audio pipeline"); return ret; } - ret = _mmcamcorder_remove_encoder_pipeline(handle); + ret = _mmcamcorder_remove_encode_pipeline(handle); if (ret != MM_ERROR_NONE) { - _mmcam_dbg_err("Fail to remove encoder pipeline"); + MMCAM_LOG_ERROR("Fail to remove encoder pipeline"); return ret; } - return ret; -} - - -void _mmcamcorder_destroy_video_pipeline(MMHandleType handle) -{ - mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle); - _MMCamcorderSubContext *sc = NULL; - GstPad *reqpad1 = NULL; - GstPad *reqpad2 = NULL; - - mmf_return_if_fail(hcamcorder); - sc = MMF_CAMCORDER_SUBCONTEXT(handle); - - mmf_return_if_fail(sc); - mmf_return_if_fail(sc->element); - - _mmcam_dbg_log(""); - - if (sc->element[_MMCAMCORDER_MAIN_PIPE].gst) { - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE); - _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_NULL); - - _mmcamcorder_remove_all_handlers((MMHandleType)hcamcorder, _MMCAMCORDER_HANDLER_CATEGORY_ALL); + /* Remove remained message */ + if (bus) { + GstMessage *gst_msg = NULL; + while ((gst_msg = gst_bus_pop(bus)) != NULL) { + _mmcamcorder_pipeline_cb_message(bus, gst_msg, (gpointer)hcamcorder); + gst_message_unref(gst_msg); + } + gst_object_unref(bus); + } - reqpad1 = gst_element_get_static_pad(sc->element[_MMCAMCORDER_VIDEOSRC_TEE].gst, "src0"); - reqpad2 = gst_element_get_static_pad(sc->element[_MMCAMCORDER_VIDEOSRC_TEE].gst, "src1"); - gst_element_release_request_pad(sc->element[_MMCAMCORDER_VIDEOSRC_TEE].gst, reqpad1); - gst_element_release_request_pad(sc->element[_MMCAMCORDER_VIDEOSRC_TEE].gst, reqpad2); - gst_object_unref(reqpad1); - gst_object_unref(reqpad2); + MMCAM_LOG_INFO("done"); - /* object disposing problem happen. */ - _mmcam_dbg_log("Reference count of pipeline(%d)", GST_OBJECT_REFCOUNT_VALUE(sc->element[_MMCAMCORDER_MAIN_PIPE].gst)); - gst_object_unref(sc->element[_MMCAMCORDER_MAIN_PIPE].gst); - } + return ret; } @@ -404,14 +540,16 @@ int _mmcamcorder_video_command(MMHandleType handle, int command) { int size = 0; int fileformat = 0; + int count = 0; + int gop_interval = 0; int ret = MM_ERROR_NONE; double motion_rate = _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE; - char *temp_filename = NULL; char *err_name = NULL; + char *target_filename = NULL; + GstCameraControl *CameraControl = NULL; gint fps = 0; GstElement *pipeline = NULL; - GstPad *pad = NULL; _MMCamcorderVideoInfo *info = NULL; _MMCamcorderSubContext *sc = NULL; @@ -426,7 +564,7 @@ int _mmcamcorder_video_command(MMHandleType handle, int command) info = sc->info_video; - _mmcam_dbg_log("Command(%d)", command); + MMCAM_LOG_INFO("Command(%d)", command); pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst; @@ -434,119 +572,218 @@ int _mmcamcorder_video_command(MMHandleType handle, int command) case _MMCamcorder_CMD_RECORD: { if (_mmcamcorder_get_state((MMHandleType)hcamcorder) != MM_CAMCORDER_STATE_PAUSED) { + /** + * start recording + */ + gboolean storage_validity = FALSE; guint imax_size = 0; guint imax_time = 0; - - /* Play record start sound */ - _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_FILEPATH_REC_START_SND, TRUE); + int root_directory_length = 0; /* Recording */ - _mmcam_dbg_log("Record Start"); + MMCAM_LOG_INFO("Record Start - dual stream %d", info->support_dual_stream); - /* set hybrid mode when ITLV format */ - if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ITLV_JPEG_UYVY) { - _mmcam_dbg_log("flush cache TRUE"); - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "flush-cache", TRUE); - } + /* init record_dual_stream */ + info->record_dual_stream = FALSE; - ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY); + ret = mm_camcorder_get_attributes(handle, &err_name, + MMCAM_CAMERA_FPS, &fps, + MMCAM_CAMERA_WIDTH, &(info->preview_width), + MMCAM_CAMERA_HEIGHT, &(info->preview_height), + MMCAM_VIDEO_WIDTH, &(info->video_width), + MMCAM_VIDEO_HEIGHT, &(info->video_height), + MMCAM_FILE_FORMAT, &fileformat, + MMCAM_TARGET_FILENAME, &target_filename, &size, + MMCAM_TARGET_MAX_SIZE, &imax_size, + MMCAM_TARGET_TIME_LIMIT, &imax_time, + MMCAM_FILE_FORMAT, &(info->fileformat), + MMCAM_CAMERA_RECORDING_MOTION_RATE, &motion_rate, + MMCAM_ROOT_DIRECTORY, &hcamcorder->root_directory, &root_directory_length, + NULL); if (ret != MM_ERROR_NONE) { + MMCAM_LOG_WARNING("Get attrs fail. (%s:%x)", err_name, ret); + SAFE_FREE(err_name); goto _ERR_CAMCORDER_VIDEO_COMMAND; } - _mmcamcorder_conf_get_value_int(hcamcorder->conf_main, - CONFIGURE_CATEGORY_MAIN_RECORD, - "DropVideoFrame", - &(sc->drop_vframe)); - - _mmcamcorder_conf_get_value_int(hcamcorder->conf_main, - CONFIGURE_CATEGORY_MAIN_RECORD, - "PassFirstVideoFrame", - &(sc->pass_first_vframe)); - - _mmcam_dbg_log("Drop video frame count[%d], Pass fisrt video frame count[%d]", - sc->drop_vframe, sc->pass_first_vframe); - - ret = mm_camcorder_get_attributes(handle, &err_name, - MMCAM_CAMERA_FPS, &fps, - MMCAM_CAMERA_RECORDING_MOTION_RATE, &motion_rate, - MMCAM_FILE_FORMAT, &fileformat, - MMCAM_TARGET_FILENAME, &temp_filename, &size, - MMCAM_TARGET_MAX_SIZE, &imax_size, - MMCAM_TARGET_TIME_LIMIT, &imax_time, - MMCAM_FILE_FORMAT, &(info->fileformat), - NULL); - if (ret != MM_ERROR_NONE) { - _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, ret); - SAFE_FREE (err_name); + if (!target_filename && !hcamcorder->mstream_cb) { + MMCAM_LOG_ERROR("filename is not set and muxed stream cb is NULL"); + ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT; goto _ERR_CAMCORDER_VIDEO_COMMAND; } /* set max size */ - if (imax_size <= 0) { + if (imax_size <= 0) info->max_size = 0; /* do not check */ - } else { + else info->max_size = ((guint64)imax_size) << 10; /* to byte */ - } /* set max time */ - if (imax_time <= 0) { + if (imax_time <= 0) info->max_time = 0; /* do not check */ - } else { - info->max_time = ((guint64)imax_time) * 1000; /* to millisecond */ + else + info->max_time = (guint64)((double)imax_time * (double)1000 * motion_rate); /* to millisecond */ + + ret = _mmcamcorder_get_storage_validity(hcamcorder, target_filename, + _MMCAMCORDER_VIDEO_MINIMUM_SPACE, &storage_validity); + if (ret != MM_ERROR_NONE) { + MMCAM_LOG_ERROR("storage validation failed[0x%x]:%d", ret, storage_validity); + return ret; } - if (sc->is_modified_rate) { - info->record_timestamp_ratio = (_MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE/motion_rate); - _mmcam_dbg_log("high speed recording fps:%d, slow_rate:%f, timestamp_ratio:%f", - fps, motion_rate, info->record_timestamp_ratio); + _mmcamcorder_adjust_recording_max_size(target_filename, &info->max_size); + + g_mutex_lock(&hcamcorder->task_thread_lock); + if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst == NULL && + hcamcorder->task_thread_state == _MMCAMCORDER_TASK_THREAD_STATE_NONE) { + /* Play record start sound */ + _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_SAMPLE_SOUND_NAME_REC_START, FALSE); + } + g_mutex_unlock(&hcamcorder->task_thread_lock); + + MMCAM_LOG_WARNING("video size [%dx%d]", info->video_width, info->video_height); + + if (info->video_width == 0 || info->video_height == 0) { + MMCAM_LOG_WARNING("video size is invalid [%dx%d] use preview size [%dx%d]", + info->video_width, info->video_height, info->preview_width, info->preview_height); + info->video_width = info->preview_width; + info->video_height = info->preview_height; + } + + if (info->support_dual_stream) { + MMCAM_LOG_WARNING("DUAL STREAM MODE"); + + info->record_dual_stream = TRUE; + + /* No need to restart preview */ + info->restart_preview = FALSE; + + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "video-width", info->video_width); + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "video-height", info->video_height); + } else if (_mmcamcorder_is_encoded_preview_pixel_format(sc->info_image->preview_format) && + info->preview_width == info->video_width && + info->preview_height == info->video_height) { + MMCAM_LOG_INFO("Encoded[%d] preview mode and same resolution", sc->info_image->preview_format); + /* No need to restart preview */ + info->restart_preview = FALSE; + } else if (info->use_videoscale && + info->preview_width >= info->video_width && + info->preview_height >= info->video_height) { + info->restart_preview = FALSE; } else { - info->record_timestamp_ratio = _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE; - _mmcam_dbg_log("normal recording"); + info->restart_preview = TRUE; + /* reset use_videoscale */ + info->use_videoscale = FALSE; } - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "hold-af-after-capturing", TRUE); - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "req-negotiation", TRUE); + /* set recording hint */ + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", TRUE); - ret =_mmcamcorder_add_recorder_pipeline((MMHandleType)hcamcorder); - if (ret != MM_ERROR_NONE) { - goto _ERR_CAMCORDER_VIDEO_COMMAND; + if (info->restart_preview) { + /* stop preview and set new size */ + MMCAM_LOG_INFO("restart preview"); + + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE); + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE); + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "keep-camera-preview", TRUE); + + ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY); + + /* check decoder recreation */ + if (!_mmcamcorder_recreate_decoder_for_encoded_preview(handle)) { + MMCAM_LOG_ERROR("_mmcamcorder_recreate_decoder_for_encoded_preview failed"); + ret = MM_ERROR_CAMCORDER_INTERNAL; + goto _ERR_CAMCORDER_VIDEO_COMMAND; + } + + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE); + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE); + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "keep-camera-preview", FALSE); + + if (ret != MM_ERROR_NONE) + goto _ERR_CAMCORDER_VIDEO_COMMAND; + + if (!_mmcamcorder_set_camera_resolution(handle, info->video_width, info->video_height)) { + ret = MM_ERROR_CAMCORDER_INTERNAL; + goto _ERR_CAMCORDER_VIDEO_COMMAND; + } + + /* Start preview again with new setting */ + ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING); + if (ret != MM_ERROR_NONE) + goto _ERR_CAMCORDER_VIDEO_COMMAND; + + if (motion_rate < 1.0) { + MMCAM_LOG_WARNING("wait for stabilization of frame"); + usleep(300000); + } + } else { + MMCAM_LOG_INFO("no need to restart preview"); } - info->filename = strdup(temp_filename); - if (!info->filename) { - _mmcam_dbg_err("strdup was failed"); - goto _ERR_CAMCORDER_VIDEO_COMMAND; + _mmcamcorder_conf_get_value_int(handle, hcamcorder->conf_main, + CONFIGURE_CATEGORY_MAIN_RECORD, + "DropVideoFrame", + &(sc->drop_vframe)); + + _mmcamcorder_conf_get_value_int(handle, hcamcorder->conf_main, + CONFIGURE_CATEGORY_MAIN_RECORD, + "PassFirstVideoFrame", + &(sc->pass_first_vframe)); + + MMCAM_LOG_INFO("Drop video frame count[%d], Pass first video frame count[%d]", + sc->drop_vframe, sc->pass_first_vframe); + + info->record_drop_count = (guint)motion_rate; + info->record_motion_rate = motion_rate; + if (sc->is_modified_rate) + info->record_timestamp_ratio = (_MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE/motion_rate); + else + info->record_timestamp_ratio = _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE; + + MMCAM_LOG_WARNING("recording fps %d, motion rate %f, timestamp_ratio %f", + fps, info->record_motion_rate, info->record_timestamp_ratio); + + /* set push buffer flag */ + info->push_encoding_buffer = PUSH_ENCODING_BUFFER_INIT; + info->base_video_ts = 0; + + /* connect video stream cb signal if it supports dual stream. */ + if (info->record_dual_stream) { + if (_mmcamcorder_connect_video_stream_cb_signal((MMHandleType)hcamcorder) != MM_ERROR_NONE) + goto _ERR_CAMCORDER_VIDEO_COMMAND; } - _mmcam_dbg_log("Record start : set file name using attribute - %s ",info->filename); + /* start video stream */ + if (info->record_dual_stream) { + CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst); + if (CameraControl) { + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE); - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename); - MMCAMCORDER_G_OBJECT_SET( sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", 0); + MMCAM_LOG_INFO("GST_CAMERA_CONTROL_RECORD_COMMAND_START"); + gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_START); - /* Adjust display FPS */ - sc->display_interval = 0; - sc->previous_slot_time = 0; + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE); + } else { + MMCAM_LOG_ERROR("could not get camera control"); + } + } - /* gst_element_set_base_time(GST_ELEMENT(pipeline), (GstClockTime)0); - if you want to use audio clock, enable this block - for change recorder_pipeline state to paused. */ - __ta__(" _MMCamcorder_CMD_RECORD:GST_STATE_PAUSED2", - ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PAUSED); - ); - if (ret != MM_ERROR_NONE) { - /* Remove recorder pipeline and recording file which size maybe zero */ - __ta__(" record fail:remove_recorder_pipeline", - _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder); - ); - if (info->filename) { - _mmcam_dbg_log("file delete(%s)", info->filename); - unlink(info->filename); - g_free(info->filename); - info->filename = NULL; + /* check pre-created encode pipeline */ + g_mutex_lock(&hcamcorder->task_thread_lock); + if (sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst == NULL && + hcamcorder->task_thread_state == _MMCAMCORDER_TASK_THREAD_STATE_NONE) { + /* create encoding pipeline */ + ret = _mmcamcorder_video_prepare_record((MMHandleType)hcamcorder); + if (ret != MM_ERROR_NONE) { + g_mutex_unlock(&hcamcorder->task_thread_lock); + goto _ERR_CAMCORDER_VIDEO_COMMAND; } - goto _ERR_CAMCORDER_VIDEO_COMMAND; } + g_mutex_unlock(&hcamcorder->task_thread_lock); + + /* check recording start sound */ + _mmcamcorder_sound_solo_play_wait(handle); /**< To fix video recording hanging 1. use gst_element_set_start_time() instead of gst_pipeline_set_new_stream_time() @@ -554,52 +791,75 @@ int _mmcamcorder_video_command(MMHandleType handle, int command) basetime wouldn't change if you set (GstClockTime)0. 3. Move set start time position below PAUSED of pipeline. */ - gst_element_set_start_time(GST_ELEMENT(pipeline), (GstClockTime)1); + /* + gst_element_set_start_time(GST_ELEMENT(sc->element[_MMCAMCORDER_MAIN_PIPE].gst), (GstClockTime)1); + gst_element_set_start_time(GST_ELEMENT(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), (GstClockTime)1); + */ + info->video_frame_count = 0; + info->is_first_frame = TRUE; info->audio_frame_count = 0; info->filesize = 0; sc->ferror_send = FALSE; sc->ferror_count = 0; - sc->error_occurs = FALSE; + hcamcorder->error_occurs = FALSE; sc->bget_eos = FALSE; + sc->muxed_stream_offset = 0; - __ta__(" _MMCamcorder_CMD_RECORD:GST_STATE_PLAYING2", - ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING); - ); + ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PLAYING); if (ret != MM_ERROR_NONE) { + /* stop video stream */ + if (info->record_dual_stream) { + CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst); + if (CameraControl) { + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE); + + MMCAM_LOG_INFO("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP"); + gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP); + + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE); + } else { + MMCAM_LOG_ERROR("failed to get camera control"); + } + } + /* Remove recorder pipeline and recording file which size maybe zero */ - __ta__(" record fail:remove_recorder_pipeline", _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder); - ); if (info->filename) { - _mmcam_dbg_log("file delete(%s)", info->filename); + MMCAM_LOG_INFO("file delete(%s)", info->filename); unlink(info->filename); - g_free(info->filename); - info->filename = NULL; } goto _ERR_CAMCORDER_VIDEO_COMMAND; } + + /*set the GOP so that video record will get a new key frame*/ + if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264) { + if (mm_camcorder_get_attributes(handle, NULL, + MMCAM_ENCODED_PREVIEW_GOP_INTERVAL, &gop_interval, NULL) == MM_ERROR_NONE) + _mmcamcorder_set_encoded_preview_gop_interval(handle, gop_interval); + else + MMCAM_LOG_ERROR("get gop interval failed"); + } } else { /* Resume case */ - int video_enc = 0; - - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", FALSE); - - mm_camcorder_get_attributes(handle, NULL, MMCAM_VIDEO_ENCODER, &video_enc, NULL); - if (video_enc == MM_VIDEO_CODEC_MPEG4) { - MMCAMCORDER_G_OBJECT_SET( sc->element[_MMCAMCORDER_ENCSINK_VENC].gst, "force-intra", TRUE); + if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264) { + if (mm_camcorder_get_attributes(handle, NULL, + MMCAM_ENCODED_PREVIEW_GOP_INTERVAL, &gop_interval, NULL) == MM_ERROR_NONE) + _mmcamcorder_set_encoded_preview_gop_interval(handle, gop_interval); + else + MMCAM_LOG_ERROR("get gop interval failed"); } - _mmcam_dbg_log("Object property settings done"); + MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", FALSE); + + MMCAM_LOG_INFO("Object property settings done"); } } break; case _MMCamcorder_CMD_PAUSE: { - int count = 0; - - if (info->b_commiting) { - _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command); + if (info->b_committing) { + MMCAM_LOG_WARNING("now on committing previous file!!(command : %d)", command); return MM_ERROR_CAMCORDER_CMD_IS_RUNNING; } @@ -609,12 +869,10 @@ int _mmcamcorder_video_command(MMHandleType handle, int command) if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) { break; } else if (count == _MMCAMCORDER_RETRIAL_COUNT) { - _mmcam_dbg_err("Pause fail, frame count %" G_GUINT64_FORMAT "", - info->video_frame_count); + MMCAM_LOG_ERROR("Pause fail, frame count %"G_GUINT64_FORMAT, info->video_frame_count); return MM_ERROR_CAMCORDER_INVALID_CONDITION; } else { - _mmcam_dbg_warn("Waiting for enough video frame, retrial[%d], frame %" G_GUINT64_FORMAT "", - count, info->video_frame_count); + MMCAM_LOG_WARNING("Waiting for enough video frame, retrial[%d], frame %"G_GUINT64_FORMAT, count, info->video_frame_count); } usleep(_MMCAMCORDER_FRAME_WAIT_TIME); @@ -623,50 +881,107 @@ int _mmcamcorder_video_command(MMHandleType handle, int command) if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) { break; } else if (count == _MMCAMCORDER_RETRIAL_COUNT) { - _mmcam_dbg_err("Pause fail, frame count VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]", - info->video_frame_count, info->audio_frame_count); + MMCAM_LOG_ERROR("Pause fail, frame count VIDEO[%"G_GUINT64_FORMAT"], AUDIO [%"G_GUINT64_FORMAT"]", + info->video_frame_count, info->audio_frame_count); return MM_ERROR_CAMCORDER_INVALID_CONDITION; } else { - _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]", - count, info->video_frame_count, info->audio_frame_count); + MMCAM_LOG_WARNING("Waiting for enough frames, retrial [%d], VIDEO[%"G_GUINT64_FORMAT"], AUDIO [%"G_GUINT64_FORMAT"]", + count, info->video_frame_count, info->audio_frame_count); } usleep(_MMCAMCORDER_FRAME_WAIT_TIME); } } - /* tee block */ - MMCAMCORDER_G_OBJECT_SET( sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", TRUE); + /* block encodebin */ + MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "runtime-pause", TRUE); break; } case _MMCamcorder_CMD_CANCEL: { - if (info->b_commiting) { - _mmcam_dbg_warn("now on commiting previous file!!(command : %d)", command); + if (info->b_committing) { + MMCAM_LOG_WARNING("now on committing previous file!!(command : %d)", command); return MM_ERROR_CAMCORDER_CMD_IS_RUNNING; } - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE); - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "hold-af-after-capturing", FALSE); + for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) { + /* capturing */ + if (hcamcorder->capture_in_recording == FALSE) { + break; + } else if (count == _MMCAMCORDER_RETRIAL_COUNT) { + MMCAM_LOG_ERROR("Failed to Wait capture data"); + hcamcorder->capture_in_recording = FALSE; + break; + } else { + MMCAM_LOG_WARNING("Waiting for capture data - retrial [%d]", count); + } - __ta__(" _MMCamcorder_CMD_CANCEL:GST_STATE_READY", - ret =_mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY); - ); - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE); - if (ret != MM_ERROR_NONE) { + usleep(_MMCAMCORDER_FRAME_WAIT_TIME); + } + + /* block push buffer */ + info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP; + + ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder); + if (ret != MM_ERROR_NONE) goto _ERR_CAMCORDER_VIDEO_COMMAND; + + /* set recording hint */ + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", FALSE); + + /* stop video stream */ + if (info->record_dual_stream) { + CameraControl = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst); + if (CameraControl) { + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE); + + MMCAM_LOG_INFO("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP"); + gst_camera_control_set_record_command(CameraControl, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP); + + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE); + } else { + MMCAM_LOG_ERROR("failed to get camera control"); + } } - __ta__(" __mmcamcorder_remove_recorder_pipeline", - _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder); - ); + if (info->restart_preview) { + /* restart preview */ + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE); + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE); + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "keep-camera-preview", TRUE); + + ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_READY); + + /* check decoder recreation */ + if (!_mmcamcorder_recreate_decoder_for_encoded_preview(handle)) { + MMCAM_LOG_ERROR("_mmcamcorder_recreate_decoder_for_encoded_preview failed"); + ret = MM_ERROR_CAMCORDER_INTERNAL; + } + + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE); + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE); + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "keep-camera-preview", FALSE); + + if (ret != MM_ERROR_NONE) + goto _ERR_CAMCORDER_VIDEO_COMMAND; + + /* reset restart_preview for inset window layout */ + info->restart_preview = FALSE; + + if (!_mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height)) { + ret = MM_ERROR_CAMCORDER_INTERNAL; + goto _ERR_CAMCORDER_VIDEO_COMMAND; + } + + ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING); + if (ret != MM_ERROR_NONE) + goto _ERR_CAMCORDER_VIDEO_COMMAND; + } /* remove target file */ if (info->filename) { - _mmcam_dbg_log("file delete(%s)", info->filename); + MMCAM_LOG_INFO("file delete(%s)", info->filename); unlink(info->filename); - g_free(info->filename); - info->filename = NULL; } sc->isMaxsizePausing = FALSE; @@ -676,117 +991,114 @@ int _mmcamcorder_video_command(MMHandleType handle, int command) sc->previous_slot_time = 0; info->video_frame_count = 0; info->audio_frame_count = 0; - info->filesize =0; - - /* set flush cache as FALSE */ - if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ITLV_JPEG_UYVY) { - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "flush-cache", FALSE); - } - - __ta__(" _MMCamcorder_CMD_CANCEL:GST_STATE_PLAYING", - ret =_mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING); - ); - if (ret != MM_ERROR_NONE) { - goto _ERR_CAMCORDER_VIDEO_COMMAND; - } + info->filesize = 0; + hcamcorder->capture_in_recording = FALSE; break; } case _MMCamcorder_CMD_COMMIT: { - int count = 0; + guint64 free_space; - if (info->b_commiting) { - _mmcam_dbg_err("now on commiting previous file!!(command : %d)", command); + if (info->b_committing) { + MMCAM_LOG_ERROR("now on committing previous file!!(command : %d)", command); return MM_ERROR_CAMCORDER_CMD_IS_RUNNING; } else { - _mmcam_dbg_log("_MMCamcorder_CMD_COMMIT : start"); - info->b_commiting = TRUE; + MMCAM_LOG_INFO("_MMCamcorder_CMD_COMMIT : start"); + info->b_committing = TRUE; + sc->bget_eos = FALSE; } for (count = 0 ; count <= _MMCAMCORDER_RETRIAL_COUNT ; count++) { if (sc->audio_disable) { /* check only video frame */ - if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) { + if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && + hcamcorder->capture_in_recording == FALSE) { break; } else if (count == _MMCAMCORDER_RETRIAL_COUNT) { - _mmcam_dbg_err("Commit fail, frame count is %" G_GUINT64_FORMAT "", - info->video_frame_count); - info->b_commiting = FALSE; - return MM_ERROR_CAMCORDER_INVALID_CONDITION; + MMCAM_LOG_ERROR("Commit fail, frame count is %"G_GUINT64_FORMAT", capturing %d", + info->video_frame_count, hcamcorder->capture_in_recording); + + if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME) { + MMCAM_LOG_WARNING("video frames are enough. keep going..."); + } else { + info->b_committing = FALSE; + return MM_ERROR_CAMCORDER_INVALID_CONDITION; + } } else { - _mmcam_dbg_warn("Waiting for enough video frame, retrial [%d], frame %" G_GUINT64_FORMAT "", - count, info->video_frame_count); + MMCAM_LOG_WARNING("Waiting for enough video frame, retrial [%d], frame %"G_GUINT64_FORMAT", capturing %d", + count, info->video_frame_count, hcamcorder->capture_in_recording); } - - usleep(_MMCAMCORDER_FRAME_WAIT_TIME); } else { /* check both of video and audio frame */ - if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) { + if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && + info->audio_frame_count && + hcamcorder->capture_in_recording == FALSE) { break; } else if (count == _MMCAMCORDER_RETRIAL_COUNT) { - _mmcam_dbg_err("Commit fail, VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]", - info->video_frame_count, info->audio_frame_count); + MMCAM_LOG_ERROR("Commit fail, VIDEO[%"G_GUINT64_FORMAT"], AUDIO [%"G_GUINT64_FORMAT"], capturing %d", + info->video_frame_count, info->audio_frame_count, hcamcorder->capture_in_recording); + + if (info->video_frame_count >= _MMCAMCORDER_MINIMUM_FRAME && info->audio_frame_count) { + MMCAM_LOG_WARNING("video/audio frames are enough. keep going..."); + } else { + info->b_committing = FALSE; + return MM_ERROR_CAMCORDER_INVALID_CONDITION; + } - info->b_commiting = FALSE; return MM_ERROR_CAMCORDER_INVALID_CONDITION; } else { - _mmcam_dbg_warn("Waiting for enough frames, retrial [%d], VIDEO[%" G_GUINT64_FORMAT "], AUDIO [%" G_GUINT64_FORMAT "]", - count, info->video_frame_count, info->audio_frame_count); + MMCAM_LOG_WARNING("Waiting for enough frames, retrial [%d], VIDEO[%"G_GUINT64_FORMAT"], AUDIO [%"G_GUINT64_FORMAT"], capturing %d", + count, info->video_frame_count, info->audio_frame_count, hcamcorder->capture_in_recording); } + } + if (hcamcorder->capture_in_recording) { + gint64 end_time = g_get_monotonic_time() + (200 * G_TIME_SPAN_MILLISECOND); + if (!_MMCAMCORDER_CMD_WAIT_UNTIL(handle, end_time)) + MMCAM_LOG_WARNING("timeout"); + } else { usleep(_MMCAMCORDER_FRAME_WAIT_TIME); } } - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "hold-af-after-capturing", FALSE); - - if (sc->error_occurs) { - GstPad *video= NULL; - GstPad *audio = NULL; - int ret = 0; - - _mmcam_dbg_err("Committing Error case"); - - video = gst_element_get_static_pad(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "sink"); - ret = gst_pad_send_event (video, gst_event_new_eos()); - _mmcam_dbg_err("Sending EOS video sink : %d", ret); - gst_object_unref(video); - - video = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_VENC].gst, "src"); - gst_pad_push_event (video, gst_event_new_flush_start()); - gst_pad_push_event (video, gst_event_new_flush_stop()); - ret = gst_pad_push_event (video, gst_event_new_eos()); - _mmcam_dbg_err("Sending EOS video encoder src pad : %d", ret); - gst_object_unref(video); - - if (sc->audio_disable == FALSE) { - audio = gst_element_get_static_pad(sc->element[_MMCAMCORDER_ENCSINK_AENC].gst, "src"); - gst_pad_push_event (audio, gst_event_new_flush_start()); - gst_pad_push_event (audio, gst_event_new_flush_stop()); - ret = gst_element_send_event(sc->element[_MMCAMCORDER_AUDIOSRC_SRC].gst, gst_event_new_eos()); - _mmcam_dbg_err("Sending EOS audio encoder src pad : %d", ret); - gst_object_unref(audio); - } - } else { - if (sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst != NULL) { - ret = gst_element_send_event(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, gst_event_new_eos()); - _mmcam_dbg_warn("send eos to videosrc result : %d", ret); - } + /* block push buffer */ + info->push_encoding_buffer = PUSH_ENCODING_BUFFER_STOP; + MMCAM_LOG_INFO("block push buffer to appsrc"); - if (sc->element[_MMCAMCORDER_AUDIOSRC_SRC].gst != NULL) { - pad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_AUDIOSRC_SRC].gst, "src"); - ret = gst_element_send_event(sc->element[_MMCAMCORDER_AUDIOSRC_SRC].gst, gst_event_new_eos()); - gst_object_unref(pad); - pad = NULL; + _mmcamcorder_get_freespace(hcamcorder->storage_info.type, &free_space); + if (free_space < _MMCAMCORDER_MINIMUM_SPACE) { + MMCAM_LOG_WARNING("_MMCamcorder_CMD_COMMIT out of storage [%" G_GUINT64_FORMAT "]", free_space); + ret = MM_ERROR_OUT_OF_STORAGE; + goto _ERR_CAMCORDER_VIDEO_COMMAND; + } - _mmcam_dbg_warn("send eos to audiosrc result : %d", ret); + if (sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst != NULL) { + if (gst_element_send_event(sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, gst_event_new_eos())) { + MMCAM_LOG_WARNING("VIDEO: send eos to appsrc done"); + } else { + MMCAM_LOG_ERROR("VIDEO: send EOS failed"); + info->b_committing = FALSE; + ret = MM_ERROR_CAMCORDER_INTERNAL; + goto _ERR_CAMCORDER_VIDEO_COMMAND; } + } else { + MMCAM_LOG_ERROR("No video stream source"); + info->b_committing = FALSE; + ret = MM_ERROR_CAMCORDER_INTERNAL; + goto _ERR_CAMCORDER_VIDEO_COMMAND; } - if (hcamcorder->quick_device_close) { - _mmcam_dbg_warn("quick_device_close"); - /* close device quickly */ - MMCAMCORDER_G_OBJECT_SET( sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "quick-device-close",TRUE); + if (sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst != NULL) { + if (gst_element_send_event(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst, gst_event_new_eos())) { + MMCAM_LOG_WARNING("AUDIO: send eos to audiosrc done"); + } else { + MMCAM_LOG_ERROR("AUDIO: send EOS failed"); + info->b_committing = FALSE; + ret = MM_ERROR_CAMCORDER_INTERNAL; + goto _ERR_CAMCORDER_VIDEO_COMMAND; + } + } else { + MMCAM_LOG_INFO("No audio stream"); } /* sc */ @@ -794,18 +1106,15 @@ int _mmcamcorder_video_command(MMHandleType handle, int command) sc->previous_slot_time = 0; /* Wait EOS */ - _mmcam_dbg_log("Start to wait EOS"); - ret =_mmcamcorder_get_eos_message(handle); - - /* set flush cache as FALSE */ - if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ITLV_JPEG_UYVY) { - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "flush-cache", FALSE); - } - + MMCAM_LOG_INFO("Start to wait EOS"); + ret = _mmcamcorder_get_eos_message(handle); if (ret != MM_ERROR_NONE) { - info->b_commiting = FALSE; + info->b_committing = FALSE; goto _ERR_CAMCORDER_VIDEO_COMMAND; } + + /* reset flag */ + hcamcorder->capture_in_recording = FALSE; } break; default: @@ -816,11 +1125,8 @@ int _mmcamcorder_video_command(MMHandleType handle, int command) return MM_ERROR_NONE; _ERR_CAMCORDER_VIDEO_COMMAND: - if (ret != MM_ERROR_NONE && sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst != NULL) { - int op_status = 0; - MMCAMCORDER_G_OBJECT_GET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "operation-status", &op_status); - _mmcam_dbg_err("Current Videosrc status[0x%x]", op_status); - } + if (command == _MMCamcorder_CMD_RECORD) + _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder); return ret; } @@ -832,9 +1138,6 @@ int _mmcamcorder_video_handle_eos(MMHandleType handle) int enabletag = 0; guint64 file_size = 0; - GstPad *pad = NULL; - GstElement *pipeline = NULL; - mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle); _MMCamcorderSubContext *sc = NULL; _MMCamcorderVideoInfo *info = NULL; @@ -849,214 +1152,229 @@ int _mmcamcorder_video_handle_eos(MMHandleType handle) info = sc->info_video; - _mmcam_dbg_err(""); + MMCAM_LOG_ERROR(""); - MMTA_ACUM_ITEM_BEGIN(" _mmcamcorder_video_handle_eos", 0); - - pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst; + /* Play record stop sound */ + _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_SAMPLE_SOUND_NAME_REC_STOP, FALSE); /* remove blocking part */ - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE); - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE); + MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", FALSE); mm_camcorder_get_attributes(handle, NULL, - MMCAM_TAG_ENABLE, &enabletag, - NULL); + MMCAM_RECORDER_TAG_ENABLE, &enabletag, + NULL); - _mmcam_dbg_log("Set state of pipeline as PAUSED"); - __ta__(" _MMCamcorder_CMD_COMMIT:GST_STATE_PAUSED", - ret = _mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PAUSED); - ); - if (ret != MM_ERROR_NONE) { - _mmcam_dbg_warn("_MMCamcorder_CMD_COMMIT:GST_STATE_READY or PAUSED failed. error[%x]", ret); - } + ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder); + if (ret != MM_ERROR_NONE) + MMCAM_LOG_WARNING("_MMCamcorder_CMD_COMMIT:__mmcamcorder_remove_recorder_pipeline failed. error[%x]", ret); - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE); + /* set recording hint */ + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "recording-hint", FALSE); - __ta__(" _MMCamcorder_CMD_COMMIT:__mmcamcorder_remove_recorder_pipeline", - ret = _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder); - ); - if (ret != MM_ERROR_NONE) { - _mmcam_dbg_warn("_MMCamcorder_CMD_COMMIT:__mmcamcorder_remove_recorder_pipeline failed. error[%x]", ret); - } + /* stop video stream */ + if (info->record_dual_stream) { + GstCameraControl *control = GST_CAMERA_CONTROL(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst); + if (control) { + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", TRUE); - if (enabletag && !(sc->ferror_send)) { - __ta__( " _MMCamcorder_CMD_COMMIT:__mmcamcorder_add_locationinfo", - ret = __mmcamcorder_add_locationinfo((MMHandleType)hcamcorder, info->fileformat); - ); - if (ret) { - _mmcam_dbg_log("Writing location information SUCCEEDED !!"); + MMCAM_LOG_INFO("GST_CAMERA_CONTROL_RECORD_COMMAND_STOP"); + gst_camera_control_set_record_command(control, GST_CAMERA_CONTROL_RECORD_COMMAND_STOP); + + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "stop-video", FALSE); } else { - _mmcam_dbg_err("Writing location information FAILED !!"); + MMCAM_LOG_ERROR("failed to get camera control"); } } + if (enabletag && !(sc->ferror_send)) { + ret = __mmcamcorder_add_metadata((MMHandleType)hcamcorder, info->fileformat); + MMCAM_LOG_INFO("Writing location information [%s] !!", ret ? "SUCCEEDED" : "FAILED"); + } + /* Check file size */ if (info->max_size > 0) { _mmcamcorder_get_file_size(info->filename, &file_size); - _mmcam_dbg_log("MAX size %lld byte - created filesize %lld byte", - info->max_size, file_size); + MMCAM_LOG_INFO("MAX size %"G_GUINT64_FORMAT" byte - created filesize %"G_GUINT64_FORMAT" byte", + info->max_size, file_size); if (file_size > info->max_size) { _MMCamcorderMsgItem message; - _mmcam_dbg_err("File size is greater than max size !!"); + MMCAM_LOG_ERROR("File size is greater than max size !!"); message.id = MM_MESSAGE_CAMCORDER_ERROR; message.param.code = MM_ERROR_CAMCORDER_FILE_SIZE_OVER; - _mmcamcroder_send_message((MMHandleType)hcamcorder, &message); + _mmcamcorder_send_message((MMHandleType)hcamcorder, &message); } } - /* Flush EOS event to avoid pending pipeline */ - _mmcam_dbg_log("Flush EOS event"); - pad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "sink"); - gst_pad_push_event(pad, gst_event_new_flush_start()); - gst_pad_push_event(pad, gst_event_new_flush_stop()); - gst_object_unref(pad); - pad = NULL; - - pad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "src"); - gst_pad_push_event(pad, gst_event_new_flush_start()); - gst_pad_push_event(pad, gst_event_new_flush_stop()); - gst_object_unref(pad); - pad = NULL; - - _mmcam_dbg_log("Set state as PLAYING"); - __ta__(" _MMCamcorder_CMD_COMMIT:GST_STATE_PLAYING", - ret =_mmcamcorder_gst_set_state(handle, pipeline, GST_STATE_PLAYING); - ); - /* Do not return when error is occurred. - Recording file was created successfully, but starting pipeline failed */ - if (ret != MM_ERROR_NONE) { - msg.id = MM_MESSAGE_CAMCORDER_ERROR; - msg.param.code = ret; - _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg); - _mmcam_dbg_err("Failed to set state PLAYING[%x]", ret); - } + if (info->restart_preview) { + /* block queue */ + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", TRUE); + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", TRUE); + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "keep-camera-preview", TRUE); + + MMCAM_LOG_INFO("Set state of pipeline as READY"); + ret = _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_READY); + + /* check decoder recreation */ + if (!_mmcamcorder_recreate_decoder_for_encoded_preview(handle)) { + MMCAM_LOG_ERROR("_mmcamcorder_recreate_decoder_for_encoded_preview failed"); + ret = MM_ERROR_CAMCORDER_INTERNAL; + } + + /* unblock queue */ + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "empty-buffers", FALSE); + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "empty-buffers", FALSE); + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSINK_SINK].gst, "keep-camera-preview", FALSE); + + if (ret != MM_ERROR_NONE) { + msg.id = MM_MESSAGE_CAMCORDER_ERROR; + msg.param.code = ret; + _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg); + MMCAM_LOG_ERROR("Failed to set state READY[%x]", ret); + } - if (hcamcorder->state_change_by_system != _MMCAMCORDER_STATE_CHANGE_BY_ASM) { - /* Play record stop sound */ - __ta__(" _MMCamcorder_CMD_COMMIT:_mmcamcorder_sound_solo_play", - _mmcamcorder_sound_solo_play(handle, _MMCAMCORDER_FILEPATH_REC_STOP_SND, TRUE); - ); + /* reset restart_preview for inset window layout */ + info->restart_preview = FALSE; + + /* recover preview size */ + if (!_mmcamcorder_set_camera_resolution(handle, info->preview_width, info->preview_height)) { + msg.id = MM_MESSAGE_CAMCORDER_ERROR; + msg.param.code = MM_ERROR_CAMCORDER_INTERNAL; + _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg); + MMCAM_LOG_ERROR("Failed to set camera resolution %dx%d", + info->preview_width, info->preview_height); + } + + ret = _mmcamcorder_gst_set_state(handle, sc->element[_MMCAMCORDER_MAIN_PIPE].gst, GST_STATE_PLAYING); + /* Do not return when error is occurred. + Recording file was created successfully, but starting pipeline failed */ + if (ret != MM_ERROR_NONE) { + msg.id = MM_MESSAGE_CAMCORDER_ERROR; + msg.param.code = ret; + _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg); + MMCAM_LOG_ERROR("Failed to set state PLAYING[%x]", ret); + } } else { - _mmcam_dbg_log("Do NOT play recording stop sound because of ASM stop"); + MMCAM_LOG_INFO("No need to restart preview"); } /* Send recording report to application */ msg.id = MM_MESSAGE_CAMCORDER_VIDEO_CAPTURED; - report = (MMCamRecordingReport *)malloc(sizeof(MMCamRecordingReport)); - if (!report) { - _mmcam_dbg_err("Recording report fail(%s). Out of memory.", info->filename); - } else { - report->recording_filename = strdup(info->filename); - msg.param.data= report; - msg.param.code = 1; - _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg); - } + report = (MMCamRecordingReport *)g_malloc(sizeof(MMCamRecordingReport)); + report->recording_filename = g_strdup(info->filename); + msg.param.data = report; + msg.param.code = 1; + _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg); /* Finishing */ sc->pipeline_time = 0; sc->pause_time = 0; sc->isMaxsizePausing = FALSE; /*In async function, this variable should set in callback function. */ sc->isMaxtimePausing = FALSE; - sc->error_occurs = FALSE; + hcamcorder->error_occurs = FALSE; info->video_frame_count = 0; info->audio_frame_count = 0; info->filesize = 0; - g_free(info->filename); - info->filename = NULL; - info->b_commiting = FALSE; + info->b_committing = FALSE; - MMTA_ACUM_ITEM_END(" _mmcamcorder_video_handle_eos", 0); - MMTA_ACUM_ITEM_END("Real Commit Time", 0); + /* check recording stop sound */ + _mmcamcorder_sound_solo_play_wait(handle); - _mmcam_dbg_err("_MMCamcorder_CMD_COMMIT : end"); + MMCAM_LOG_ERROR("_MMCamcorder_CMD_COMMIT : end"); return TRUE; } -/** - * This function is record video data probing function. - * If this function is linked with certain pad by gst_pad_add_buffer_probe(), - * this function will be called when data stream pass through the pad. - * - * @param[in] pad probing pad which calls this function. - * @param[in] buffer buffer which contains stream data. - * @param[in] u_data user data. - * @return This function returns true on success, or false value with error - * @remarks - * @see - */ -static gboolean __mmcamcorder_eventprobe_monitor(GstPad *pad, GstEvent *event, gpointer u_data) -{ - switch (GST_EVENT_TYPE(event)) { - case GST_EVENT_UNKNOWN: - /* upstream events */ - case GST_EVENT_QOS: - case GST_EVENT_SEEK: - case GST_EVENT_NAVIGATION: - case GST_EVENT_LATENCY: - /* downstream serialized events */ - case GST_EVENT_NEWSEGMENT : - case GST_EVENT_TAG: - case GST_EVENT_BUFFERSIZE: - _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event)); - break; - case GST_EVENT_EOS: - _mmcam_dbg_warn("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event)); - break; - /* bidirectional events */ - case GST_EVENT_FLUSH_START: - case GST_EVENT_FLUSH_STOP: - _mmcam_dbg_err("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event)); - break; - default: - _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event)); - break; - } - - return TRUE; -} - - -static gboolean __mmcamcorder_audio_dataprobe_check(GstPad *pad, GstBuffer *buffer, gpointer u_data) +static GstPadProbeReturn __mmcamcorder_audio_dataprobe_check(GstPad *pad, GstPadProbeInfo *info, gpointer u_data) { mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data); + GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info); + GstMapInfo mapinfo; _MMCamcorderSubContext *sc = NULL; - _MMCamcorderVideoInfo *info = NULL; + _MMCamcorderVideoInfo *videoinfo = NULL; + _MMCamcorderMsgItem msg; + guint64 buffer_size = 0; + guint64 trailer_size = 0; + guint64 max_size = 0; - mmf_return_val_if_fail(hcamcorder, TRUE); - mmf_return_val_if_fail(buffer, FALSE); + mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK); + mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP); sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder); - mmf_return_val_if_fail(sc && sc->info_video, TRUE); - info = sc->info_video; + mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK); + videoinfo = sc->info_video; + + /* get buffer size */ + if (!gst_buffer_map(buffer, &mapinfo, GST_MAP_READ)) { + MMCAM_LOG_WARNING("map failed : buffer %p", buffer); + return GST_PAD_PROBE_OK; + } + + buffer_size = mapinfo.size; + gst_buffer_unmap(buffer, &mapinfo); - /*_mmcam_dbg_err("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));*/ + g_mutex_lock(&videoinfo->size_check_lock); - if (info->audio_frame_count == 0) { - info->filesize += (guint64)GST_BUFFER_SIZE(buffer); - info->audio_frame_count++; - return TRUE; + if (videoinfo->audio_frame_count == 0) { + videoinfo->filesize += buffer_size; + videoinfo->audio_frame_count++; + g_mutex_unlock(&videoinfo->size_check_lock); + return GST_PAD_PROBE_OK; } if (sc->ferror_send || sc->isMaxsizePausing) { - _mmcam_dbg_warn("Recording is paused, drop frames"); + MMCAM_LOG_WARNING("Recording is paused, drop frames"); + g_mutex_unlock(&videoinfo->size_check_lock); + return GST_PAD_PROBE_DROP; + } + + /* get trailer size */ + if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) + MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size); + else + trailer_size = 0; + + /* check max size of recorded file */ + max_size = videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE; + if (videoinfo->max_size > 0 && videoinfo->max_size < max_size) { + GstState pipeline_state = GST_STATE_VOID_PENDING; + GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst; + + MMCAM_LOG_WARNING("Max size[%"G_GUINT64_FORMAT"], current size[%"G_GUINT64_FORMAT"],"\ + " buffer size[%"G_GUINT64_FORMAT"], trailer size[%"G_GUINT64_FORMAT"]", + videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size); + + if (!sc->isMaxsizePausing) { + sc->isMaxsizePausing = TRUE; + gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ; + if (pipeline_state == GST_STATE_PLAYING) + MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE); + + msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE; + _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg); + } + + g_mutex_unlock(&videoinfo->size_check_lock); + return FALSE; } - info->filesize += (guint64)GST_BUFFER_SIZE(buffer); - info->audio_frame_count++; + videoinfo->filesize += buffer_size; + videoinfo->audio_frame_count++; - return TRUE; + MMCAM_LOG_DEBUG("video rec[%"GST_TIME_FORMAT"], size[%"G_GUINT64_FORMAT"(trailer:%"G_GUINT64_FORMAT")]", + GST_TIME_ARGS(GST_BUFFER_PTS(buffer)), videoinfo->filesize + trailer_size, trailer_size); + + g_mutex_unlock(&videoinfo->size_check_lock); + + return GST_PAD_PROBE_OK; } -static gboolean __mmcamcorder_video_dataprobe_record(GstPad *pad, GstBuffer *buffer, gpointer u_data) +static GstPadProbeReturn __mmcamcorder_video_dataprobe_encoded(GstPad *pad, GstPadProbeInfo *info, gpointer u_data) { - static int count = 0; gint ret = 0; guint vq_size = 0; guint aq_size = 0; @@ -1064,136 +1382,166 @@ static gboolean __mmcamcorder_video_dataprobe_record(GstPad *pad, GstBuffer *buf guint64 buffer_size = 0; guint64 trailer_size = 0; guint64 queued_buffer = 0; - char *filename = NULL; + guint64 max_size = 0; + GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info); + GstMapInfo mapinfo; + storage_state_e storage_state = STORAGE_STATE_UNMOUNTABLE; mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data); _MMCamcorderMsgItem msg; _MMCamcorderSubContext *sc = NULL; - _MMCamcorderVideoInfo *info = NULL; + _MMCamcorderVideoInfo *videoinfo = NULL; - mmf_return_val_if_fail(hcamcorder, TRUE); - mmf_return_val_if_fail(buffer, FALSE); + mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK); + mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP); sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder); - mmf_return_val_if_fail(sc && sc->info_video, TRUE); - info = sc->info_video; + mmf_return_val_if_fail(sc && sc->info_video, GST_PAD_PROBE_OK); + videoinfo = sc->info_video; - /*_mmcam_dbg_log("[%" GST_TIME_FORMAT "]", GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));*/ if (sc->ferror_send) { - _mmcam_dbg_warn("file write error, drop frames"); - return FALSE; + MMCAM_LOG_WARNING("file write error, drop frames"); + return GST_PAD_PROBE_DROP; } - info->video_frame_count++; - if (info->video_frame_count <= (guint64)_MMCAMCORDER_MINIMUM_FRAME) { - /* _mmcam_dbg_log("Pass minimum frame: info->video_frame_count: %" G_GUINT64_FORMAT " ", - info->video_frame_count); */ - info->filesize += (guint64)GST_BUFFER_SIZE(buffer); - return TRUE; + if (!gst_buffer_map(buffer, &mapinfo, GST_MAP_READ)) { + MMCAM_LOG_WARNING("map failed : buffer[%p]", buffer); + return GST_PAD_PROBE_OK; } - buffer_size = GST_BUFFER_SIZE(buffer); + + buffer_size = mapinfo.size; + gst_buffer_unmap(buffer, &mapinfo); + + videoinfo->video_frame_count++; + if (videoinfo->video_frame_count <= (guint64)_MMCAMCORDER_MINIMUM_FRAME) { + MMCAM_LOG_DEBUG("Pass minimum frame[%"G_GUINT64_FORMAT"]", videoinfo->video_frame_count); + g_mutex_lock(&videoinfo->size_check_lock); + videoinfo->filesize += buffer_size; + g_mutex_unlock(&videoinfo->size_check_lock); + return GST_PAD_PROBE_OK; + } /* get trailer size */ - if (info->fileformat == MM_FILE_FORMAT_3GP || info->fileformat == MM_FILE_FORMAT_MP4) { - MMCAMCORDER_G_OBJECT_GET(sc->element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size); - } else { + if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) + MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size); + else trailer_size = 0; + + /* check free space */ + ret = _mmcamcorder_get_freespace(hcamcorder->storage_info.type, &free_space); + if (ret != 0) { + MMCAM_LOG_ERROR("Error occurred. [%d]", ret); + if (sc->ferror_count == 2 && sc->ferror_send == FALSE) { + sc->ferror_send = TRUE; + + msg.id = MM_MESSAGE_CAMCORDER_ERROR; + msg.param.code = MM_ERROR_FILE_READ; + + _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg); + } else { + sc->ferror_count++; + } + + return GST_PAD_PROBE_DROP; /* skip this buffer */ } - /* to minimizing free space check overhead */ - count = count % _MMCAMCORDER_FREE_SPACE_CHECK_INTERVAL; - if (count++ == 0) { - filename = info->filename; - ret = _mmcamcorder_get_freespace(filename, &free_space); + if (free_space == 0) { + /* check storage state */ + storage_get_state(hcamcorder->storage_info.id, &storage_state); - /*_mmcam_dbg_log("check free space for recording");*/ + MMCAM_LOG_WARNING("storage state %d", storage_state); + + if (storage_state == STORAGE_STATE_REMOVED || + storage_state == STORAGE_STATE_UNMOUNTABLE) { + MMCAM_LOG_ERROR("storage was removed!"); + + _MMCAMCORDER_LOCK(hcamcorder); + + if (sc->ferror_send == FALSE) { + MMCAM_LOG_ERROR("OUT_OF_STORAGE error"); - switch (ret) { - case -2: /* file not exist */ - case -1: /* failed to get free space */ - _mmcam_dbg_err("Error occured. [%d]", ret); - if (sc->ferror_count == 2 && sc->ferror_send == FALSE) { sc->ferror_send = TRUE; + + _MMCAMCORDER_UNLOCK(hcamcorder); + msg.id = MM_MESSAGE_CAMCORDER_ERROR; - if (ret == -2) { - msg.param.code = MM_ERROR_FILE_NOT_FOUND; - } else { - msg.param.code = MM_ERROR_FILE_READ; - } - _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg); + msg.param.code = MM_ERROR_OUT_OF_STORAGE; + + _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg); } else { - sc->ferror_count++; + _MMCAMCORDER_UNLOCK(hcamcorder); + MMCAM_LOG_WARNING("error was already sent"); } - return FALSE; /* skip this buffer */ - break; - default: /* succeeded to get free space */ - /* check free space for recording */ - /* get queued buffer size */ - if (sc->element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst) { - MMCAMCORDER_G_OBJECT_GET(sc->element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst, "current-level-bytes", &aq_size); - } - if (sc->element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst) { - MMCAMCORDER_G_OBJECT_GET(sc->element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst, "current-level-bytes", &vq_size); - } + return GST_PAD_PROBE_DROP; + } + } - queued_buffer = aq_size + vq_size; + /* get queued buffer size */ + if (sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst) + MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_AENC_QUE].gst, "current-level-bytes", &aq_size); - /* check free space */ - if (free_space < (_MMCAMCORDER_MINIMUM_SPACE + buffer_size + trailer_size + queued_buffer)) { - _mmcam_dbg_warn("No more space for recording!!! Recording is paused."); - _mmcam_dbg_warn("Free Space : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]," \ - " buffer size : [%" G_GUINT64_FORMAT "], queued buffer size : [%" G_GUINT64_FORMAT "]", \ - free_space, trailer_size, buffer_size, queued_buffer); + if (sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst) + MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_VENC_QUE].gst, "current-level-bytes", &vq_size); - if (!sc->isMaxsizePausing) { - MMCAMCORDER_G_OBJECT_SET( sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE); - sc->isMaxsizePausing = TRUE; + queued_buffer = aq_size + vq_size; - msg.id = MM_MESSAGE_CAMCORDER_NO_FREE_SPACE; - _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg); - } + if (free_space < (_MMCAMCORDER_MINIMUM_SPACE + buffer_size + trailer_size + queued_buffer)) { + MMCAM_LOG_WARNING("Free Space : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]," \ + " buffer size : [%" G_GUINT64_FORMAT "], queued buffer size : [%" G_GUINT64_FORMAT "]", \ + free_space, trailer_size, buffer_size, queued_buffer); - return FALSE; - } - break; + if (!sc->isMaxsizePausing) { + MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE); + sc->isMaxsizePausing = TRUE; + + msg.id = MM_MESSAGE_CAMCORDER_NO_FREE_SPACE; + _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg); } + + return GST_PAD_PROBE_DROP; } + g_mutex_lock(&videoinfo->size_check_lock); + /* check max size of recorded file */ - if (info->max_size > 0 && - info->max_size < info->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE) { + max_size = videoinfo->filesize + buffer_size + trailer_size + _MMCAMCORDER_MMS_MARGIN_SPACE; + if (videoinfo->max_size > 0 && videoinfo->max_size < max_size) { GstState pipeline_state = GST_STATE_VOID_PENDING; GstElement *pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst; - _mmcam_dbg_warn("Max size!!! Recording is paused."); - _mmcam_dbg_warn("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \ - " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]", - info->max_size, info->filesize, buffer_size, trailer_size); + + MMCAM_LOG_WARNING("Max size : [%" G_GUINT64_FORMAT "], current file size : [%" G_GUINT64_FORMAT "]," \ + " buffer size : [%" G_GUINT64_FORMAT "], trailer size : [%" G_GUINT64_FORMAT "]", + videoinfo->max_size, videoinfo->filesize, buffer_size, trailer_size); if (!sc->isMaxsizePausing) { + sc->isMaxsizePausing = TRUE; gst_element_get_state(pipeline, &pipeline_state, NULL, -1) ; - if (pipeline_state == GST_STATE_PLAYING) { - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE); - sc->isMaxsizePausing = TRUE; - } + if (pipeline_state == GST_STATE_PLAYING) + MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE); msg.id = MM_MESSAGE_CAMCORDER_MAX_SIZE; - _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg); + _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg); } - return FALSE; + g_mutex_unlock(&videoinfo->size_check_lock); + + return GST_PAD_PROBE_DROP; } - info->filesize += (guint64)buffer_size; + videoinfo->filesize += (guint64)buffer_size; - _mmcam_dbg_log("filesize %lld Byte, ", info->filesize); + MMCAM_LOG_DEBUG("video rec[%"GST_TIME_FORMAT"], size[%"G_GUINT64_FORMAT"(trailer:%"G_GUINT64_FORMAT")]", + GST_TIME_ARGS(GST_BUFFER_PTS(buffer)), videoinfo->filesize + trailer_size, trailer_size); - return TRUE; + g_mutex_unlock(&videoinfo->size_check_lock); + + return GST_PAD_PROBE_OK; } -static gboolean __mmcamcorder_video_dataprobe_audio_disable(GstPad *pad, GstBuffer *buffer, gpointer u_data) +static GstPadProbeReturn __mmcamcorder_video_dataprobe_audio_disable(GstPad *pad, GstPadProbeInfo *info, gpointer u_data) { guint64 trailer_size = 0; guint64 rec_pipe_time = 0; @@ -1204,158 +1552,171 @@ static gboolean __mmcamcorder_video_dataprobe_audio_disable(GstPad *pad, GstBuff mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data); _MMCamcorderMsgItem msg; _MMCamcorderSubContext *sc = NULL; - _MMCamcorderVideoInfo *info = NULL; + _MMCamcorderVideoInfo *videoinfo = NULL; - mmf_return_val_if_fail(buffer, FALSE); - mmf_return_val_if_fail(hcamcorder, TRUE); + GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info); + + mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP); + mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK); sc = MMF_CAMCORDER_SUBCONTEXT(u_data); - mmf_return_val_if_fail(sc, TRUE); - mmf_return_val_if_fail(sc->info_video, TRUE); + mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK); + mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK); - info = sc->info_video; + videoinfo = sc->info_video; - b_time = GST_BUFFER_TIMESTAMP(buffer); + b_time = GST_BUFFER_PTS(buffer); rec_pipe_time = GST_TIME_AS_MSECONDS(b_time); - if (info->fileformat == MM_FILE_FORMAT_3GP || info->fileformat == MM_FILE_FORMAT_MP4) { - MMCAMCORDER_G_OBJECT_GET(sc->element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size); - } else { + if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) + MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size); + else trailer_size = 0; - } /* check max time */ - if (info->max_time > 0 && rec_pipe_time > info->max_time) { - _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \ - rec_pipe_time, info->max_time); + if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) { + MMCAM_LOG_WARNING("Time current [%" G_GUINT64_FORMAT "], Max [%" G_GUINT64_FORMAT "], motion rate [%lf]", \ + rec_pipe_time, videoinfo->max_time, videoinfo->record_motion_rate); if (!sc->isMaxtimePausing) { - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE); + MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE); sc->isMaxtimePausing = TRUE; msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS; msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time; - msg.param.recording_status.filesize = (unsigned long long)((info->filesize + trailer_size) >> 10); + msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10); msg.param.recording_status.remained_time = 0; - _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg); + _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg); msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT; - _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg); + _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg); } - return FALSE; + return GST_PAD_PROBE_DROP; } /* calculate remained time can be recorded */ - if (info->max_time > 0 && info->max_time < (remained_time + rec_pipe_time)) { - remained_time = info->max_time - rec_pipe_time; - } else if (info->max_size > 0) { - long double max_size = (long double)info->max_size; - long double current_size = (long double)(info->filesize + trailer_size); + if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) { + remained_time = videoinfo->max_time - rec_pipe_time; + } else if (videoinfo->max_size > 0) { + long double max_size = (long double)videoinfo->max_size; + long double current_size = (long double)(videoinfo->filesize + trailer_size); remained_time = (unsigned int)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time; } msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS; msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time; - msg.param.recording_status.filesize = (unsigned long long)((info->filesize + trailer_size) >> 10); + msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10); msg.param.recording_status.remained_time = remained_time; - _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg); + _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg); + + if (videoinfo->record_timestamp_ratio != _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) { + guint record_motion_rate = (guint)videoinfo->record_motion_rate; - _mmcam_dbg_log("time [%" GST_TIME_FORMAT "], size [%d]", - GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize); + MMCAM_LOG_VERBOSE("record_motion_rate[%u], videoinfo->record_drop_count[%u]", + record_motion_rate, videoinfo->record_drop_count); + + /* drop some frame if fast motion */ + if (videoinfo->record_motion_rate > _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) { + if (record_motion_rate != (videoinfo->record_drop_count++)) { + MMCAM_LOG_VERBOSE("drop frame"); + return GST_PAD_PROBE_DROP; + } - if (info->record_timestamp_ratio != _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) { - GST_BUFFER_TIMESTAMP(buffer) = b_time * (info->record_timestamp_ratio); + videoinfo->record_drop_count = 1; + MMCAM_LOG_VERBOSE("pass frame"); + } + + GST_BUFFER_PTS(buffer) = b_time * (videoinfo->record_timestamp_ratio); + GST_BUFFER_DTS(buffer) = GST_BUFFER_PTS(buffer); } - return TRUE; + return GST_PAD_PROBE_OK; } -static gboolean __mmcamcorder_audioque_dataprobe(GstPad *pad, GstBuffer *buffer, gpointer u_data) +static GstPadProbeReturn __mmcamcorder_audioque_dataprobe(GstPad *pad, GstPadProbeInfo *info, gpointer u_data) { _MMCamcorderMsgItem msg; guint64 trailer_size = 0; guint64 rec_pipe_time = 0; _MMCamcorderSubContext *sc = NULL; - GstElement *pipeline = NULL; mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data); - _MMCamcorderVideoInfo *info = NULL; + _MMCamcorderVideoInfo *videoinfo = NULL; unsigned int remained_time = 0; + GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info); - mmf_return_val_if_fail(buffer, FALSE); - mmf_return_val_if_fail(hcamcorder, TRUE); + mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP); + mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK); sc = MMF_CAMCORDER_SUBCONTEXT(u_data); - mmf_return_val_if_fail(sc, TRUE); - mmf_return_val_if_fail(sc->info_video, TRUE); - mmf_return_val_if_fail(sc->element, TRUE); + mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK); + mmf_return_val_if_fail(sc->info_video, GST_PAD_PROBE_OK); + mmf_return_val_if_fail(sc->element, GST_PAD_PROBE_OK); - info = sc->info_video; - pipeline = sc->element[_MMCAMCORDER_MAIN_PIPE].gst; + videoinfo = sc->info_video; - if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_TIMESTAMP(buffer))) { - _mmcam_dbg_err( "Buffer timestamp is invalid, check it"); - return TRUE; + if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_PTS(buffer))) { + MMCAM_LOG_ERROR("Buffer timestamp is invalid, check it"); + return GST_PAD_PROBE_OK; } - rec_pipe_time = GST_TIME_AS_MSECONDS(GST_BUFFER_TIMESTAMP(buffer)); + rec_pipe_time = GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer)); - if (info->fileformat == MM_FILE_FORMAT_3GP || info->fileformat == MM_FILE_FORMAT_MP4) { - MMCAMCORDER_G_OBJECT_GET(sc->element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size); - } else { + if (videoinfo->fileformat == MM_FILE_FORMAT_3GP || videoinfo->fileformat == MM_FILE_FORMAT_MP4) + MMCAMCORDER_G_OBJECT_GET(sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst, "expected-trailer-size", &trailer_size); + else trailer_size = 0; - } /* calculate remained time can be recorded */ - if (info->max_time > 0 && info->max_time < (remained_time + rec_pipe_time)) { - remained_time = info->max_time - rec_pipe_time; - } else if (info->max_size > 0) { - long double max_size = (long double)info->max_size; - long double current_size = (long double)(info->filesize + trailer_size); + if (videoinfo->max_time > 0 && videoinfo->max_time < (remained_time + rec_pipe_time)) { + remained_time = videoinfo->max_time - rec_pipe_time; + } else if (videoinfo->max_size > 0) { + long double max_size = (long double)videoinfo->max_size; + long double current_size = (long double)(videoinfo->filesize + trailer_size); remained_time = (unsigned long long)((long double)rec_pipe_time * (max_size/current_size)) - rec_pipe_time; } - if (info->max_time > 0 && rec_pipe_time > info->max_time) { - _mmcam_dbg_warn("Current time : [%" G_GUINT64_FORMAT "], Maximum time : [%" G_GUINT64_FORMAT "]", \ - rec_pipe_time, info->max_time); + if (videoinfo->max_time > 0 && rec_pipe_time > videoinfo->max_time) { + MMCAM_LOG_WARNING("Time current [%" G_GUINT64_FORMAT "], Max [%" G_GUINT64_FORMAT "], motion rate [%lf]", \ + rec_pipe_time, videoinfo->max_time, videoinfo->record_motion_rate); if (!sc->isMaxtimePausing) { - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE); + MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", TRUE); sc->isMaxtimePausing = TRUE; msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS; msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time; - msg.param.recording_status.filesize = (unsigned long long)((info->filesize + trailer_size) >> 10); + msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10); msg.param.recording_status.remained_time = 0; - _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg); + _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg); msg.id = MM_MESSAGE_CAMCORDER_TIME_LIMIT; - _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg); + _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg); } - return FALSE; + return GST_PAD_PROBE_DROP; } msg.id = MM_MESSAGE_CAMCORDER_RECORDING_STATUS; msg.param.recording_status.elapsed = (unsigned long long)rec_pipe_time; - msg.param.recording_status.filesize = (unsigned long long)((info->filesize + trailer_size) >> 10); + msg.param.recording_status.filesize = (unsigned long long)((videoinfo->filesize + trailer_size) >> 10); msg.param.recording_status.remained_time = remained_time; - _mmcamcroder_send_message((MMHandleType)hcamcorder, &msg); + _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg); - _mmcam_dbg_log("audio data probe :: time [%" GST_TIME_FORMAT "], size [%lld KB]", - GST_TIME_ARGS(rec_pipe_time), msg.param.recording_status.filesize); + MMCAM_LOG_DEBUG("audio data probe[%" GST_TIME_FORMAT "], size[%"G_GUINT64_FORMAT"]", + GST_TIME_ARGS(GST_BUFFER_PTS(buffer)), videoinfo->filesize + trailer_size); - return TRUE; + return GST_PAD_PROBE_OK; } -static gboolean __mmcamcorder_audio_dataprobe_audio_mute(GstPad *pad, GstBuffer *buffer, gpointer u_data) +static GstPadProbeReturn __mmcamcorder_audio_dataprobe_audio_mute(GstPad *pad, GstPadProbeInfo *info, gpointer u_data) { mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data); double volume = 0.0; @@ -1363,69 +1724,78 @@ static gboolean __mmcamcorder_audio_dataprobe_audio_mute(GstPad *pad, GstBuffer int channel = 0; int err = MM_ERROR_UNKNOWN; char *err_name = NULL; + GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info); + GstMapInfo mapinfo; - mmf_return_val_if_fail(buffer, FALSE); - mmf_return_val_if_fail(hcamcorder, FALSE); + mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP); + mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_DROP); - /*_mmcam_dbg_log("AUDIO SRC time stamp : [%" GST_TIME_FORMAT "] \n", GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));*/ err = mm_camcorder_get_attributes((MMHandleType)hcamcorder, &err_name, - MMCAM_AUDIO_VOLUME, &volume, - MMCAM_AUDIO_FORMAT, &format, - MMCAM_AUDIO_CHANNEL, &channel, - NULL); + MMCAM_AUDIO_VOLUME, &volume, + MMCAM_AUDIO_FORMAT, &format, + MMCAM_AUDIO_CHANNEL, &channel, + NULL); if (err != MM_ERROR_NONE) { - _mmcam_dbg_warn("Get attrs fail. (%s:%x)", err_name, err); + MMCAM_LOG_WARNING("Get attrs fail. (%s:%x)", err_name, err); SAFE_FREE(err_name); - return err; + return GST_PAD_PROBE_OK; } - /* Set audio stream NULL */ - if (volume == 0.0) { - memset(GST_BUFFER_DATA(buffer), 0, GST_BUFFER_SIZE(buffer)); + memset(&mapinfo, 0x0, sizeof(GstMapInfo)); + + if (!gst_buffer_map(buffer, &mapinfo, GST_MAP_READWRITE)) { + MMCAM_LOG_WARNING("map failed : buffer[%p]", buffer); + return GST_PAD_PROBE_OK; } + /* Set audio stream NULL */ + if (volume == 0.0) + memset(mapinfo.data, 0, mapinfo.size); + + MMCAM_LOG_DEBUG("audio stream[%"GST_TIME_FORMAT"] - cb[%p], fmt[%d], ch[%d], size[%"G_GSIZE_FORMAT"]", + GST_TIME_ARGS(GST_BUFFER_PTS(buffer)), hcamcorder->astream_cb, format, channel, mapinfo.size); + /* CALL audio stream callback */ - if (hcamcorder->astream_cb && buffer && GST_BUFFER_DATA(buffer)) { + if (hcamcorder->astream_cb) { MMCamcorderAudioStreamDataType stream; if (_mmcamcorder_get_state((MMHandleType)hcamcorder) < MM_CAMCORDER_STATE_PREPARE) { - _mmcam_dbg_warn("Not ready for stream callback"); - return TRUE; + MMCAM_LOG_WARNING("Not ready for stream callback"); + gst_buffer_unmap(buffer, &mapinfo); + return GST_PAD_PROBE_OK; } - /*_mmcam_dbg_log("Call video steramCb, data[%p], Width[%d],Height[%d], Format[%d]", - GST_BUFFER_DATA(buffer), width, height, format);*/ - - stream.data = (void *)GST_BUFFER_DATA(buffer); + stream.data = (void *)mapinfo.data; stream.format = format; stream.channel = channel; - stream.length = GST_BUFFER_SIZE(buffer); - stream.timestamp = (unsigned int)(GST_BUFFER_TIMESTAMP(buffer)/1000000); /* nano -> milli second */ + stream.length = mapinfo.size; + stream.timestamp = (unsigned int)(GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer))); _MMCAMCORDER_LOCK_ASTREAM_CALLBACK(hcamcorder); - if (hcamcorder->astream_cb) { + if (hcamcorder->astream_cb) hcamcorder->astream_cb(&stream, hcamcorder->astream_cb_param); - } _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK(hcamcorder); } - return TRUE; + gst_buffer_unmap(buffer, &mapinfo); + + return GST_PAD_PROBE_OK; } -static gboolean __mmcamcorder_add_locationinfo(MMHandleType handle, int fileformat) +static gboolean __mmcamcorder_add_metadata(MMHandleType handle, int fileformat) { gboolean bret = FALSE; switch (fileformat) { case MM_FILE_FORMAT_3GP: case MM_FILE_FORMAT_MP4: - bret = __mmcamcorder_add_locationinfo_mp4(handle); + bret = __mmcamcorder_add_metadata_mp4(handle); break; default: - _mmcam_dbg_warn("Unsupported fileformat to insert location info (%d)", fileformat); + MMCAM_LOG_WARNING("Unsupported fileformat to insert location info (%d)", fileformat); break; } @@ -1433,7 +1803,7 @@ static gboolean __mmcamcorder_add_locationinfo(MMHandleType handle, int fileform } -static gboolean __mmcamcorder_add_locationinfo_mp4(MMHandleType handle) +static gboolean __mmcamcorder_add_metadata_mp4(MMHandleType handle) { FILE *f = NULL; guchar buf[4]; @@ -1446,9 +1816,11 @@ static gboolean __mmcamcorder_add_locationinfo_mp4(MMHandleType handle) gdouble altitude = 0; int err = 0; int orientation = 0; + int gps_enable = 0; char *err_name = NULL; char err_msg[MAX_ERROR_MESSAGE_LEN] = {'\0',}; - _MMCamcorderLocationInfo location_info = {0,}; + _MMCamcorderLocationInfo location_info = {0, 0, 0}; + _MMCamcorderLocationInfo geo_info = {0, 0, 0}; _MMCamcorderVideoInfo *info = NULL; mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle); @@ -1460,127 +1832,137 @@ static gboolean __mmcamcorder_add_locationinfo_mp4(MMHandleType handle) mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED); mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED); - _mmcam_dbg_log(""); + MMCAM_LOG_INFO(""); info = sc->info_video; - f = fopen(info->filename, "rb+"); + f = fopen64(info->filename, "rb+"); if (f == NULL) { strerror_r(errno, err_msg, MAX_ERROR_MESSAGE_LEN); - _mmcam_dbg_err("file open failed [%s]", err_msg); + MMCAM_LOG_ERROR("file open failed [%s]", err_msg); return FALSE; } mm_camcorder_get_attributes(handle, &err_name, - MMCAM_TAG_LATITUDE, &latitude, - MMCAM_TAG_LONGITUDE, &longitude, - MMCAM_TAG_ALTITUDE, &altitude, - MMCAM_TAG_VIDEO_ORIENTATION, &orientation, - NULL); + MMCAM_TAG_LATITUDE, &latitude, + MMCAM_TAG_LONGITUDE, &longitude, + MMCAM_TAG_ALTITUDE, &altitude, + MMCAM_TAG_VIDEO_ORIENTATION, &orientation, + MMCAM_TAG_GPS_ENABLE, &gps_enable, + NULL); if (err_name) { - _mmcam_dbg_warn("Get tag attrs fail. (%s:%x)", err_name, err); - SAFE_FREE (err_name); + MMCAM_LOG_WARNING("Get tag attrs fail. (%s:%x)", err_name, err); + SAFE_FREE(err_name); } location_info.longitude = _mmcamcorder_double_to_fix(longitude); location_info.latitude = _mmcamcorder_double_to_fix(latitude); location_info.altitude = _mmcamcorder_double_to_fix(altitude); - + geo_info.longitude = longitude *10000; + geo_info.latitude = latitude *10000; + geo_info.altitude = altitude *10000; /* find udta container. if, there are udta container, write loci box after that else, make udta container and write loci box. */ - if (_mmcamcorder_find_tag(f, MMCAM_FOURCC('u','d','t','a'), TRUE)) { + if (_mmcamcorder_find_fourcc(f, MMCAM_FOURCC('u', 'd', 't', 'a'), TRUE)) { size_t nread = 0; - _mmcam_dbg_log("find udta container"); + MMCAM_LOG_INFO("find udta container"); /* read size */ - if (fseek(f, -8L, SEEK_CUR) != 0) { + if (fseek(f, -8L, SEEK_CUR) != 0) goto fail; - } - udta_pos = ftell(f); - if (udta_pos < 0) { + udta_pos = ftello(f); + if (udta_pos < 0) goto ftell_fail; - } nread = fread(&buf, sizeof(char), sizeof(buf), f); - _mmcam_dbg_log("recorded file fread %d", nread); + MMCAM_LOG_INFO("recorded file fread %zu", nread); udta_size = _mmcamcorder_get_container_size(buf); /* goto end of udta and write 'loci' box */ - if (fseek(f, (udta_size-4L), SEEK_CUR) != 0) { + if (fseek(f, (udta_size-4L), SEEK_CUR) != 0) goto fail; - } - if (!_mmcamcorder_write_loci(f, location_info)) { - goto fail; + if (gps_enable) { + if (!_mmcamcorder_write_loci(f, location_info)) { + MMCAM_LOG_ERROR("failed to write loci"); + goto fail; + } + + if (!_mmcamcorder_write_geodata(f, geo_info)) { + MMCAM_LOG_ERROR("failed to write geodata"); + goto fail; + } } - current_pos = ftell(f); - if (current_pos < 0) { + current_pos = ftello(f); + if (current_pos < 0) goto ftell_fail; - } - if (!_mmcamcorder_update_size(f, udta_pos, current_pos)) { + if (!_mmcamcorder_update_size(f, udta_pos, current_pos)) goto fail; - } } else { - _mmcam_dbg_log("No udta container"); - if (fseek(f, 0, SEEK_END) != 0) { + MMCAM_LOG_INFO("No udta container"); + if (fseek(f, 0, SEEK_END) != 0) goto fail; - } - if (!_mmcamcorder_write_udta(f, location_info)) { + if (!_mmcamcorder_write_udta(f, gps_enable, location_info, geo_info)) { + MMCAM_LOG_ERROR("failed to write udta"); goto fail; } } /* find moov container. update moov container size. */ - if((current_pos = ftell(f))<0) + if ((current_pos = ftello(f)) < 0) goto ftell_fail; - if (_mmcamcorder_find_tag(f, MMCAM_FOURCC('m','o','o','v'), TRUE)) { - gint64 internal_pos = ftell(f); + if (_mmcamcorder_find_tag(f, MMCAM_FOURCC('m', 'o', 'o', 'v'), TRUE)) { + gint64 internal_pos = ftello(f); - _mmcam_dbg_log("found moov container"); - if (fseek(f, -8L, SEEK_CUR) !=0) { + MMCAM_LOG_INFO("found moov container"); + if (fseek(f, -8L, SEEK_CUR) != 0) goto fail; - } - moov_pos = ftell(f); - if (moov_pos < 0) { + moov_pos = ftello(f); + if (moov_pos < 0) goto ftell_fail; - } - if (!_mmcamcorder_update_size(f, moov_pos, current_pos)) { + if (!_mmcamcorder_update_size(f, moov_pos, current_pos)) goto fail; - } /* add orientation info */ - fseek(f, internal_pos, SEEK_SET); - if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t','r','a','k'), FALSE)) { - _mmcam_dbg_err("failed to find [trak] tag"); + if (fseeko(f, internal_pos, SEEK_SET) < 0) { + MMCAM_LOG_ERROR("fseeko failed : errno %d", errno); + goto fail; + } + + if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t', 'r', 'a', 'k'), FALSE)) { + MMCAM_LOG_ERROR("failed to find [trak] tag"); goto fail; } - if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t','k','h','d'), FALSE)) { - _mmcam_dbg_err("failed to find [tkhd] tag"); + if (!_mmcamcorder_find_tag(f, MMCAM_FOURCC('t', 'k', 'h', 'd'), FALSE)) { + MMCAM_LOG_ERROR("failed to find [tkhd] tag"); goto fail; } - _mmcam_dbg_log("found [tkhd] tag"); + MMCAM_LOG_INFO("found [tkhd] tag"); /* seek to start position of composition matrix */ - fseek(f, _OFFSET_COMPOSITION_MATRIX, SEEK_CUR); - - /* update composition matrix for orientation */ - _mmcamcorder_update_composition_matrix(f, orientation); + if (fseek(f, OFFSET_COMPOSITION_MATRIX, SEEK_CUR) == 0) { + /* update composition matrix for orientation */ + _mmcamcorder_update_composition_matrix(f, orientation); + } else { + MMCAM_LOG_ERROR("fseek failed : errno %d", errno); + goto fail; + } } else { - _mmcam_dbg_err("No 'moov' container"); + MMCAM_LOG_ERROR("No 'moov' container"); goto fail; } @@ -1592,7 +1974,102 @@ fail: return FALSE; ftell_fail: - _mmcam_dbg_err("ftell() returns negative value."); + MMCAM_LOG_ERROR("ftell() returns negative value."); fclose(f); return FALSE; } + + +int _mmcamcorder_connect_video_stream_cb_signal(MMHandleType handle) +{ + mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle); + _MMCamcorderSubContext *sc = NULL; + + mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED); + + sc = MMF_CAMCORDER_SUBCONTEXT(handle); + mmf_return_val_if_fail(sc && sc->element, MM_ERROR_CAMCORDER_NOT_INITIALIZED); + + /* check video source element */ + if (sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst) { + MMCAM_LOG_WARNING("connect video stream cb signal to _MMCAMCORDER_VIDEOSRC_SRC"); + MMCAMCORDER_SIGNAL_CONNECT(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, + _MMCAMCORDER_HANDLER_VIDEOREC, "video-stream-cb", + G_CALLBACK(__mmcamcorder_video_stream_cb), + hcamcorder); + return MM_ERROR_NONE; + } else { + MMCAM_LOG_ERROR("videosrc element is not created yet"); + return MM_ERROR_CAMCORDER_NOT_INITIALIZED; + } +} + + +int _mmcamcorder_video_prepare_record(MMHandleType handle) +{ + int ret = MM_ERROR_NONE; + int size = 0; + char *temp_filename = NULL; + + _MMCamcorderVideoInfo *info = NULL; + _MMCamcorderSubContext *sc = NULL; + mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle); + + mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED); + + sc = MMF_CAMCORDER_SUBCONTEXT(handle); + mmf_return_val_if_fail(sc, MM_ERROR_CAMCORDER_NOT_INITIALIZED); + mmf_return_val_if_fail(sc->info_video, MM_ERROR_CAMCORDER_NOT_INITIALIZED); + + info = sc->info_video; + + MMCAM_LOG_WARNING("start"); + + /* create encoding pipeline */ + ret = _mmcamcorder_create_recorder_pipeline((MMHandleType)hcamcorder); + if (ret != MM_ERROR_NONE) + goto _ERR_PREPARE_RECORD; + + SAFE_G_FREE(info->filename); + + mm_camcorder_get_attributes(handle, NULL, + MMCAM_TARGET_FILENAME, &temp_filename, &size, + NULL); + if (temp_filename) { + info->filename = g_strdup(temp_filename); + if (!info->filename) { + MMCAM_LOG_ERROR("strdup[src:%p] was failed", temp_filename); + goto _ERR_PREPARE_RECORD; + } + + MMCAM_LOG_INFO("Record file name [%s]", info->filename); + MMCAMCORDER_G_OBJECT_SET_POINTER(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename); + } else { + MMCAM_LOG_INFO("Recorded data will be written in [%s]", _MMCamcorder_FILENAME_NULL); + MMCAMCORDER_G_OBJECT_SET_POINTER(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", _MMCamcorder_FILENAME_NULL); + } + + MMCAMCORDER_G_OBJECT_SET(sc->encode_element[_MMCAMCORDER_ENCSINK_ENCBIN].gst, "block", 0); + + /* Adjust display FPS */ + sc->display_interval = 0; + sc->previous_slot_time = 0; + + ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PAUSED); + if (ret != MM_ERROR_NONE) + goto _ERR_PREPARE_RECORD; + + MMCAM_LOG_WARNING("done"); + + return ret; + +_ERR_PREPARE_RECORD: + /* Remove recorder pipeline and recording file which size maybe zero */ + _mmcamcorder_remove_recorder_pipeline((MMHandleType)hcamcorder); + if (info && info->filename) { + MMCAM_LOG_INFO("file delete(%s)", info->filename); + unlink(info->filename); + } + + return ret; +}