From 02d194fc29293eede2e332206f7b2066ce2b28a9 Mon Sep 17 00:00:00 2001 From: Jeongmo Yang Date: Thu, 12 Jan 2017 17:14:33 +0900 Subject: [PATCH] Add new callback for muxed stream [Version] 0.10.102 [Profile] Common [Issue Type] Update [Dependency module] N/A [Test] [M(T) - Boot=(OK), sdb=(OK), Home=(OK), Touch=(OK), Version=tizen-3.0-mobile_20170104.1] Change-Id: Ifaae2a1ad0792fc7e9a3148dc124becbdef2e3b2 Signed-off-by: Jeongmo Yang --- packaging/libmm-camcorder.spec | 2 +- src/include/mm_camcorder.h | 57 ++++++++++++ src/include/mm_camcorder_gstcommon.h | 2 + src/include/mm_camcorder_internal.h | 23 +++++ src/mm_camcorder.c | 10 ++- src/mm_camcorder_attribute.c | 76 ++++++++++++++-- src/mm_camcorder_audiorec.c | 146 +++++++++++++++--------------- src/mm_camcorder_gstcommon.c | 167 ++++++++++++++++++++++++++++++----- src/mm_camcorder_internal.c | 28 ++++++ src/mm_camcorder_videorec.c | 58 +++--------- 10 files changed, 420 insertions(+), 149 deletions(-) diff --git a/packaging/libmm-camcorder.spec b/packaging/libmm-camcorder.spec index 77643a3..5870783 100644 --- a/packaging/libmm-camcorder.spec +++ b/packaging/libmm-camcorder.spec @@ -1,6 +1,6 @@ Name: libmm-camcorder Summary: Camera and recorder library -Version: 0.10.101 +Version: 0.10.102 Release: 0 Group: Multimedia/Libraries License: Apache-2.0 diff --git a/src/include/mm_camcorder.h b/src/include/mm_camcorder.h index 787be2d..edc29c2 100644 --- a/src/include/mm_camcorder.h +++ b/src/include/mm_camcorder.h @@ -1798,6 +1798,16 @@ typedef struct { /** + * Structure for muxed stream data. + */ +typedef struct { + void *data; /**< pointer of muxed stream */ + unsigned int length; /**< length of stream buffer (in byte) */ + unsigned long long offset; /**< current offset for data */ +} MMCamcorderMuxedStreamDataType; + + +/** * Prerequisite information for mm_camcorder_create() * The information to set prior to create. */ @@ -1881,6 +1891,21 @@ typedef gboolean (*mm_camcorder_audio_stream_callback)(MMCamcorderAudioStreamDat /** + * Function definition for muxed stream callback. + * Be careful! In this function, you can't call functions that change the state of camcorder such as mm_camcorder_stop(), + * mm_camcorder_unrealize(), mm_camcorder_record(), mm_camcorder_commit(), and mm_camcorder_cancel(), etc. + * Please don't hang this function long. It may cause low performance of camcorder or occur timeout error from encoding pipeline. + * I recommend to you releasing this function ASAP. + * + * @param[in] stream Reference pointer to muxed stream data + * @param[in] user_param User parameter which is received from user when callback function was set + * @return This function returns true on success, or false on failure. + * @remarks + */ +typedef gboolean (*mm_camcorder_muxed_stream_callback)(MMCamcorderMuxedStreamDataType *stream, void *user_param); + + +/** * Function definition for video capture callback. * Like '#mm_camcorder_video_stream_callback', you can't call mm_camcorder_stop() while you are hanging this function. * @@ -2910,6 +2935,38 @@ int mm_camcorder_set_audio_stream_callback(MMHandleType camcorder, mm_camcorder_ /** + * mm_camcorder_set_muxed_stream_callback:\n + * Set callback for user defined muxed stream callback function. + * Users can retrieve muxed data using registered callback. + * The callback function holds the same buffer that will be recorded. + * + * @param[in] camcorder A handle of camcorder. + * @param[in] callback Function pointer of callback function. + * @param[in] user_data User parameter for passing to callback function. + * @return This function returns zero(MM_ERROR_NONE) on success, or negative value with error code.\n + * Please refer 'mm_error.h' to know the exact meaning of the error. + * @see mm_camcorder_muxed_stream_callback + * @pre None + * @post None + * @remarks registered 'callback' is called on internal thread of camcorder. Regardless of the status of main loop, this function will be called. + * @par example + * @code + +#include + +gboolean setting_muxed_stream_callback() +{ + //set callback + mm_camcorder_set_muxed_stream_callback(hcam, (mm_camcorder_muxed_stream_callback)camcordertest_muxed_stream_cb, (void*)hcam); + + return TRUE; +} + * @endcode + */ +int mm_camcorder_set_muxed_stream_callback(MMHandleType camcorder, mm_camcorder_muxed_stream_callback callback, void *user_data); + + +/** * mm_camcorder_get_state:\n * Get the current state of camcorder. * mm_camcorder is working on the base of its state. An user should check the state of mm_camcorder before calling its functions. diff --git a/src/include/mm_camcorder_gstcommon.h b/src/include/mm_camcorder_gstcommon.h index 63f1058..42c46c1 100644 --- a/src/include/mm_camcorder_gstcommon.h +++ b/src/include/mm_camcorder_gstcommon.h @@ -181,6 +181,8 @@ bool _mmcamcorder_set_encoded_preview_bitrate(MMHandleType handle, int bitrate); bool _mmcamcorder_set_encoded_preview_gop_interval(MMHandleType handle, int gop); bool _mmcamcorder_set_sound_stream_info(GstElement *element, char *stream_type, int stream_index); void _mmcamcorder_set_encoder_bitrate(MMCamcorderEncoderType type, int codec, int bitrate, GstElement *element); +GstPadProbeReturn __mmcamcorder_muxed_dataprobe(GstPad *pad, GstPadProbeInfo *info, gpointer u_data); +GstPadProbeReturn __mmcamcorder_eventprobe_monitor(GstPad *pad, GstPadProbeInfo *info, gpointer u_data); #ifdef __cplusplus } #endif diff --git a/src/include/mm_camcorder_internal.h b/src/include/mm_camcorder_internal.h index 1f2660f..3b0b83c 100644 --- a/src/include/mm_camcorder_internal.h +++ b/src/include/mm_camcorder_internal.h @@ -413,6 +413,11 @@ extern "C" { #define _MMCAMCORDER_TRYLOCK_ASTREAM_CALLBACK(handle) _MMCAMCORDER_TRYLOCK_FUNC(_MMCAMCORDER_GET_ASTREAM_CALLBACK_LOCK(handle)) #define _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK(handle) _MMCAMCORDER_UNLOCK_FUNC(_MMCAMCORDER_GET_ASTREAM_CALLBACK_LOCK(handle)) +#define _MMCAMCORDER_GET_MSTREAM_CALLBACK_LOCK(handle) (_MMCAMCORDER_CAST_MTSAFE(handle).mstream_cb_lock) +#define _MMCAMCORDER_LOCK_MSTREAM_CALLBACK(handle) _MMCAMCORDER_LOCK_FUNC(_MMCAMCORDER_GET_MSTREAM_CALLBACK_LOCK(handle)) +#define _MMCAMCORDER_TRYLOCK_MSTREAM_CALLBACK(handle) _MMCAMCORDER_TRYLOCK_FUNC(_MMCAMCORDER_GET_MSTREAM_CALLBACK_LOCK(handle)) +#define _MMCAMCORDER_UNLOCK_MSTREAM_CALLBACK(handle) _MMCAMCORDER_UNLOCK_FUNC(_MMCAMCORDER_GET_MSTREAM_CALLBACK_LOCK(handle)) + #ifdef _MMCAMCORDER_MURPHY_SUPPORT /* for resource conflict */ #define _MMCAMCORDER_GET_RESOURCE_LOCK(handle) (_MMCAMCORDER_CAST_MTSAFE(handle).resource_lock) @@ -629,6 +634,7 @@ typedef struct { GMutex vcapture_cb_lock; /**< Mutex (for video capture callback) */ GMutex vstream_cb_lock; /**< Mutex (for video stream callback) */ GMutex astream_cb_lock; /**< Mutex (for audio stream callback) */ + GMutex mstream_cb_lock; /**< Mutex (for muxed stream callback) */ #ifdef _MMCAMCORDER_MURPHY_SUPPORT GCond resource_cond; /**< Condition (for resource check) */ GMutex resource_lock; /**< Mutex (for resource check) */ @@ -656,6 +662,7 @@ typedef struct { gboolean bencbin_capture; /**< Use Encodebin for capturing */ gboolean audio_disable; /**< whether audio is disabled or not when record */ int videosrc_rotate; /**< rotate of videosrc */ + unsigned long long muxed_stream_offset; /**< current offset for muxed stream data */ /* For dropping video frame when start recording */ int drop_vframe; /**< When this value is bigger than zero and pass_first_vframe is zero, MSL will drop video frame though cam_stability count is bigger then zero. */ @@ -717,6 +724,8 @@ typedef struct mmf_camcorder { void *vstream_cb_param; /**< Video stream callback parameter */ mm_camcorder_audio_stream_callback astream_cb; /**< Audio stream callback */ void *astream_cb_param; /**< Audio stream callback parameter */ + mm_camcorder_muxed_stream_callback mstream_cb; /**< Muxed stream callback */ + void *mstream_cb_param; /**< Muxed stream callback parameter */ mm_camcorder_video_capture_callback vcapture_cb; /**< Video capture callback */ void *vcapture_cb_param; /**< Video capture callback parameter */ int (*command)(MMHandleType, int); /**< camcorder's command */ @@ -1028,6 +1037,20 @@ int _mmcamcorder_set_audio_stream_callback(MMHandleType handle, void *user_data); /** + * This function is to set callback for muxed stream. + * + * @param[in] hcamcorder Specifies the camcorder handle + * @param[in] callback Specifies the function pointer of callback function + * @param[in] user_data Specifies the user poiner for passing to callback function + * + * @return This function returns zero on success, or negative value with error code. + * @see mmcamcorder_error_type + */ +int _mmcamcorder_set_muxed_stream_callback(MMHandleType handle, + mm_camcorder_muxed_stream_callback callback, + void *user_data); + +/** * This function is to set callback for video capture. * * @param[in] hcamcorder Specifies the camcorder handle diff --git a/src/mm_camcorder.c b/src/mm_camcorder.c index ca60b9e..9a77ee3 100644 --- a/src/mm_camcorder.c +++ b/src/mm_camcorder.c @@ -319,6 +319,14 @@ int mm_camcorder_set_audio_stream_callback(MMHandleType camcorder, mm_camcorder_ } +int mm_camcorder_set_muxed_stream_callback(MMHandleType camcorder, mm_camcorder_muxed_stream_callback callback, void *user_data) +{ + mmf_return_val_if_fail((void *)camcorder, MM_ERROR_CAMCORDER_INVALID_ARGUMENT); + + return _mmcamcorder_set_muxed_stream_callback(camcorder, callback, user_data); +} + + int mm_camcorder_set_video_capture_callback(MMHandleType camcorder, mm_camcorder_video_capture_callback callback, void* user_data) { mmf_return_val_if_fail((void *)camcorder, MM_ERROR_CAMCORDER_INVALID_ARGUMENT); @@ -426,7 +434,7 @@ void mm_camcorder_emit_signal(MMHandleType camcorder, const char *object_name, { mmf_return_if_fail((void *)camcorder); - _mmcamcorder_emit_signal(camcorder,object_name, interface_name, signal_name, value); + _mmcamcorder_emit_signal(camcorder, object_name, interface_name, signal_name, value); return; } diff --git a/src/mm_camcorder_attribute.c b/src/mm_camcorder_attribute.c index 41b288e..2120f1f 100644 --- a/src/mm_camcorder_attribute.c +++ b/src/mm_camcorder_attribute.c @@ -3389,9 +3389,13 @@ bool _mmcamcorder_commit_image_encoder_quality(MMHandleType handle, int attr_idx bool _mmcamcorder_commit_target_filename(MMHandleType handle, int attr_idx, const mmf_value_t *value) { - _MMCamcorderSubContext *sc = NULL; - const char *filename = NULL; + int ret = MM_ERROR_NONE; int size = 0; + const char *filename = NULL; + _MMCamcorderSubContext *sc = NULL; + GstElement *encode_link = NULL; + GstElement *encode_sink = NULL; + GstElement *encode_pipeline = NULL; mmf_return_val_if_fail(handle && value, FALSE); @@ -3416,9 +3420,71 @@ bool _mmcamcorder_commit_target_filename(MMHandleType handle, int attr_idx, cons } if (sc->encode_element && sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst) { - _mmcam_dbg_log("new file location set.[%s] filesink %p", filename, sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst); - MMCAMCORDER_G_OBJECT_SET_POINTER(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", filename); - _mmcam_dbg_log("new file location set.(%s)", filename); + encode_sink = sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst; + encode_pipeline = sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst; + + _mmcam_dbg_log("file location set.[%s], current encode sink [%s]", + filename, GST_OBJECT_NAME(gst_element_get_factory(encode_sink))); + + /* check whether it's filesink or not */ + if (strncmp(GST_OBJECT_NAME(gst_element_get_factory(encode_sink)), "filesink", strlen("filesink"))) { + _mmcam_dbg_log("remove current sink and create filesink"); + + /* remove fakesink and create/add filesink to encode pipeline */ + /* set NULL state */ + ret = _mmcamcorder_gst_set_state(handle, encode_sink, GST_STATE_NULL); + if (ret != MM_ERROR_NONE) { + _mmcam_dbg_err("failed to set NULL encoder sink"); + return FALSE; + } + + /* remove encode sink - pads will be unlinked automatically in remove function */ + if (!gst_bin_remove(GST_BIN(encode_pipeline), encode_sink)) { + _mmcam_dbg_err("failed to remove encode sink from pipeline"); + return FALSE; + } + + _mmcam_dbg_log("remove done"); + + /* create filesink */ + encode_sink = gst_element_factory_make("filesink", NULL); + if (!encode_sink) { + _mmcam_dbg_err("filesink creation failed"); + return FALSE; + } + + sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst = encode_sink; + + /* set release notification callback */ + g_object_weak_ref(G_OBJECT(encode_sink), (GWeakNotify)_mmcamcorder_element_release_noti, sc); + + /* add to pipeline */ + if (!gst_bin_add(GST_BIN(encode_pipeline), encode_sink)) { + _mmcam_dbg_err("failed to add filesink to encode pipeline"); + gst_object_unref(encode_sink); + return FALSE; + } + + /* link filesink */ + if (sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst) { + /* mux element is used */ + _mmcam_dbg_log("Link mux to encode_sink"); + encode_link = sc->encode_element[_MMCAMCORDER_ENCSINK_MUX].gst; + } else { + /* no mux element */ + _mmcam_dbg_log("Link audio encoder to encode_sink"); + encode_link = sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst; + } + + if (!_MM_GST_ELEMENT_LINK(encode_link, encode_sink)) { + _mmcam_dbg_err("Link FAILED"); + return FALSE; + } + + _mmcam_dbg_log("Link OK"); + } + + MMCAMCORDER_G_OBJECT_SET_POINTER(encode_sink, "location", filename); } else { _mmcam_dbg_log("element is not created yet. [%s] will be set later...", filename); } diff --git a/src/mm_camcorder_audiorec.c b/src/mm_camcorder_audiorec.c index 70eb27a..d0e3afe 100644 --- a/src/mm_camcorder_audiorec.c +++ b/src/mm_camcorder_audiorec.c @@ -62,10 +62,12 @@ static gboolean __mmcamcorder_audio_add_metadata_info_m4a(MMHandleType handle); static int __mmcamcorder_create_audiop_with_encodebin(MMHandleType handle) { int err = MM_ERROR_NONE; - int ret = MM_ERROR_NONE; + int file_name_len = 0; + char *file_name = NULL; const char *aenc_name = NULL; const char *mux_name = NULL; + const char *sink_name = NULL; GstBus *bus = NULL; GstPad *srcpad = NULL; @@ -113,7 +115,26 @@ static int __mmcamcorder_create_audiop_with_encodebin(MMHandleType handle) err = _mmcamcorder_create_encodesink_bin((MMHandleType)hcamcorder, MM_CAMCORDER_ENCBIN_PROFILE_AUDIO); if (err != MM_ERROR_NONE) return err; + + /* Add and link elements */ + gst_bin_add_many(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), + sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst, + sc->encode_element[_MMCAMCORDER_ENCSINK_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_LINK_UNREF(srcpad, sinkpad, err, pipeline_creation_error); } else { + err = mm_camcorder_get_attributes(handle, NULL, + MMCAM_TARGET_FILENAME, &file_name, &file_name_len, + NULL); + if (err != MM_ERROR_NONE) { + _mmcam_dbg_err("failed to get filename [0x%x]", err); + err = MM_ERROR_CAMCORDER_RESOURCE_CREATION; + goto pipeline_creation_error; + } + /* without muxing. can't use encodebin. */ aenc_elem = _mmcamcorder_get_type_element(handle, MM_CAM_AUDIO_ENCODER); if (!aenc_elem) { @@ -129,6 +150,8 @@ static int __mmcamcorder_create_audiop_with_encodebin(MMHandleType handle) goto pipeline_creation_error; } + element_list = g_list_append(element_list, &sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN]); + _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_AQUE, "queue", NULL, element_list, err); if (strcmp(aenc_name, "wavenc") != 0) @@ -136,58 +159,27 @@ static int __mmcamcorder_create_audiop_with_encodebin(MMHandleType handle) _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_AENC, aenc_name, NULL, element_list, err); - _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_SINK, "filesink", NULL, element_list, err); - } + if (file_name) + sink_name = "filesink"; + else + sink_name = "fakesink"; - /* Add and link elements */ - if (info->bMuxing) { - /* IF MUX is indicated create MUX */ - gst_bin_add_many(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), - sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst, - sc->encode_element[_MMCAMCORDER_ENCSINK_BIN].gst, - NULL); + _mmcam_dbg_log("encode sink : %s", sink_name); - 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); - } else { - /* IF MUX in not chosen then record in raw amr file */ - if (!strcmp(aenc_name, "wavenc")) { - gst_bin_add_many(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), - sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst, - sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, - sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst, - sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, - NULL); + _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_SINK, sink_name, NULL, element_list, err); - ret = _MM_GST_ELEMENT_LINK_MANY(sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst, - sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, - sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst, - sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, - NULL); - if (!ret) { - err = MM_ERROR_CAMCORDER_GST_LINK; - goto pipeline_creation_error; - } - } else { - gst_bin_add_many(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), - sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst, - sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, - sc->encode_element[_MMCAMCORDER_ENCSINK_CONV].gst, - sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst, - sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, - NULL); + /* add elements to encode pipeline */ + if (!_mmcamcorder_add_elements_to_bin(GST_BIN(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst), element_list)) { + _mmcam_dbg_err("add encode elements error."); + err = MM_ERROR_CAMCORDER_RESOURCE_CREATION; + goto pipeline_creation_error; + } - ret = _MM_GST_ELEMENT_LINK_MANY(sc->encode_element[_MMCAMCORDER_AUDIOSRC_BIN].gst, - sc->encode_element[_MMCAMCORDER_ENCSINK_AQUE].gst, - sc->encode_element[_MMCAMCORDER_ENCSINK_CONV].gst, - sc->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst, - sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, - NULL); - if (!ret) { - err = MM_ERROR_CAMCORDER_GST_LINK; - goto pipeline_creation_error; - } + /* link elements */ + if (!_mmcamcorder_link_elements(element_list)) { + _mmcam_dbg_err("encode element link error."); + err = MM_ERROR_CAMCORDER_GST_LINK; + goto pipeline_creation_error; } } @@ -204,6 +196,14 @@ static int __mmcamcorder_create_audiop_with_encodebin(MMHandleType handle) gst_object_unref(srcpad); srcpad = NULL; + sinkpad = gst_element_get_static_pad(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "sink"); + MMCAMCORDER_ADD_BUFFER_PROBE(sinkpad, _MMCAMCORDER_HANDLER_AUDIOREC, + __mmcamcorder_muxed_dataprobe, hcamcorder); + MMCAMCORDER_ADD_EVENT_PROBE(sinkpad, _MMCAMCORDER_HANDLER_AUDIOREC, + __mmcamcorder_eventprobe_monitor, hcamcorder); + gst_object_unref(sinkpad); + sinkpad = NULL; + bus = gst_pipeline_get_bus(GST_PIPELINE(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst)); /* register message callback */ @@ -403,25 +403,28 @@ _mmcamcorder_audio_command(MMHandleType handle, int command) goto _ERR_CAMCORDER_AUDIO_COMMAND; } - if (temp_filename == NULL) { - _mmcam_dbg_err("filename is not set"); + if (!temp_filename && !hcamcorder->mstream_cb) { + _mmcam_dbg_err("filename is not set and muxed stream cb is NULL"); ret = MM_ERROR_CAMCORDER_INVALID_ARGUMENT; goto _ERR_CAMCORDER_AUDIO_COMMAND; } - info->filename = g_strdup(temp_filename); - if (!info->filename) { - _mmcam_dbg_err("STRDUP was failed"); - goto _ERR_CAMCORDER_AUDIO_COMMAND; - } + if (temp_filename) { + info->filename = g_strdup(temp_filename); + if (!info->filename) { + _mmcam_dbg_err("STRDUP was failed"); + goto _ERR_CAMCORDER_AUDIO_COMMAND; + } - _mmcam_dbg_log("Record start : set file name using attribute - %s\n ", info->filename); + _mmcam_dbg_log("Record start : set file name using attribute - %s\n ", info->filename); - MMCAMCORDER_G_OBJECT_SET_POINTER(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename); + MMCAMCORDER_G_OBJECT_SET_POINTER(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, "location", info->filename); + } sc->ferror_send = FALSE; sc->ferror_count = 0; sc->bget_eos = FALSE; + sc->muxed_stream_offset = 0; info->filesize = 0; /* set max size */ @@ -755,6 +758,7 @@ static GstPadProbeReturn __mmcamcorder_audio_dataprobe_voicerecorder(GstPad *pad { mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data); double volume = 0.0; + int current_state = MM_CAMCORDER_STATE_NONE; int format = 0; int channel = 0; float curdcb = 0.0; @@ -766,6 +770,12 @@ static GstPadProbeReturn __mmcamcorder_audio_dataprobe_voicerecorder(GstPad *pad mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK); + current_state = _mmcamcorder_get_state((MMHandleType)hcamcorder); + if (current_state < MM_CAMCORDER_STATE_PREPARE) { + _mmcam_dbg_warn("Not ready for stream callback"); + return GST_PAD_PROBE_OK; + } + memset(&mapinfo, 0x0, sizeof(GstMapInfo)); /* Set volume to audio input */ @@ -793,16 +803,12 @@ static GstPadProbeReturn __mmcamcorder_audio_dataprobe_voicerecorder(GstPad *pad msg.param.rec_volume_dB = curdcb; _mmcamcorder_send_message((MMHandleType)hcamcorder, &msg); + _MMCAMCORDER_LOCK_ASTREAM_CALLBACK(hcamcorder); + /* CALL audio stream callback */ - if ((hcamcorder->astream_cb) && buffer && mapinfo.data && mapinfo.size > 0) { + if (hcamcorder->astream_cb && buffer && mapinfo.data && mapinfo.size > 0) { MMCamcorderAudioStreamDataType stream; - if (_mmcamcorder_get_state((MMHandleType)hcamcorder) < MM_CAMCORDER_STATE_PREPARE) { - _mmcam_dbg_warn("Not ready for stream callback"); - gst_buffer_unmap(buffer, &mapinfo); - return GST_PAD_PROBE_OK; - } - /* _mmcam_dbg_log("Call audio steramCb, data[%p], format[%d], channel[%d], length[%d], volume_dB[%f]", GST_BUFFER_DATA(buffer), format, channel, GST_BUFFER_SIZE(buffer), curdcb); @@ -815,15 +821,13 @@ static GstPadProbeReturn __mmcamcorder_audio_dataprobe_voicerecorder(GstPad *pad stream.timestamp = (unsigned int)(GST_BUFFER_PTS(buffer)/1000000); /* nano -> msecond */ stream.volume_dB = curdcb; - _MMCAMCORDER_LOCK_ASTREAM_CALLBACK(hcamcorder); - - if (hcamcorder->astream_cb) - hcamcorder->astream_cb(&stream, hcamcorder->astream_cb_param); - - _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK(hcamcorder); + hcamcorder->astream_cb(&stream, hcamcorder->astream_cb_param); } + _MMCAMCORDER_UNLOCK_ASTREAM_CALLBACK(hcamcorder); + gst_buffer_unmap(buffer, &mapinfo); + return GST_PAD_PROBE_OK; } diff --git a/src/mm_camcorder_gstcommon.c b/src/mm_camcorder_gstcommon.c index 73aedbf..7dd6f6b 100644 --- a/src/mm_camcorder_gstcommon.c +++ b/src/mm_camcorder_gstcommon.c @@ -730,6 +730,7 @@ void _mmcamcorder_set_encoder_bitrate(MMCamcorderEncoderType type, int codec, in int _mmcamcorder_create_encodesink_bin(MMHandleType handle, MMCamcorderEncodebinProfile profile) { int err = MM_ERROR_NONE; + int file_name_len = 0; int channel = 0; int audio_enc = 0; int v_bitrate = 0; @@ -748,6 +749,7 @@ int _mmcamcorder_create_encodesink_bin(MMHandleType handle, MMCamcorderEncodebin const char *str_aar = NULL; const char *str_acs = NULL; char *err_name = NULL; + char *file_name = NULL; const char *videoconvert_name = NULL; GstCaps *audio_caps = NULL; GstCaps *video_caps = NULL; @@ -867,6 +869,7 @@ int _mmcamcorder_create_encodesink_bin(MMHandleType handle, MMCamcorderEncodebin MMCAM_AUDIO_CHANNEL, &channel, MMCAM_VIDEO_ENCODER_BITRATE, &v_bitrate, MMCAM_AUDIO_ENCODER_BITRATE, &a_bitrate, + MMCAM_TARGET_FILENAME, &file_name, &file_name_len, NULL); if (err != MM_ERROR_NONE) { @@ -1063,15 +1066,20 @@ int _mmcamcorder_create_encodesink_bin(MMHandleType handle, MMCamcorderEncodebin /* Sink */ if (profile != MM_CAMCORDER_ENCBIN_PROFILE_IMAGE) { /* for recording */ - _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 (file_name) { + _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); - _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_SINK, gst_element_rsink_name, NULL, element_list, err); + _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_SINK, gst_element_rsink_name, NULL, element_list, err); - _mmcamcorder_conf_set_value_element_property(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, RecordsinkElement); + _mmcamcorder_conf_set_value_element_property(sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst, RecordsinkElement); + } else { + /* if file_name is not set, add fakesink for muxed stream callback */ + _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_SINK, "fakesink", NULL, element_list, err); + } } else { /* for stillshot */ _MMCAMCORDER_ELEMENT_MAKE(sc, sc->encode_element, _MMCAMCORDER_ENCSINK_SINK, "fakesink", NULL, element_list, err); @@ -1545,7 +1553,7 @@ static GstPadProbeReturn __mmcamcorder_video_dataprobe_preview(GstPad *pad, GstP GstMapInfo mapinfo; mmf_return_val_if_fail(buffer, GST_PAD_PROBE_DROP); - mmf_return_val_if_fail(gst_buffer_n_memory(buffer) , GST_PAD_PROBE_DROP); + mmf_return_val_if_fail(gst_buffer_n_memory(buffer), GST_PAD_PROBE_DROP); mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_DROP); sc = MMF_CAMCORDER_SUBCONTEXT(u_data); @@ -1939,27 +1947,143 @@ static GstPadProbeReturn __mmcamcorder_video_dataprobe_push_buffer_to_record(Gst } +GstPadProbeReturn __mmcamcorder_muxed_dataprobe(GstPad *pad, GstPadProbeInfo *info, gpointer u_data) +{ + MMCamcorderMuxedStreamDataType stream; + mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data); + _MMCamcorderSubContext *sc = NULL; + GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info); + GstMapInfo mapinfo; + + mmf_return_val_if_fail(buffer, GST_PAD_PROBE_OK); + mmf_return_val_if_fail(gst_buffer_n_memory(buffer), GST_PAD_PROBE_OK); + mmf_return_val_if_fail(hcamcorder, GST_PAD_PROBE_OK); + + sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder); + mmf_return_val_if_fail(sc, GST_PAD_PROBE_OK); + + if (!gst_buffer_map(buffer, &mapinfo, GST_MAP_READ)) { + _mmcam_dbg_warn("map failed : buffer %p", buffer); + return GST_PAD_PROBE_OK; + } + + /* call application callback */ + _MMCAMCORDER_LOCK_MSTREAM_CALLBACK(hcamcorder); + + if (hcamcorder->mstream_cb) { + stream.data = (void *)mapinfo.data; + stream.length = mapinfo.size; + stream.offset = sc->muxed_stream_offset; + hcamcorder->mstream_cb(&stream, hcamcorder->mstream_cb_param); + } + + _MMCAMCORDER_UNLOCK_MSTREAM_CALLBACK(hcamcorder); + + /* calculate current offset */ + sc->muxed_stream_offset += mapinfo.size; + + gst_buffer_unmap(buffer, &mapinfo); + + return GST_PAD_PROBE_OK; +} + + +GstPadProbeReturn __mmcamcorder_eventprobe_monitor(GstPad *pad, GstPadProbeInfo *info, gpointer u_data) +{ + GstEvent *event = GST_PAD_PROBE_INFO_EVENT(info); + mmf_camcorder_t *hcamcorder = NULL; + _MMCamcorderSubContext *sc = NULL; + GstObject *parent = NULL; + + 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_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_SEGMENT: + _mmcam_dbg_log("[%s:%s] gots %s", GST_DEBUG_PAD_NAME(pad), GST_EVENT_TYPE_NAME(event)); + + hcamcorder = MMF_CAMCORDER(u_data); + if (!hcamcorder) { + _mmcam_dbg_warn("NULL handle"); + break; + } + + sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder); + if (!sc) { + _mmcam_dbg_warn("NULL sub context"); + break; + } + + if (!sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst) { + _mmcam_dbg_warn("no encoder sink"); + break; + } + + parent = gst_pad_get_parent(pad); + if (!parent) { + _mmcam_dbg_warn("get parent failed"); + break; + } + + if (parent == (GstObject *)sc->encode_element[_MMCAMCORDER_ENCSINK_SINK].gst) { + const GstSegment *segment; + gst_event_parse_segment(event, &segment); + if (segment->format == GST_FORMAT_BYTES) { + _mmcam_dbg_log("change current offset %llu -> %llu", + sc->muxed_stream_offset, segment->start); + + sc->muxed_stream_offset = (unsigned long long)segment->start; + } + } + + gst_object_unref(parent); + parent = NULL; + 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 GST_PAD_PROBE_OK; +} + + int __mmcamcorder_get_amrnb_bitrate_mode(int bitrate) { int result = MM_CAMCORDER_MR475; - if (bitrate < 5150) { + if (bitrate < 5150) result = MM_CAMCORDER_MR475; /*AMR475*/ - } else if (bitrate < 5900) { + else if (bitrate < 5900) result = MM_CAMCORDER_MR515; /*AMR515*/ - } else if (bitrate < 6700) { + else if (bitrate < 6700) result = MM_CAMCORDER_MR59; /*AMR59*/ - } else if (bitrate < 7400) { + else if (bitrate < 7400) result = MM_CAMCORDER_MR67; /*AMR67*/ - } else if (bitrate < 7950) { + else if (bitrate < 7950) result = MM_CAMCORDER_MR74; /*AMR74*/ - } else if (bitrate < 10200) { + else if (bitrate < 10200) result = MM_CAMCORDER_MR795; /*AMR795*/ - } else if (bitrate < 12200) { + else if (bitrate < 12200) result = MM_CAMCORDER_MR102; /*AMR102*/ - } else { + else result = MM_CAMCORDER_MR122; /*AMR122*/ - } return result; } @@ -2055,7 +2179,7 @@ int _mmcamcorder_check_codec_fileformat_compatibility(const char *codec_type, in } else if (!strcmp(codec_type, MMCAM_VIDEO_ENCODER)) { if (codec > MM_VIDEO_CODEC_INVALID && codec < MM_VIDEO_CODEC_NUM && file_format > MM_FILE_FORMAT_INVALID && file_format < MM_FILE_FORMAT_NUM) { - if (videocodec_fileformat_compatibility_table[ codec][file_format] == 0) { + if (videocodec_fileformat_compatibility_table[codec][file_format] == 0) { _mmcam_dbg_err("Video codec[%d] and file format[%d] compatibility FAILED.", codec, file_format); return MM_ERROR_CAMCORDER_ENCODER_WRONG_TYPE; } @@ -2287,11 +2411,8 @@ bool _mmcamcorder_set_videosrc_caps(MMHandleType handle, unsigned int fourcc, in motion_rate, capture_width, capture_height, fps_auto, sc->info_video->video_width, sc->info_video->video_height); - if (motion_rate != _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE) { - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "high-speed-fps", fps); - } else { - MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "high-speed-fps", 0); - } + MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, + "high-speed-fps", (motion_rate != _MMCAMCORDER_DEFAULT_RECORDING_MOTION_RATE ? fps : 0)); MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "capture-width", capture_width); MMCAMCORDER_G_OBJECT_SET(sc->element[_MMCAMCORDER_VIDEOSRC_SRC].gst, "capture-height", capture_height); diff --git a/src/mm_camcorder_internal.c b/src/mm_camcorder_internal.c index 8d87ff4..5b80a1e 100644 --- a/src/mm_camcorder_internal.c +++ b/src/mm_camcorder_internal.c @@ -143,6 +143,7 @@ int _mmcamcorder_create(MMHandleType *handle, MMCamPreset *info) g_mutex_init(&(hcamcorder->mtsafe).vcapture_cb_lock); g_mutex_init(&(hcamcorder->mtsafe).vstream_cb_lock); g_mutex_init(&(hcamcorder->mtsafe).astream_cb_lock); + g_mutex_init(&(hcamcorder->mtsafe).mstream_cb_lock); #ifdef _MMCAMCORDER_MURPHY_SUPPORT g_cond_init(&(hcamcorder->mtsafe).resource_cond); g_mutex_init(&(hcamcorder->mtsafe).resource_lock); @@ -565,6 +566,7 @@ _ERR_DEFAULT_VALUE_INIT: g_mutex_clear(&(hcamcorder->mtsafe).vcapture_cb_lock); g_mutex_clear(&(hcamcorder->mtsafe).vstream_cb_lock); g_mutex_clear(&(hcamcorder->mtsafe).astream_cb_lock); + g_mutex_clear(&(hcamcorder->mtsafe).mstream_cb_lock); #ifdef _MMCAMCORDER_MURPHY_SUPPORT g_cond_clear(&(hcamcorder->mtsafe).resource_cond); g_mutex_clear(&(hcamcorder->mtsafe).resource_lock); @@ -802,6 +804,7 @@ int _mmcamcorder_destroy(MMHandleType handle) g_mutex_clear(&(hcamcorder->mtsafe).vcapture_cb_lock); g_mutex_clear(&(hcamcorder->mtsafe).vstream_cb_lock); g_mutex_clear(&(hcamcorder->mtsafe).astream_cb_lock); + g_mutex_clear(&(hcamcorder->mtsafe).mstream_cb_lock); #ifdef _MMCAMCORDER_MURPHY_SUPPORT g_cond_clear(&(hcamcorder->mtsafe).resource_cond); g_mutex_clear(&(hcamcorder->mtsafe).resource_lock); @@ -2039,6 +2042,31 @@ int _mmcamcorder_set_audio_stream_callback(MMHandleType handle, mm_camcorder_aud } +int _mmcamcorder_set_muxed_stream_callback(MMHandleType handle, mm_camcorder_muxed_stream_callback callback, void *user_data) +{ + mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle); + + _mmcam_dbg_log(""); + + mmf_return_val_if_fail(hcamcorder, MM_ERROR_CAMCORDER_NOT_INITIALIZED); + + if (callback == NULL) + _mmcam_dbg_warn("Muxed Stream Callback is disabled, because application sets it to NULL"); + + if (!_MMCAMCORDER_TRYLOCK_MSTREAM_CALLBACK(hcamcorder)) { + _mmcam_dbg_warn("Application's muxed stream callback is running now"); + return MM_ERROR_CAMCORDER_INVALID_CONDITION; + } + + hcamcorder->mstream_cb = callback; + hcamcorder->mstream_cb_param = user_data; + + _MMCAMCORDER_UNLOCK_MSTREAM_CALLBACK(hcamcorder); + + return MM_ERROR_NONE; +} + + int _mmcamcorder_set_video_capture_callback(MMHandleType handle, mm_camcorder_video_capture_callback callback, void *user_data) { mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle); diff --git a/src/mm_camcorder_videorec.c b/src/mm_camcorder_videorec.c index 83f85da..644a610 100644 --- a/src/mm_camcorder_videorec.c +++ b/src/mm_camcorder_videorec.c @@ -54,7 +54,6 @@ static GstPadProbeReturn __mmcamcorder_video_dataprobe_audio_disable(GstPad *pad 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); -static GstPadProbeReturn __mmcamcorder_eventprobe_monitor(GstPad *pad, GstPadProbeInfo *info, gpointer u_data); /*======================================================================================= | FUNCTION DEFINITIONS | @@ -225,8 +224,7 @@ int _mmcamcorder_create_recorder_pipeline(MMHandleType handle) /* 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->encode_element[_MMCAMCORDER_ENCSINK_AENC].gst, "sink"); MMCAMCORDER_ADD_BUFFER_PROBE(sinkpad, _MMCAMCORDER_HANDLER_VIDEOREC, @@ -280,6 +278,14 @@ int _mmcamcorder_create_recorder_pipeline(MMHandleType handle) srcpad = NULL; } + 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); + sinkpad = NULL; + bus = gst_pipeline_get_bus(GST_PIPELINE(sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst)); /* register pipeline message callback */ @@ -763,6 +769,7 @@ int _mmcamcorder_video_command(MMHandleType handle, int command) sc->ferror_count = 0; hcamcorder->error_occurs = FALSE; sc->bget_eos = FALSE; + sc->muxed_stream_offset = 0; ret = _mmcamcorder_gst_set_state(handle, sc->encode_element[_MMCAMCORDER_ENCODE_MAIN_PIPE].gst, GST_STATE_PLAYING); if (ret != MM_ERROR_NONE) { @@ -1280,51 +1287,6 @@ int _mmcamcorder_video_handle_eos(MMHandleType handle) } -/** - * 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 GstPadProbeReturn __mmcamcorder_eventprobe_monitor(GstPad *pad, GstPadProbeInfo *info, gpointer u_data) -{ - GstEvent *event = GST_PAD_PROBE_INFO_EVENT(info); - 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_SEGMENT: - 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 GST_PAD_PROBE_OK; -} - - static GstPadProbeReturn __mmcamcorder_audio_dataprobe_check(GstPad *pad, GstPadProbeInfo *info, gpointer u_data) { mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data); -- 2.7.4