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
/**
+ * 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.
*/
/**
+ * 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.
*
/**
+ * 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 <mm_camcorder.h>
+
+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.
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
#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)
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) */
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. */
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 */
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
}
+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);
{
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;
}
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);
}
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);
}
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;
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) {
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)
_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;
}
}
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 */
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 */
{
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;
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 */
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);
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;
}
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;
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;
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) {
/* 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);
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);
}
+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;
}
} 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;
}
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);
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);
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);
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);
}
+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);
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 |
/* 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,
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 */
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) {
}
-/**
- * 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);