diff --git a/ChangeLog b/ChangeLog index d4cf7875f5..5447139d2e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2004-07-30 Wim Taymans + + * ext/theora/theoradec.c: (gst_theora_dec_class_init), + (gst_theora_dec_init), (theora_get_formats), + (theora_dec_src_convert), (theora_dec_sink_convert), + (theora_dec_src_query), (theora_dec_src_event), (theora_dec_event), + (theora_dec_chain), (theora_dec_set_property), + (theora_dec_get_property): + * ext/theora/theoraenc.c: (gst_border_mode_get_type), + (gst_theora_enc_class_init), (gst_theora_enc_init), + (theora_enc_sink_link), (theora_enc_chain), + (theora_enc_set_property), (theora_enc_get_property): + Added cropping option to theora decoder. + Added border option to theora encoder. + 2004-07-30 Zaheer Abbas Merali * ext/libpng/gstpngenc.c: (gst_pngenc_class_init), diff --git a/ext/theora/theoradec.c b/ext/theora/theoradec.c index 2500df403a..93f3ac1f2e 100644 --- a/ext/theora/theoradec.c +++ b/ext/theora/theoradec.c @@ -60,6 +60,8 @@ struct _GstTheoraDec gboolean need_keyframe; gint width, height; gint offset_x, offset_y; + + gboolean crop; }; struct _GstTheoraDecClass @@ -67,6 +69,13 @@ struct _GstTheoraDecClass GstElementClass parent_class; }; +#define THEORA_DEF_CROP TRUE +enum +{ + ARG_0, + ARG_CROP +}; + static GstElementDetails theora_dec_details = { "TheoraDec", "Codec/Decoder/Video", @@ -94,6 +103,11 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_BOILERPLATE (GstTheoraDec, gst_theora_dec, GstElement, GST_TYPE_ELEMENT); +static void theora_dec_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void theora_dec_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); + static void theora_dec_chain (GstPad * pad, GstData * data); static GstElementStateReturn theora_dec_change_state (GstElement * element); static gboolean theora_dec_src_event (GstPad * pad, GstEvent * event); @@ -125,8 +139,17 @@ gst_theora_dec_base_init (gpointer g_class) static void gst_theora_dec_class_init (GstTheoraDecClass * klass) { + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + gobject_class->set_property = theora_dec_set_property; + gobject_class->get_property = theora_dec_get_property; + + g_object_class_install_property (gobject_class, ARG_CROP, + g_param_spec_boolean ("crop", "Crop", + "Crop the image to the visible region", THEORA_DEF_CROP, + (GParamFlags) G_PARAM_READWRITE)); + gstelement_class->change_state = theora_dec_change_state; GST_DEBUG_CATEGORY_INIT (theoradec_debug, "theoradec", 0, "Theora decoder"); @@ -157,6 +180,8 @@ gst_theora_dec_init (GstTheoraDec * dec) gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad); GST_FLAG_SET (dec, GST_ELEMENT_EVENT_AWARE); + + dec->crop = THEORA_DEF_CROP; } /* FIXME: copy from libtheora, theora should somehow make this available for seeking */ @@ -565,14 +590,22 @@ theora_dec_chain (GstPad * pad, GstData * data) dec->info.frame_width, dec->info.frame_height, dec->info.offset_x, dec->info.offset_y); - /* add black borders to make width/height/offsets even. we need this because - * we cannot express an offset to the peer plugin. */ - dec->width = - ROUND_UP_2 (dec->info.frame_width + (dec->info.offset_x & 1)); - dec->height = - ROUND_UP_2 (dec->info.frame_height + (dec->info.offset_y & 1)); - dec->offset_x = dec->info.offset_x & ~1; - dec->offset_y = dec->info.offset_y & ~1; + if (dec->crop) { + /* add black borders to make width/height/offsets even. we need this because + * we cannot express an offset to the peer plugin. */ + dec->width = + ROUND_UP_2 (dec->info.frame_width + (dec->info.offset_x & 1)); + dec->height = + ROUND_UP_2 (dec->info.frame_height + (dec->info.offset_y & 1)); + dec->offset_x = dec->info.offset_x & ~1; + dec->offset_y = dec->info.offset_y & ~1; + } else { + /* no cropping, use the encoded dimensions */ + dec->width = dec->info.width; + dec->height = dec->info.height; + dec->offset_x = 0; + dec->offset_y = 0; + } GST_DEBUG_OBJECT (dec, "after fixup frame dimension %dx%d, offset %d:%d", dec->width, dec->height, dec->offset_x, dec->offset_y); @@ -726,3 +759,35 @@ theora_dec_change_state (GstElement * element) return parent_class->change_state (element); } + +static void +theora_dec_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstTheoraDec *dec = GST_THEORA_DEC (object); + + switch (prop_id) { + case ARG_CROP: + dec->crop = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +theora_dec_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstTheoraDec *dec = GST_THEORA_DEC (object); + + switch (prop_id) { + case ARG_CROP: + g_value_set_boolean (value, dec->crop); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} diff --git a/ext/theora/theoraenc.c b/ext/theora/theoraenc.c index dd5591f42b..ed353cdbd9 100644 --- a/ext/theora/theoraenc.c +++ b/ext/theora/theoraenc.c @@ -43,6 +43,33 @@ GST_DEBUG_CATEGORY (theoraenc_debug); typedef struct _GstTheoraEnc GstTheoraEnc; typedef struct _GstTheoraEncClass GstTheoraEncClass; +typedef enum +{ + BORDER_NONE, + BORDER_BLACK, + BORDER_MIRROR +} +GstTheoraEncBorderMode; + +#define GST_TYPE_BORDER_MODE (gst_border_mode_get_type()) +static GType +gst_border_mode_get_type (void) +{ + static GType border_mode_type = 0; + static GEnumValue border_mode[] = { + {BORDER_NONE, "BORDER_NONE", "No Border"}, + {BORDER_BLACK, "BORDER_BLACK", "Black Border"}, + {BORDER_MIRROR, "BORDER_MIRROR", "Mirror image in borders"}, + {0, NULL, NULL}, + }; + + if (!border_mode_type) { + border_mode_type = + g_enum_register_static ("GstTheoraEncBorderMode", border_mode); + } + return border_mode_type; +} + struct _GstTheoraEnc { GstElement element; @@ -57,6 +84,7 @@ struct _GstTheoraEnc theora_comment comment; gboolean center; + GstTheoraEncBorderMode border; gint video_bitrate; /* bitrate target for Theora video */ gint video_quality; /* Theora quality selector 0 = low, 63 = high */ @@ -88,6 +116,7 @@ struct _GstTheoraEncClass #define ROUND_UP_8(x) (((x) + 7) & ~7) #define THEORA_DEF_CENTER TRUE +#define THEORA_DEF_BORDER BORDER_BLACK #define THEORA_DEF_BITRATE 0 #define THEORA_DEF_QUALITY 16 #define THEORA_DEF_QUICK TRUE @@ -102,6 +131,7 @@ enum { ARG_0, ARG_CENTER, + ARG_BORDER, ARG_BITRATE, ARG_QUALITY, ARG_QUICK, @@ -174,6 +204,11 @@ gst_theora_enc_class_init (GstTheoraEncClass * klass) g_param_spec_boolean ("center", "Center", "Center image when sizes not multiple of 16", THEORA_DEF_CENTER, (GParamFlags) G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, ARG_BORDER, + g_param_spec_enum ("border", "Border", + "Border color to add when sizes not multiple of 16", + GST_TYPE_BORDER_MODE, THEORA_DEF_BORDER, + (GParamFlags) G_PARAM_READWRITE)); /* general encoding stream options */ g_object_class_install_property (gobject_class, ARG_BITRATE, g_param_spec_int ("bitrate", "Bitrate", "Compressed video bitrate (kbps)", @@ -229,6 +264,9 @@ gst_theora_enc_init (GstTheoraEnc * enc) gst_pad_use_explicit_caps (enc->srcpad); gst_element_add_pad (GST_ELEMENT (enc), enc->srcpad); + enc->center = THEORA_DEF_CENTER; + enc->border = THEORA_DEF_BORDER; + enc->video_bitrate = THEORA_DEF_BITRATE; enc->video_quality = THEORA_DEF_QUALITY; enc->quick = THEORA_DEF_QUICK; @@ -494,6 +532,7 @@ theora_enc_chain (GstPad * pad, GstData * data) gint dst_y_stride, dst_uv_stride; gint width, height; gint cwidth, cheight; + gint offset_x, right_x, right_border; /* source width/height */ width = enc->width; @@ -513,33 +552,74 @@ theora_enc_chain (GstPad * pad, GstData * data) newbuf = gst_pad_alloc_buffer (enc->srcpad, GST_BUFFER_OFFSET_NONE, y_size * 3 / 2); - yuv.y = GST_BUFFER_DATA (newbuf); - yuv.u = yuv.y + y_size; - yuv.v = yuv.u + y_size / 4; - - /* center if needed */ - dest_y = yuv.y + enc->offset_x + enc->offset_y * dst_y_stride; - dest_u = - yuv.u + (enc->offset_x / 2) + (enc->offset_y / 2) * dst_uv_stride; - dest_v = - yuv.v + (enc->offset_x / 2) + (enc->offset_y / 2) * dst_uv_stride; + dest_y = yuv.y = GST_BUFFER_DATA (newbuf); + dest_u = yuv.u = yuv.y + y_size; + dest_v = yuv.v = yuv.u + y_size / 4; src_y = GST_BUFFER_DATA (buf); src_u = src_y + src_y_stride * ROUND_UP_2 (height); src_v = src_u + src_uv_stride * ROUND_UP_2 (height) / 2; + if (enc->border != BORDER_NONE) { + /* fill top border */ + for (i = 0; i < enc->offset_y; i++) { + memset (dest_y, 0, dst_y_stride); + dest_y += dst_y_stride; + } + } else { + dest_y += dst_y_stride * enc->offset_y; + } + + offset_x = enc->offset_x; + right_x = width + enc->offset_x; + right_border = dst_y_stride - right_x; + /* copy Y plane */ for (i = 0; i < height; i++) { - memcpy (dest_y, src_y, width); + memcpy (dest_y + offset_x, src_y, width); + if (enc->border != BORDER_NONE) { + memset (dest_y, 0, offset_x); + memset (dest_y + right_x, 0, right_border); + } dest_y += dst_y_stride; src_y += src_y_stride; } + if (enc->border != BORDER_NONE) { + /* fill bottom border */ + for (i = height + enc->offset_y; i < enc->info.height; i++) { + memset (dest_y, 0, dst_y_stride); + dest_y += dst_y_stride; + } + + /* fill top border chroma */ + for (i = 0; i < enc->offset_y / 2; i++) { + memset (dest_u, 128, dst_uv_stride); + memset (dest_v, 128, dst_uv_stride); + dest_u += dst_uv_stride; + dest_v += dst_uv_stride; + } + } else { + dest_u += dst_uv_stride * enc->offset_y / 2; + dest_v += dst_uv_stride * enc->offset_y / 2; + } + + offset_x = enc->offset_x / 2; + right_x = cwidth + offset_x; + right_border = dst_uv_stride - right_x; + /* copy UV planes */ for (i = 0; i < cheight; i++) { - memcpy (dest_u, src_u, cwidth); - memcpy (dest_v, src_v, cwidth); + memcpy (dest_v + offset_x, src_v, cwidth); + memcpy (dest_u + offset_x, src_u, cwidth); + + if (enc->border != BORDER_NONE) { + memset (dest_u, 128, offset_x); + memset (dest_u + right_x, 128, right_border); + memset (dest_v, 128, offset_x); + memset (dest_v + right_x, 128, right_border); + } dest_u += dst_uv_stride; dest_v += dst_uv_stride; @@ -547,6 +627,16 @@ theora_enc_chain (GstPad * pad, GstData * data) src_v += src_uv_stride; } + if (enc->border != BORDER_NONE) { + /* fill bottom border */ + for (i = cheight + enc->offset_y / 2; i < enc->info_height / 2; i++) { + memset (dest_u, 128, dst_uv_stride); + memset (dest_v, 128, dst_uv_stride); + dest_u += dst_uv_stride; + dest_v += dst_uv_stride; + } + } + gst_buffer_unref (buf); buf = newbuf; } @@ -604,6 +694,9 @@ theora_enc_set_property (GObject * object, guint prop_id, case ARG_CENTER: enc->center = g_value_get_boolean (value); break; + case ARG_BORDER: + enc->border = g_value_get_enum (value); + break; case ARG_BITRATE: enc->video_bitrate = g_value_get_int (value) * 1000; enc->video_quality = 0; @@ -649,6 +742,9 @@ theora_enc_get_property (GObject * object, guint prop_id, case ARG_CENTER: g_value_set_boolean (value, enc->center); break; + case ARG_BORDER: + g_value_set_enum (value, enc->border); + break; case ARG_BITRATE: g_value_set_int (value, enc->video_bitrate / 1000); break;