#define USE_AUDIO_CLOCK_TUNE
#define _MMCAMCORDER_WAIT_EOS_TIME 60.0 /* sec */
#define _MMCAMCORDER_CONVERT_OUTPUT_BUFFER_NUM 6
-#define _MMCAMCORDER_MIN_TIME_TO_PASS_FRAME 30000000 /* ns */
-#define _MMCAMCORDER_FRAME_PASS_MIN_FPS 30
#define _MMCAMCORDER_NANOSEC_PER_1SEC 1000000000
#define _MMCAMCORDER_NANOSEC_PER_1MILISEC 1000
-----------------------------------------------------------------------*/
/* STATIC INTERNAL FUNCTION */
/**
- * These functions are preview 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.
+ * These are video data probing functions.
+ * If there are pads which set probe callback by gst_pad_add_probe(),
+ * the functions 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
+ * @param[in] pad probing pad which calls this function.
+ * @param[in] buffer buffer which contains stream data.
+ * @param[in] u_data user data.
+ * @return Refer #GstPadProbeReturn in gstpad.h
* @remarks
- * @see __mmcamcorder_create_preview_pipeline()
+ * @see __mmcamcorder_create_preview_pipeline()
*/
static GstPadProbeReturn __mmcamcorder_video_dataprobe_preview(GstPad *pad, GstPadProbeInfo *info, gpointer u_data);
-static GstPadProbeReturn __mmcamcorder_video_dataprobe_push_buffer_to_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data);
+static GstPadProbeReturn __mmcamcorder_video_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data);
+
static int __mmcamcorder_get_amrnb_bitrate_mode(int bitrate);
static guint32 _mmcamcorder_convert_fourcc_string_to_value(const gchar* format_name);
#ifdef _MMCAMCORDER_PRODUCT_TV
goto pipeline_creation_error;
}
- /* set dataprobe for video recording */
- if (_mmcamcorder_is_encoded_preview_pixel_format(sc->info_image->preview_format))
- srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "src");
- else
- srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "src");
+ /* set dataprobe for video recording if it does not support dual stream. */
+ if (sc->info_video->record_dual_stream == FALSE) {
+ if (_mmcamcorder_is_encoded_preview_pixel_format(sc->info_image->preview_format))
+ srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_VIDEOSRC_QUE].gst, "src");
+ else
+ srcpad = gst_element_get_static_pad(sc->element[_MMCAMCORDER_VIDEOSINK_QUE].gst, "src");
- MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_PREVIEW,
- __mmcamcorder_video_dataprobe_push_buffer_to_record, hcamcorder);
- gst_object_unref(srcpad);
- srcpad = NULL;
+ MMCAMCORDER_ADD_BUFFER_PROBE(srcpad, _MMCAMCORDER_HANDLER_PREVIEW,
+ __mmcamcorder_video_dataprobe_record, hcamcorder);
+ gst_object_unref(srcpad);
+ srcpad = NULL;
+ }
bus = gst_pipeline_get_bus(GST_PIPELINE(sc->element[_MMCAMCORDER_MAIN_PIPE].gst));
/* set flag */
if (sc->info_video->push_encoding_buffer == PUSH_ENCODING_BUFFER_INIT) {
- sc->info_video->get_first_I_frame = FALSE;
sc->info_video->push_encoding_buffer = PUSH_ENCODING_BUFFER_RUN;
_mmcam_dbg_warn("set push_encoding_buffer RUN");
}
return GST_PAD_PROBE_OK;
}
-static GstPadProbeReturn __mmcamcorder_video_dataprobe_push_buffer_to_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
-{
- mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
- _MMCamcorderSubContext *sc = NULL;
- GstClockTime diff = 0; /* nsec */
- GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
-
- 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(hcamcorder, GST_PAD_PROBE_DROP);
-
- sc = MMF_CAMCORDER_SUBCONTEXT(u_data);
- mmf_return_val_if_fail(sc, GST_PAD_PROBE_DROP);
-
- /* push buffer in appsrc to encode */
- if (!sc->info_video) {
- _mmcam_dbg_warn("sc->info_video is NULL!!");
- return GST_PAD_PROBE_DROP;
- }
-
- if (sc->info_video->push_encoding_buffer == PUSH_ENCODING_BUFFER_RUN &&
- sc->info_video->record_dual_stream == FALSE &&
- sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst) {
- int ret = 0;
- GstClock *clock = NULL;
-
- /*
- _mmcam_dbg_log("GST_BUFFER_FLAG_DELTA_UNIT is set : %d",
- GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT));
- */
-
- /* check first I frame */
- if (sc->info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264 &&
- sc->info_video->get_first_I_frame == FALSE) {
- if (!GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT)) {
- _mmcam_dbg_warn("first I frame is come");
- sc->info_video->get_first_I_frame = TRUE;
- } else {
- _mmcam_dbg_warn("NOT I frame.. skip this buffer");
- return GST_PAD_PROBE_OK;
- }
- }
-
- if (sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst) {
- if (sc->info_video->is_firstframe) {
- clock = GST_ELEMENT_CLOCK(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst);
- if (clock) {
- gst_object_ref(clock);
- sc->info_video->base_video_ts = GST_BUFFER_PTS(buffer) - (gst_clock_get_time(clock) - GST_ELEMENT(sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst)->base_time);
- gst_object_unref(clock);
- }
- }
- } else {
- if (sc->info_video->is_firstframe) {
- /* for image capture with encodebin(ex:emulator) */
- if (sc->bencbin_capture && sc->info_image->capturing) {
- g_mutex_lock(&hcamcorder->task_thread_lock);
- _mmcam_dbg_log("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);
- }
- sc->info_video->base_video_ts = GST_BUFFER_PTS(buffer);
- }
- }
- GST_BUFFER_PTS(buffer) = GST_BUFFER_PTS(buffer) - sc->info_video->base_video_ts;
- GST_BUFFER_DTS(buffer) = GST_BUFFER_PTS(buffer);
-
- /*_mmcam_dbg_log("buffer %p, timestamp %"GST_TIME_FORMAT, buffer, GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
-
- if (0) {
- GstCaps *caps = gst_pad_get_current_caps(pad);
- if (caps) {
- char *caps_string = gst_caps_to_string(caps);
- if (caps_string) {
- _mmcam_dbg_log("%s", caps_string);
- g_free(caps_string);
- caps_string = NULL;
- }
- gst_caps_unref(caps);
- caps = NULL;
- } else {
- _mmcam_dbg_warn("failed to get caps from pad");
- }
- }
- g_signal_emit_by_name(sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, "push-buffer", buffer, &ret);
-
- /*_mmcam_dbg_log("push buffer result : 0x%x", ret);*/
-
- if (sc->info_video->is_firstframe) {
- sc->info_video->is_firstframe = FALSE;
-
- /* drop buffer if it's from tizen allocator */
- if (gst_is_tizen_memory(gst_buffer_peek_memory(buffer, 0))) {
- _mmcam_dbg_warn("drop first buffer from tizen allocator to avoid copy in basesrc");
- return GST_PAD_PROBE_DROP;
- }
- }
- }
-
- /* skip display if too fast FPS */
- if (sc->info_video && sc->info_video->fps > _MMCAMCORDER_FRAME_PASS_MIN_FPS) {
- if (sc->info_video->prev_preview_ts != 0) {
- diff = GST_BUFFER_PTS(buffer) - sc->info_video->prev_preview_ts;
- if (diff < _MMCAMCORDER_MIN_TIME_TO_PASS_FRAME) {
- _mmcam_dbg_log("it's too fast. drop frame...");
- return GST_PAD_PROBE_DROP;
- }
- }
-
- /*_mmcam_dbg_log("diff %llu", diff);*/
-
- sc->info_video->prev_preview_ts = GST_BUFFER_PTS(buffer);
- }
+static GstPadProbeReturn __mmcamcorder_video_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
+{
+ if (_mmcamcorder_video_push_buffer(u_data, GST_PAD_PROBE_INFO_BUFFER(info)))
+ return GST_PAD_PROBE_OK;
- return GST_PAD_PROBE_OK;
+ return GST_PAD_PROBE_DROP;
}
/*=======================================================================================
| INCLUDE FILES |
=======================================================================================*/
+#include <gst/allocators/gsttizenmemory.h>
#include <gst/video/cameracontrol.h>
#include <gst/app/gstappsrc.h>
#include "mm_camcorder_internal.h"
#include "mm_camcorder_videorec.h"
/*---------------------------------------------------------------------------------------
-| GLOBAL VARIABLE DEFINITIONS for internal |
----------------------------------------------------------------------------------------*/
-#define _MMCAMCORDER_LOCATION_INFO /* for add gps information */
-#define MAX_ERROR_MESSAGE_LEN 128
-
-/*---------------------------------------------------------------------------------------
| LOCAL VARIABLE DEFINITIONS for internal |
---------------------------------------------------------------------------------------*/
-#define _MMCAMCORDER_MINIMUM_FRAME 5
-#define _MMCAMCORDER_RETRIAL_COUNT 15
-#define _MMCAMCORDER_FRAME_WAIT_TIME 200000 /* us */
-#define _OFFSET_COMPOSITION_MATRIX 40L
-#define _GOP_GEN_INTERVAL 1000000000 /* ns */
-#define _MMCAMCORDER_VIDEO_MINIMUM_SPACE (_MMCAMCORDER_MINIMUM_SPACE << 1) /* byte */
+#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 FUNCTION PROTOTYPES: |
/* STATIC INTERNAL FUNCTION */
static gboolean __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_record(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);
/*---------------------------------------------------------------------------------------
| GLOBAL FUNCTION DEFINITIONS: |
---------------------------------------------------------------------------------------*/
-static gboolean __mmcamcorder_video_stream_cb(GstElement *element, GstSample *sample, gpointer u_data)
+gboolean _mmcamcorder_video_push_buffer(void *handle, GstBuffer *buffer)
{
- mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(u_data);
+ mmf_camcorder_t *hcamcorder = MMF_CAMCORDER(handle);
_MMCamcorderSubContext *sc = NULL;
+ _MMCamcorderImageInfo *info_image = NULL;
+ _MMCamcorderVideoInfo *info_video = NULL;
+ _MMCamcorderGstElement *element = NULL;
+ GstClockTime diff = 0; /* nsec */
+
+ mmf_return_val_if_fail(hcamcorder, FALSE);
+ mmf_return_val_if_fail(MMF_CAMCORDER_SUBCONTEXT(hcamcorder), FALSE);
- GstBuffer *buffer = gst_sample_get_buffer(sample);
mmf_return_val_if_fail(buffer, FALSE);
mmf_return_val_if_fail(gst_buffer_n_memory(buffer), FALSE);
- mmf_return_val_if_fail(hcamcorder, FALSE);
sc = MMF_CAMCORDER_SUBCONTEXT(hcamcorder);
- mmf_return_val_if_fail(sc, FALSE);
- /*
- _mmcam_dbg_log("ENTER - push_encoding_buffer %d, buffer %p, MALLOCDATA %p, size %d",
- sc->info_video->push_encoding_buffer, buffer, GST_BUFFER_MALLOCDATA(buffer), GST_BUFFER_SIZE(buffer));
- */
+ 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_dbg_log("GST_BUFFER_FLAG_DELTA_UNIT is set : %d",
+ GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT));
+ */
- /* push buffer in appsrc to encode */
- if (sc->info_video->push_encoding_buffer == PUSH_ENCODING_BUFFER_RUN &&
- sc->info_video->record_dual_stream &&
- sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst) {
- GstFlowReturn ret = 0;
- GstClock *pipe_clock = NULL;
-
- if (sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst) {
- if (sc->info_video->is_firstframe) {
- sc->info_video->is_firstframe = FALSE;
- pipe_clock = GST_ELEMENT_CLOCK(sc->encode_element[_MMCAMCORDER_AUDIOSRC_SRC].gst);
- if (pipe_clock) {
- gst_object_ref(pipe_clock);
- sc->info_video->base_video_ts = GST_BUFFER_PTS(buffer) - (gst_clock_get_time(pipe_clock) - GST_ELEMENT(sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst)->base_time);
- gst_object_unref(pipe_clock);
+ if (info_video->is_first_frame) {
+ /* check first I frame for H.264 stream */
+ if (info_image->preview_format == MM_PIXEL_FORMAT_ENCODED_H264) {
+ if (GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT)) {
+ _mmcam_dbg_warn("NOT I frame.. skip this buffer");
+ return TRUE;
+ } else {
+ _mmcam_dbg_warn("[H.264] first I frame");
}
}
- } else {
- if (sc->info_video->is_firstframe) {
- sc->info_video->is_firstframe = FALSE;
- sc->info_video->base_video_ts = GST_BUFFER_PTS(buffer);
+
+ /* 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 = GST_BUFFER_PTS(buffer) - (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 (sc->bencbin_capture && info_image->capturing) {
+ g_mutex_lock(&hcamcorder->task_thread_lock);
+ _mmcam_dbg_log("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 = GST_BUFFER_PTS(buffer);
}
}
- GST_BUFFER_PTS(buffer) = GST_BUFFER_PTS(buffer) - sc->info_video->base_video_ts;
+ GST_BUFFER_PTS(buffer) = GST_BUFFER_PTS(buffer) - info_video->base_video_ts;
GST_BUFFER_DTS(buffer) = GST_BUFFER_PTS(buffer);
- ret = gst_app_src_push_buffer((GstAppSrc *)sc->encode_element[_MMCAMCORDER_ENCSINK_SRC].gst, buffer);
- if (ret != GST_FLOW_OK && ret != GST_FLOW_FLUSHING) {
- _mmcam_dbg_err("gst_app_src_push_buffer failed [0x%x]", ret);
- gst_buffer_unref(buffer);
- buffer = NULL;
+ /*_mmcam_dbg_log("buffer %p, timestamp %"GST_TIME_FORMAT, buffer, GST_TIME_ARGS(GST_BUFFER_PTS(buffer)));*/
+
+ if (info_video->record_dual_stream) {
+ /* It will NOT INCREASE reference count of buffer */
+ ret = gst_app_src_push_buffer((GstAppSrc *)element[_MMCAMCORDER_ENCSINK_SRC].gst, buffer);
+ } else {
+ /* It will INCREASE reference count of buffer */
+ g_signal_emit_by_name(element[_MMCAMCORDER_ENCSINK_SRC].gst, "push-buffer", buffer, &ret);
}
/*_mmcam_dbg_log("push buffer result : 0x%x", ret);*/
- } else {
- _mmcam_dbg_warn("unref video buffer immediately - push encoding buffer %d",
- sc->info_video->push_encoding_buffer);
- gst_buffer_unref(buffer);
- buffer = NULL;
+ 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_dbg_warn("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) {
+ diff = GST_BUFFER_PTS(buffer) - info_video->prev_preview_ts;
+ if (diff < _MMCAMCORDER_MIN_TIME_TO_PASS_FRAME) {
+ _mmcam_dbg_log("it's too fast. drop frame...");
+ return FALSE;
+ }
+ }
+
+ /*_mmcam_dbg_log("diff %llu", diff);*/
+
+ info_video->prev_preview_ts = GST_BUFFER_PTS(buffer);
}
return TRUE;
}
+static gboolean __mmcamcorder_video_stream_cb(GstElement *element, GstSample *sample, gpointer u_data)
+{
+ return _mmcamcorder_video_push_buffer(u_data, gst_sample_get_buffer(sample));
+}
+
+
int _mmcamcorder_create_recorder_pipeline(MMHandleType handle)
{
int i = 0;
if (!strcmp(gst_element_rsink_name, "filesink")) {
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;
info->push_encoding_buffer = PUSH_ENCODING_BUFFER_INIT;
info->base_video_ts = 0;
- /* connect video stream cb signal */
- /*130826 Connect video stream cb for handling fast record frame cb*/
+ /* 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;
*/
info->video_frame_count = 0;
- info->is_firstframe = TRUE;
+ info->is_first_frame = TRUE;
info->audio_frame_count = 0;
info->filesize = 0;
sc->ferror_send = FALSE;
}
-static GstPadProbeReturn __mmcamcorder_video_dataprobe_record(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
+static GstPadProbeReturn __mmcamcorder_video_dataprobe_encoded(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
{
gint ret = 0;
guint vq_size = 0;
_mmcam_dbg_log("found [tkhd] tag");
/* seek to start position of composition matrix */
- if (fseek(f, _OFFSET_COMPOSITION_MATRIX, SEEK_CUR) == 0) {
+ if (fseek(f, OFFSET_COMPOSITION_MATRIX, SEEK_CUR) == 0) {
/* update composition matrix for orientation */
_mmcamcorder_update_composition_matrix(f, orientation);
} else {