/* GStreamer * Copyright (C) 2004 David A. Schleef <ds@schleef.org> * Copyright (C) 2004 Ronald S. Bultje <rbultje@ronald.bitfreak.net> * * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <string.h> #include <gst/video/video.h> #include "gstdiracdec.h" /* elementfactory information */ static GstElementDetails gst_diracdec_details = { "Dirac stream decoder", "Codec/Decoder/Video", "Decode DIRAC streams", "David Schleef <ds@schleef.org>\n" "Ronald Bultje <rbultje@ronald.bitfreak.net>", }; GST_DEBUG_CATEGORY (diracdec_debug); #define GST_CAT_DEFAULT diracdec_debug enum { /* FILL ME */ LAST_SIGNAL }; enum { ARG_0 /* FILL ME */ }; static void gst_diracdec_base_init (gpointer g_class); static void gst_diracdec_class_init (GstDiracDec * klass); static void gst_diracdec_init (GstDiracDec * diracdec); static void gst_diracdec_dispose (GObject * object); static void gst_diracdec_chain (GstPad * pad, GstData * data); static GstStateChangeReturn gst_diracdec_change_state (GstElement * element, GstStateChange transition); static GstElementClass *parent_class = NULL; /*static guint gst_diracdec_signals[LAST_SIGNAL] = { 0 }; */ GType gst_diracdec_get_type (void) { static GType diracdec_type = 0; if (!diracdec_type) { static const GTypeInfo diracdec_info = { sizeof (GstDiracDecClass), gst_diracdec_base_init, NULL, (GClassInitFunc) gst_diracdec_class_init, NULL, NULL, sizeof (GstDiracDec), 0, (GInstanceInitFunc) gst_diracdec_init, }; diracdec_type = g_type_register_static (GST_TYPE_ELEMENT, "GstDiracDec", &diracdec_info, (GTypeFlags) 0); } return diracdec_type; } static GstStaticPadTemplate gst_diracdec_src_pad_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, /* FIXME: 444 (planar? packed?), 411 (Y41B? Y41P?) */ GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ I420, YUY2, Y800 }")) ); static GstStaticPadTemplate gst_diracdec_sink_pad_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/x-dirac") ); static void gst_diracdec_base_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_diracdec_src_pad_template)); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_diracdec_sink_pad_template)); gst_element_class_set_details (element_class, &gst_diracdec_details); } static void gst_diracdec_class_init (GstDiracDec * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass); parent_class = GST_ELEMENT_CLASS (g_type_class_ref (GST_TYPE_ELEMENT)); gobject_class->dispose = gst_diracdec_dispose; element_class->change_state = gst_diracdec_change_state; GST_DEBUG_CATEGORY_INIT (diracdec_debug, "diracdec", 0, "DIRAC decoder"); } static void gst_diracdec_dispose (GObject * object) { G_OBJECT_CLASS (parent_class)->dispose (object); } static void gst_diracdec_init (GstDiracDec * diracdec) { GST_DEBUG ("gst_diracdec_init: initializing"); /* create the sink and src pads */ diracdec->sinkpad = gst_pad_new_from_template (gst_static_pad_template_get (&gst_diracdec_sink_pad_template), "sink"); gst_pad_set_chain_function (diracdec->sinkpad, gst_diracdec_chain); gst_element_add_pad (GST_ELEMENT (diracdec), diracdec->sinkpad); diracdec->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&gst_diracdec_src_pad_template), "src"); gst_pad_use_explicit_caps (diracdec->srcpad); gst_element_add_pad (GST_ELEMENT (diracdec), diracdec->srcpad); /* no capsnego done yet */ diracdec->width = -1; diracdec->height = -1; diracdec->fps = 0; diracdec->fcc = 0; } static guint32 gst_diracdec_chroma_to_fourcc (dirac_chroma_t chroma) { guint32 fourcc = 0; switch (chroma) { case Yonly: fourcc = GST_MAKE_FOURCC ('Y', '8', '0', '0'); break; case format422: fourcc = GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'); break; /* planar? */ case format420: case format444: case format411: default: break; } return fourcc; } static gboolean gst_diracdec_link (GstDiracDec * diracdec, gint width, gint height, gdouble fps, guint32 fourcc) { GstCaps *caps; if (width == diracdec->width && height == diracdec->height && fps == diracdec->fps && fourcc == diracdec->fcc) { return TRUE; } if (!fourcc) { g_warning ("Chroma not supported\n"); return FALSE; } caps = gst_caps_new_simple ("video/x-raw-yuv", "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, "format", GST_TYPE_FOURCC, fourcc, "framerate", G_TYPE_DOUBLE, fps, NULL); if (gst_pad_set_explicit_caps (diracdec->srcpad, caps)) { diracdec->width = width; diracdec->height = height; switch (fourcc) { case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'): diracdec->size = width * height * 2; break; case GST_MAKE_FOURCC ('Y', '8', '0', '0'): diracdec->size = width * height; break; } diracdec->fcc = fourcc; diracdec->fps = fps; return TRUE; } return FALSE; } static void gst_diracdec_chain (GstPad * pad, GstData * _data) { GstDiracDec *diracdec = GST_DIRACDEC (gst_pad_get_parent (pad)); GstBuffer *buf = GST_BUFFER (_data), *out; gboolean c = TRUE; /* get state and do something */ while (c) { switch (dirac_parse (diracdec->decoder)) { case STATE_BUFFER: if (buf) { /* provide data to decoder */ dirac_buffer (diracdec->decoder, GST_BUFFER_DATA (buf), GST_BUFFER_DATA (buf) + GST_BUFFER_SIZE (buf)); gst_buffer_unref (buf); buf = NULL; } else { /* need more data */ c = FALSE; } break; case STATE_SEQUENCE:{ guint8 *buf[3]; gint fps_num, fps_denom; fps_num = diracdec->decoder->seq_params.frame_rate.numerator; fps_denom = diracdec->decoder->seq_params.frame_rate.denominator; /* start-of-sequence - allocate buffer */ if (!gst_diracdec_link (diracdec, diracdec->decoder->seq_params.width, diracdec->decoder->seq_params.height, (gdouble) fps_num / (gdouble) fps_denom, gst_diracdec_chroma_to_fourcc (diracdec->decoder->seq_params. chroma))) { GST_ELEMENT_ERROR (diracdec, CORE, NEGOTIATION, (NULL), ("Failed to set caps to %dx%d @ %d fps (format=" GST_FOURCC_FORMAT "/%d)", diracdec->decoder->seq_params.width, diracdec->decoder->seq_params.height, diracdec->decoder->seq_params.frame_rate, gst_diracdec_chroma_to_fourcc (diracdec->decoder->seq_params. chroma), diracdec->decoder->seq_params.chroma)); c = FALSE; break; } g_free (diracdec->decoder->fbuf->buf[0]); g_free (diracdec->decoder->fbuf->buf[1]); g_free (diracdec->decoder->fbuf->buf[2]); buf[0] = (guchar *) g_malloc (diracdec->decoder->seq_params.width * diracdec->decoder->seq_params.height); if (diracdec->decoder->seq_params.chroma != Yonly) { buf[1] = (guchar *) g_malloc (diracdec->decoder->seq_params.chroma_width * diracdec->decoder->seq_params.chroma_height); buf[2] = (guchar *) g_malloc (diracdec->decoder->seq_params.chroma_width * diracdec->decoder->seq_params.chroma_height); } dirac_set_buf (diracdec->decoder, buf, NULL); break; } case STATE_SEQUENCE_END: /* end-of-sequence - free buffer */ g_free (diracdec->decoder->fbuf->buf[0]); diracdec->decoder->fbuf->buf[0] = NULL; g_free (diracdec->decoder->fbuf->buf[1]); diracdec->decoder->fbuf->buf[1] = NULL; g_free (diracdec->decoder->fbuf->buf[2]); diracdec->decoder->fbuf->buf[2] = NULL; break; case STATE_PICTURE_START: /* start of one picture */ break; case STATE_PICTURE_AVAIL: /* one picture is decoded */ out = gst_pad_alloc_buffer (diracdec->srcpad, 0, diracdec->size); memcpy (GST_BUFFER_DATA (out), diracdec->decoder->fbuf->buf[0], diracdec->width * diracdec->height); if (diracdec->fcc != GST_MAKE_FOURCC ('Y', '8', '0', '0')) { memcpy (GST_BUFFER_DATA (out) + (diracdec->width * diracdec->height), diracdec->decoder->fbuf->buf[1], diracdec->decoder->seq_params.chroma_width * diracdec->decoder->seq_params.chroma_height); memcpy (GST_BUFFER_DATA (out) + (diracdec->decoder->seq_params.chroma_width * diracdec->decoder->seq_params.chroma_height) + (diracdec->width * diracdec->height), diracdec->decoder->fbuf->buf[2], diracdec->decoder->seq_params.chroma_width * diracdec->decoder->seq_params.chroma_height); } GST_BUFFER_TIMESTAMP (out) = (guint64) (GST_SECOND * diracdec->decoder->frame_params.fnum / diracdec->fps); GST_BUFFER_DURATION (out) = (guint64) (GST_SECOND / diracdec->fps); gst_pad_push (diracdec->srcpad, GST_DATA (out)); break; case STATE_INVALID: default: GST_ELEMENT_ERROR (diracdec, LIBRARY, TOO_LAZY, (NULL), (NULL)); c = FALSE; break; } } } static GstStateChangeReturn gst_diracdec_change_state (GstElement * element, GstStateChange transition) { GstDiracDec *diracdec = GST_DIRACDEC (element); switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: if (!(diracdec->decoder = dirac_decoder_init (0))) return GST_STATE_CHANGE_FAILURE; break; case GST_STATE_CHANGE_READY_TO_NULL: dirac_decoder_close (diracdec->decoder); diracdec->width = diracdec->height = -1; diracdec->fps = 0.; diracdec->fcc = 0; break; default: break; } return parent_class->change_state (element, transition); }