/* GStreamer * Copyright (c) 2011, Texas Instruments Incorporated * Copyright (c) 2011, Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Author: Alessandro Decina */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gstducati.h" #include "gstducatividenc.h" #include "gstducatibufferpriv.h" #include #include #define GST_CAT_DEFAULT gst_ducati_debug #define DEFAULT_BITRATE 2048 #define DEFAULT_RATE_PRESET GST_DUCATI_VIDENC_RATE_PRESET_STORAGE #define DEFAULT_INTRA_INTERVAL 15 #define GST_TYPE_DUCATI_VIDENC_RATE_PRESET (gst_ducati_videnc_rate_preset_get_type ()) enum { LAST_SIGNAL }; enum { PROP_0, PROP_BITRATE, PROP_RATE_PRESET, PROP_INTRA_INTERVAL }; static void gst_ducati_videnc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_ducati_videnc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static GstDucatiBufferPriv *get_buffer_priv (GstDucatiVidEnc * self, GstBuffer * buf, gint stride, gint height); static gboolean gst_ducati_videnc_set_format (GstBaseVideoEncoder * base_video_encoder, GstVideoState * state); static gboolean gst_ducati_videnc_start (GstBaseVideoEncoder * base_video_encoder); static gboolean gst_ducati_videnc_stop (GstBaseVideoEncoder * base_video_encoder); static GstFlowReturn gst_ducati_videnc_finish (GstBaseVideoEncoder * base_video_encoder); static GstFlowReturn gst_ducati_videnc_handle_frame (GstBaseVideoEncoder * base_video_encoder, GstVideoFrame * frame); static gboolean gst_ducati_videnc_allocate_params_default (GstDucatiVidEnc * self, gint params_sz, gint dynparams_sz, gint status_sz, gint inargs_sz, gint outargs_sz); static gboolean gst_ducati_videnc_configure_default (GstDucatiVidEnc * self); static gboolean gst_ducati_videnc_event (GstBaseVideoEncoder * enc, GstEvent * event); GST_BOILERPLATE (GstDucatiVidEnc, gst_ducati_videnc, GstBaseVideoEncoder, GST_TYPE_BASE_VIDEO_ENCODER); /* the values for the following enums are taken from the codec */ enum { GST_DUCATI_VIDENC_RATE_PRESET_LOW_DELAY = 1, /**< CBR rate control for video conferencing. */ GST_DUCATI_VIDENC_RATE_PRESET_STORAGE = 2, /**< VBR rate control for local storage (DVD) * recording. */ GST_DUCATI_VIDENC_RATE_PRESET_TWOPASS = 3, /**< Two pass rate control for non real time * applications. */ GST_DUCATI_VIDENC_RATE_PRESET_NONE = 4, /**< No configurable video rate control * mechanism. */ GST_DUCATI_VIDENC_RATE_PRESET_USER_DEFINED = 5,/**< User defined configuration using extended * parameters. */ }; static GType gst_ducati_videnc_rate_preset_get_type (void) { static GType type = 0; if (!type) { static const GEnumValue vals[] = { {GST_DUCATI_VIDENC_RATE_PRESET_LOW_DELAY, "Low Delay", "low-delay"}, {GST_DUCATI_VIDENC_RATE_PRESET_STORAGE, "Storage", "storage"}, {GST_DUCATI_VIDENC_RATE_PRESET_TWOPASS, "Two-Pass", "two-pass"}, {GST_DUCATI_VIDENC_RATE_PRESET_NONE, "None", "None"}, {GST_DUCATI_VIDENC_RATE_PRESET_USER_DEFINED, "User defined", "user-defined"}, {0, NULL, NULL}, }; type = g_enum_register_static ("GstDucatiVidEncRatePreset", vals); } return type; } static void gst_ducati_videnc_base_init (gpointer g_class) { } static void gst_ducati_videnc_class_init (GstDucatiVidEncClass * klass) { GObjectClass *gobject_class; GstBaseVideoEncoderClass *basevideoencoder_class; gobject_class = G_OBJECT_CLASS (klass); basevideoencoder_class = GST_BASE_VIDEO_ENCODER_CLASS (klass); gobject_class->set_property = gst_ducati_videnc_set_property; gobject_class->get_property = gst_ducati_videnc_get_property; basevideoencoder_class->set_format = GST_DEBUG_FUNCPTR (gst_ducati_videnc_set_format); basevideoencoder_class->start = GST_DEBUG_FUNCPTR (gst_ducati_videnc_start); basevideoencoder_class->stop = GST_DEBUG_FUNCPTR (gst_ducati_videnc_stop); basevideoencoder_class->finish = GST_DEBUG_FUNCPTR (gst_ducati_videnc_finish); basevideoencoder_class->handle_frame = GST_DEBUG_FUNCPTR (gst_ducati_videnc_handle_frame); basevideoencoder_class->event = GST_DEBUG_FUNCPTR (gst_ducati_videnc_event); klass->allocate_params = gst_ducati_videnc_allocate_params_default; klass->configure = gst_ducati_videnc_configure_default; g_object_class_install_property (gobject_class, PROP_BITRATE, g_param_spec_int ("bitrate", "Bitrate", "Bitrate in kbit/sec", -1, 100 * 1024, DEFAULT_BITRATE, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_RATE_PRESET, g_param_spec_enum ("rate-preset", "H.264 Rate Control", "H.264 Rate Control", GST_TYPE_DUCATI_VIDENC_RATE_PRESET, DEFAULT_RATE_PRESET, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_INTRA_INTERVAL, g_param_spec_int ("intra-interval", "Intra-frame interval", "Interval between intra frames (keyframes)", 0, INT_MAX, DEFAULT_INTRA_INTERVAL, G_PARAM_READWRITE)); } static void gst_ducati_videnc_init (GstDucatiVidEnc * self, GstDucatiVidEncClass * klass) { GST_DEBUG ("gst_ducati_videnc_init"); self->device = NULL; self->engine = NULL; self->codec = NULL; self->params = NULL; self->status = NULL; self->inBufs = NULL; self->outBufs = NULL; self->inArgs = NULL; self->outArgs = NULL; self->input_pool = NULL; self->output_pool = NULL; self->bitrate = DEFAULT_BITRATE * 1000; self->rate_preset = DEFAULT_RATE_PRESET; self->intra_interval = DEFAULT_INTRA_INTERVAL; } static gboolean gst_ducati_videnc_set_format (GstBaseVideoEncoder * base_video_encoder, GstVideoState * state) { GstDucatiVidEnc *self = GST_DUCATIVIDENC (base_video_encoder); self->configure = TRUE; return TRUE; } static void gst_ducati_videnc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstDucatiVidEnc *self = GST_DUCATIVIDENC (object); g_return_if_fail (GST_IS_DUCATIVIDENC (object)); self = GST_DUCATIVIDENC (object); switch (prop_id) { case PROP_BITRATE: self->bitrate = g_value_get_int (value) * 1000; break; case PROP_RATE_PRESET: self->rate_preset = g_value_get_enum (value); break; case PROP_INTRA_INTERVAL: self->intra_interval = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void gst_ducati_videnc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstDucatiVidEnc *self = GST_DUCATIVIDENC (object); g_return_if_fail (GST_IS_DUCATIVIDENC (object)); self = GST_DUCATIVIDENC (object); switch (prop_id) { case PROP_BITRATE: g_value_set_int (value, self->bitrate / 1000); break; case PROP_RATE_PRESET: g_value_set_enum (value, self->rate_preset); break; case PROP_INTRA_INTERVAL: g_value_set_int (value, self->intra_interval); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static gboolean gst_ducati_videnc_configure (GstDucatiVidEnc * self) { int err; int i; int max_out_size = 0; const GstVideoState *state; state = gst_base_video_encoder_get_state (GST_BASE_VIDEO_ENCODER (self)); if (!GST_DUCATIVIDENC_GET_CLASS (self)->configure (self)) return FALSE; if (self->codec == NULL) { const gchar *codec_name; codec_name = GST_DUCATIVIDENC_GET_CLASS (self)->codec_name; self->codec = VIDENC2_create (self->engine, (String) codec_name, self->params); if (self->codec == NULL) { GST_ERROR_OBJECT (self, "couldn't create codec"); return FALSE; } } err = VIDENC2_control (self->codec, XDM_SETPARAMS, self->dynParams, self->status); if (err) { GST_ERROR_OBJECT (self, "XDM_SETPARAMS err=%d, extendedError=%08x", err, self->status->extendedError); gst_ducati_log_extended_error_info (self->status->extendedError); return FALSE; } err = VIDENC2_control (self->codec, XDM_GETBUFINFO, self->dynParams, self->status); if (err) { GST_ERROR_OBJECT (self, "XDM_GETBUFINFO err=%d, extendedError=%08x", err, self->status->extendedError); return FALSE; } self->outBufs->numBufs = self->status->bufInfo.minNumOutBufs; for (i = 0; i < self->outBufs->numBufs; i++) { int size = self->status->bufInfo.minOutBufSize[i].bytes; if (size > max_out_size) max_out_size = size; } g_assert (self->input_pool == NULL); self->input_pool = gst_drm_buffer_pool_new (GST_ELEMENT (self), dce_get_fd (), NULL, state->bytes_per_picture); g_assert (self->output_pool == NULL); self->output_pool = gst_drm_buffer_pool_new (GST_ELEMENT (self), dce_get_fd (), NULL, max_out_size); GST_INFO_OBJECT (self, "configured"); self->configure = FALSE; return TRUE; } static gboolean gst_ducati_videnc_configure_default (GstDucatiVidEnc * self) { VIDENC2_DynamicParams *dynParams; VIDENC2_Params *params; const GstVideoState *state; int i; state = gst_base_video_encoder_get_state (GST_BASE_VIDEO_ENCODER (self)); if (self->rect.w == 0) self->rect.w = state->width; if (self->rect.h == 0) self->rect.h = state->height; params = (VIDENC2_Params *) self->params; params->encodingPreset = 0x03; params->rateControlPreset = self->rate_preset; params->maxHeight = self->rect.h; params->maxWidth = self->rect.w; params->dataEndianness = XDM_BYTE; params->maxInterFrameInterval = 1; params->maxBitRate = -1; params->minBitRate = 0; params->inputChromaFormat = XDM_YUV_420SP; params->inputContentType = IVIDEO_PROGRESSIVE; params->operatingMode = IVIDEO_ENCODE_ONLY; params->inputDataMode = IVIDEO_ENTIREFRAME; params->outputDataMode = IVIDEO_ENTIREFRAME; params->numInputDataUnits = 1; params->numOutputDataUnits = 1; for (i = 0; i < IVIDEO_MAX_NUM_METADATA_PLANES; i++) { params->metadataType[i] = IVIDEO_METADATAPLANE_NONE; } dynParams = (VIDENC2_DynamicParams *) self->dynParams; dynParams->refFrameRate = gst_util_uint64_scale (1000, state->fps_n, state->fps_d); dynParams->targetFrameRate = dynParams->refFrameRate; dynParams->inputWidth = self->rect.w; dynParams->inputHeight = self->rect.h; dynParams->targetBitRate = self->bitrate; dynParams->intraFrameInterval = self->intra_interval; dynParams->captureWidth = dynParams->inputWidth; dynParams->forceFrame = IVIDEO_NA_FRAME; dynParams->interFrameInterval = 1; dynParams->mvAccuracy = IVIDENC2_MOTIONVECTOR_QUARTERPEL; dynParams->sampleAspectRatioHeight = 1; dynParams->sampleAspectRatioWidth = 1; dynParams->generateHeader = XDM_ENCODE_AU; dynParams->ignoreOutbufSizeFlag = 1; dynParams->lateAcquireArg = -1; self->inBufs->chromaFormat = XDM_YUV_420SP; self->inBufs->numPlanes = 2; return TRUE; } static gboolean gst_ducati_videnc_open_engine (GstDucatiVidEnc * self) { int error_code; if (self->device == NULL) { self->device = dce_init (); if (self->device == NULL) return FALSE; } self->engine = Engine_open ((String) "ivahd_vidsvr", NULL, &error_code); if (self->engine == NULL) { GST_ERROR_OBJECT (self, "couldn't open engine"); return FALSE; } return TRUE; } static gboolean gst_ducati_videnc_allocate_params (GstDucatiVidEnc * self) { return GST_DUCATIVIDENC_GET_CLASS (self)->allocate_params (self, sizeof (IVIDENC2_Params), sizeof (IVIDENC2_DynamicParams), sizeof (IVIDENC2_Status), sizeof (IVIDENC2_InArgs), sizeof (IVIDENC2_OutArgs)); } static gboolean gst_ducati_videnc_allocate_params_default (GstDucatiVidEnc * self, gint params_sz, gint dynparams_sz, gint status_sz, gint inargs_sz, gint outargs_sz) { self->params = dce_alloc (params_sz); memset (self->params, 0, params_sz); self->params->size = params_sz; self->dynParams = dce_alloc (dynparams_sz); memset (self->dynParams, 0, dynparams_sz); self->dynParams->size = dynparams_sz; self->status = dce_alloc (status_sz); memset (self->status, 0, status_sz); self->status->size = status_sz; self->inBufs = dce_alloc (sizeof (IVIDEO2_BufDesc)); memset (self->inBufs, 0, sizeof (IVIDEO2_BufDesc)); self->outBufs = dce_alloc (sizeof (XDM2_BufDesc)); memset (self->outBufs, 0, sizeof (XDM2_BufDesc)); self->inArgs = dce_alloc (inargs_sz); memset (self->inArgs, 0, inargs_sz); self->inArgs->size = inargs_sz; self->outArgs = dce_alloc (outargs_sz); memset (self->outArgs, 0, outargs_sz); self->outArgs->size = outargs_sz; GST_INFO_OBJECT (self, "started"); return TRUE; } static gboolean gst_ducati_videnc_free_params (GstDucatiVidEnc * self) { if (self->params) { dce_free (self->params); self->params = NULL; } if (self->dynParams) { dce_free (self->dynParams); self->dynParams = NULL; } if (self->inArgs) { dce_free (self->inArgs); self->inArgs = NULL; } if (self->outArgs) { dce_free (self->outArgs); self->outArgs = NULL; } if (self->status) { dce_free (self->status); self->status = NULL; } if (self->inBufs) { dce_free (self->inBufs); self->inBufs = NULL; } if (self->outBufs) { dce_free (self->outBufs); self->outBufs = NULL; } if (self->codec) { VIDENC2_delete (self->codec); self->codec = NULL; } return TRUE; } static void gst_ducati_videnc_close_engine (GstDucatiVidEnc * self) { if (self->engine) { Engine_close (self->engine); self->engine = NULL; } if (self->device) { dce_deinit (self->device); self->device = NULL; } } static gboolean gst_ducati_videnc_start (GstBaseVideoEncoder * base_video_encoder) { GstDucatiVidEnc *self = GST_DUCATIVIDENC (base_video_encoder); self->configure = TRUE; memset (&self->rect, 0, sizeof (GstDucatiVideoRectangle)); if (!gst_ducati_videnc_open_engine (self)) goto fail; if (!gst_ducati_videnc_allocate_params (self)) goto fail; return TRUE; fail: gst_ducati_videnc_free_params (self); gst_ducati_videnc_close_engine (self); return FALSE; } static gboolean gst_ducati_videnc_stop (GstBaseVideoEncoder * base_video_encoder) { GstDucatiVidEnc *self = GST_DUCATIVIDENC (base_video_encoder); gst_ducati_videnc_free_params (self); gst_ducati_videnc_close_engine (self); if (self->input_pool) { gst_drm_buffer_pool_destroy (self->input_pool); self->input_pool = NULL; } if (self->output_pool) { gst_drm_buffer_pool_destroy (self->output_pool); self->output_pool = NULL; } /* reset cropping rect */ memset (&self->rect, 0, sizeof (GstDucatiVideoRectangle)); return TRUE; } static GstFlowReturn gst_ducati_videnc_finish (GstBaseVideoEncoder * base_video_encoder) { GstDucatiVidEnc *self = GST_DUCATIVIDENC (base_video_encoder); GST_DEBUG_OBJECT (self, "finish"); return GST_FLOW_OK; } static GstFlowReturn gst_ducati_videnc_handle_frame (GstBaseVideoEncoder * base_video_encoder, GstVideoFrame * frame) { GstDucatiVidEnc *self = GST_DUCATIVIDENC (base_video_encoder); GstBuffer *inbuf, *outbuf; GstDucatiBufferPriv *priv_in, *priv_out; XDAS_Int32 err; const GstVideoState *state; int i; state = gst_base_video_encoder_get_state (base_video_encoder); if (G_UNLIKELY (self->configure)) { if (!gst_ducati_videnc_configure (self)) { GST_DEBUG_OBJECT (self, "configure failed"); GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL), (NULL)); return GST_FLOW_ERROR; } } inbuf = gst_buffer_ref (frame->sink_buffer); have_inbuf: priv_in = get_buffer_priv (self, inbuf, state->width, state->height); if (priv_in == NULL) { GST_DEBUG_OBJECT (self, "memcpying input"); gst_buffer_unref (inbuf); inbuf = GST_BUFFER (gst_drm_buffer_pool_get (self->input_pool, FALSE)); memcpy (GST_BUFFER_DATA (inbuf), GST_BUFFER_DATA (frame->sink_buffer), GST_BUFFER_SIZE (frame->sink_buffer)); goto have_inbuf; } outbuf = GST_BUFFER (gst_drm_buffer_pool_get (self->output_pool, FALSE)); priv_out = get_buffer_priv (self, outbuf, state->width, state->height); self->inBufs->planeDesc[0].buf = (XDAS_Int8 *) omap_bo_handle (priv_in->bo); self->inBufs->planeDesc[0].memType = XDM_MEMTYPE_BO; self->inBufs->planeDesc[0].bufSize.tileMem.width = state->width; self->inBufs->planeDesc[0].bufSize.tileMem.height = state->height; self->inBufs->planeDesc[1].buf = (XDAS_Int8 *) priv_in->uv_offset; self->inBufs->planeDesc[1].memType = XDM_MEMTYPE_BO_OFFSET; self->inBufs->planeDesc[1].bufSize.tileMem.width = state->width; self->inBufs->planeDesc[1].bufSize.tileMem.height = state->height / 2; /* setting imageRegion doesn't seem to be strictly needed if activeFrameRegion * is set but we set it anyway... */ self->inBufs->imageRegion.topLeft.x = self->rect.x; self->inBufs->imageRegion.topLeft.y = self->rect.y; self->inBufs->imageRegion.bottomRight.x = self->rect.x + self->rect.w; self->inBufs->imageRegion.bottomRight.y = self->rect.y + self->rect.h; self->inBufs->activeFrameRegion.topLeft.x = self->rect.x; self->inBufs->activeFrameRegion.topLeft.y = self->rect.y; self->inBufs->activeFrameRegion.bottomRight.x = self->rect.x + self->rect.w; self->inBufs->activeFrameRegion.bottomRight.y = self->rect.y + self->rect.h; self->inBufs->imagePitch[0] = state->width; self->inBufs->imagePitch[1] = state->width; self->inBufs->topFieldFirstFlag = TRUE; self->outBufs->numBufs = 1; self->outBufs->descs[0].buf = (XDAS_Int8 *) omap_bo_handle (priv_out->bo); self->outBufs->descs[0].bufSize.bytes = GST_BUFFER_SIZE (outbuf); self->outBufs->descs[0].memType = XDM_MEMTYPE_BO; self->inArgs->inputID = GPOINTER_TO_INT (inbuf); err = VIDENC2_process (self->codec, self->inBufs, self->outBufs, self->inArgs, self->outArgs); if (err) { GST_WARNING_OBJECT (self, "process failed: err=%d, extendedError=%08x", err, self->outArgs->extendedError); gst_ducati_log_extended_error_info (self->outArgs->extendedError); err = VIDENC2_control (self->codec, XDM_GETSTATUS, (IVIDENC2_DynamicParams *) self->dynParams, self->status); GST_WARNING_OBJECT (self, "XDM_GETSTATUS: err=%d, extendedError=%08x", err, self->status->extendedError); return GST_FLOW_ERROR; } if (!strcmp (GST_DUCATIVIDENC_GET_CLASS (self)->codec_name, "ivahd_h264enc")) { if (self->outArgs->encodedFrameType == IVIDEO_IDR_FRAME) frame->is_sync_point = TRUE; } else { if (self->outArgs->encodedFrameType == IVIDEO_I_FRAME) frame->is_sync_point = TRUE; } frame->src_buffer = gst_buffer_new_and_alloc (self->outArgs->bytesGenerated); memcpy (GST_BUFFER_DATA (frame->src_buffer), GST_BUFFER_DATA (outbuf), self->outArgs->bytesGenerated); gst_buffer_unref (outbuf); for (i = 0; self->outArgs->freeBufID[i]; i++) { GstBuffer *buf = (GstBuffer *) self->outArgs->freeBufID[i]; GST_LOG_OBJECT (self, "free buffer: %p", buf); gst_buffer_unref (buf); } return gst_base_video_encoder_finish_frame (base_video_encoder, frame); } static gboolean gst_ducati_videnc_event (GstBaseVideoEncoder * enc, GstEvent * event) { gboolean handled = FALSE; GstDucatiVidEnc *self = GST_DUCATIVIDENC (enc); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CROP: gst_event_parse_crop (event, &self->rect.y, &self->rect.x, &self->rect.w, &self->rect.h); GST_INFO_OBJECT (self, "got crop event top %d left %d %dx%d", self->rect.x, self->rect.y, self->rect.w, self->rect.h); handled = TRUE; break; default: break; } return handled; } static GstDucatiBufferPriv * get_buffer_priv (GstDucatiVidEnc * self, GstBuffer * buf, gint stride, gint height) { GstDucatiBufferPriv *priv = gst_ducati_buffer_priv_get (buf); if (!priv) { GstVideoFormat format = GST_VIDEO_FORMAT_NV12; GstDmaBuf *dmabuf = gst_buffer_get_dma_buf (buf); /* if it isn't a dmabuf buffer that we can import, then there * is nothing we can do with it: */ if (!dmabuf) { GST_DEBUG_OBJECT (self, "not importing non dmabuf buffer"); return NULL; } priv = gst_ducati_buffer_priv_new (); priv->bo = omap_bo_from_dmabuf (self->device, gst_dma_buf_get_fd (dmabuf)); priv->uv_offset = gst_video_format_get_component_offset (format, 1, stride, height); priv->size = gst_video_format_get_size (format, stride, height); gst_ducati_buffer_priv_set (buf, priv); } return priv; }