From a27682279dc1c2ce1e5260c755a845adf11b7091 Mon Sep 17 00:00:00 2001 From: David Schleef Date: Thu, 13 May 2010 01:04:04 -0700 Subject: [PATCH] [MOVED FROM BAD 001/134] vp8: Add encoder/decoder --- ext/vp8/Makefile.am | 42 + ext/vp8/gst/video/gstbasevideocodec.c | 574 +++++++++++++ ext/vp8/gst/video/gstbasevideocodec.h | 146 ++++ ext/vp8/gst/video/gstbasevideodecoder.c | 1418 +++++++++++++++++++++++++++++++ ext/vp8/gst/video/gstbasevideodecoder.h | 170 ++++ ext/vp8/gst/video/gstbasevideoencoder.c | 518 +++++++++++ ext/vp8/gst/video/gstbasevideoencoder.h | 120 +++ ext/vp8/gst/video/gstbasevideoparse.c | 863 +++++++++++++++++++ ext/vp8/gst/video/gstbasevideoparse.h | 137 +++ ext/vp8/gst/video/gstbasevideoutils.c | 278 ++++++ ext/vp8/gst/video/gstbasevideoutils.h | 98 +++ ext/vp8/gst/video/gstvideocompat.c | 872 +++++++++++++++++++ ext/vp8/gst/video/gstvideocompat.h | 71 ++ ext/vp8/gstvp8dec.c | 397 +++++++++ ext/vp8/gstvp8enc.c | 611 +++++++++++++ ext/vp8/plugin.c | 52 ++ 16 files changed, 6367 insertions(+) create mode 100644 ext/vp8/Makefile.am create mode 100644 ext/vp8/gst/video/gstbasevideocodec.c create mode 100644 ext/vp8/gst/video/gstbasevideocodec.h create mode 100644 ext/vp8/gst/video/gstbasevideodecoder.c create mode 100644 ext/vp8/gst/video/gstbasevideodecoder.h create mode 100644 ext/vp8/gst/video/gstbasevideoencoder.c create mode 100644 ext/vp8/gst/video/gstbasevideoencoder.h create mode 100644 ext/vp8/gst/video/gstbasevideoparse.c create mode 100644 ext/vp8/gst/video/gstbasevideoparse.h create mode 100644 ext/vp8/gst/video/gstbasevideoutils.c create mode 100644 ext/vp8/gst/video/gstbasevideoutils.h create mode 100644 ext/vp8/gst/video/gstvideocompat.c create mode 100644 ext/vp8/gst/video/gstvideocompat.h create mode 100644 ext/vp8/gstvp8dec.c create mode 100644 ext/vp8/gstvp8enc.c create mode 100644 ext/vp8/plugin.c diff --git a/ext/vp8/Makefile.am b/ext/vp8/Makefile.am new file mode 100644 index 0000000..583f263 --- /dev/null +++ b/ext/vp8/Makefile.am @@ -0,0 +1,42 @@ +plugin_LTLIBRARIES = \ + libgstvp8.la + +libgstvp8_la_SOURCES = \ + gstvp8dec.c \ + gstvp8enc.c \ + plugin.c \ + gst/video/gstbasevideocodec.c \ + gst/video/gstbasevideodecoder.c \ + gst/video/gstbasevideoencoder.c \ + gst/video/gstbasevideoparse.c \ + gst/video/gstbasevideoutils.c \ + gst/video/gstvideocompat.c + +fixbaseclasses = \ + -DGstBaseVideoCodec=VP8BaseVideoCodec \ + -DGstBaseVideoCodecClass=VP8BaseVideoCodecClass \ + -DGstBaseVideoEncoder=VP8BaseVideoEncoder \ + -DGstBaseVideoEncoderClass=VP8BaseVideoEncoderClass \ + -DGstBaseVideoDecoder=VP8BaseVideoDecoder \ + -DGstBaseVideoDecoderClass=VP8BaseVideoDecoderClass \ + -DGstBaseVideoParse=VP8BaseVideoParse \ + -DGstBaseVideoParseClass=VP8BaseVideoParseClass + +libgstvp8_la_CFLAGS = \ + $(GST_CFLAGS) \ + $(GST_BASE_CFLAGS) \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(fixbaseclasses) +libgstvp8_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstvp8_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) +libgstvp8_la_LIBADD += $(GST_PLUGINS_BASE_LIBS) -lgsttag-@GST_MAJORMINOR@ -lgstvideo-@GST_MAJORMINOR@ +libgstvp8_la_LIBADD += $(VPX_LIBS) + +noinst_HEADERS = \ + gst/video/gstbasevideocodec.h \ + gst/video/gstbasevideodecoder.h \ + gst/video/gstbasevideoencoder.h \ + gst/video/gstbasevideoparse.h \ + gst/video/gstbasevideoutils.h \ + gst/video/gstvideocompat.h + diff --git a/ext/vp8/gst/video/gstbasevideocodec.c b/ext/vp8/gst/video/gstbasevideocodec.c new file mode 100644 index 0000000..75d0381 --- /dev/null +++ b/ext/vp8/gst/video/gstbasevideocodec.c @@ -0,0 +1,574 @@ +/* Schrodinger + * Copyright (C) 2006 David Schleef + * + * 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 "gstbasevideocodec.h" + +#include +#include + +GST_DEBUG_CATEGORY (basevideo_debug); +#define GST_CAT_DEFAULT basevideo_debug + +/* GstBaseVideoCodec signals and args */ +enum +{ + LAST_SIGNAL +}; + +enum +{ + ARG_0 +}; + +static void gst_base_video_codec_finalize (GObject * object); + +//static const GstQueryType *gst_base_video_codec_get_query_types (GstPad *pad); +//static gboolean gst_base_video_codec_src_query (GstPad *pad, GstQuery *query); +//static gboolean gst_base_video_codec_sink_query (GstPad *pad, GstQuery *query); +//static gboolean gst_base_video_codec_src_event (GstPad *pad, GstEvent *event); +//static gboolean gst_base_video_codec_sink_event (GstPad *pad, GstEvent *event); +static GstStateChangeReturn gst_base_video_codec_change_state (GstElement * + element, GstStateChange transition); +//static GstFlowReturn gst_base_video_codec_push_all (GstBaseVideoCodec *base_video_codec, +// gboolean at_eos); + + +GST_BOILERPLATE (GstBaseVideoCodec, gst_base_video_codec, GstElement, + GST_TYPE_ELEMENT); + +static void +gst_base_video_codec_base_init (gpointer g_class) +{ + GST_DEBUG_CATEGORY_INIT (basevideo_debug, "vp8basevideo", 0, + "Base Video Classes"); + +} + +static void +gst_base_video_codec_class_init (GstBaseVideoCodecClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + + gobject_class = G_OBJECT_CLASS (klass); + element_class = GST_ELEMENT_CLASS (klass); + + gobject_class->finalize = gst_base_video_codec_finalize; + + element_class->change_state = gst_base_video_codec_change_state; +} + +static void +gst_base_video_codec_init (GstBaseVideoCodec * base_video_codec, + GstBaseVideoCodecClass * klass) +{ + GstPadTemplate *pad_template; + + GST_DEBUG ("gst_base_video_codec_init"); + + pad_template = + gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass), "sink"); + g_return_if_fail (pad_template != NULL); + + base_video_codec->sinkpad = gst_pad_new_from_template (pad_template, "sink"); + //gst_pad_set_query_function (base_video_codec->sinkpad, + // gst_base_video_codec_sink_query); + gst_element_add_pad (GST_ELEMENT (base_video_codec), + base_video_codec->sinkpad); + + pad_template = + gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass), "src"); + g_return_if_fail (pad_template != NULL); + + base_video_codec->srcpad = gst_pad_new_from_template (pad_template, "src"); + gst_element_add_pad (GST_ELEMENT (base_video_codec), + base_video_codec->srcpad); + + base_video_codec->input_adapter = gst_adapter_new (); + base_video_codec->output_adapter = gst_adapter_new (); + +} + +static void +gst_base_video_codec_reset (GstBaseVideoCodec * base_video_codec) +{ + GST_DEBUG ("reset"); + + base_video_codec->system_frame_number = 0; + + //gst_segment_init (&base_video_codec->state.segment, GST_FORMAT_TIME); + gst_adapter_clear (base_video_codec->input_adapter); + gst_adapter_clear (base_video_codec->output_adapter); + +} + +static void +gst_base_video_codec_finalize (GObject * object) +{ + GstBaseVideoCodec *base_video_codec; + + g_return_if_fail (GST_IS_BASE_VIDEO_CODEC (object)); + base_video_codec = GST_BASE_VIDEO_CODEC (object); + + if (base_video_codec->input_adapter) { + g_object_unref (base_video_codec->input_adapter); + } + if (base_video_codec->output_adapter) { + g_object_unref (base_video_codec->output_adapter); + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +#ifdef unused +static const GstQueryType * +gst_base_video_codec_get_query_types (GstPad * pad) +{ + static const GstQueryType query_types[] = { + GST_QUERY_POSITION, + GST_QUERY_DURATION, + GST_QUERY_CONVERT, + 0 + }; + + return query_types; +} +#endif + +#if 0 +static gboolean +gst_base_video_codec_src_convert (GstPad * pad, + GstFormat src_format, gint64 src_value, + GstFormat * dest_format, gint64 * dest_value) +{ + gboolean res; + GstBaseVideoCodec *dec; + + if (src_format == *dest_format) { + *dest_value = src_value; + return TRUE; + } + + dec = GST_BASE_VIDEO_CODEC (gst_pad_get_parent (pad)); + + if (src_format == GST_FORMAT_DEFAULT && *dest_format == GST_FORMAT_TIME) { + if (dec->fps_d != 0) { + *dest_value = gst_util_uint64_scale (granulepos_to_frame (src_value), + dec->fps_d * GST_SECOND, dec->fps_n); + res = TRUE; + } else { + res = FALSE; + } + } else { + GST_WARNING ("unhandled conversion from %d to %d", src_format, + *dest_format); + res = FALSE; + } + + gst_object_unref (dec); + + return res; +} + +static gboolean +gst_base_video_codec_sink_convert (GstPad * pad, + GstFormat src_format, gint64 src_value, + GstFormat * dest_format, gint64 * dest_value) +{ + gboolean res = TRUE; + GstBaseVideoCodec *dec; + + if (src_format == *dest_format) { + *dest_value = src_value; + return TRUE; + } + + dec = GST_BASE_VIDEO_CODEC (gst_pad_get_parent (pad)); + + /* FIXME: check if we are in a decoding state */ + + switch (src_format) { + case GST_FORMAT_DEFAULT: + switch (*dest_format) { + case GST_FORMAT_TIME: + *dest_value = gst_util_uint64_scale (src_value, + dec->fps_d * GST_SECOND, dec->fps_n); + break; + default: + res = FALSE; + } + break; + case GST_FORMAT_TIME: + switch (*dest_format) { + case GST_FORMAT_DEFAULT: + { + *dest_value = gst_util_uint64_scale (src_value, + dec->fps_n, dec->fps_d * GST_SECOND); + break; + } + default: + res = FALSE; + break; + } + break; + default: + res = FALSE; + break; + } + + gst_object_unref (dec); + + return res; +} +#endif + +#ifdef unused +static gboolean +gst_base_video_codec_src_query (GstPad * pad, GstQuery * query) +{ + GstBaseVideoCodec *base_codec; + gboolean res = FALSE; + + base_codec = GST_BASE_VIDEO_CODEC (gst_pad_get_parent (pad)); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_POSITION: + { + GstFormat format; + gint64 time; + gint64 value; + + gst_query_parse_position (query, &format, NULL); + + time = gst_util_uint64_scale (base_codec->system_frame_number, + base_codec->state.fps_n, base_codec->state.fps_d); + time += base_codec->state.segment.time; + GST_DEBUG ("query position %lld", time); + res = gst_base_video_encoded_video_convert (&base_codec->state, + GST_FORMAT_TIME, time, &format, &value); + if (!res) + goto error; + + gst_query_set_position (query, format, value); + break; + } + case GST_QUERY_DURATION: + res = gst_pad_query (GST_PAD_PEER (base_codec->sinkpad), query); + if (!res) + goto error; + break; + case GST_QUERY_CONVERT: + { + GstFormat src_fmt, dest_fmt; + gint64 src_val, dest_val; + + GST_DEBUG ("query convert"); + + gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); + res = gst_base_video_encoded_video_convert (&base_codec->state, + src_fmt, src_val, &dest_fmt, &dest_val); + if (!res) + goto error; + gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); + break; + } + default: + res = gst_pad_query_default (pad, query); + break; + } +done: + gst_object_unref (base_codec); + + return res; +error: + GST_DEBUG_OBJECT (base_codec, "query failed"); + goto done; +} +#endif + +#ifdef unused +static gboolean +gst_base_video_codec_sink_query (GstPad * pad, GstQuery * query) +{ + GstBaseVideoCodec *base_video_codec; + gboolean res = FALSE; + + base_video_codec = GST_BASE_VIDEO_CODEC (gst_pad_get_parent (pad)); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONVERT: + { + GstFormat src_fmt, dest_fmt; + gint64 src_val, dest_val; + + gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); + res = gst_base_video_encoded_video_convert (&base_video_codec->state, + src_fmt, src_val, &dest_fmt, &dest_val); + if (!res) + goto error; + gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); + break; + } + default: + res = gst_pad_query_default (pad, query); + break; + } +done: + gst_object_unref (base_video_codec); + + return res; +error: + GST_DEBUG_OBJECT (base_video_codec, "query failed"); + goto done; +} +#endif + +#ifdef unused +static gboolean +gst_base_video_codec_src_event (GstPad * pad, GstEvent * event) +{ + GstBaseVideoCodec *base_video_codec; + gboolean res = FALSE; + + base_video_codec = GST_BASE_VIDEO_CODEC (gst_pad_get_parent (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + { + GstFormat format, tformat; + gdouble rate; + GstEvent *real_seek; + GstSeekFlags flags; + GstSeekType cur_type, stop_type; + gint64 cur, stop; + gint64 tcur, tstop; + + gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, + &cur, &stop_type, &stop); + gst_event_unref (event); + + tformat = GST_FORMAT_TIME; + res = gst_base_video_encoded_video_convert (&base_video_codec->state, + format, cur, &tformat, &tcur); + if (!res) + goto convert_error; + res = gst_base_video_encoded_video_convert (&base_video_codec->state, + format, stop, &tformat, &tstop); + if (!res) + goto convert_error; + + real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME, + flags, cur_type, tcur, stop_type, tstop); + + res = gst_pad_push_event (base_video_codec->sinkpad, real_seek); + + break; + } +#if 0 + case GST_EVENT_QOS: + { + gdouble proportion; + GstClockTimeDiff diff; + GstClockTime timestamp; + + gst_event_parse_qos (event, &proportion, &diff, ×tamp); + + GST_OBJECT_LOCK (base_video_codec); + base_video_codec->proportion = proportion; + base_video_codec->earliest_time = timestamp + diff; + GST_OBJECT_UNLOCK (base_video_codec); + + GST_DEBUG_OBJECT (base_video_codec, + "got QoS %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT, + GST_TIME_ARGS (timestamp), diff); + + res = gst_pad_push_event (base_video_codec->sinkpad, event); + break; + } +#endif + default: + res = gst_pad_push_event (base_video_codec->sinkpad, event); + break; + } +done: + gst_object_unref (base_video_codec); + return res; + +convert_error: + GST_DEBUG_OBJECT (base_video_codec, "could not convert format"); + goto done; +} +#endif + +#ifdef unused +static gboolean +gst_base_video_codec_sink_event (GstPad * pad, GstEvent * event) +{ + GstBaseVideoCodec *base_video_codec; + gboolean ret = FALSE; + + base_video_codec = GST_BASE_VIDEO_CODEC (gst_pad_get_parent (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_START: + ret = gst_pad_push_event (base_video_codec->srcpad, event); + break; + case GST_EVENT_FLUSH_STOP: + gst_base_video_codec_reset (base_video_codec); + ret = gst_pad_push_event (base_video_codec->srcpad, event); + break; + case GST_EVENT_EOS: + if (gst_base_video_codec_push_all (base_video_codec, + FALSE) == GST_FLOW_ERROR) { + gst_event_unref (event); + return FALSE; + } + + ret = gst_pad_push_event (base_video_codec->srcpad, event); + break; + case GST_EVENT_NEWSEGMENT: + { + gboolean update; + GstFormat format; + gdouble rate; + gint64 start, stop, time; + + gst_event_parse_new_segment (event, &update, &rate, &format, &start, + &stop, &time); + + if (format != GST_FORMAT_TIME) + goto newseg_wrong_format; + + if (rate <= 0.0) + goto newseg_wrong_rate; + + GST_DEBUG ("newsegment %lld %lld", start, time); + gst_segment_set_newsegment (&base_video_codec->state.segment, update, + rate, format, start, stop, time); + + ret = gst_pad_push_event (base_video_codec->srcpad, event); + break; + } + default: + ret = gst_pad_push_event (base_video_codec->srcpad, event); + break; + } +done: + gst_object_unref (base_video_codec); + return ret; + +newseg_wrong_format: + GST_DEBUG_OBJECT (base_video_codec, "received non TIME newsegment"); + gst_event_unref (event); + goto done; + +newseg_wrong_rate: + GST_DEBUG_OBJECT (base_video_codec, "negative rates not supported"); + gst_event_unref (event); + goto done; +} +#endif + + +static GstStateChangeReturn +gst_base_video_codec_change_state (GstElement * element, + GstStateChange transition) +{ + GstBaseVideoCodec *base_video_codec = GST_BASE_VIDEO_CODEC (element); + GstStateChangeReturn ret; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_base_video_codec_reset (base_video_codec); + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + default: + break; + } + + ret = parent_class->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_base_video_codec_reset (base_video_codec); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + + return ret; +} + +#if 0 +guint64 +gst_base_video_codec_get_timestamp (GstBaseVideoCodec * base_video_codec, + int picture_number) +{ + if (picture_number < 0) { + return base_video_codec->timestamp_offset - + (gint64) gst_util_uint64_scale (-picture_number, + base_video_codec->state.fps_d * GST_SECOND, + base_video_codec->state.fps_n); + } else { + return base_video_codec->timestamp_offset + + gst_util_uint64_scale (picture_number, + base_video_codec->state.fps_d * GST_SECOND, + base_video_codec->state.fps_n); + } +} +#endif + +GstVideoFrame * +gst_base_video_codec_new_frame (GstBaseVideoCodec * base_video_codec) +{ + GstVideoFrame *frame; + + frame = g_malloc0 (sizeof (GstVideoFrame)); + + frame->system_frame_number = base_video_codec->system_frame_number; + base_video_codec->system_frame_number++; + + return frame; +} + +void +gst_base_video_codec_free_frame (GstVideoFrame * frame) +{ + if (frame->sink_buffer) { + gst_buffer_unref (frame->sink_buffer); + } +#if 0 + if (frame->src_buffer) { + gst_buffer_unref (frame->src_buffer); + } +#endif + + g_free (frame); +} diff --git a/ext/vp8/gst/video/gstbasevideocodec.h b/ext/vp8/gst/video/gstbasevideocodec.h new file mode 100644 index 0000000..1930ff2 --- /dev/null +++ b/ext/vp8/gst/video/gstbasevideocodec.h @@ -0,0 +1,146 @@ +/* GStreamer + * Copyright (C) 2008 David Schleef + * + * 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. + */ + +#ifndef _GST_BASE_VIDEO_CODEC_H_ +#define _GST_BASE_VIDEO_CODEC_H_ + +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_BASE_VIDEO_CODEC \ + (gst_base_video_codec_get_type()) +#define GST_BASE_VIDEO_CODEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASE_VIDEO_CODEC,GstBaseVideoCodec)) +#define GST_BASE_VIDEO_CODEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASE_VIDEO_CODEC,GstBaseVideoCodecClass)) +#define GST_BASE_VIDEO_CODEC_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_BASE_VIDEO_CODEC,GstBaseVideoCodecClass)) +#define GST_IS_BASE_VIDEO_CODEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASE_VIDEO_CODEC)) +#define GST_IS_BASE_VIDEO_CODEC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASE_VIDEO_CODEC)) + +/** + * GST_BASE_VIDEO_CODEC_SINK_NAME: + * + * The name of the templates for the sink pad. + */ +#define GST_BASE_VIDEO_CODEC_SINK_NAME "sink" +/** + * GST_BASE_VIDEO_CODEC_SRC_NAME: + * + * The name of the templates for the source pad. + */ +#define GST_BASE_VIDEO_CODEC_SRC_NAME "src" + +/** + * GST_BASE_VIDEO_CODEC_SRC_PAD: + * @obj: base video codec instance + * + * Gives the pointer to the source #GstPad object of the element. + */ +#define GST_BASE_VIDEO_CODEC_SRC_PAD(obj) (((GstBaseVideoCodec *) (obj))->srcpad) + +/** + * GST_BASE_VIDEO_CODEC_SINK_PAD: + * @obj: base video codec instance + * + * Gives the pointer to the sink #GstPad object of the element. + */ +#define GST_BASE_VIDEO_CODEC_SINK_PAD(obj) (((GstBaseVideoCodec *) (obj))->sinkpad) + +/** + * GST_BASE_VIDEO_CODEC_FLOW_NEED_DATA: + * + */ +#define GST_BASE_VIDEO_CODEC_FLOW_NEED_DATA GST_FLOW_CUSTOM_SUCCESS + +typedef struct _GstBaseVideoCodec GstBaseVideoCodec; +typedef struct _GstBaseVideoCodecClass GstBaseVideoCodecClass; + +struct _GstBaseVideoCodec +{ + GstElement element; + + /*< private >*/ + GstPad *sinkpad; + GstPad *srcpad; + GstAdapter *input_adapter; + GstAdapter *output_adapter; + +#if 0 + /* FIXME need to move from subclasses */ + GstVideoState state; +#endif + + //int reorder_depth; + + //gboolean have_sync; + //gboolean discont; + //gboolean started; + + //GstVideoFrame *current_frame; + //int distance_from_sync; + + //gboolean sink_clipping; + + //guint64 presentation_frame_number; + guint64 system_frame_number; + + //GstCaps *caps; + //gboolean set_output_caps; + + //GstClockTime buffer_timestamp; + + GstClockTime timestamp_offset; +}; + +struct _GstBaseVideoCodecClass +{ + GstElementClass element_class; + + gboolean (*start) (GstBaseVideoCodec *codec); + gboolean (*stop) (GstBaseVideoCodec *codec); + gboolean (*reset) (GstBaseVideoCodec *codec); + GstFlowReturn (*parse_data) (GstBaseVideoCodec *codec, gboolean at_eos); + int (*scan_for_sync) (GstAdapter *adapter, gboolean at_eos, + int offset, int n); + GstFlowReturn (*shape_output) (GstBaseVideoCodec *codec, GstVideoFrame *frame); + GstCaps *(*get_caps) (GstBaseVideoCodec *codec); + +}; + +GType gst_base_video_codec_get_type (void); + +#if 0 +guint64 gst_base_video_codec_get_timestamp (GstBaseVideoCodec *codec, + int picture_number); +#endif + +GstVideoFrame * gst_base_video_codec_new_frame (GstBaseVideoCodec *base_video_codec); +void gst_base_video_codec_free_frame (GstVideoFrame *frame); + + +G_END_DECLS + +#endif + diff --git a/ext/vp8/gst/video/gstbasevideodecoder.c b/ext/vp8/gst/video/gstbasevideodecoder.c new file mode 100644 index 0000000..640717b --- /dev/null +++ b/ext/vp8/gst/video/gstbasevideodecoder.c @@ -0,0 +1,1418 @@ +/* GStreamer + * Copyright (C) 2008 David Schleef + * + * 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 "gstbasevideodecoder.h" + +#include + +GST_DEBUG_CATEGORY_EXTERN (basevideo_debug); +#define GST_CAT_DEFAULT basevideo_debug + +static void gst_base_video_decoder_finalize (GObject * object); + +static gboolean gst_base_video_decoder_sink_setcaps (GstPad * pad, + GstCaps * caps); +static gboolean gst_base_video_decoder_sink_event (GstPad * pad, + GstEvent * event); +static gboolean gst_base_video_decoder_src_event (GstPad * pad, + GstEvent * event); +static GstFlowReturn gst_base_video_decoder_chain (GstPad * pad, + GstBuffer * buf); +static gboolean gst_base_video_decoder_sink_query (GstPad * pad, + GstQuery * query); +//static GstFlowReturn gst_base_video_decoder_process (GstBaseVideoDecoder *base_video_decoder); +static GstStateChangeReturn gst_base_video_decoder_change_state (GstElement * + element, GstStateChange transition); +static const GstQueryType *gst_base_video_decoder_get_query_types (GstPad * + pad); +static gboolean gst_base_video_decoder_src_query (GstPad * pad, + GstQuery * query); +static gboolean gst_base_video_decoder_src_convert (GstPad * pad, + GstFormat src_format, gint64 src_value, GstFormat * dest_format, + gint64 * dest_value); +static void gst_base_video_decoder_reset (GstBaseVideoDecoder * + base_video_decoder); + +static GstFlowReturn +gst_base_video_decoder_have_frame_2 (GstBaseVideoDecoder * base_video_decoder); + +static guint64 +gst_base_video_decoder_get_timestamp (GstBaseVideoDecoder * base_video_decoder, + int picture_number); +static guint64 +gst_base_video_decoder_get_field_timestamp (GstBaseVideoDecoder * + base_video_decoder, int field_offset); +static guint64 gst_base_video_decoder_get_field_duration (GstBaseVideoDecoder * + base_video_decoder, int n_fields); +static GstVideoFrame *gst_base_video_decoder_new_frame (GstBaseVideoDecoder * + base_video_decoder); +static void gst_base_video_decoder_free_frame (GstVideoFrame * frame); + +GST_BOILERPLATE (GstBaseVideoDecoder, gst_base_video_decoder, + GstBaseVideoCodec, GST_TYPE_BASE_VIDEO_CODEC); + +static void +gst_base_video_decoder_base_init (gpointer g_class) +{ + +} + +static void +gst_base_video_decoder_class_init (GstBaseVideoDecoderClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = G_OBJECT_CLASS (klass); + gstelement_class = GST_ELEMENT_CLASS (klass); + + gobject_class->finalize = gst_base_video_decoder_finalize; + + gstelement_class->change_state = gst_base_video_decoder_change_state; + + parent_class = g_type_class_peek_parent (klass); +} + +static void +gst_base_video_decoder_init (GstBaseVideoDecoder * base_video_decoder, + GstBaseVideoDecoderClass * klass) +{ + GstPad *pad; + + GST_DEBUG ("gst_base_video_decoder_init"); + + pad = GST_BASE_VIDEO_CODEC_SINK_PAD (base_video_decoder); + + gst_pad_set_chain_function (pad, gst_base_video_decoder_chain); + gst_pad_set_event_function (pad, gst_base_video_decoder_sink_event); + gst_pad_set_setcaps_function (pad, gst_base_video_decoder_sink_setcaps); + gst_pad_set_query_function (pad, gst_base_video_decoder_sink_query); + + pad = GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_decoder); + + gst_pad_set_event_function (pad, gst_base_video_decoder_src_event); + gst_pad_set_query_type_function (pad, gst_base_video_decoder_get_query_types); + gst_pad_set_query_function (pad, gst_base_video_decoder_src_query); + gst_pad_use_fixed_caps (pad); + + base_video_decoder->input_adapter = gst_adapter_new (); + base_video_decoder->output_adapter = gst_adapter_new (); + + gst_segment_init (&base_video_decoder->segment, GST_FORMAT_TIME); + gst_base_video_decoder_reset (base_video_decoder); + + base_video_decoder->current_frame = + gst_base_video_decoder_new_frame (base_video_decoder); + + base_video_decoder->sink_clipping = TRUE; +} + +static gboolean +gst_base_video_decoder_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstBaseVideoDecoder *base_video_decoder; + GstBaseVideoDecoderClass *base_video_decoder_class; + GstStructure *structure; + const GValue *codec_data; + GstVideoState *state; + gboolean ret = TRUE; + + base_video_decoder = GST_BASE_VIDEO_DECODER (gst_pad_get_parent (pad)); + base_video_decoder_class = + GST_BASE_VIDEO_DECODER_GET_CLASS (base_video_decoder); + + GST_DEBUG ("setcaps %" GST_PTR_FORMAT, caps); + + state = &base_video_decoder->state; + + if (state->codec_data) { + gst_buffer_unref (state->codec_data); + } + memset (state, 0, sizeof (GstVideoState)); + + structure = gst_caps_get_structure (caps, 0); + + gst_video_format_parse_caps (caps, NULL, &state->width, &state->height); + gst_video_parse_caps_framerate (caps, &state->fps_n, &state->fps_d); + gst_video_parse_caps_pixel_aspect_ratio (caps, &state->par_n, &state->par_d); + +#if 0 + /* requires 0.10.23 */ + state->have_interlaced = + gst_video_format_parse_caps_interlaced (caps, &state->interlaced); +#else + state->have_interlaced = gst_structure_get_boolean (structure, + "interlaced", &state->interlaced); +#endif + + codec_data = gst_structure_get_value (structure, "codec_data"); + if (codec_data && G_VALUE_TYPE (codec_data) == GST_TYPE_BUFFER) { + state->codec_data = gst_value_get_buffer (codec_data); + } + + if (base_video_decoder_class->start) { + ret = base_video_decoder_class->start (base_video_decoder); + } + + g_object_unref (base_video_decoder); + + return ret; +} + +static void +gst_base_video_decoder_finalize (GObject * object) +{ + GstBaseVideoDecoder *base_video_decoder; + GstBaseVideoDecoderClass *base_video_decoder_class; + + g_return_if_fail (GST_IS_BASE_VIDEO_DECODER (object)); + base_video_decoder = GST_BASE_VIDEO_DECODER (object); + base_video_decoder_class = GST_BASE_VIDEO_DECODER_GET_CLASS (object); + + gst_base_video_decoder_reset (base_video_decoder); + + if (base_video_decoder->input_adapter) { + g_object_unref (base_video_decoder->input_adapter); + base_video_decoder->input_adapter = NULL; + } + if (base_video_decoder->output_adapter) { + g_object_unref (base_video_decoder->output_adapter); + base_video_decoder->output_adapter = NULL; + } + + GST_DEBUG_OBJECT (object, "finalize"); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_base_video_decoder_sink_event (GstPad * pad, GstEvent * event) +{ + GstBaseVideoDecoder *base_video_decoder; + GstBaseVideoDecoderClass *base_video_decoder_class; + gboolean ret = FALSE; + + base_video_decoder = GST_BASE_VIDEO_DECODER (gst_pad_get_parent (pad)); + base_video_decoder_class = + GST_BASE_VIDEO_DECODER_GET_CLASS (base_video_decoder); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + { + if (!base_video_decoder->packetized) { + GstFlowReturn flow_ret; + + do { + flow_ret = + base_video_decoder_class->parse_data (base_video_decoder, TRUE); + } while (flow_ret == GST_FLOW_OK); + } + + if (base_video_decoder_class->finish) { + base_video_decoder_class->finish (base_video_decoder); + } + + ret = + gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_decoder), + event); + } + break; + case GST_EVENT_NEWSEGMENT: + { + gboolean update; + double rate; + double applied_rate; + GstFormat format; + gint64 start; + gint64 stop; + gint64 position; + GstSegment *segment = &base_video_decoder->segment; + + gst_event_parse_new_segment_full (event, &update, &rate, + &applied_rate, &format, &start, &stop, &position); + + if (format != GST_FORMAT_TIME) + goto newseg_wrong_format; + + if (!update) { + gst_base_video_decoder_reset (base_video_decoder); + } + + base_video_decoder->timestamp_offset = start; + + gst_segment_set_newsegment_full (segment, + update, rate, applied_rate, format, start, stop, position); + + GST_WARNING ("new segment: format %d rate %g start %" GST_TIME_FORMAT + " stop %" GST_TIME_FORMAT + " position %" GST_TIME_FORMAT + " update %d", + format, rate, + GST_TIME_ARGS (segment->start), + GST_TIME_ARGS (segment->stop), GST_TIME_ARGS (segment->time), update); + + ret = + gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_decoder), + event); + } + break; + default: + /* FIXME this changes the order of events */ + ret = + gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_decoder), + event); + break; + } + +done: + gst_object_unref (base_video_decoder); + return ret; + +newseg_wrong_format: + { + GST_DEBUG_OBJECT (base_video_decoder, "received non TIME newsegment"); + gst_event_unref (event); + goto done; + } +} + +static gboolean +gst_base_video_decoder_src_event (GstPad * pad, GstEvent * event) +{ + GstBaseVideoDecoder *base_video_decoder; + gboolean res = FALSE; + + base_video_decoder = GST_BASE_VIDEO_DECODER (gst_pad_get_parent (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + { + GstFormat format, tformat; + gdouble rate; + GstEvent *real_seek; + GstSeekFlags flags; + GstSeekType cur_type, stop_type; + gint64 cur, stop; + gint64 tcur, tstop; + + GST_DEBUG ("seek event"); + + gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, + &cur, &stop_type, &stop); + gst_event_unref (event); + + tformat = GST_FORMAT_TIME; + res = + gst_base_video_decoder_src_convert (pad, format, cur, &tformat, + &tcur); + if (!res) + goto convert_error; + res = + gst_base_video_decoder_src_convert (pad, format, stop, &tformat, + &tstop); + if (!res) + goto convert_error; + + real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME, + flags, cur_type, tcur, stop_type, tstop); + + res = + gst_pad_push_event (GST_BASE_VIDEO_CODEC_SINK_PAD + (base_video_decoder), real_seek); + + break; + } + case GST_EVENT_QOS: + { + gdouble proportion; + GstClockTimeDiff diff; + GstClockTime timestamp; + + gst_event_parse_qos (event, &proportion, &diff, ×tamp); + + GST_OBJECT_LOCK (base_video_decoder); + base_video_decoder->proportion = proportion; + base_video_decoder->earliest_time = timestamp + diff; + GST_OBJECT_UNLOCK (base_video_decoder); + + GST_DEBUG_OBJECT (base_video_decoder, + "got QoS %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT ", %g", + GST_TIME_ARGS (timestamp), diff, proportion); + + res = + gst_pad_push_event (GST_BASE_VIDEO_CODEC_SINK_PAD + (base_video_decoder), event); + break; + } + default: + res = + gst_pad_push_event (GST_BASE_VIDEO_CODEC_SINK_PAD + (base_video_decoder), event); + break; + } +done: + gst_object_unref (base_video_decoder); + return res; + +convert_error: + GST_DEBUG_OBJECT (base_video_decoder, "could not convert format"); + goto done; +} + + +#if 0 +static gboolean +gst_base_video_decoder_sink_convert (GstPad * pad, + GstFormat src_format, gint64 src_value, + GstFormat * dest_format, gint64 * dest_value) +{ + gboolean res = TRUE; + GstBaseVideoDecoder *enc; + + if (src_format == *dest_format) { + *dest_value = src_value; + return TRUE; + } + + enc = GST_BASE_VIDEO_DECODER (gst_pad_get_parent (pad)); + + /* FIXME: check if we are in a decoding state */ + + switch (src_format) { + case GST_FORMAT_BYTES: + switch (*dest_format) { +#if 0 + case GST_FORMAT_DEFAULT: + *dest_value = gst_util_uint64_scale_int (src_value, 1, + enc->bytes_per_picture); + break; +#endif + case GST_FORMAT_TIME: + /* seems like a rather silly conversion, implement me if you like */ + default: + res = FALSE; + } + break; + case GST_FORMAT_DEFAULT: + switch (*dest_format) { + case GST_FORMAT_TIME: + *dest_value = gst_util_uint64_scale (src_value, + GST_SECOND * enc->fps_d, enc->fps_n); + break; +#if 0 + case GST_FORMAT_BYTES: + *dest_value = gst_util_uint64_scale_int (src_value, + enc->bytes_per_picture, 1); + break; +#endif + default: + res = FALSE; + } + break; + default: + res = FALSE; + break; + } +} +#endif + +static gboolean +gst_base_video_decoder_src_convert (GstPad * pad, + GstFormat src_format, gint64 src_value, + GstFormat * dest_format, gint64 * dest_value) +{ + gboolean res = TRUE; + GstBaseVideoDecoder *enc; + + if (src_format == *dest_format) { + *dest_value = src_value; + return TRUE; + } + + enc = GST_BASE_VIDEO_DECODER (gst_pad_get_parent (pad)); + + /* FIXME: check if we are in a encoding state */ + + GST_DEBUG ("src convert"); + switch (src_format) { +#if 0 + case GST_FORMAT_DEFAULT: + switch (*dest_format) { + case GST_FORMAT_TIME: + *dest_value = gst_util_uint64_scale (granulepos_to_frame (src_value), + enc->fps_d * GST_SECOND, enc->fps_n); + break; + default: + res = FALSE; + } + break; + case GST_FORMAT_TIME: + switch (*dest_format) { + case GST_FORMAT_DEFAULT: + { + *dest_value = gst_util_uint64_scale (src_value, + enc->fps_n, enc->fps_d * GST_SECOND); + break; + } + default: + res = FALSE; + break; + } + break; +#endif + default: + res = FALSE; + break; + } + + gst_object_unref (enc); + + return res; +} + +static const GstQueryType * +gst_base_video_decoder_get_query_types (GstPad * pad) +{ + static const GstQueryType query_types[] = { + GST_QUERY_POSITION, + GST_QUERY_DURATION, + GST_QUERY_CONVERT, + 0 + }; + + return query_types; +} + +static gboolean +gst_base_video_decoder_src_query (GstPad * pad, GstQuery * query) +{ + GstBaseVideoDecoder *enc; + gboolean res = TRUE; + + enc = GST_BASE_VIDEO_DECODER (gst_pad_get_parent (pad)); + + switch GST_QUERY_TYPE + (query) { + case GST_QUERY_POSITION: + { + GstFormat format; + gint64 time; + + gst_query_parse_position (query, &format, NULL); + GST_DEBUG ("query in format %d", format); + + if (format != GST_FORMAT_TIME) { + goto error; + } + + time = enc->last_timestamp; + time = gst_segment_to_stream_time (&enc->segment, GST_FORMAT_TIME, time); + + gst_query_set_position (query, format, time); + + res = TRUE; + + break; + } + case GST_QUERY_DURATION: + { + res = gst_pad_peer_query (enc->base_video_codec.sinkpad, query); + break; + } + case GST_QUERY_CONVERT: + { + GstFormat src_fmt, dest_fmt; + gint64 src_val, dest_val; + + GST_DEBUG ("convert query"); + + gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); + res = + gst_base_video_decoder_src_convert (pad, src_fmt, src_val, &dest_fmt, + &dest_val); + if (!res) + goto error; + gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); + break; + } + default: + res = gst_pad_query_default (pad, query); + } + gst_object_unref (enc); + return res; + +error: + GST_ERROR_OBJECT (enc, "query failed"); + gst_object_unref (enc); + return res; +} + +static gboolean +gst_base_video_decoder_sink_query (GstPad * pad, GstQuery * query) +{ + GstBaseVideoDecoder *base_video_decoder; + gboolean res = FALSE; + + base_video_decoder = GST_BASE_VIDEO_DECODER (gst_pad_get_parent (pad)); + + GST_DEBUG_OBJECT (base_video_decoder, "sink query fps=%d/%d", + base_video_decoder->state.fps_n, base_video_decoder->state.fps_d); + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONVERT: + { + GstFormat src_fmt, dest_fmt; + gint64 src_val, dest_val; + + gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); + res = gst_base_video_rawvideo_convert (&base_video_decoder->state, + src_fmt, src_val, &dest_fmt, &dest_val); + if (!res) + goto error; + gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); + break; + } + default: + res = gst_pad_query_default (pad, query); + break; + } +done: + gst_object_unref (base_video_decoder); + + return res; +error: + GST_DEBUG_OBJECT (base_video_decoder, "query failed"); + goto done; +} + + +#if 0 +static gboolean +gst_pad_is_negotiated (GstPad * pad) +{ + GstCaps *caps; + + g_return_val_if_fail (pad != NULL, FALSE); + + caps = gst_pad_get_negotiated_caps (pad); + if (caps) { + gst_caps_unref (caps); + return TRUE; + } + + return FALSE; +} +#endif + +typedef struct _Timestamp Timestamp; +struct _Timestamp +{ + guint64 offset; + GstClockTime timestamp; + GstClockTime duration; +}; + +static void +gst_base_video_decoder_add_timestamp (GstBaseVideoDecoder * base_video_decoder, + GstBuffer * buffer) +{ + Timestamp *ts; + + ts = g_malloc (sizeof (Timestamp)); + + GST_DEBUG ("adding timestamp %lld %" GST_TIME_FORMAT, + base_video_decoder->input_offset, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer))); + + ts->offset = base_video_decoder->input_offset; + ts->timestamp = GST_BUFFER_TIMESTAMP (buffer); + ts->duration = GST_BUFFER_DURATION (buffer); + + base_video_decoder->timestamps = + g_list_append (base_video_decoder->timestamps, ts); +} + +static void +gst_base_video_decoder_get_timestamp_at_offset (GstBaseVideoDecoder * + base_video_decoder, guint64 offset, GstClockTime * timestamp, + GstClockTime * duration) +{ + Timestamp *ts; + GList *g; + + *timestamp = GST_CLOCK_TIME_NONE; + *duration = GST_CLOCK_TIME_NONE; + + g = base_video_decoder->timestamps; + while (g) { + ts = g->data; + if (ts->offset <= offset) { + *timestamp = ts->timestamp; + *duration = ts->duration; + g_free (ts); + g = g_list_next (g); + base_video_decoder->timestamps = + g_list_remove (base_video_decoder->timestamps, ts); + } else { + break; + } + } + + GST_DEBUG ("got timestamp %lld %" GST_TIME_FORMAT, + offset, GST_TIME_ARGS (*timestamp)); +} + +static void +gst_base_video_decoder_reset (GstBaseVideoDecoder * base_video_decoder) +{ + GstBaseVideoDecoderClass *base_video_decoder_class; + GList *g; + + base_video_decoder_class = + GST_BASE_VIDEO_DECODER_GET_CLASS (base_video_decoder); + + GST_DEBUG ("reset"); + + base_video_decoder->started = FALSE; + + base_video_decoder->discont = TRUE; + base_video_decoder->have_sync = FALSE; + + base_video_decoder->timestamp_offset = GST_CLOCK_TIME_NONE; + base_video_decoder->system_frame_number = 0; + base_video_decoder->presentation_frame_number = 0; + base_video_decoder->base_picture_number = 0; + base_video_decoder->last_timestamp = GST_CLOCK_TIME_NONE; + + base_video_decoder->input_offset = 0; + base_video_decoder->frame_offset = 0; + + /* This function could be called from finalize() */ + if (base_video_decoder->input_adapter) { + gst_adapter_clear (base_video_decoder->input_adapter); + } + if (base_video_decoder->output_adapter) { + gst_adapter_clear (base_video_decoder->output_adapter); + } + + if (base_video_decoder->caps) { + gst_caps_unref (base_video_decoder->caps); + base_video_decoder->caps = NULL; + } + //gst_segment_init (&base_video_decoder->segment, GST_FORMAT_TIME); + + if (base_video_decoder->current_frame) { + gst_base_video_decoder_free_frame (base_video_decoder->current_frame); + base_video_decoder->current_frame = NULL; + } + + base_video_decoder->have_src_caps = FALSE; + + for (g = g_list_first (base_video_decoder->frames); g; g = g_list_next (g)) { + GstVideoFrame *frame = g->data; + gst_base_video_decoder_free_frame (frame); + } + g_list_free (base_video_decoder->frames); + base_video_decoder->frames = NULL; + + if (base_video_decoder_class->reset) { + base_video_decoder_class->reset (base_video_decoder); + } +} + +static GstFlowReturn +gst_base_video_decoder_chain (GstPad * pad, GstBuffer * buf) +{ + GstBaseVideoDecoder *base_video_decoder; + GstBaseVideoDecoderClass *klass; + GstFlowReturn ret; + + GST_DEBUG ("chain %" GST_TIME_FORMAT " duration %" GST_TIME_FORMAT, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), + GST_TIME_ARGS (GST_BUFFER_DURATION (buf))); + +#if 0 + /* requiring the pad to be negotiated makes it impossible to use + * oggdemux or filesrc ! decoder */ + if (!gst_pad_is_negotiated (pad)) { + GST_DEBUG ("not negotiated"); + return GST_FLOW_NOT_NEGOTIATED; + } +#endif + + base_video_decoder = GST_BASE_VIDEO_DECODER (gst_pad_get_parent (pad)); + klass = GST_BASE_VIDEO_DECODER_GET_CLASS (base_video_decoder); + + GST_DEBUG_OBJECT (base_video_decoder, "chain"); + + if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT))) { + GST_DEBUG_OBJECT (base_video_decoder, "received DISCONT buffer"); + gst_base_video_decoder_reset (base_video_decoder); + } + + if (!base_video_decoder->started) { + klass->start (base_video_decoder); + base_video_decoder->started = TRUE; + } + + if (base_video_decoder->current_frame == NULL) { + base_video_decoder->current_frame = + gst_base_video_decoder_new_frame (base_video_decoder); + } + + if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { + gst_base_video_decoder_add_timestamp (base_video_decoder, buf); + } + base_video_decoder->input_offset += GST_BUFFER_SIZE (buf); + +#if 0 + if (base_video_decoder->timestamp_offset == GST_CLOCK_TIME_NONE && + GST_BUFFER_TIMESTAMP (buf) != GST_CLOCK_TIME_NONE) { + GST_DEBUG ("got new offset %lld", GST_BUFFER_TIMESTAMP (buf)); + base_video_decoder->timestamp_offset = GST_BUFFER_TIMESTAMP (buf); + } +#endif + + if (base_video_decoder->packetized) { + base_video_decoder->current_frame->sink_buffer = buf; + + ret = gst_base_video_decoder_have_frame_2 (base_video_decoder); + } else { + + gst_adapter_push (base_video_decoder->input_adapter, buf); + + if (!base_video_decoder->have_sync) { + int n, m; + + GST_DEBUG ("no sync, scanning"); + + n = gst_adapter_available (base_video_decoder->input_adapter); + m = klass->scan_for_sync (base_video_decoder, FALSE, 0, n); + if (m == -1) { + gst_object_unref (base_video_decoder); + return GST_FLOW_OK; + } + + if (m >= n) { + GST_ERROR ("subclass scanned past end %d >= %d", m, n); + } + + gst_adapter_flush (base_video_decoder->input_adapter, m); + + if (m < n) { + GST_DEBUG ("found possible sync after %d bytes (of %d)", m, n); + + /* this is only "maybe" sync */ + base_video_decoder->have_sync = TRUE; + } + + if (!base_video_decoder->have_sync) { + gst_object_unref (base_video_decoder); + return GST_FLOW_OK; + } + } + + do { + ret = klass->parse_data (base_video_decoder, FALSE); + } while (ret == GST_FLOW_OK); + + if (ret == GST_BASE_VIDEO_DECODER_FLOW_NEED_DATA) { + gst_object_unref (base_video_decoder); + return GST_FLOW_OK; + } + } + + gst_object_unref (base_video_decoder); + return ret; +} + +static GstStateChangeReturn +gst_base_video_decoder_change_state (GstElement * element, + GstStateChange transition) +{ + GstBaseVideoDecoder *base_video_decoder; + GstBaseVideoDecoderClass *base_video_decoder_class; + GstStateChangeReturn ret; + + base_video_decoder = GST_BASE_VIDEO_DECODER (element); + base_video_decoder_class = GST_BASE_VIDEO_DECODER_GET_CLASS (element); + + switch (transition) { + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + if (base_video_decoder_class->stop) { + base_video_decoder_class->stop (base_video_decoder); + } + break; + default: + break; + } + + return ret; +} + +static void +gst_base_video_decoder_free_frame (GstVideoFrame * frame) +{ + g_return_if_fail (frame != NULL); + + if (frame->sink_buffer) { + gst_buffer_unref (frame->sink_buffer); + } + if (frame->src_buffer) { + gst_buffer_unref (frame->src_buffer); + } + + g_free (frame); +} + +static GstVideoFrame * +gst_base_video_decoder_new_frame (GstBaseVideoDecoder * base_video_decoder) +{ + GstVideoFrame *frame; + + frame = g_malloc0 (sizeof (GstVideoFrame)); + + frame->system_frame_number = base_video_decoder->system_frame_number; + base_video_decoder->system_frame_number++; + + frame->decode_frame_number = frame->system_frame_number - + base_video_decoder->reorder_depth; + + frame->decode_timestamp = -1; + frame->presentation_timestamp = -1; + frame->presentation_duration = -1; + frame->n_fields = 2; + + return frame; +} + +GstFlowReturn +gst_base_video_decoder_finish_frame (GstBaseVideoDecoder * base_video_decoder, + GstVideoFrame * frame) +{ + GstBaseVideoDecoderClass *base_video_decoder_class; + GstBuffer *src_buffer; + + GST_DEBUG ("finish frame"); + + base_video_decoder_class = + GST_BASE_VIDEO_DECODER_GET_CLASS (base_video_decoder); + + GST_DEBUG ("finish frame sync=%d pts=%lld", frame->is_sync_point, + frame->presentation_timestamp); + + if (GST_CLOCK_TIME_IS_VALID (frame->presentation_timestamp)) { + if (frame->presentation_timestamp != base_video_decoder->timestamp_offset) { + GST_DEBUG ("sync timestamp %lld diff %lld", + frame->presentation_timestamp, + frame->presentation_timestamp - base_video_decoder->segment.start); + base_video_decoder->timestamp_offset = frame->presentation_timestamp; + base_video_decoder->field_index = 0; + } else { + /* This case is for one initial timestamp and no others, e.g., + * filesrc ! decoder ! xvimagesink */ + GST_WARNING ("sync timestamp didn't change, ignoring"); + frame->presentation_timestamp = GST_CLOCK_TIME_NONE; + } + } else { + if (frame->is_sync_point) { + GST_WARNING ("sync point doesn't have timestamp"); + if (!GST_CLOCK_TIME_IS_VALID (base_video_decoder->timestamp_offset)) { + GST_WARNING + ("No base timestamp. Assuming frames start at segment start"); + base_video_decoder->timestamp_offset = + base_video_decoder->segment.start; + base_video_decoder->field_index = 0; + } + } + } + frame->field_index = base_video_decoder->field_index; + base_video_decoder->field_index += frame->n_fields; + + if (frame->presentation_timestamp == GST_CLOCK_TIME_NONE) { + frame->presentation_timestamp = + gst_base_video_decoder_get_field_timestamp (base_video_decoder, + frame->field_index); + frame->presentation_duration = GST_CLOCK_TIME_NONE; + frame->decode_timestamp = + gst_base_video_decoder_get_timestamp (base_video_decoder, + frame->decode_frame_number); + } + if (frame->presentation_duration == GST_CLOCK_TIME_NONE) { + frame->presentation_duration = + gst_base_video_decoder_get_field_duration (base_video_decoder, + frame->n_fields); + } + + if (GST_CLOCK_TIME_IS_VALID (base_video_decoder->last_timestamp)) { + if (frame->presentation_timestamp < base_video_decoder->last_timestamp) { + GST_WARNING ("decreasing timestamp (%lld < %lld)", + frame->presentation_timestamp, base_video_decoder->last_timestamp); + } + } + base_video_decoder->last_timestamp = frame->presentation_timestamp; + + GST_BUFFER_FLAG_UNSET (frame->src_buffer, GST_BUFFER_FLAG_DELTA_UNIT); + if (base_video_decoder->state.interlaced) { +#ifndef GST_VIDEO_BUFFER_TFF +#define GST_VIDEO_BUFFER_TFF (GST_MINI_OBJECT_FLAG_LAST << 5) +#endif +#ifndef GST_VIDEO_BUFFER_RFF +#define GST_VIDEO_BUFFER_RFF (GST_MINI_OBJECT_FLAG_LAST << 6) +#endif +#ifndef GST_VIDEO_BUFFER_ONEFIELD +#define GST_VIDEO_BUFFER_ONEFIELD (GST_MINI_OBJECT_FLAG_LAST << 7) +#endif + int tff = base_video_decoder->state.top_field_first; + + if (frame->field_index & 1) { + tff ^= 1; + } + if (tff) { + GST_BUFFER_FLAG_SET (frame->src_buffer, GST_VIDEO_BUFFER_TFF); + } else { + GST_BUFFER_FLAG_UNSET (frame->src_buffer, GST_VIDEO_BUFFER_TFF); + } + GST_BUFFER_FLAG_UNSET (frame->src_buffer, GST_VIDEO_BUFFER_RFF); + GST_BUFFER_FLAG_UNSET (frame->src_buffer, GST_VIDEO_BUFFER_ONEFIELD); + if (frame->n_fields == 3) { + GST_BUFFER_FLAG_SET (frame->src_buffer, GST_VIDEO_BUFFER_RFF); + } else if (frame->n_fields == 1) { + GST_BUFFER_FLAG_UNSET (frame->src_buffer, GST_VIDEO_BUFFER_ONEFIELD); + } + } + + GST_BUFFER_TIMESTAMP (frame->src_buffer) = frame->presentation_timestamp; + GST_BUFFER_DURATION (frame->src_buffer) = frame->presentation_duration; + GST_BUFFER_OFFSET (frame->src_buffer) = -1; + GST_BUFFER_OFFSET_END (frame->src_buffer) = -1; + + GST_DEBUG ("pushing frame %" GST_TIME_FORMAT, + GST_TIME_ARGS (frame->presentation_timestamp)); + + base_video_decoder->frames = + g_list_remove (base_video_decoder->frames, frame); + + gst_base_video_decoder_set_src_caps (base_video_decoder); + + src_buffer = frame->src_buffer; + frame->src_buffer = NULL; + + gst_base_video_decoder_free_frame (frame); + + if (base_video_decoder->sink_clipping) { + gint64 start = GST_BUFFER_TIMESTAMP (src_buffer); + gint64 stop = GST_BUFFER_TIMESTAMP (src_buffer) + + GST_BUFFER_DURATION (src_buffer); + + if (gst_segment_clip (&base_video_decoder->segment, GST_FORMAT_TIME, + start, stop, &start, &stop)) { + GST_BUFFER_TIMESTAMP (src_buffer) = start; + GST_BUFFER_DURATION (src_buffer) = stop - start; + GST_DEBUG ("accepting buffer inside segment: %" GST_TIME_FORMAT + " %" GST_TIME_FORMAT + " seg %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT + " time %" GST_TIME_FORMAT, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (src_buffer)), + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (src_buffer) + + GST_BUFFER_DURATION (src_buffer)), + GST_TIME_ARGS (base_video_decoder->segment.start), + GST_TIME_ARGS (base_video_decoder->segment.stop), + GST_TIME_ARGS (base_video_decoder->segment.time)); + } else { + GST_DEBUG ("dropping buffer outside segment: %" GST_TIME_FORMAT + " %" GST_TIME_FORMAT + " seg %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT + " time %" GST_TIME_FORMAT, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (src_buffer)), + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (src_buffer) + + GST_BUFFER_DURATION (src_buffer)), + GST_TIME_ARGS (base_video_decoder->segment.start), + GST_TIME_ARGS (base_video_decoder->segment.stop), + GST_TIME_ARGS (base_video_decoder->segment.time)); + gst_buffer_unref (src_buffer); + return GST_FLOW_OK; + } + } + + return gst_pad_push (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_decoder), + src_buffer); +} + +GstFlowReturn +gst_base_video_decoder_skip_frame (GstBaseVideoDecoder * base_video_decoder, + GstVideoFrame * frame) +{ + GstBaseVideoDecoderClass *base_video_decoder_class; + + GST_DEBUG ("finish frame"); + + base_video_decoder_class = + GST_BASE_VIDEO_DECODER_GET_CLASS (base_video_decoder); + + GST_DEBUG ("finish frame sync=%d pts=%lld", frame->is_sync_point, + frame->presentation_timestamp); + + if (GST_CLOCK_TIME_IS_VALID (frame->presentation_timestamp)) { + if (frame->presentation_timestamp != base_video_decoder->timestamp_offset) { + GST_DEBUG ("sync timestamp %lld diff %lld", + frame->presentation_timestamp, + frame->presentation_timestamp - base_video_decoder->segment.start); + base_video_decoder->timestamp_offset = frame->presentation_timestamp; + base_video_decoder->field_index = 0; + } else { + /* This case is for one initial timestamp and no others, e.g., + * filesrc ! decoder ! xvimagesink */ + GST_WARNING ("sync timestamp didn't change, ignoring"); + frame->presentation_timestamp = GST_CLOCK_TIME_NONE; + } + } else { + if (frame->is_sync_point) { + GST_WARNING ("sync point doesn't have timestamp"); + if (GST_CLOCK_TIME_IS_VALID (base_video_decoder->timestamp_offset)) { + GST_WARNING + ("No base timestamp. Assuming frames start at segment start"); + base_video_decoder->timestamp_offset = + base_video_decoder->segment.start; + base_video_decoder->field_index = 0; + } + } + } + frame->field_index = base_video_decoder->field_index; + base_video_decoder->field_index += frame->n_fields; + + if (frame->presentation_timestamp == GST_CLOCK_TIME_NONE) { + frame->presentation_timestamp = + gst_base_video_decoder_get_field_timestamp (base_video_decoder, + frame->field_index); + frame->presentation_duration = GST_CLOCK_TIME_NONE; + frame->decode_timestamp = + gst_base_video_decoder_get_timestamp (base_video_decoder, + frame->decode_frame_number); + } + if (frame->presentation_duration == GST_CLOCK_TIME_NONE) { + frame->presentation_duration = + gst_base_video_decoder_get_field_duration (base_video_decoder, + frame->n_fields); + } + + base_video_decoder->last_timestamp = frame->presentation_timestamp; + + GST_DEBUG ("skipping frame %" GST_TIME_FORMAT, + GST_TIME_ARGS (frame->presentation_timestamp)); + + base_video_decoder->frames = + g_list_remove (base_video_decoder->frames, frame); + + gst_base_video_decoder_free_frame (frame); + + return GST_FLOW_OK; +} + +int +gst_base_video_decoder_get_height (GstBaseVideoDecoder * base_video_decoder) +{ + return base_video_decoder->state.height; +} + +int +gst_base_video_decoder_get_width (GstBaseVideoDecoder * base_video_decoder) +{ + return base_video_decoder->state.width; +} + +GstFlowReturn +gst_base_video_decoder_end_of_stream (GstBaseVideoDecoder * base_video_decoder, + GstBuffer * buffer) +{ + + if (base_video_decoder->frames) { + GST_DEBUG ("EOS with frames left over"); + } + + return gst_pad_push (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_decoder), + buffer); +} + +void +gst_base_video_decoder_add_to_frame (GstBaseVideoDecoder * base_video_decoder, + int n_bytes) +{ + GstBuffer *buf; + + GST_DEBUG ("add to frame"); + + if (n_bytes == 0) + return; + + if (gst_adapter_available (base_video_decoder->output_adapter) == 0) { + base_video_decoder->frame_offset = base_video_decoder->input_offset - + gst_adapter_available (base_video_decoder->input_adapter); + } + buf = gst_adapter_take_buffer (base_video_decoder->input_adapter, n_bytes); + + gst_adapter_push (base_video_decoder->output_adapter, buf); +} + +static guint64 +gst_base_video_decoder_get_timestamp (GstBaseVideoDecoder * base_video_decoder, + int picture_number) +{ + if (base_video_decoder->state.fps_d == 0) { + return -1; + } + if (picture_number < base_video_decoder->base_picture_number) { + return base_video_decoder->timestamp_offset - + (gint64) gst_util_uint64_scale (base_video_decoder->base_picture_number + - picture_number, base_video_decoder->state.fps_d * GST_SECOND, + base_video_decoder->state.fps_n); + } else { + return base_video_decoder->timestamp_offset + + gst_util_uint64_scale (picture_number - + base_video_decoder->base_picture_number, + base_video_decoder->state.fps_d * GST_SECOND, + base_video_decoder->state.fps_n); + } +} + +static guint64 +gst_base_video_decoder_get_field_timestamp (GstBaseVideoDecoder * + base_video_decoder, int field_offset) +{ + if (base_video_decoder->state.fps_d == 0) { + return GST_CLOCK_TIME_NONE; + } + if (field_offset < 0) { + GST_WARNING ("field offset < 0"); + return GST_CLOCK_TIME_NONE; + } + return base_video_decoder->timestamp_offset + + gst_util_uint64_scale (field_offset, + base_video_decoder->state.fps_d * GST_SECOND, + base_video_decoder->state.fps_n * 2); +} + +static guint64 +gst_base_video_decoder_get_field_duration (GstBaseVideoDecoder * + base_video_decoder, int n_fields) +{ + if (base_video_decoder->state.fps_d == 0) { + return GST_CLOCK_TIME_NONE; + } + if (n_fields < 0) { + GST_WARNING ("n_fields < 0"); + return GST_CLOCK_TIME_NONE; + } + return gst_util_uint64_scale (n_fields, + base_video_decoder->state.fps_d * GST_SECOND, + base_video_decoder->state.fps_n * 2); +} + + +GstFlowReturn +gst_base_video_decoder_have_frame (GstBaseVideoDecoder * base_video_decoder) +{ + GstBuffer *buffer; + int n_available; + GstClockTime timestamp; + GstClockTime duration; + + GST_DEBUG ("have_frame"); + + n_available = gst_adapter_available (base_video_decoder->output_adapter); + if (n_available) { + buffer = gst_adapter_take_buffer (base_video_decoder->output_adapter, + n_available); + } else { + buffer = gst_buffer_new_and_alloc (0); + } + + base_video_decoder->current_frame->sink_buffer = buffer; + + gst_base_video_decoder_get_timestamp_at_offset (base_video_decoder, + base_video_decoder->frame_offset, ×tamp, &duration); + + GST_BUFFER_TIMESTAMP (buffer) = timestamp; + GST_BUFFER_DURATION (buffer) = duration; + + return gst_base_video_decoder_have_frame_2 (base_video_decoder); +} + +static GstFlowReturn +gst_base_video_decoder_have_frame_2 (GstBaseVideoDecoder * base_video_decoder) +{ + GstVideoFrame *frame = base_video_decoder->current_frame; + GstBaseVideoDecoderClass *base_video_decoder_class; + GstFlowReturn ret = GST_FLOW_OK; + + base_video_decoder_class = + GST_BASE_VIDEO_DECODER_GET_CLASS (base_video_decoder); + + frame->distance_from_sync = base_video_decoder->distance_from_sync; + base_video_decoder->distance_from_sync++; + + frame->presentation_timestamp = GST_BUFFER_TIMESTAMP (frame->sink_buffer); + frame->presentation_duration = GST_BUFFER_DURATION (frame->sink_buffer); + + GST_DEBUG ("pts %" GST_TIME_FORMAT, + GST_TIME_ARGS (frame->presentation_timestamp)); + GST_DEBUG ("dts %" GST_TIME_FORMAT, GST_TIME_ARGS (frame->decode_timestamp)); + GST_DEBUG ("dist %d", frame->distance_from_sync); + + base_video_decoder->frames = g_list_append (base_video_decoder->frames, + frame); + + /* do something with frame */ + ret = base_video_decoder_class->handle_frame (base_video_decoder, frame); + if (!GST_FLOW_IS_SUCCESS (ret)) { + GST_DEBUG ("flow error!"); + } + + /* create new frame */ + base_video_decoder->current_frame = + gst_base_video_decoder_new_frame (base_video_decoder); + + return ret; +} + +GstVideoState * +gst_base_video_decoder_get_state (GstBaseVideoDecoder * base_video_decoder) +{ + return &base_video_decoder->state; + +} + +void +gst_base_video_decoder_set_state (GstBaseVideoDecoder * base_video_decoder, + GstVideoState * state) +{ + memcpy (&base_video_decoder->state, state, sizeof (*state)); + +} + +void +gst_base_video_decoder_lost_sync (GstBaseVideoDecoder * base_video_decoder) +{ + g_return_if_fail (GST_IS_BASE_VIDEO_DECODER (base_video_decoder)); + + GST_DEBUG ("lost_sync"); + + if (gst_adapter_available (base_video_decoder->input_adapter) >= 1) { + gst_adapter_flush (base_video_decoder->input_adapter, 1); + } + + base_video_decoder->have_sync = FALSE; +} + +void +gst_base_video_decoder_set_sync_point (GstBaseVideoDecoder * base_video_decoder) +{ + GST_DEBUG ("set_sync_point"); + + base_video_decoder->current_frame->is_sync_point = TRUE; + base_video_decoder->distance_from_sync = 0; +} + +GstVideoFrame * +gst_base_video_decoder_get_oldest_frame (GstBaseVideoDecoder * + base_video_decoder) +{ + GList *g; + + g = g_list_first (base_video_decoder->frames); + + if (g == NULL) + return NULL; + return (GstVideoFrame *) (g->data); +} + +GstVideoFrame * +gst_base_video_decoder_get_frame (GstBaseVideoDecoder * base_video_decoder, + int frame_number) +{ + GList *g; + + for (g = g_list_first (base_video_decoder->frames); g; g = g_list_next (g)) { + GstVideoFrame *frame = g->data; + + if (frame->system_frame_number == frame_number) { + return frame; + } + } + + return NULL; +} + +void +gst_base_video_decoder_set_src_caps (GstBaseVideoDecoder * base_video_decoder) +{ + GstCaps *caps; + GstVideoState *state = &base_video_decoder->state; + + if (base_video_decoder->have_src_caps) + return; + + caps = gst_video_format_new_caps (state->format, + state->width, state->height, + state->fps_n, state->fps_d, state->par_n, state->par_d); + gst_caps_set_simple (caps, "interlaced", + G_TYPE_BOOLEAN, state->interlaced, NULL); + + GST_DEBUG ("setting caps %" GST_PTR_FORMAT, caps); + + gst_pad_set_caps (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_decoder), caps); + + base_video_decoder->have_src_caps = TRUE; + + gst_caps_unref (caps); +} + +GstFlowReturn +gst_base_video_decoder_alloc_src_frame (GstBaseVideoDecoder * + base_video_decoder, GstVideoFrame * frame) +{ + GstFlowReturn flow_ret; + int num_bytes; + + gst_base_video_decoder_set_src_caps (base_video_decoder); + + num_bytes = gst_video_format_get_size (base_video_decoder->state.format, + base_video_decoder->state.width, base_video_decoder->state.height); + flow_ret = + gst_pad_alloc_buffer_and_set_caps (GST_BASE_VIDEO_CODEC_SRC_PAD + (base_video_decoder), GST_BUFFER_OFFSET_NONE, num_bytes, + GST_PAD_CAPS (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_decoder)), + &frame->src_buffer); + + if (flow_ret != GST_FLOW_OK) { + GST_WARNING ("failed to get buffer"); + } + + return flow_ret; +} diff --git a/ext/vp8/gst/video/gstbasevideodecoder.h b/ext/vp8/gst/video/gstbasevideodecoder.h new file mode 100644 index 0000000..30b3c6c --- /dev/null +++ b/ext/vp8/gst/video/gstbasevideodecoder.h @@ -0,0 +1,170 @@ +/* GStreamer + * Copyright (C) 2008 David Schleef + * + * 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. + */ + +#ifndef _GST_BASE_VIDEO_DECODER_H_ +#define _GST_BASE_VIDEO_DECODER_H_ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_BASE_VIDEO_DECODER \ + (gst_base_video_decoder_get_type()) +#define GST_BASE_VIDEO_DECODER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASE_VIDEO_DECODER,GstBaseVideoDecoder)) +#define GST_BASE_VIDEO_DECODER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASE_VIDEO_DECODER,GstBaseVideoDecoderClass)) +#define GST_BASE_VIDEO_DECODER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_BASE_VIDEO_DECODER,GstBaseVideoDecoderClass)) +#define GST_IS_BASE_VIDEO_DECODER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASE_VIDEO_DECODER)) +#define GST_IS_BASE_VIDEO_DECODER_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASE_VIDEO_DECODER)) + +/** + * GST_BASE_VIDEO_DECODER_SINK_NAME: + * + * The name of the templates for the sink pad. + */ +#define GST_BASE_VIDEO_DECODER_SINK_NAME "sink" +/** + * GST_BASE_VIDEO_DECODER_SRC_NAME: + * + * The name of the templates for the source pad. + */ +#define GST_BASE_VIDEO_DECODER_SRC_NAME "src" + +/** + * * GST_BASE_VIDEO_DECODER_FLOW_NEED_DATA: + * * + * */ +#define GST_BASE_VIDEO_DECODER_FLOW_NEED_DATA GST_FLOW_CUSTOM_SUCCESS + + +typedef struct _GstBaseVideoDecoder GstBaseVideoDecoder; +typedef struct _GstBaseVideoDecoderClass GstBaseVideoDecoderClass; + +struct _GstBaseVideoDecoder +{ + GstBaseVideoCodec base_video_codec; + + /*< private >*/ + GstAdapter *input_adapter; + GstAdapter *output_adapter; + + GList *frames; + + gboolean have_sync; + gboolean discont; + gboolean started; + + GstVideoState state; + GstSegment segment; + + gboolean sink_clipping; + + guint64 presentation_frame_number; + guint64 system_frame_number; + + GstCaps *caps; + gboolean have_src_caps; + + GstVideoFrame *current_frame; + + int distance_from_sync; + int reorder_depth; + + GstClockTime buffer_timestamp; + + GstClockTime timestamp_offset; + + gdouble proportion; + GstClockTime earliest_time; + + //GstBuffer *codec_data; + + guint64 input_offset; + guint64 frame_offset; + GstClockTime last_timestamp; + + guint64 base_picture_number; + + int field_index; + + gboolean is_delta_unit; + gboolean packetized; + + GList *timestamps; +}; + +struct _GstBaseVideoDecoderClass +{ + GstBaseVideoCodecClass base_video_codec_class; + + gboolean (*set_format) (GstBaseVideoDecoder *coder, GstVideoFormat, + int width, int height, int fps_n, int fps_d, + int par_n, int par_d); + gboolean (*start) (GstBaseVideoDecoder *coder); + gboolean (*stop) (GstBaseVideoDecoder *coder); + gboolean (*reset) (GstBaseVideoDecoder *coder); + int (*scan_for_sync) (GstBaseVideoDecoder *decoder, gboolean at_eos, + int offset, int n); + GstFlowReturn (*parse_data) (GstBaseVideoDecoder *decoder, gboolean at_eos); + GstFlowReturn (*finish) (GstBaseVideoDecoder *coder); + GstFlowReturn (*handle_frame) (GstBaseVideoDecoder *coder, GstVideoFrame *frame); + GstFlowReturn (*shape_output) (GstBaseVideoDecoder *coder, GstVideoFrame *frame); + GstCaps *(*get_caps) (GstBaseVideoDecoder *coder); + +}; + +GType gst_base_video_decoder_get_type (void); + +int gst_base_video_decoder_get_width (GstBaseVideoDecoder *coder); +int gst_base_video_decoder_get_height (GstBaseVideoDecoder *coder); + +guint64 gst_base_video_decoder_get_timestamp_offset (GstBaseVideoDecoder *coder); + +GstVideoFrame *gst_base_video_decoder_get_frame (GstBaseVideoDecoder *coder, + int frame_number); +GstVideoFrame *gst_base_video_decoder_get_oldest_frame (GstBaseVideoDecoder *coder); +void gst_base_video_decoder_add_to_frame (GstBaseVideoDecoder *base_video_decoder, + int n_bytes); +GstFlowReturn gst_base_video_decoder_finish_frame (GstBaseVideoDecoder *base_video_decoder, + GstVideoFrame *frame); +GstFlowReturn gst_base_video_decoder_skip_frame (GstBaseVideoDecoder * base_video_decoder, + GstVideoFrame * frame); +GstFlowReturn gst_base_video_decoder_end_of_stream (GstBaseVideoDecoder *base_video_decoder, + GstBuffer *buffer); +GstFlowReturn +gst_base_video_decoder_have_frame (GstBaseVideoDecoder *base_video_decoder); +GstVideoState * gst_base_video_decoder_get_state (GstBaseVideoDecoder *base_video_decoder); +void gst_base_video_decoder_set_state (GstBaseVideoDecoder *base_video_decoder, + GstVideoState *state); +void gst_base_video_decoder_lost_sync (GstBaseVideoDecoder *base_video_decoder); +void gst_base_video_decoder_set_sync_point (GstBaseVideoDecoder *base_video_decoder); + +void gst_base_video_decoder_set_src_caps (GstBaseVideoDecoder *base_video_decoder); + +GstFlowReturn gst_base_video_decoder_alloc_src_frame (GstBaseVideoDecoder *base_video_decoder, + GstVideoFrame *frame); + +G_END_DECLS + +#endif + diff --git a/ext/vp8/gst/video/gstbasevideoencoder.c b/ext/vp8/gst/video/gstbasevideoencoder.c new file mode 100644 index 0000000..4d68c9b --- /dev/null +++ b/ext/vp8/gst/video/gstbasevideoencoder.c @@ -0,0 +1,518 @@ +/* GStreamer + * Copyright (C) 2008 David Schleef + * + * 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 "gstbasevideoencoder.h" +#include "gstbasevideoutils.h" + +GST_DEBUG_CATEGORY_EXTERN (basevideo_debug); +#define GST_CAT_DEFAULT basevideo_debug + +static void gst_base_video_encoder_finalize (GObject * object); + +static gboolean gst_base_video_encoder_sink_setcaps (GstPad * pad, + GstCaps * caps); +static gboolean gst_base_video_encoder_sink_event (GstPad * pad, + GstEvent * event); +static GstFlowReturn gst_base_video_encoder_chain (GstPad * pad, + GstBuffer * buf); +//static GstFlowReturn gst_base_video_encoder_process (GstBaseVideoEncoder *base_video_encoder); +static GstStateChangeReturn gst_base_video_encoder_change_state (GstElement * + element, GstStateChange transition); +static const GstQueryType *gst_base_video_encoder_get_query_types (GstPad * + pad); +static gboolean gst_base_video_encoder_src_query (GstPad * pad, + GstQuery * query); + + +GST_BOILERPLATE (GstBaseVideoEncoder, gst_base_video_encoder, GstBaseVideoCodec, + GST_TYPE_BASE_VIDEO_CODEC); + +static void +gst_base_video_encoder_base_init (gpointer g_class) +{ + +} + +static void +gst_base_video_encoder_class_init (GstBaseVideoEncoderClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = G_OBJECT_CLASS (klass); + gstelement_class = GST_ELEMENT_CLASS (klass); + + gobject_class->finalize = gst_base_video_encoder_finalize; + + gstelement_class->change_state = gst_base_video_encoder_change_state; + + parent_class = g_type_class_peek_parent (klass); +} + +static void +gst_base_video_encoder_init (GstBaseVideoEncoder * base_video_encoder, + GstBaseVideoEncoderClass * klass) +{ + GstPad *pad; + + GST_DEBUG ("gst_base_video_encoder_init"); + + pad = GST_BASE_VIDEO_CODEC_SINK_PAD (base_video_encoder); + + gst_pad_set_chain_function (pad, gst_base_video_encoder_chain); + gst_pad_set_event_function (pad, gst_base_video_encoder_sink_event); + gst_pad_set_setcaps_function (pad, gst_base_video_encoder_sink_setcaps); + //gst_pad_set_query_function (pad, gst_base_video_encoder_sink_query); + + pad = GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder); + + gst_pad_set_query_type_function (pad, gst_base_video_encoder_get_query_types); + gst_pad_set_query_function (pad, gst_base_video_encoder_src_query); +} + +static gboolean +gst_base_video_encoder_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstBaseVideoEncoder *base_video_encoder; + GstBaseVideoEncoderClass *base_video_encoder_class; + gboolean ret; + + base_video_encoder = GST_BASE_VIDEO_ENCODER (gst_pad_get_parent (pad)); + base_video_encoder_class = + GST_BASE_VIDEO_ENCODER_GET_CLASS (base_video_encoder); + + GST_DEBUG ("setcaps"); + + gst_base_video_state_from_caps (&base_video_encoder->state, caps); + + ret = base_video_encoder_class->set_format (base_video_encoder, + &base_video_encoder->state); + if (ret) { + ret = base_video_encoder_class->start (base_video_encoder); + } + + g_object_unref (base_video_encoder); + + return ret; +} + +static void +gst_base_video_encoder_finalize (GObject * object) +{ + GstBaseVideoEncoder *base_video_encoder; + GstBaseVideoEncoderClass *base_video_encoder_class; + GList *g; + + g_return_if_fail (GST_IS_BASE_VIDEO_ENCODER (object)); + base_video_encoder = GST_BASE_VIDEO_ENCODER (object); + base_video_encoder_class = GST_BASE_VIDEO_ENCODER_GET_CLASS (object); + + GST_DEBUG ("finalize"); + + for (g = base_video_encoder->frames; g; g = g_list_next (g)) { + gst_base_video_codec_free_frame ((GstVideoFrame *) g->data); + } + g_list_free (base_video_encoder->frames); + + if (base_video_encoder->caps) { + gst_caps_unref (base_video_encoder->caps); + base_video_encoder->caps = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_base_video_encoder_sink_event (GstPad * pad, GstEvent * event) +{ + GstBaseVideoEncoder *base_video_encoder; + GstBaseVideoEncoderClass *base_video_encoder_class; + gboolean ret = FALSE; + + base_video_encoder = GST_BASE_VIDEO_ENCODER (gst_pad_get_parent (pad)); + base_video_encoder_class = + GST_BASE_VIDEO_ENCODER_GET_CLASS (base_video_encoder); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + { + if (base_video_encoder_class->finish) { + base_video_encoder_class->finish (base_video_encoder); + } + + ret = + gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), + event); + } + break; + case GST_EVENT_NEWSEGMENT: + { + gboolean update; + double rate; + double applied_rate; + GstFormat format; + gint64 start; + gint64 stop; + gint64 position; + + gst_event_parse_new_segment_full (event, &update, &rate, + &applied_rate, &format, &start, &stop, &position); + + if (format != GST_FORMAT_TIME) + goto newseg_wrong_format; + + GST_DEBUG ("new segment %lld %lld", start, position); + + gst_segment_set_newsegment_full (&base_video_encoder->segment, + update, rate, applied_rate, format, start, stop, position); + + ret = + gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), + event); + } + break; + default: + /* FIXME this changes the order of events */ + ret = + gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), + event); + break; + } + +done: + gst_object_unref (base_video_encoder); + return ret; + +newseg_wrong_format: + { + GST_DEBUG_OBJECT (base_video_encoder, "received non TIME newsegment"); + gst_event_unref (event); + goto done; + } +} + +static const GstQueryType * +gst_base_video_encoder_get_query_types (GstPad * pad) +{ + static const GstQueryType query_types[] = { + //GST_QUERY_POSITION, + //GST_QUERY_DURATION, + GST_QUERY_CONVERT, + GST_QUERY_LATENCY, + 0 + }; + + return query_types; +} + +static gboolean +gst_base_video_encoder_src_query (GstPad * pad, GstQuery * query) +{ + GstBaseVideoEncoder *enc; + gboolean res; + GstPad *peerpad; + + enc = GST_BASE_VIDEO_ENCODER (gst_pad_get_parent (pad)); + peerpad = gst_pad_get_peer (GST_BASE_VIDEO_CODEC_SINK_PAD (enc)); + + switch GST_QUERY_TYPE + (query) { + case GST_QUERY_CONVERT: + { + GstFormat src_fmt, dest_fmt; + gint64 src_val, dest_val; + + gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); + res = + gst_base_video_encoded_video_convert (&enc->state, src_fmt, src_val, + &dest_fmt, &dest_val); + if (!res) + goto error; + gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); + break; + } + case GST_QUERY_LATENCY: + { + gboolean live; + GstClockTime min_latency, max_latency; + + res = gst_pad_query (peerpad, query); + if (res) { + gst_query_parse_latency (query, &live, &min_latency, &max_latency); + + min_latency += enc->min_latency; + if (max_latency != GST_CLOCK_TIME_NONE) { + max_latency += enc->max_latency; + } + + gst_query_set_latency (query, live, min_latency, max_latency); + } + } + break; + default: + res = gst_pad_query_default (pad, query); + } + gst_object_unref (peerpad); + gst_object_unref (enc); + return res; + +error: + GST_DEBUG_OBJECT (enc, "query failed"); + gst_object_unref (peerpad); + gst_object_unref (enc); + return res; +} + +static gboolean +gst_pad_is_negotiated (GstPad * pad) +{ + GstCaps *caps; + + g_return_val_if_fail (pad != NULL, FALSE); + + caps = gst_pad_get_negotiated_caps (pad); + if (caps) { + gst_caps_unref (caps); + return TRUE; + } + + return FALSE; +} + +static GstFlowReturn +gst_base_video_encoder_chain (GstPad * pad, GstBuffer * buf) +{ + GstBaseVideoEncoder *base_video_encoder; + GstBaseVideoEncoderClass *klass; + GstVideoFrame *frame; + + if (!gst_pad_is_negotiated (pad)) { + return GST_FLOW_NOT_NEGOTIATED; + } + + base_video_encoder = GST_BASE_VIDEO_ENCODER (gst_pad_get_parent (pad)); + klass = GST_BASE_VIDEO_ENCODER_GET_CLASS (base_video_encoder); + + if (base_video_encoder->sink_clipping) { + gint64 start = GST_BUFFER_TIMESTAMP (buf); + gint64 stop = start + GST_BUFFER_DURATION (buf); + gint64 clip_start; + gint64 clip_stop; + + if (!gst_segment_clip (&base_video_encoder->segment, + GST_FORMAT_TIME, start, stop, &clip_start, &clip_stop)) { + GST_DEBUG ("clipping to segment dropped frame"); + goto done; + } + } + + frame = + gst_base_video_codec_new_frame (GST_BASE_VIDEO_CODEC + (base_video_encoder)); + frame->sink_buffer = buf; + frame->presentation_timestamp = GST_BUFFER_TIMESTAMP (buf); + frame->presentation_duration = GST_BUFFER_DURATION (buf); + frame->presentation_frame_number = + base_video_encoder->presentation_frame_number; + base_video_encoder->presentation_frame_number++; + + base_video_encoder->frames = + g_list_append (base_video_encoder->frames, frame); + + klass->handle_frame (base_video_encoder, frame); + +done: + g_object_unref (base_video_encoder); + + return GST_FLOW_OK; +} + +static GstStateChangeReturn +gst_base_video_encoder_change_state (GstElement * element, + GstStateChange transition) +{ + GstBaseVideoEncoder *base_video_encoder; + GstBaseVideoEncoderClass *base_video_encoder_class; + GstStateChangeReturn ret; + + base_video_encoder = GST_BASE_VIDEO_ENCODER (element); + base_video_encoder_class = GST_BASE_VIDEO_ENCODER_GET_CLASS (element); + + switch (transition) { + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + if (base_video_encoder_class->stop) { + base_video_encoder_class->stop (base_video_encoder); + } + break; + default: + break; + } + + return ret; +} + +GstFlowReturn +gst_base_video_encoder_finish_frame (GstBaseVideoEncoder * base_video_encoder, + GstVideoFrame * frame) +{ + GstFlowReturn ret; + GstBaseVideoEncoderClass *base_video_encoder_class; + + base_video_encoder_class = + GST_BASE_VIDEO_ENCODER_GET_CLASS (base_video_encoder); + + frame->system_frame_number = base_video_encoder->system_frame_number; + base_video_encoder->system_frame_number++; + + if (frame->is_sync_point) { + base_video_encoder->distance_from_sync = 0; + GST_BUFFER_FLAG_UNSET (frame->src_buffer, GST_BUFFER_FLAG_DELTA_UNIT); + } else { + GST_BUFFER_FLAG_SET (frame->src_buffer, GST_BUFFER_FLAG_DELTA_UNIT); + } + + frame->distance_from_sync = base_video_encoder->distance_from_sync; + base_video_encoder->distance_from_sync++; + + frame->decode_frame_number = frame->system_frame_number - 1; + if (frame->decode_frame_number < 0) { + frame->decode_timestamp = 0; + } else { + frame->decode_timestamp = gst_util_uint64_scale (frame->decode_frame_number, + GST_SECOND * base_video_encoder->state.fps_d, + base_video_encoder->state.fps_n); + } + + GST_BUFFER_TIMESTAMP (frame->src_buffer) = frame->presentation_timestamp; + GST_BUFFER_DURATION (frame->src_buffer) = frame->presentation_duration; + GST_BUFFER_OFFSET (frame->src_buffer) = frame->decode_timestamp; + + base_video_encoder->frames = + g_list_remove (base_video_encoder->frames, frame); + + if (!base_video_encoder->set_output_caps) { + if (base_video_encoder_class->get_caps) { + base_video_encoder->caps = + base_video_encoder_class->get_caps (base_video_encoder); + } else { + base_video_encoder->caps = gst_caps_new_simple ("video/unknown", NULL); + } + gst_pad_set_caps (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), + base_video_encoder->caps); + base_video_encoder->set_output_caps = TRUE; + } + + gst_buffer_set_caps (GST_BUFFER (frame->src_buffer), + base_video_encoder->caps); + + if (base_video_encoder_class->shape_output) { + ret = base_video_encoder_class->shape_output (base_video_encoder, frame); + } else { + ret = + gst_pad_push (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), + frame->src_buffer); + } + + gst_base_video_codec_free_frame (frame); + + return ret; +} + +int +gst_base_video_encoder_get_height (GstBaseVideoEncoder * base_video_encoder) +{ + return base_video_encoder->state.height; +} + +int +gst_base_video_encoder_get_width (GstBaseVideoEncoder * base_video_encoder) +{ + return base_video_encoder->state.width; +} + +const GstVideoState * +gst_base_video_encoder_get_state (GstBaseVideoEncoder * base_video_encoder) +{ + return &base_video_encoder->state; +} + +GstFlowReturn +gst_base_video_encoder_end_of_stream (GstBaseVideoEncoder * base_video_encoder, + GstBuffer * buffer) +{ + + if (base_video_encoder->frames) { + GST_WARNING ("EOS with frames left over"); + } + + return gst_pad_push (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), + buffer); +} + +void +gst_base_video_encoder_set_latency (GstBaseVideoEncoder * base_video_encoder, + GstClockTime min_latency, GstClockTime max_latency) +{ + g_return_if_fail (min_latency >= 0); + g_return_if_fail (max_latency >= min_latency); + + base_video_encoder->min_latency = min_latency; + base_video_encoder->max_latency = max_latency; + + gst_element_post_message (GST_ELEMENT_CAST (base_video_encoder), + gst_message_new_latency (GST_OBJECT_CAST (base_video_encoder))); +} + +void +gst_base_video_encoder_set_latency_fields (GstBaseVideoEncoder * + base_video_encoder, int n_fields) +{ + gint64 latency; + + latency = gst_util_uint64_scale (n_fields, + base_video_encoder->state.fps_d * GST_SECOND, + 2 * base_video_encoder->state.fps_n); + + gst_base_video_encoder_set_latency (base_video_encoder, latency, latency); + +} + +GstVideoFrame * +gst_base_video_encoder_get_oldest_frame (GstBaseVideoEncoder * + base_video_encoder) +{ + GList *g; + + g = g_list_first (base_video_encoder->frames); + + if (g == NULL) + return NULL; + return (GstVideoFrame *) (g->data); +} diff --git a/ext/vp8/gst/video/gstbasevideoencoder.h b/ext/vp8/gst/video/gstbasevideoencoder.h new file mode 100644 index 0000000..761a9d7 --- /dev/null +++ b/ext/vp8/gst/video/gstbasevideoencoder.h @@ -0,0 +1,120 @@ +/* GStreamer + * Copyright (C) 2008 David Schleef + * + * 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. + */ + +#ifndef _GST_BASE_VIDEO_ENCODER_H_ +#define _GST_BASE_VIDEO_ENCODER_H_ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_BASE_VIDEO_ENCODER \ + (gst_base_video_encoder_get_type()) +#define GST_BASE_VIDEO_ENCODER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASE_VIDEO_ENCODER,GstBaseVideoEncoder)) +#define GST_BASE_VIDEO_ENCODER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASE_VIDEO_ENCODER,GstBaseVideoEncoderClass)) +#define GST_BASE_VIDEO_ENCODER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_BASE_VIDEO_ENCODER,GstBaseVideoEncoderClass)) +#define GST_IS_BASE_VIDEO_ENCODER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASE_VIDEO_ENCODER)) +#define GST_IS_BASE_VIDEO_ENCODER_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASE_VIDEO_ENCODER)) + +/** + * GST_BASE_VIDEO_ENCODER_SINK_NAME: + * + * The name of the templates for the sink pad. + */ +#define GST_BASE_VIDEO_ENCODER_SINK_NAME "sink" +/** + * GST_BASE_VIDEO_ENCODER_SRC_NAME: + * + * The name of the templates for the source pad. + */ +#define GST_BASE_VIDEO_ENCODER_SRC_NAME "src" + + +typedef struct _GstBaseVideoEncoder GstBaseVideoEncoder; +typedef struct _GstBaseVideoEncoderClass GstBaseVideoEncoderClass; + +struct _GstBaseVideoEncoder +{ + GstBaseVideoCodec base_video_codec; + + /*< private >*/ + GList *frames; + + GstVideoState state; + GstSegment segment; + + gboolean sink_clipping; + + guint64 presentation_frame_number; + guint64 system_frame_number; + int distance_from_sync; + + GstCaps *caps; + gboolean set_output_caps; + + gint64 min_latency; + gint64 max_latency; +}; + +struct _GstBaseVideoEncoderClass +{ + GstBaseVideoCodecClass base_video_codec_class; + + gboolean (*set_format) (GstBaseVideoEncoder *coder, GstVideoState *state); + gboolean (*start) (GstBaseVideoEncoder *coder); + gboolean (*stop) (GstBaseVideoEncoder *coder); + gboolean (*finish) (GstBaseVideoEncoder *coder); + gboolean (*handle_frame) (GstBaseVideoEncoder *coder, GstVideoFrame *frame); + GstFlowReturn (*shape_output) (GstBaseVideoEncoder *coder, GstVideoFrame *frame); + GstCaps *(*get_caps) (GstBaseVideoEncoder *coder); + +}; + +GType gst_base_video_encoder_get_type (void); + +int gst_base_video_encoder_get_width (GstBaseVideoEncoder *coder); +int gst_base_video_encoder_get_height (GstBaseVideoEncoder *coder); +const GstVideoState *gst_base_video_encoder_get_state (GstBaseVideoEncoder *coder); + +guint64 gst_base_video_encoder_get_timestamp_offset (GstBaseVideoEncoder *coder); + +GstVideoFrame *gst_base_video_encoder_get_frame (GstBaseVideoEncoder *coder, + int frame_number); +GstVideoFrame *gst_base_video_encoder_get_oldest_frame (GstBaseVideoEncoder *coder); +GstFlowReturn gst_base_video_encoder_finish_frame (GstBaseVideoEncoder *base_video_encoder, + GstVideoFrame *frame); +GstFlowReturn gst_base_video_encoder_end_of_stream (GstBaseVideoEncoder *base_video_encoder, + GstBuffer *buffer); + +void gst_base_video_encoder_set_latency (GstBaseVideoEncoder *base_video_encoder, + GstClockTime min_latency, GstClockTime max_latency); +void gst_base_video_encoder_set_latency_fields (GstBaseVideoEncoder *base_video_encoder, + int n_fields); + + +G_END_DECLS + +#endif + diff --git a/ext/vp8/gst/video/gstbasevideoparse.c b/ext/vp8/gst/video/gstbasevideoparse.c new file mode 100644 index 0000000..468ee68 --- /dev/null +++ b/ext/vp8/gst/video/gstbasevideoparse.c @@ -0,0 +1,863 @@ +/* Schrodinger + * Copyright (C) 2006 David Schleef + * + * 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 "gstbasevideoparse.h" + +#include +#include + +GST_DEBUG_CATEGORY_EXTERN (basevideo_debug); +#define GST_CAT_DEFAULT basevideo_debug + + + +/* GstBaseVideoParse signals and args */ +enum +{ + LAST_SIGNAL +}; + +enum +{ + ARG_0 +}; + +static void gst_base_video_parse_finalize (GObject * object); + +static const GstQueryType *gst_base_video_parse_get_query_types (GstPad * pad); +static gboolean gst_base_video_parse_src_query (GstPad * pad, GstQuery * query); +static gboolean gst_base_video_parse_sink_query (GstPad * pad, + GstQuery * query); +static gboolean gst_base_video_parse_src_event (GstPad * pad, GstEvent * event); +static gboolean gst_base_video_parse_sink_event (GstPad * pad, + GstEvent * event); +static GstStateChangeReturn gst_base_video_parse_change_state (GstElement * + element, GstStateChange transition); +static GstFlowReturn gst_base_video_parse_push_all (GstBaseVideoParse * + base_video_parse, gboolean at_eos); +static GstFlowReturn gst_base_video_parse_chain (GstPad * pad, GstBuffer * buf); +static void gst_base_video_parse_free_frame (GstVideoFrame * frame); +static GstVideoFrame *gst_base_video_parse_new_frame (GstBaseVideoParse * + base_video_parse); + + +GST_BOILERPLATE (GstBaseVideoParse, gst_base_video_parse, + GstBaseVideoCodec, GST_TYPE_BASE_VIDEO_CODEC); + +static void +gst_base_video_parse_base_init (gpointer g_class) +{ + +} + +static void +gst_base_video_parse_class_init (GstBaseVideoParseClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + + gobject_class = G_OBJECT_CLASS (klass); + element_class = GST_ELEMENT_CLASS (klass); + + gobject_class->finalize = gst_base_video_parse_finalize; + + element_class->change_state = gst_base_video_parse_change_state; +} + +static void +gst_base_video_parse_init (GstBaseVideoParse * base_video_parse, + GstBaseVideoParseClass * klass) +{ + GstPad *pad; + + GST_DEBUG ("gst_base_video_parse_init"); + + pad = GST_BASE_VIDEO_CODEC_SINK_PAD (base_video_parse); + + gst_pad_set_chain_function (pad, gst_base_video_parse_chain); + gst_pad_set_query_function (pad, gst_base_video_parse_sink_query); + gst_pad_set_event_function (pad, gst_base_video_parse_sink_event); + + pad = GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_parse); + + gst_pad_set_query_type_function (pad, gst_base_video_parse_get_query_types); + gst_pad_set_query_function (pad, gst_base_video_parse_src_query); + gst_pad_set_event_function (pad, gst_base_video_parse_src_event); + + base_video_parse->input_adapter = gst_adapter_new (); + base_video_parse->output_adapter = gst_adapter_new (); + + base_video_parse->reorder_depth = 1; + + base_video_parse->current_frame = + gst_base_video_parse_new_frame (base_video_parse); +} + +static void +gst_base_video_parse_reset (GstBaseVideoParse * base_video_parse) +{ + GST_DEBUG ("reset"); + + base_video_parse->discont = TRUE; + base_video_parse->have_sync = FALSE; + + base_video_parse->system_frame_number = 0; + base_video_parse->presentation_frame_number = 0; + + if (base_video_parse->caps) { + gst_caps_unref (base_video_parse->caps); + base_video_parse->caps = NULL; + } + + gst_segment_init (&base_video_parse->segment, GST_FORMAT_TIME); + gst_adapter_clear (base_video_parse->input_adapter); + gst_adapter_clear (base_video_parse->output_adapter); + + if (base_video_parse->current_frame) { + gst_base_video_parse_free_frame (base_video_parse->current_frame); + } + base_video_parse->current_frame = + gst_base_video_parse_new_frame (base_video_parse); + +} + +static void +gst_base_video_parse_finalize (GObject * object) +{ + GstBaseVideoParse *base_video_parse; + + g_return_if_fail (GST_IS_BASE_VIDEO_PARSE (object)); + base_video_parse = GST_BASE_VIDEO_PARSE (object); + + if (base_video_parse->input_adapter) { + g_object_unref (base_video_parse->input_adapter); + } + if (base_video_parse->output_adapter) { + g_object_unref (base_video_parse->output_adapter); + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static const GstQueryType * +gst_base_video_parse_get_query_types (GstPad * pad) +{ + static const GstQueryType query_types[] = { + GST_QUERY_POSITION, + GST_QUERY_DURATION, + GST_QUERY_CONVERT, + 0 + }; + + return query_types; +} + +#if 0 +static gboolean +gst_base_video_parse_src_convert (GstPad * pad, + GstFormat src_format, gint64 src_value, + GstFormat * dest_format, gint64 * dest_value) +{ + gboolean res; + GstBaseVideoParse *dec; + + if (src_format == *dest_format) { + *dest_value = src_value; + return TRUE; + } + + dec = GST_BASE_VIDEO_PARSE (gst_pad_get_parent (pad)); + + if (src_format == GST_FORMAT_DEFAULT && *dest_format == GST_FORMAT_TIME) { + if (dec->fps_d != 0) { + *dest_value = gst_util_uint64_scale (granulepos_to_frame (src_value), + dec->fps_d * GST_SECOND, dec->fps_n); + res = TRUE; + } else { + res = FALSE; + } + } else { + GST_WARNING ("unhandled conversion from %d to %d", src_format, + *dest_format); + res = FALSE; + } + + gst_object_unref (dec); + + return res; +} + +static gboolean +gst_base_video_parse_sink_convert (GstPad * pad, + GstFormat src_format, gint64 src_value, + GstFormat * dest_format, gint64 * dest_value) +{ + gboolean res = TRUE; + GstBaseVideoParse *dec; + + if (src_format == *dest_format) { + *dest_value = src_value; + return TRUE; + } + + dec = GST_BASE_VIDEO_PARSE (gst_pad_get_parent (pad)); + + /* FIXME: check if we are in a decoding state */ + + switch (src_format) { + case GST_FORMAT_DEFAULT: + switch (*dest_format) { + case GST_FORMAT_TIME: + *dest_value = gst_util_uint64_scale (src_value, + dec->fps_d * GST_SECOND, dec->fps_n); + break; + default: + res = FALSE; + } + break; + case GST_FORMAT_TIME: + switch (*dest_format) { + case GST_FORMAT_DEFAULT: + { + *dest_value = gst_util_uint64_scale (src_value, + dec->fps_n, dec->fps_d * GST_SECOND); + break; + } + default: + res = FALSE; + break; + } + break; + default: + res = FALSE; + break; + } + + gst_object_unref (dec); + + return res; +} +#endif + +static gboolean +gst_base_video_parse_src_query (GstPad * pad, GstQuery * query) +{ + GstBaseVideoParse *base_parse; + gboolean res = FALSE; + + base_parse = GST_BASE_VIDEO_PARSE (gst_pad_get_parent (pad)); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_POSITION: + { + GstFormat format; + gint64 time; + gint64 value; + + gst_query_parse_position (query, &format, NULL); + + time = gst_util_uint64_scale (base_parse->presentation_frame_number, + base_parse->state.fps_n, base_parse->state.fps_d); + time += base_parse->segment.time; + GST_DEBUG ("query position %lld", time); + res = gst_base_video_encoded_video_convert (&base_parse->state, + GST_FORMAT_TIME, time, &format, &value); + if (!res) + goto error; + + gst_query_set_position (query, format, value); + break; + } + case GST_QUERY_DURATION: + res = + gst_pad_query (GST_PAD_PEER (GST_BASE_VIDEO_CODEC_SINK_PAD + (base_parse)), query); + if (!res) + goto error; + break; + case GST_QUERY_CONVERT: + { + GstFormat src_fmt, dest_fmt; + gint64 src_val, dest_val; + + GST_WARNING ("query convert"); + + gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); + res = gst_base_video_encoded_video_convert (&base_parse->state, + src_fmt, src_val, &dest_fmt, &dest_val); + if (!res) + goto error; + gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); + break; + } + default: + res = gst_pad_query_default (pad, query); + break; + } +done: + gst_object_unref (base_parse); + + return res; +error: + GST_DEBUG_OBJECT (base_parse, "query failed"); + goto done; +} + +static gboolean +gst_base_video_parse_sink_query (GstPad * pad, GstQuery * query) +{ + GstBaseVideoParse *base_video_parse; + gboolean res = FALSE; + + base_video_parse = GST_BASE_VIDEO_PARSE (gst_pad_get_parent (pad)); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONVERT: + { + GstFormat src_fmt, dest_fmt; + gint64 src_val, dest_val; + + gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); + res = gst_base_video_encoded_video_convert (&base_video_parse->state, + src_fmt, src_val, &dest_fmt, &dest_val); + if (!res) + goto error; + gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); + break; + } + default: + res = gst_pad_query_default (pad, query); + break; + } +done: + gst_object_unref (base_video_parse); + + return res; +error: + GST_DEBUG_OBJECT (base_video_parse, "query failed"); + goto done; +} + +static gboolean +gst_base_video_parse_src_event (GstPad * pad, GstEvent * event) +{ + GstBaseVideoParse *base_video_parse; + gboolean res = FALSE; + + base_video_parse = GST_BASE_VIDEO_PARSE (gst_pad_get_parent (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + { + GstFormat format, tformat; + gdouble rate; + GstEvent *real_seek; + GstSeekFlags flags; + GstSeekType cur_type, stop_type; + gint64 cur, stop; + gint64 tcur, tstop; + + gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, + &cur, &stop_type, &stop); + gst_event_unref (event); + + tformat = GST_FORMAT_TIME; + res = gst_base_video_encoded_video_convert (&base_video_parse->state, + format, cur, &tformat, &tcur); + if (!res) + goto convert_error; + res = gst_base_video_encoded_video_convert (&base_video_parse->state, + format, stop, &tformat, &tstop); + if (!res) + goto convert_error; + + real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME, + flags, cur_type, tcur, stop_type, tstop); + + res = + gst_pad_push_event (GST_BASE_VIDEO_CODEC_SINK_PAD (base_video_parse), + real_seek); + + break; + } +#if 0 + case GST_EVENT_QOS: + { + gdouble proportion; + GstClockTimeDiff diff; + GstClockTime timestamp; + + gst_event_parse_qos (event, &proportion, &diff, ×tamp); + + GST_OBJECT_LOCK (base_video_parse); + base_video_parse->proportion = proportion; + base_video_parse->earliest_time = timestamp + diff; + GST_OBJECT_UNLOCK (base_video_parse); + + GST_DEBUG_OBJECT (base_video_parse, + "got QoS %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT, + GST_TIME_ARGS (timestamp), diff); + + res = gst_pad_push_event (base_video_parse->sinkpad, event); + break; + } +#endif + default: + res = + gst_pad_push_event (GST_BASE_VIDEO_CODEC_SINK_PAD (base_video_parse), + event); + break; + } +done: + gst_object_unref (base_video_parse); + return res; + +convert_error: + GST_DEBUG_OBJECT (base_video_parse, "could not convert format"); + goto done; +} + +static gboolean +gst_base_video_parse_sink_event (GstPad * pad, GstEvent * event) +{ + GstBaseVideoParse *base_video_parse; + gboolean ret = FALSE; + + base_video_parse = GST_BASE_VIDEO_PARSE (gst_pad_get_parent (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_START: + ret = + gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_parse), + event); + break; + case GST_EVENT_FLUSH_STOP: + gst_base_video_parse_reset (base_video_parse); + ret = + gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_parse), + event); + break; + case GST_EVENT_EOS: + if (gst_base_video_parse_push_all (base_video_parse, + FALSE) == GST_FLOW_ERROR) { + gst_event_unref (event); + return FALSE; + } + + ret = + gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_parse), + event); + break; + case GST_EVENT_NEWSEGMENT: + { + gboolean update; + GstFormat format; + gdouble rate; + gint64 start, stop, time; + + gst_event_parse_new_segment (event, &update, &rate, &format, &start, + &stop, &time); + + if (format != GST_FORMAT_TIME) + goto newseg_wrong_format; + + if (rate <= 0.0) + goto newseg_wrong_rate; + + GST_DEBUG ("newsegment %lld %lld", start, time); + gst_segment_set_newsegment (&base_video_parse->segment, update, + rate, format, start, stop, time); + + ret = + gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_parse), + event); + break; + } + default: + ret = + gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_parse), + event); + break; + } +done: + gst_object_unref (base_video_parse); + return ret; + +newseg_wrong_format: + GST_DEBUG_OBJECT (base_video_parse, "received non TIME newsegment"); + gst_event_unref (event); + goto done; + +newseg_wrong_rate: + GST_DEBUG_OBJECT (base_video_parse, "negative rates not supported"); + gst_event_unref (event); + goto done; +} + + +static GstStateChangeReturn +gst_base_video_parse_change_state (GstElement * element, + GstStateChange transition) +{ + GstBaseVideoParse *base_parse = GST_BASE_VIDEO_PARSE (element); + GstStateChangeReturn ret; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_base_video_parse_reset (base_parse); + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_base_video_parse_reset (base_parse); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + + return ret; +} + +static guint64 +gst_base_video_parse_get_timestamp (GstBaseVideoParse * base_video_parse, + int picture_number) +{ + if (picture_number < 0) { + return base_video_parse->timestamp_offset - + (gint64) gst_util_uint64_scale (-picture_number, + base_video_parse->state.fps_d * GST_SECOND, + base_video_parse->state.fps_n); + } else { + return base_video_parse->timestamp_offset + + gst_util_uint64_scale (picture_number, + base_video_parse->state.fps_d * GST_SECOND, + base_video_parse->state.fps_n); + } +} + +static GstFlowReturn +gst_base_video_parse_push_all (GstBaseVideoParse * base_video_parse, + gboolean at_eos) +{ + GstFlowReturn ret = GST_FLOW_OK; + + /* FIXME do stuff */ + + return ret; +} + +static GstFlowReturn +gst_base_video_parse_chain (GstPad * pad, GstBuffer * buf) +{ + GstBaseVideoParse *base_video_parse; + GstBaseVideoParseClass *klass; + GstBuffer *buffer; + GstFlowReturn ret; + + GST_DEBUG ("chain with %d bytes", GST_BUFFER_SIZE (buf)); + + base_video_parse = GST_BASE_VIDEO_PARSE (GST_PAD_PARENT (pad)); + klass = GST_BASE_VIDEO_PARSE_GET_CLASS (base_video_parse); + + if (!base_video_parse->started) { + klass->start (base_video_parse); + base_video_parse->started = TRUE; + } + + if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT))) { + GST_DEBUG_OBJECT (base_video_parse, "received DISCONT buffer"); + gst_base_video_parse_reset (base_video_parse); + base_video_parse->discont = TRUE; + base_video_parse->have_sync = FALSE; + } + + if (GST_BUFFER_TIMESTAMP (buf) != GST_CLOCK_TIME_NONE) { + base_video_parse->last_timestamp = GST_BUFFER_TIMESTAMP (buf); + } + gst_adapter_push (base_video_parse->input_adapter, buf); + + if (!base_video_parse->have_sync) { + int n, m; + + GST_DEBUG ("no sync, scanning"); + + n = gst_adapter_available (base_video_parse->input_adapter); + m = klass->scan_for_sync (base_video_parse->input_adapter, FALSE, 0, n); + + gst_adapter_flush (base_video_parse->input_adapter, m); + + if (m < n) { + GST_DEBUG ("found possible sync after %d bytes (of %d)", m, n); + + /* this is only "maybe" sync */ + base_video_parse->have_sync = TRUE; + } + + if (!base_video_parse->have_sync) { + return GST_FLOW_OK; + } + } + + buffer = gst_adapter_get_buffer (base_video_parse->input_adapter); + + //base_video_parse->buffer_timestamp = GST_BUFFER_TIMESTAMP (buffer); + gst_buffer_unref (buffer); + + /* FIXME check klass->parse_data */ + + do { + ret = klass->parse_data (base_video_parse, FALSE); + } while (ret == GST_FLOW_OK); + + if (ret == GST_BASE_VIDEO_PARSE_FLOW_NEED_DATA) { + return GST_FLOW_OK; + } + return ret; +} + +GstVideoState * +gst_base_video_parse_get_state (GstBaseVideoParse * base_video_parse) +{ + return &base_video_parse->state; +} + +void +gst_base_video_parse_set_state (GstBaseVideoParse * base_video_parse, + GstVideoState * state) +{ + GST_DEBUG ("set_state"); + + memcpy (&base_video_parse->state, state, sizeof (GstVideoState)); + + /* FIXME set caps */ + +} + + +gboolean +gst_base_video_parse_set_src_caps (GstBaseVideoParse * base_video_parse, + GstCaps * caps) +{ + g_return_val_if_fail (GST_IS_BASE_VIDEO_PARSE (base_video_parse), FALSE); + + GST_DEBUG ("set_src_caps"); + + return gst_pad_set_caps (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_parse), + caps); +} + +void +gst_base_video_parse_lost_sync (GstBaseVideoParse * base_video_parse) +{ + g_return_if_fail (GST_IS_BASE_VIDEO_PARSE (base_video_parse)); + + GST_DEBUG ("lost_sync"); + + if (gst_adapter_available (base_video_parse->input_adapter) >= 1) { + gst_adapter_flush (base_video_parse->input_adapter, 1); + } + + base_video_parse->have_sync = FALSE; +} + +GstVideoFrame * +gst_base_video_parse_get_frame (GstBaseVideoParse * base_video_parse) +{ + g_return_val_if_fail (GST_IS_BASE_VIDEO_PARSE (base_video_parse), NULL); + + return base_video_parse->current_frame; +} + +void +gst_base_video_parse_add_to_frame (GstBaseVideoParse * base_video_parse, + int n_bytes) +{ + GstBuffer *buf; + + GST_DEBUG ("add_to_frame"); + + buf = gst_adapter_take_buffer (base_video_parse->input_adapter, n_bytes); + + gst_adapter_push (base_video_parse->output_adapter, buf); +} + +GstFlowReturn +gst_base_video_parse_finish_frame (GstBaseVideoParse * base_video_parse) +{ + GstVideoFrame *frame = base_video_parse->current_frame; + GstBuffer *buffer; + GstBaseVideoParseClass *base_video_parse_class; + GstFlowReturn ret; + + GST_DEBUG ("finish_frame"); + + base_video_parse_class = GST_BASE_VIDEO_PARSE_GET_CLASS (base_video_parse); + + buffer = gst_adapter_take_buffer (base_video_parse->output_adapter, + gst_adapter_available (base_video_parse->output_adapter)); + + if (frame->is_sync_point) { + base_video_parse->timestamp_offset = base_video_parse->last_timestamp - + gst_util_uint64_scale (frame->presentation_frame_number, + base_video_parse->state.fps_d * GST_SECOND, + base_video_parse->state.fps_n); + base_video_parse->distance_from_sync = 0; + } + + frame->distance_from_sync = base_video_parse->distance_from_sync; + base_video_parse->distance_from_sync++; + + frame->presentation_timestamp = + gst_base_video_parse_get_timestamp (base_video_parse, + frame->presentation_frame_number); + frame->presentation_duration = + gst_base_video_parse_get_timestamp (base_video_parse, + frame->presentation_frame_number + 1) - frame->presentation_timestamp; + frame->decode_timestamp = + gst_base_video_parse_get_timestamp (base_video_parse, + frame->decode_frame_number); + + GST_BUFFER_TIMESTAMP (buffer) = frame->presentation_timestamp; + GST_BUFFER_DURATION (buffer) = frame->presentation_duration; + if (frame->decode_frame_number < 0) { + GST_BUFFER_OFFSET (buffer) = 0; + } else { + GST_BUFFER_OFFSET (buffer) = frame->decode_timestamp; + } + GST_BUFFER_OFFSET_END (buffer) = GST_CLOCK_TIME_NONE; + + GST_DEBUG ("pts %" GST_TIME_FORMAT, + GST_TIME_ARGS (frame->presentation_timestamp)); + GST_DEBUG ("dts %" GST_TIME_FORMAT, GST_TIME_ARGS (frame->decode_timestamp)); + GST_DEBUG ("dist %d", frame->distance_from_sync); + + if (frame->is_sync_point) { + GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); + } else { + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); + } + + frame->src_buffer = buffer; + ret = base_video_parse_class->shape_output (base_video_parse, frame); + + gst_base_video_parse_free_frame (base_video_parse->current_frame); + + /* create new frame */ + base_video_parse->current_frame = + gst_base_video_parse_new_frame (base_video_parse); + + return ret; +} + +static void +gst_base_video_parse_free_frame (GstVideoFrame * frame) +{ + if (frame->sink_buffer) { + gst_buffer_unref (frame->sink_buffer); + } +#if 0 + if (frame->src_buffer) { + gst_buffer_unref (frame->src_buffer); + } +#endif + + g_free (frame); +} + +static GstVideoFrame * +gst_base_video_parse_new_frame (GstBaseVideoParse * base_video_parse) +{ + GstVideoFrame *frame; + + frame = g_malloc0 (sizeof (GstVideoFrame)); + + frame->system_frame_number = base_video_parse->system_frame_number; + base_video_parse->system_frame_number++; + + frame->decode_frame_number = frame->system_frame_number - + base_video_parse->reorder_depth; + + return frame; +} + +void +gst_base_video_parse_set_sync_point (GstBaseVideoParse * base_video_parse) +{ + GST_DEBUG ("set_sync_point"); + + base_video_parse->current_frame->is_sync_point = TRUE; + + base_video_parse->distance_from_sync = 0; +} + +GstFlowReturn +gst_base_video_parse_push (GstBaseVideoParse * base_video_parse, + GstBuffer * buffer) +{ + GstBaseVideoParseClass *base_video_parse_class; + + base_video_parse_class = GST_BASE_VIDEO_PARSE_GET_CLASS (base_video_parse); + + if (base_video_parse->caps == NULL) { + gboolean ret; + + base_video_parse->caps = + base_video_parse_class->get_caps (base_video_parse); + + ret = gst_pad_set_caps (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_parse), + base_video_parse->caps); + + if (!ret) { + GST_WARNING ("pad didn't accept caps"); + return GST_FLOW_ERROR; + } + } + gst_buffer_set_caps (buffer, base_video_parse->caps); + + GST_DEBUG ("pushing ts=%lld dur=%lld off=%lld off_end=%lld", + GST_BUFFER_TIMESTAMP (buffer), + GST_BUFFER_DURATION (buffer), + GST_BUFFER_OFFSET (buffer), GST_BUFFER_OFFSET_END (buffer)); + + if (base_video_parse->discont) { + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); + base_video_parse->discont = FALSE; + } else { + GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DISCONT); + } + + return gst_pad_push (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_parse), buffer); +} diff --git a/ext/vp8/gst/video/gstbasevideoparse.h b/ext/vp8/gst/video/gstbasevideoparse.h new file mode 100644 index 0000000..5a1469f --- /dev/null +++ b/ext/vp8/gst/video/gstbasevideoparse.h @@ -0,0 +1,137 @@ +/* GStreamer + * Copyright (C) 2008 David Schleef + * + * 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. + */ + +#ifndef _GST_BASE_VIDEO_PARSE_H_ +#define _GST_BASE_VIDEO_PARSE_H_ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_BASE_VIDEO_PARSE \ + (gst_base_video_parse_get_type()) +#define GST_BASE_VIDEO_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASE_VIDEO_PARSE,GstBaseVideoParse)) +#define GST_BASE_VIDEO_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASE_VIDEO_PARSE,GstBaseVideoParseClass)) +#define GST_BASE_VIDEO_PARSE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_BASE_VIDEO_PARSE,GstBaseVideoParseClass)) +#define GST_IS_BASE_VIDEO_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASE_VIDEO_PARSE)) +#define GST_IS_BASE_VIDEO_PARSE_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASE_VIDEO_PARSE)) + +/** + * GST_BASE_VIDEO_PARSE_SINK_NAME: + * + * The name of the templates for the sink pad. + */ +#define GST_BASE_VIDEO_PARSE_SINK_NAME "sink" +/** + * GST_BASE_VIDEO_PARSE_SRC_NAME: + * + * The name of the templates for the source pad. + */ +#define GST_BASE_VIDEO_PARSE_SRC_NAME "src" + +/** + * GST_BASE_VIDEO_PARSE_FLOW_NEED_DATA: + * + */ +#define GST_BASE_VIDEO_PARSE_FLOW_NEED_DATA GST_FLOW_CUSTOM_SUCCESS + +typedef struct _GstBaseVideoParse GstBaseVideoParse; +typedef struct _GstBaseVideoParseClass GstBaseVideoParseClass; + +struct _GstBaseVideoParse +{ + GstBaseVideoCodec base_video_codec; + + /*< private >*/ + GstAdapter *input_adapter; + GstAdapter *output_adapter; + + int reorder_depth; + + gboolean have_sync; + gboolean discont; + gboolean started; + + GstVideoFrame *current_frame; + GstVideoState state; + GstSegment segment; + int distance_from_sync; + + gboolean sink_clipping; + + guint64 presentation_frame_number; + guint64 system_frame_number; + + GstCaps *caps; + gboolean set_output_caps; + + GstClockTime last_timestamp; + + gint64 timestamp_offset; +}; + +struct _GstBaseVideoParseClass +{ + GstBaseVideoCodecClass base_video_codec_class; + + gboolean (*start) (GstBaseVideoParse *parse); + gboolean (*stop) (GstBaseVideoParse *parse); + gboolean (*reset) (GstBaseVideoParse *parse); + GstFlowReturn (*parse_data) (GstBaseVideoParse *parse, gboolean at_eos); + int (*scan_for_sync) (GstAdapter *adapter, gboolean at_eos, + int offset, int n); + GstFlowReturn (*shape_output) (GstBaseVideoParse *parse, GstVideoFrame *frame); + GstCaps *(*get_caps) (GstBaseVideoParse *parse); + +}; + +GType gst_base_video_parse_get_type (void); + +int gst_base_video_parse_get_width (GstBaseVideoParse *parse); +int gst_base_video_parse_get_height (GstBaseVideoParse *parse); +GstVideoState *gst_base_video_parse_get_state (GstBaseVideoParse *parse); +void gst_base_video_parse_set_state (GstBaseVideoParse *parse, + GstVideoState *state); + +guint64 gst_base_video_parse_get_timestamp_offset (GstBaseVideoParse *parse); + +gboolean gst_base_video_parse_set_src_caps (GstBaseVideoParse *base_video_parse, GstCaps *caps); + +GstFlowReturn gst_base_video_parse_end_of_stream (GstBaseVideoParse *base_video_parse, + GstBuffer *buffer); + +void gst_base_video_parse_lost_sync (GstBaseVideoParse *base_video_parse); + +GstVideoFrame * gst_base_video_parse_get_frame (GstBaseVideoParse *base_video_parse); +void gst_base_video_parse_add_to_frame (GstBaseVideoParse *base_video_parse, int n_bytes); +GstFlowReturn gst_base_video_parse_finish_frame (GstBaseVideoParse *base_video_parse); +void gst_base_video_parse_set_sync_point (GstBaseVideoParse *base_video_parse); +GstFlowReturn gst_base_video_parse_push (GstBaseVideoParse *base_video_parse, + GstBuffer *buffer); + +G_END_DECLS + +#endif + diff --git a/ext/vp8/gst/video/gstbasevideoutils.c b/ext/vp8/gst/video/gstbasevideoutils.c new file mode 100644 index 0000000..69f614b --- /dev/null +++ b/ext/vp8/gst/video/gstbasevideoutils.c @@ -0,0 +1,278 @@ +/* GStreamer + * Copyright (C) 2008 David Schleef + * + * 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 "gstbasevideoutils.h" + +#include + +GST_DEBUG_CATEGORY_EXTERN (basevideo_debug); +#define GST_CAT_DEFAULT basevideo_debug + + +#if 0 +guint64 +gst_base_video_convert_bytes_to_frames (GstVideoState * state, guint64 bytes) +{ + return gst_util_uint64_scale_int (bytes, 1, state->bytes_per_picture); +} + +guint64 +gst_base_video_convert_frames_to_bytes (GstVideoState * state, guint64 frames) +{ + return frames * state->bytes_per_picture; +} +#endif + + +gboolean +gst_base_video_rawvideo_convert (GstVideoState * state, + GstFormat src_format, gint64 src_value, + GstFormat * dest_format, gint64 * dest_value) +{ + gboolean res = FALSE; + + if (src_format == *dest_format) { + *dest_value = src_value; + return TRUE; + } + + if (src_format == GST_FORMAT_BYTES && + *dest_format == GST_FORMAT_DEFAULT && state->bytes_per_picture != 0) { + /* convert bytes to frames */ + *dest_value = gst_util_uint64_scale_int (src_value, 1, + state->bytes_per_picture); + res = TRUE; + } else if (src_format == GST_FORMAT_DEFAULT && + *dest_format == GST_FORMAT_BYTES && state->bytes_per_picture != 0) { + /* convert bytes to frames */ + *dest_value = src_value * state->bytes_per_picture; + res = TRUE; + } else if (src_format == GST_FORMAT_DEFAULT && + *dest_format == GST_FORMAT_TIME && state->fps_n != 0) { + /* convert frames to time */ + /* FIXME add segment time? */ + *dest_value = gst_util_uint64_scale (src_value, + GST_SECOND * state->fps_d, state->fps_n); + res = TRUE; + } else if (src_format == GST_FORMAT_TIME && + *dest_format == GST_FORMAT_DEFAULT && state->fps_d != 0) { + /* convert time to frames */ + /* FIXME subtract segment time? */ + *dest_value = gst_util_uint64_scale (src_value, state->fps_n, + GST_SECOND * state->fps_d); + res = TRUE; + } + + /* FIXME add bytes <--> time */ + + return res; +} + +gboolean +gst_base_video_encoded_video_convert (GstVideoState * state, + GstFormat src_format, gint64 src_value, + GstFormat * dest_format, gint64 * dest_value) +{ + gboolean res = FALSE; + + if (src_format == *dest_format) { + *dest_value = src_value; + return TRUE; + } + + GST_DEBUG ("src convert"); + +#if 0 + if (src_format == GST_FORMAT_DEFAULT && *dest_format == GST_FORMAT_TIME) { + if (dec->fps_d != 0) { + *dest_value = gst_util_uint64_scale (granulepos_to_frame (src_value), + dec->fps_d * GST_SECOND, dec->fps_n); + res = TRUE; + } else { + res = FALSE; + } + } else { + GST_WARNING ("unhandled conversion from %d to %d", src_format, + *dest_format); + res = FALSE; + } +#endif + + return res; +} + +gboolean +gst_base_video_state_from_caps (GstVideoState * state, GstCaps * caps) +{ + + gst_video_format_parse_caps (caps, &state->format, + &state->width, &state->height); + + gst_video_parse_caps_framerate (caps, &state->fps_n, &state->fps_d); + + state->par_n = 1; + state->par_d = 1; + gst_video_parse_caps_pixel_aspect_ratio (caps, &state->par_n, &state->par_d); + + { + GstStructure *structure = gst_caps_get_structure (caps, 0); + state->interlaced = FALSE; + gst_structure_get_boolean (structure, "interlaced", &state->interlaced); + } + + state->clean_width = state->width; + state->clean_height = state->height; + state->clean_offset_left = 0; + state->clean_offset_top = 0; + + /* FIXME need better error handling */ + return TRUE; +} + +GstClockTime +gst_video_state_get_timestamp (const GstVideoState * state, + GstSegment * segment, int frame_number) +{ + if (frame_number < 0) { + return segment->start - + (gint64) gst_util_uint64_scale (-frame_number, + state->fps_d * GST_SECOND, state->fps_n); + } else { + return segment->start + + gst_util_uint64_scale (frame_number, + state->fps_d * GST_SECOND, state->fps_n); + } +} + +/* gst adapter */ + +static GSList * +get_chunk (GstAdapter * adapter, int offset, int *skip) +{ + GSList *g; + +#if 1 + if (skip) + *skip = 0; +#endif + + g_return_val_if_fail (offset >= 0, NULL); + g_return_val_if_fail (offset < adapter->size, NULL); + + offset += adapter->skip; + g = adapter->buflist; + while (g) { + if (offset < GST_BUFFER_SIZE (GST_BUFFER_CAST (g->data))) { + if (skip) + *skip = offset; + return g; + } + offset -= GST_BUFFER_SIZE (GST_BUFFER_CAST (g->data)); + g = g->next; + } + + g_assert_not_reached (); +} + +static int +scan_fast (guint8 * data, guint32 pattern, guint32 mask, int n) +{ + int i; + + pattern &= mask; + for (i = 0; i < n; i++) { + if ((GST_READ_UINT32_BE (data + i) & mask) == pattern) { + return i; + } + } + return n; +} + +static gboolean +scan_slow (GstAdapter * adapter, GSList * g, int skip, guint32 pattern, + guint32 mask) +{ + guint8 tmp[4]; + int j; + + pattern &= mask; + for (j = 0; j < 4; j++) { + tmp[j] = ((guint8 *) GST_BUFFER_DATA (GST_BUFFER_CAST (g->data)))[skip]; + skip++; + if (skip >= GST_BUFFER_SIZE (GST_BUFFER_CAST (g->data))) { + g = g->next; + skip = 0; + } + } + + return ((GST_READ_UINT32_BE (tmp) & mask) == pattern); +} + +int +gst_adapter_masked_scan_uint32_compat (GstAdapter * adapter, guint32 mask, + guint32 pattern, guint offset, guint n) +{ + GSList *g; + int j; + int k; + int skip; + int m; + + g_return_val_if_fail (n >= 0, -1); + g_return_val_if_fail (offset >= 0, -1); + g_return_val_if_fail (offset + n + 4 <= adapter->size, -1); + + g = get_chunk (adapter, offset, &skip); + j = 0; + while (j < n) { + m = MIN (GST_BUFFER_SIZE (GST_BUFFER_CAST (g->data)) - skip - 4, 0); + if (m > 0) { + k = scan_fast (GST_BUFFER_DATA (GST_BUFFER_CAST (g->data)) + skip, + pattern, mask, m); + if (k < m) { + return offset + j + k; + } + j += m; + skip += m; + } else { + if (scan_slow (adapter, g, skip, pattern, mask)) { + return offset + j; + } + j++; + skip++; + } + if (skip >= GST_BUFFER_SIZE (GST_BUFFER_CAST (g->data))) { + g = g->next; + skip = 0; + } + } + + return -1; +} + +GstBuffer * +gst_adapter_get_buffer (GstAdapter * adapter) +{ + return gst_buffer_ref (GST_BUFFER_CAST (adapter->buflist->data)); + +} diff --git a/ext/vp8/gst/video/gstbasevideoutils.h b/ext/vp8/gst/video/gstbasevideoutils.h new file mode 100644 index 0000000..3a49810 --- /dev/null +++ b/ext/vp8/gst/video/gstbasevideoutils.h @@ -0,0 +1,98 @@ +/* GStreamer + * Copyright (C) 2008 David Schleef + * + * 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. + */ + +#ifndef _GST_BASE_VIDEO_UTILS_H_ +#define _GST_BASE_VIDEO_UTILS_H_ + +#include +#include "gstvideocompat.h" +#include + +G_BEGIN_DECLS + +typedef struct _GstVideoState GstVideoState; +typedef struct _GstVideoFrame GstVideoFrame; + +struct _GstVideoState +{ + GstVideoFormat format; + int width, height; + int fps_n, fps_d; + int par_n, par_d; + + gboolean have_interlaced; + gboolean interlaced; + gboolean top_field_first; + + int clean_width, clean_height; + int clean_offset_left, clean_offset_top; + + int bytes_per_picture; + + //GstSegment segment; + + int picture_number; + GstBuffer *codec_data; + +}; + +struct _GstVideoFrame +{ + guint64 decode_timestamp; + guint64 presentation_timestamp; + guint64 presentation_duration; + + gint system_frame_number; + gint decode_frame_number; + gint presentation_frame_number; + + int distance_from_sync; + gboolean is_sync_point; + gboolean is_eos; + + GstBuffer *sink_buffer; + GstBuffer *src_buffer; + + int field_index; + int n_fields; + + void *coder_hook; +}; + +gboolean gst_base_video_rawvideo_convert (GstVideoState *state, + GstFormat src_format, gint64 src_value, + GstFormat * dest_format, gint64 *dest_value); +gboolean gst_base_video_encoded_video_convert (GstVideoState *state, + GstFormat src_format, gint64 src_value, + GstFormat * dest_format, gint64 *dest_value); + +gboolean gst_base_video_state_from_caps (GstVideoState *state, + GstCaps *caps); + +GstClockTime gst_video_state_get_timestamp (const GstVideoState *state, + GstSegment *segment, int frame_number); + +int gst_adapter_masked_scan_uint32_compat (GstAdapter *adapter, + guint32 pattern, guint32 mask, guint offset, guint n); +GstBuffer *gst_adapter_get_buffer (GstAdapter *adapter); + +G_END_DECLS + +#endif + diff --git a/ext/vp8/gst/video/gstvideocompat.c b/ext/vp8/gst/video/gstvideocompat.c new file mode 100644 index 0000000..b9516bb --- /dev/null +++ b/ext/vp8/gst/video/gstvideocompat.c @@ -0,0 +1,872 @@ +/* + * Copyright 2007 David A. Schleef + * + * This comes solely from commit 1c3e012fc3e4b9aba19d44855bf8c8bf6ec44cd5 + * to gst-plugins-base. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "gstvideocompat.h" + +#if 0 +/** + * gst_video_format_parse_caps: + * @caps: the #GstCaps to parse + * @format: the #GstVideoFormat of the video represented by @caps (output) + * @width: the width of the video represented by @caps (output) + * @height: the height of the video represented by @caps (output) + * + * Determines the #GstVideoFormat of @caps and places it in the location + * pointed to by @format. Extracts the size of the video and places it + * in the location pointed to by @width and @height. If @caps does not + * reprsent one of the raw video formats listed in #GstVideoFormat, the + * function will fail and return FALSE. + * + * Since: 0.10.16 + * + * Returns: TRUE if @caps was parsed correctly. + */ +gboolean +gst_video_format_parse_caps (GstCaps * caps, GstVideoFormat * format, + int *width, int *height) +{ + GstStructure *structure; + gboolean ok = TRUE; + + if (!gst_caps_is_fixed (caps)) + return FALSE; + + structure = gst_caps_get_structure (caps, 0); + + if (format) { + if (gst_structure_has_name (structure, "video/x-raw-yuv")) { + guint32 fourcc; + + ok &= gst_structure_get_fourcc (structure, "format", &fourcc); + + *format = gst_video_format_from_fourcc (fourcc); + if (*format == GST_VIDEO_FORMAT_UNKNOWN) { + ok = FALSE; + } +#if 0 + } else if (gst_structure_has_name (structure, "video/x-raw-rgb")) { + int depth; + int bpp; + int endianness; + int red_mask; + int green_mask; + int blue_mask; + + ok &= gst_structure_get_int (structure, "depth", &depth); + ok &= gst_structure_get_int (structure, "bpp", &bpp); + ok &= gst_structure_get_int (structure, "endianness", &endianness); + ok &= gst_structure_get_int (structure, "red_mask", &red_mask); + ok &= gst_structure_get_int (structure, "green_mask", &green_mask); + ok &= gst_structure_get_int (structure, "blue_mask", &blue_mask); + + if (depth != 24 || bpp != 32 || endianness != G_BIG_ENDIAN) { + ok = FALSE; + } else { + *format = gst_video_format_from_rgb32_masks (red_mask, green_mask, + blue_mask); + if (*format == GST_VIDEO_FORMAT_UNKNOWN) { + ok = FALSE; + } + } +#endif + } else { + ok = FALSE; + } + } + + if (width) { + ok &= gst_structure_get_int (structure, "width", width); + } + + if (height) { + ok &= gst_structure_get_int (structure, "height", height); + } + + return ok; +} + +/** + * gst_video_parse_caps_framerate: + * @caps: + * @fps_n: pointer to numerator of frame rate (output) + * @fps_d: pointer to denominator of frame rate (output) + * + * Extracts the frame rate from @caps and places the values in the locations + * pointed to by @fps_n and @fps_d. Returns TRUE if the values could be + * parsed correctly, FALSE if not. + * + * This function can be used with #GstCaps that have any media type; it + * is not limited to formats handled by #GstVideoFormat. + * + * Since: 0.10.16 + * + * Returns: TRUE if @caps was parsed correctly. + */ +gboolean +gst_video_parse_caps_framerate (GstCaps * caps, int *fps_n, int *fps_d) +{ + GstStructure *structure; + + if (!gst_caps_is_fixed (caps)) + return FALSE; + + structure = gst_caps_get_structure (caps, 0); + + return gst_structure_get_fraction (structure, "framerate", fps_n, fps_d); +} + +/** + * gst_video_parse_caps_pixel_aspect_ratio: + * @caps: + * @par_n: pointer to numerator of pixel aspect ratio (output) + * @par_d: pointer to denominator of pixel aspect ratio (output) + * + * Extracts the pixel aspect ratio from @caps and places the values in + * the locations pointed to by @par_n and @par_d. Returns TRUE if the + * values could be parsed correctly, FALSE if not. + * + * This function can be used with #GstCaps that have any media type; it + * is not limited to formats handled by #GstVideoFormat. + * + * Since: 0.10.16 + * + * Returns: TRUE if @caps was parsed correctly. + */ +gboolean +gst_video_parse_caps_pixel_aspect_ratio (GstCaps * caps, int *par_n, int *par_d) +{ + GstStructure *structure; + + if (!gst_caps_is_fixed (caps)) + return FALSE; + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_fraction (structure, "pixel-aspect-ratio", + par_n, par_d)) { + *par_n = 1; + *par_d = 1; + } + return TRUE; +} + +/** + * gst_video_format_new_caps: + * @format: the #GstVideoFormat describing the raw video format + * @width: width of video + * @height: height of video + * @framerate_n: numerator of frame rate + * @framerate_d: denominator of frame rate + * @par_n: numerator of pixel aspect ratio + * @par_d: denominator of pixel aspect ratio + * + * Creates a new #GstCaps object based on the parameters provided. + * + * Since: 0.10.16 + * + * Returns: a new #GstCaps object, or NULL if there was an error + */ +GstCaps * +gst_video_format_new_caps (GstVideoFormat format, int width, int height, + int framerate_n, int framerate_d, int par_n, int par_d) +{ + if (gst_video_format_is_yuv (format)) { + return gst_caps_new_simple ("video/x-raw-yuv", + "format", GST_TYPE_FOURCC, gst_video_format_to_fourcc (format), + "width", G_TYPE_INT, width, + "height", G_TYPE_INT, height, + "framerate", GST_TYPE_FRACTION, framerate_n, framerate_d, + "pixel-aspect-ratio", GST_TYPE_FRACTION, par_n, par_d, NULL); + } + if (gst_video_format_is_rgb (format)) { + int red_mask; + int blue_mask; + int green_mask; + + red_mask = + 0xff000000U >> gst_video_format_get_component_offset (format, 0, width, + height); + green_mask = + 0xff000000U >> gst_video_format_get_component_offset (format, 1, width, + height); + blue_mask = + 0xff000000U >> gst_video_format_get_component_offset (format, 2, width, + height); + + return gst_caps_new_simple ("video/x-raw-rgb", + "bpp", G_TYPE_INT, 32, + "depth", G_TYPE_INT, 24, + "endianness", G_TYPE_INT, G_BIG_ENDIAN, + "red_mask", G_TYPE_INT, red_mask, + "green_mask", G_TYPE_INT, green_mask, + "blue_mask", G_TYPE_INT, blue_mask, + "width", G_TYPE_INT, width, + "height", G_TYPE_INT, height, + "framerate", GST_TYPE_FRACTION, framerate_n, framerate_d, + "pixel-aspect-ratio", GST_TYPE_FRACTION, par_n, par_d, NULL); + } + return NULL; +} + +/** + * gst_video_format_from_fourcc: + * @fourcc: a FOURCC value representing raw YUV video + * + * Converts a FOURCC value into the corresponding #GstVideoFormat. + * If the FOURCC cannot be represented by #GstVideoFormat, + * #GST_VIDEO_FORMAT_UNKNOWN is returned. + * + * Since: 0.10.16 + * + * Returns: the #GstVideoFormat describing the FOURCC value + */ +GstVideoFormat +gst_video_format_from_fourcc (guint32 fourcc) +{ + switch (fourcc) { + case GST_MAKE_FOURCC ('I', '4', '2', '0'): + return GST_VIDEO_FORMAT_I420; + case GST_MAKE_FOURCC ('Y', 'V', '1', '2'): + return GST_VIDEO_FORMAT_YV12; + case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'): + return GST_VIDEO_FORMAT_YUY2; + case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'): + return GST_VIDEO_FORMAT_UYVY; + case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'): + return GST_VIDEO_FORMAT_AYUV; + default: + return GST_VIDEO_FORMAT_UNKNOWN; + } +} + +/** + * gst_video_format_to_fourcc: + * @format: a #GstVideoFormat video format + * + * Converts a #GstVideoFormat value into the corresponding FOURCC. Only + * a few YUV formats have corresponding FOURCC values. If @format has + * no corresponding FOURCC value, 0 is returned. + * + * Since: 0.10.16 + * + * Returns: the FOURCC corresponding to @format + */ +guint32 +gst_video_format_to_fourcc (GstVideoFormat format) +{ + switch (format) { + case GST_VIDEO_FORMAT_I420: + return GST_MAKE_FOURCC ('I', '4', '2', '0'); + case GST_VIDEO_FORMAT_YV12: + return GST_MAKE_FOURCC ('Y', 'V', '1', '2'); + case GST_VIDEO_FORMAT_YUY2: + return GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'); + case GST_VIDEO_FORMAT_UYVY: + return GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'); + case GST_VIDEO_FORMAT_AYUV: + return GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'); + default: + return 0; + } +} + +/** + * gst_video_format_from_rgb32_masks: + * @red_mask: red bit mask + * @green_mask: green bit mask + * @blue_mask: blue bit mask + * + * Converts red, green, blue bit masks into the corresponding + * #GstVideoFormat. + * + * Since: 0.10.16 + * + * Returns: the #GstVideoFormat corresponding to the bit masks + */ +GstVideoFormat +gst_video_format_from_rgb32_masks (int red_mask, int green_mask, int blue_mask) +{ + if (red_mask == 0xff000000 && green_mask == 0x00ff0000 && + blue_mask == 0x0000ff00) { + return GST_VIDEO_FORMAT_RGBx; + } + if (red_mask == 0x0000ff00 && green_mask == 0x00ff0000 && + blue_mask == 0xff000000) { + return GST_VIDEO_FORMAT_BGRx; + } + if (red_mask == 0x00ff0000 && green_mask == 0x0000ff00 && + blue_mask == 0x000000ff) { + return GST_VIDEO_FORMAT_xRGB; + } + if (red_mask == 0x000000ff && green_mask == 0x0000ff00 && + blue_mask == 0x00ff0000) { + return GST_VIDEO_FORMAT_xBGR; + } + + return GST_VIDEO_FORMAT_UNKNOWN; +} + +/** + * gst_video_format_is_rgb: + * @format: a #GstVideoFormat + * + * Since: 0.10.16 + * + * Returns: TRUE if @format represents RGB video + */ +gboolean +gst_video_format_is_rgb (GstVideoFormat format) +{ + switch (format) { + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + case GST_VIDEO_FORMAT_AYUV: + return FALSE; + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_xRGB: + case GST_VIDEO_FORMAT_xBGR: + return TRUE; + default: + return FALSE; + } +} + +/** + * gst_video_format_is_yuv: + * @format: a #GstVideoFormat + * + * Since: 0.10.16 + * + * Returns: TRUE if @format represents YUV video + */ +gboolean +gst_video_format_is_yuv (GstVideoFormat format) +{ + switch (format) { + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + case GST_VIDEO_FORMAT_AYUV: + return TRUE; + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_xRGB: + case GST_VIDEO_FORMAT_xBGR: + return FALSE; + default: + return FALSE; + } +} + +/** + * gst_video_format_has_alpha: + * @format: a #GstVideoFormat + * + * Since: 0.10.16 + * + * Returns: TRUE if @format has an alpha channel + */ +gboolean +gst_video_format_has_alpha (GstVideoFormat format) +{ + switch (format) { + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + return FALSE; + case GST_VIDEO_FORMAT_AYUV: + return TRUE; + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_xRGB: + case GST_VIDEO_FORMAT_xBGR: + return FALSE; + default: + return FALSE; + } +} + +/** + * gst_video_format_get_row_stride: + * @format: a #GstVideoFormat + * @component: the component index + * @width: the width of video + * + * Calculates the row stride (number of bytes from one row of pixels to + * the next) for the video component with an index of @component. For + * YUV video, Y, U, and V have component indices of 0, 1, and 2, + * respectively. For RGB video, R, G, and B have component indicies of + * 0, 1, and 2, respectively. Alpha channels, if present, have a component + * index of 3. The @width parameter always represents the width of the + * video, not the component. + * + * Since: 0.10.16 + * + * Returns: row stride of component @component + */ +int +gst_video_format_get_row_stride (GstVideoFormat format, int component, + int width) +{ + switch (format) { + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + if (component == 0) { + return GST_ROUND_UP_4 (width); + } else { + return GST_ROUND_UP_4 (GST_ROUND_UP_2 (width) / 2); + } + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + return GST_ROUND_UP_4 (width * 2); + case GST_VIDEO_FORMAT_AYUV: + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_xRGB: + case GST_VIDEO_FORMAT_xBGR: + return width * 4; + default: + return 0; + } +} + +/** + * gst_video_format_get_pixel_stride: + * @format: a #GstVideoFormat + * @component: the component index + * + * Calculates the pixel stride (number of bytes from one pixel to the + * pixel to its immediate left) for the video component with an index + * of @component. See @gst_video_format_get_row_stride for a description + * of the component index. + * + * Since: 0.10.16 + * + * Returns: pixel stride of component @component + */ +int +gst_video_format_get_pixel_stride (GstVideoFormat format, int component) +{ + switch (format) { + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + return 1; + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + if (component == 0) { + return 2; + } else { + return 4; + } + case GST_VIDEO_FORMAT_AYUV: + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_xRGB: + case GST_VIDEO_FORMAT_xBGR: + return 4; + default: + return 0; + } +} + +/** + * gst_video_format_get_component_width: + * @format: a #GstVideoFormat + * @component: the component index + * @width: the width of video + * + * Calculates the width of the component. See + * @gst_video_format_get_row_stride for a description + * of the component index. + * + * Since: 0.10.16 + * + * Returns: width of component @component + */ +int +gst_video_format_get_component_width (GstVideoFormat format, int component, + int width) +{ + switch (format) { + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + if (component == 0) { + return width; + } else { + return GST_ROUND_UP_2 (width) / 2; + } + case GST_VIDEO_FORMAT_AYUV: + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_xRGB: + case GST_VIDEO_FORMAT_xBGR: + return width; + default: + return 0; + } +} + +/** + * gst_video_format_get_component_height: + * @format: a #GstVideoFormat + * @component: the component index + * @height: the height of video + * + * Calculates the height of the component. See + * @gst_video_format_get_row_stride for a description + * of the component index. + * + * Since: 0.10.16 + * + * Returns: height of component @component + */ +int +gst_video_format_get_component_height (GstVideoFormat format, int component, + int height) +{ + switch (format) { + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + if (component == 0) { + return height; + } else { + return GST_ROUND_UP_2 (height) / 2; + } + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + case GST_VIDEO_FORMAT_AYUV: + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_xRGB: + case GST_VIDEO_FORMAT_xBGR: + return height; + default: + return 0; + } +} + +/** + * gst_video_format_get_component_offset: + * @format: a #GstVideoFormat + * @component: the component index + * @width: the width of video + * @height: the height of video + * + * Calculates the offset (in bytes) of the first pixel of the component + * with index @component. For packed formats, this will typically be a + * small integer (0, 1, 2, 3). For planar formats, this will be a + * (relatively) large offset to the beginning of the second or third + * component planes. See @gst_video_format_get_row_stride for a description + * of the component index. + * + * Since: 0.10.16 + * + * Returns: offset of component @component + */ +int +gst_video_format_get_component_offset (GstVideoFormat format, int component, + int width, int height) +{ + switch (format) { + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + if (component == 0) { + return 0; + } else { + int offset; + + offset = GST_ROUND_UP_4 (width) * GST_ROUND_UP_2 (height); + if (component == 2) { + offset += GST_ROUND_UP_4 (GST_ROUND_UP_2 (width) / 2) * + (GST_ROUND_UP_2 (height) / 2); + } + return offset; + } + case GST_VIDEO_FORMAT_YUY2: + if (component == 0) + return 0; + if (component == 1) + return 1; + if (component == 2) + return 3; + return 0; + case GST_VIDEO_FORMAT_UYVY: + if (component == 0) + return 1; + if (component == 1) + return 0; + if (component == 2) + return 2; + return 0; + case GST_VIDEO_FORMAT_AYUV: + if (component == 0) + return 1; + if (component == 1) + return 2; + if (component == 2) + return 3; + if (component == 3) + return 0; + return 0; + case GST_VIDEO_FORMAT_RGBx: + if (component == 0) + return 0; + if (component == 1) + return 1; + if (component == 2) + return 2; + if (component == 3) + return 3; + return 0; + case GST_VIDEO_FORMAT_BGRx: + if (component == 0) + return 2; + if (component == 1) + return 1; + if (component == 2) + return 0; + if (component == 3) + return 3; + return 0; + case GST_VIDEO_FORMAT_xRGB: + if (component == 0) + return 1; + if (component == 1) + return 2; + if (component == 2) + return 3; + if (component == 3) + return 0; + return 0; + case GST_VIDEO_FORMAT_xBGR: + if (component == 0) + return 3; + if (component == 1) + return 2; + if (component == 2) + return 1; + if (component == 3) + return 0; + return 0; + default: + return 0; + } +} + +/** + * gst_video_format_get_size: + * @format: a #GstVideoFormat + * @width: the width of video + * @height: the height of video + * + * Calculates the total number of bytes in the raw video format. This + * number should be used when allocating a buffer for raw video. + * + * Since: 0.10.16 + * + * Returns: size (in bytes) of raw video format + */ +int +gst_video_format_get_size (GstVideoFormat format, int width, int height) +{ + int size; + + switch (format) { + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + size = GST_ROUND_UP_4 (width) * GST_ROUND_UP_2 (height); + size += GST_ROUND_UP_4 (GST_ROUND_UP_2 (width) / 2) * + (GST_ROUND_UP_2 (height) / 2) * 2; + return size; + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + return GST_ROUND_UP_4 (width * 2) * height; + case GST_VIDEO_FORMAT_AYUV: + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_xRGB: + case GST_VIDEO_FORMAT_xBGR: + return width * 4 * height; + default: + return 0; + } +} + +/** + * gst_video_format_convert: + * @format: a #GstVideoFormat + * @width: the width of video + * @height: the height of video + * @fps_n: frame rate numerator + * @fps_d: frame rate denominator + * @src_format: #GstFormat of the @src_value + * @src_value: value to convert + * @dest_format: #GstFormat of the @dest_value + * @dest_value: pointer to destination value + * + * Converts among various #GstFormat types. This function handles + * GST_FORMAT_BYTES, GST_FORMAT_TIME, and GST_FORMAT_DEFAULT. For + * raw video, GST_FORMAT_DEFAULT corresponds to video frames. This + * function can be to handle pad queries of the type GST_QUERY_CONVERT. + * + * Since: 0.10.16 + * + * Returns: TRUE if the conversion was successful. + */ +gboolean +gst_video_format_convert (GstVideoFormat format, int width, int height, + int fps_n, int fps_d, + GstFormat src_format, gint64 src_value, + GstFormat dest_format, gint64 * dest_value) +{ + gboolean ret = FALSE; + int size; + + size = gst_video_format_get_size (format, width, height); + + GST_DEBUG ("converting value %" G_GINT64_FORMAT " from %s to %s", + src_value, gst_format_get_name (src_format), + gst_format_get_name (dest_format)); + + if (src_format == dest_format) { + *dest_value = src_value; + ret = TRUE; + goto done; + } + + if (src_value == -1) { + *dest_value = -1; + ret = TRUE; + goto done; + } + + /* bytes to frames */ + if (src_format == GST_FORMAT_BYTES && dest_format == GST_FORMAT_DEFAULT) { + if (size != 0) { + *dest_value = gst_util_uint64_scale_int (src_value, 1, size); + } else { + GST_WARNING ("blocksize is 0"); + *dest_value = 0; + } + ret = TRUE; + goto done; + } + + /* frames to bytes */ + if (src_format == GST_FORMAT_DEFAULT && dest_format == GST_FORMAT_BYTES) { + *dest_value = gst_util_uint64_scale_int (src_value, size, 1); + ret = TRUE; + goto done; + } + + /* time to frames */ + if (src_format == GST_FORMAT_TIME && dest_format == GST_FORMAT_DEFAULT) { + if (fps_d != 0) { + *dest_value = gst_util_uint64_scale (src_value, + fps_n, GST_SECOND * fps_d); + } else { + GST_WARNING ("framerate denominator is 0"); + *dest_value = 0; + } + ret = TRUE; + goto done; + } + + /* frames to time */ + if (src_format == GST_FORMAT_DEFAULT && dest_format == GST_FORMAT_TIME) { + if (fps_n != 0) { + *dest_value = gst_util_uint64_scale (src_value, + GST_SECOND * fps_d, fps_n); + } else { + GST_WARNING ("framerate numerator is 0"); + *dest_value = 0; + } + ret = TRUE; + goto done; + } + + /* time to bytes */ + if (src_format == GST_FORMAT_TIME && dest_format == GST_FORMAT_BYTES) { + if (fps_d != 0) { + *dest_value = gst_util_uint64_scale (src_value, + fps_n * size, GST_SECOND * fps_d); + } else { + GST_WARNING ("framerate denominator is 0"); + *dest_value = 0; + } + ret = TRUE; + goto done; + } + + /* bytes to time */ + if (src_format == GST_FORMAT_BYTES && dest_format == GST_FORMAT_TIME) { + if (fps_n != 0 && size != 0) { + *dest_value = gst_util_uint64_scale (src_value, + GST_SECOND * fps_d, fps_n * size); + } else { + GST_WARNING ("framerate denominator and/or blocksize is 0"); + *dest_value = 0; + } + ret = TRUE; + } + +done: + + GST_DEBUG ("ret=%d result %" G_GINT64_FORMAT, ret, *dest_value); + + return ret; +} + +void +gst_adapter_copy (GstAdapter * adapter, guint8 * dest, guint offset, guint size) +{ + GSList *g; + int skip; + + g_return_if_fail (GST_IS_ADAPTER (adapter)); + g_return_if_fail (size > 0); + + /* we don't have enough data, return. This is unlikely + * as one usually does an _available() first instead of copying a + * random size. */ + if (G_UNLIKELY (offset + size > adapter->size)) + return; + + skip = adapter->skip; + for (g = adapter->buflist; g && size > 0; g = g_slist_next (g)) { + GstBuffer *buf; + + buf = g->data; + if (offset < GST_BUFFER_SIZE (buf) - skip) { + int n; + + n = MIN (GST_BUFFER_SIZE (buf) - skip - offset, size); + memcpy (dest, GST_BUFFER_DATA (buf) + skip + offset, n); + + dest += n; + offset = 0; + size -= n; + } else { + offset -= GST_BUFFER_SIZE (buf) - skip; + } + skip = 0; + } +} +#endif diff --git a/ext/vp8/gst/video/gstvideocompat.h b/ext/vp8/gst/video/gstvideocompat.h new file mode 100644 index 0000000..a51325c --- /dev/null +++ b/ext/vp8/gst/video/gstvideocompat.h @@ -0,0 +1,71 @@ +/* + * Copyright 2007 David A. Schleef + * + * This comes solely from commit 1c3e012fc3e4b9aba19d44855bf8c8bf6ec44cd5 + * to gst-plugins-base. + */ + +#ifndef __GST_VIDEOCOMPAT_H__ +#define __GST_VIDEOCOMPAT_H__ + +#include +#include +#include + +/* uh, yeah */ +#if GST_VERSION_MICRO < 15 + +G_BEGIN_DECLS + +typedef enum { + GST_VIDEO_FORMAT_UNKNOWN, + GST_VIDEO_FORMAT_I420, + GST_VIDEO_FORMAT_YV12, + GST_VIDEO_FORMAT_YUY2, + GST_VIDEO_FORMAT_UYVY, + GST_VIDEO_FORMAT_AYUV, + GST_VIDEO_FORMAT_RGBx, + GST_VIDEO_FORMAT_BGRx, + GST_VIDEO_FORMAT_xRGB, + GST_VIDEO_FORMAT_xBGR +} GstVideoFormat; + + +gboolean gst_video_format_parse_caps (GstCaps *caps, GstVideoFormat *format, + int *width, int *height); +gboolean gst_video_parse_caps_framerate (GstCaps *caps, + int *fps_n, int *fps_d); +gboolean gst_video_parse_caps_pixel_aspect_ratio (GstCaps *caps, + int *par_n, int *par_d); +GstCaps * gst_video_format_new_caps (GstVideoFormat format, + int width, int height, int framerate_n, int framerate_d, + int par_n, int par_d); +GstVideoFormat gst_video_format_from_fourcc (guint32 fourcc); +guint32 gst_video_format_to_fourcc (GstVideoFormat format); +GstVideoFormat gst_video_format_from_rgb32_masks (int red_mask, int green_mask, int blue_mask); +gboolean gst_video_format_is_rgb (GstVideoFormat format); +gboolean gst_video_format_is_yuv (GstVideoFormat format); +gboolean gst_video_format_has_alpha (GstVideoFormat format); +int gst_video_format_get_row_stride (GstVideoFormat format, int component, + int width); +int gst_video_format_get_pixel_stride (GstVideoFormat format, int component); +int gst_video_format_get_component_width (GstVideoFormat format, int component, + int width); +int gst_video_format_get_component_height (GstVideoFormat format, int component, + int height); +int gst_video_format_get_component_offset (GstVideoFormat format, int component, + int width, int height); +int gst_video_format_get_size (GstVideoFormat format, int width, int height); +gboolean gst_video_format_convert (GstVideoFormat format, int width, int height, + int fps_n, int fps_d, + GstFormat src_format, gint64 src_value, + GstFormat dest_format, gint64 * dest_value); + +void +gst_adapter_copy (GstAdapter * adapter, guint8 * dest, guint offset, guint size); + +G_END_DECLS +#endif + +#endif /* __GST_VIDEO_H__ */ + diff --git a/ext/vp8/gstvp8dec.c b/ext/vp8/gstvp8dec.c new file mode 100644 index 0000000..8da451c --- /dev/null +++ b/ext/vp8/gstvp8dec.c @@ -0,0 +1,397 @@ +/* VP8 plugin + * Copyright (C) 2006 David Schleef + * Copyright (C) 2008,2009,2010 Entropy Wave Inc + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +GST_DEBUG_CATEGORY (gst_vp8dec_debug); +#define GST_CAT_DEFAULT gst_vp8dec_debug + +#define GST_TYPE_VP8_DEC \ + (gst_vp8_dec_get_type()) +#define GST_VP8_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VP8_DEC,GstVP8Dec)) +#define GST_VP8_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VP8_DEC,GstVP8DecClass)) +#define GST_IS_GST_VP8_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VP8_DEC)) +#define GST_IS_GST_VP8_DEC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VP8_DEC)) + +typedef struct _GstVP8Dec GstVP8Dec; +typedef struct _GstVP8DecClass GstVP8DecClass; + +struct _GstVP8Dec +{ + GstBaseVideoDecoder base_video_decoder; + + vpx_codec_ctx_t decoder; + + /* state */ + + gboolean decoder_inited; + gboolean have_video_info; + gboolean is_625; + gboolean is_wide; + gboolean is_422; + +}; + +struct _GstVP8DecClass +{ + GstBaseVideoDecoderClass base_video_decoder_class; +}; + + +/* GstVP8Dec signals and args */ +enum +{ + LAST_SIGNAL +}; + +enum +{ + ARG_0 +}; + +static void gst_vp8_dec_finalize (GObject * object); +static void gst_vp8_dec_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_vp8_dec_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_vp8_dec_start (GstBaseVideoDecoder * decoder); +static gboolean gst_vp8_dec_stop (GstBaseVideoDecoder * decoder); +static gboolean gst_vp8_dec_reset (GstBaseVideoDecoder * decoder); +static GstFlowReturn gst_vp8_dec_parse_data (GstBaseVideoDecoder * decoder, + gboolean at_eos); +static int gst_vp8_dec_scan_for_sync (GstBaseVideoDecoder * decoder, + gboolean at_eos, int offset, int n); +static GstFlowReturn gst_vp8_dec_handle_frame (GstBaseVideoDecoder * decoder, + GstVideoFrame * frame); + +GType gst_vp8_dec_get_type (void); + +static GstStaticPadTemplate gst_vp8_dec_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-vp8") + ); + +static GstStaticPadTemplate gst_vp8_dec_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420")) + ); + +GST_BOILERPLATE (GstVP8Dec, gst_vp8_dec, GstBaseVideoDecoder, + GST_TYPE_BASE_VIDEO_DECODER); + +static void +gst_vp8_dec_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_vp8_dec_src_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_vp8_dec_sink_template)); + + gst_element_class_set_details_simple (element_class, + "On2 VP8 Decoder", + "Codec/Decoder/Video", + "Decode VP8 video streams", "David Schleef "); +} + +static void +gst_vp8_dec_class_init (GstVP8DecClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + GstBaseVideoDecoderClass *base_video_decoder_class; + + gobject_class = G_OBJECT_CLASS (klass); + element_class = GST_ELEMENT_CLASS (klass); + base_video_decoder_class = GST_BASE_VIDEO_DECODER_CLASS (klass); + + gobject_class->set_property = gst_vp8_dec_set_property; + gobject_class->get_property = gst_vp8_dec_get_property; + gobject_class->finalize = gst_vp8_dec_finalize; + + base_video_decoder_class->start = gst_vp8_dec_start; + base_video_decoder_class->stop = gst_vp8_dec_stop; + base_video_decoder_class->reset = gst_vp8_dec_reset; + base_video_decoder_class->scan_for_sync = gst_vp8_dec_scan_for_sync; + base_video_decoder_class->parse_data = gst_vp8_dec_parse_data; + base_video_decoder_class->handle_frame = gst_vp8_dec_handle_frame; +} + +static void +gst_vp8_dec_init (GstVP8Dec * gst_vp8_dec, GstVP8DecClass * klass) +{ + + GST_DEBUG ("gst_vp8_dec_init"); + + //gst_vp8_dec->decoder = ; + +} + +static void +gst_vp8_dec_finalize (GObject * object) +{ + GstVP8Dec *gst_vp8_dec; + + GST_DEBUG ("finalize"); + + g_return_if_fail (GST_IS_GST_VP8_DEC (object)); + gst_vp8_dec = GST_VP8_DEC (object); + + G_OBJECT_CLASS (parent_class)->finalize (object); + +} + +static void +gst_vp8_dec_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstVP8Dec *src; + + g_return_if_fail (GST_IS_GST_VP8_DEC (object)); + src = GST_VP8_DEC (object); + + GST_DEBUG ("gst_vp8_dec_set_property"); + switch (prop_id) { + default: + break; + } +} + +static void +gst_vp8_dec_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstVP8Dec *src; + + g_return_if_fail (GST_IS_GST_VP8_DEC (object)); + src = GST_VP8_DEC (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static gboolean +gst_vp8_dec_start (GstBaseVideoDecoder * decoder) +{ + int flags = 0; + GstVP8Dec *gst_vp8_dec = GST_VP8_DEC (decoder); + + decoder->packetized = TRUE; + + vpx_codec_dec_init (&gst_vp8_dec->decoder, &vpx_codec_vp8_dx_algo, NULL, + flags); + + return TRUE; +} + +static gboolean +gst_vp8_dec_stop (GstBaseVideoDecoder * base_video_decoder) +{ + return TRUE; +} + +static gboolean +gst_vp8_dec_reset (GstBaseVideoDecoder * base_video_decoder) +{ + GstVP8Dec *decoder; + + GST_DEBUG ("reset"); + + decoder = GST_VP8_DEC (base_video_decoder); + + decoder->decoder_inited = FALSE; + decoder->have_video_info = FALSE; + + return TRUE; +} + +static GstFlowReturn +gst_vp8_dec_parse_data (GstBaseVideoDecoder * decoder, gboolean at_eos) +{ + + return GST_FLOW_OK; +} + +static void +gst_vp8_dec_send_tags (GstVP8Dec * dec) +{ + GstTagList *list; + + list = gst_tag_list_new (); + gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, + GST_TAG_VIDEO_CODEC, "VP8 video", NULL); + + gst_element_found_tags_for_pad (GST_ELEMENT (dec), + GST_BASE_VIDEO_CODEC_SRC_PAD (dec), list); +} + +static int +gst_vp8_dec_scan_for_sync (GstBaseVideoDecoder * base_video_decoder, + gboolean at_eos, int offset, int n) +{ + int n_available = + gst_adapter_available (base_video_decoder->input_adapter) - offset; + + if (n_available < 4) { + if (at_eos) { + return n_available; + } else { + return 0; + } + } + + n_available -= 3; + + return + gst_adapter_masked_scan_uint32_compat (base_video_decoder->input_adapter, + 0xf00fff3f, 0x1f07003f, offset, MIN (n, n_available - 3)); +} + +static GstFlowReturn +gst_vp8_dec_handle_frame (GstBaseVideoDecoder * decoder, GstVideoFrame * frame) +{ + GstVP8Dec *dec; + GstFlowReturn ret; + long status; + vpx_codec_iter_t iter = NULL; + vpx_image_t *img; + + + GST_DEBUG ("handle_frame"); + + dec = GST_VP8_DEC (decoder); + + gst_base_video_decoder_set_sync_point (decoder); + + if (!dec->decoder_inited) { + + dec->decoder_inited = TRUE; + + gst_vp8_dec_send_tags (dec); + } +#if 0 + status = + dec->decoder-> + GetCompressedFrameDetails (GST_BUFFER_DATA (frame->sink_buffer), + pCompDetails); +#endif + + if (!dec->have_video_info) { +#if 0 + decoder->state.width = pCompDetails->SamplesPerLine; + decoder->state.height = pCompDetails->ActiveLinePerFrame * + pCompDetails->FieldFrameCount; +#endif + decoder->state.format = GST_VIDEO_FORMAT_I420; + + decoder->state.fps_n = 30; + decoder->state.fps_d = 1; + decoder->state.par_n = 1; + decoder->state.par_d = 1; + + dec->have_video_info = TRUE; + } + + if (GST_PAD_CAPS (GST_BASE_VIDEO_CODEC_SRC_PAD (decoder)) == NULL) { + GstCaps *caps; + + caps = gst_video_format_new_caps (decoder->state.format, + decoder->state.width, decoder->state.height, + decoder->state.fps_n, decoder->state.fps_d, + decoder->state.par_n, decoder->state.par_d); + + GST_DEBUG ("setting caps %" GST_PTR_FORMAT, caps); + + gst_pad_set_caps (GST_BASE_VIDEO_CODEC_SRC_PAD (decoder), caps); + } + + ret = gst_base_video_decoder_alloc_src_frame (decoder, frame); + if (ret != GST_FLOW_OK) { + GST_WARNING ("failed to get buffer"); + goto out; + } + + status = vpx_codec_decode (&dec->decoder, + GST_BUFFER_DATA (frame->sink_buffer), + GST_BUFFER_SIZE (frame->sink_buffer), NULL, 0); + if (status) { + return GST_FLOW_ERROR; + } + + while ((img = vpx_codec_get_frame (&dec->decoder, &iter))) { + int i; + + for (i = 0; i < decoder->state.height; i++) { + memcpy (GST_BUFFER_DATA (frame->src_buffer) + i * decoder->state.width, + img->planes[0] + i * img->stride[0], decoder->state.width); + } + for (i = 0; i < decoder->state.height / 2; i++) { + memcpy (GST_BUFFER_DATA (frame->src_buffer) + + decoder->state.width * decoder->state.height + + i * decoder->state.width / 2, + img->planes[1] + i * img->stride[1], decoder->state.width / 2); + } + for (i = 0; i < decoder->state.height / 2; i++) { + memcpy (GST_BUFFER_DATA (frame->src_buffer) + + decoder->state.width * decoder->state.height + + decoder->state.width * decoder->state.height / 4 + + i * decoder->state.width / 2, + img->planes[2] + i * img->stride[2], decoder->state.width / 2); + } + vpx_img_free (img); + } + + gst_base_video_decoder_finish_frame (decoder, frame); + + ret = GST_FLOW_OK; +out: + + return ret; +} diff --git a/ext/vp8/gstvp8enc.c b/ext/vp8/gstvp8enc.c new file mode 100644 index 0000000..3ad7784 --- /dev/null +++ b/ext/vp8/gstvp8enc.c @@ -0,0 +1,611 @@ +/* VP8 + * Copyright (C) 2006 David Schleef + * Copyright (C) 2010 Entropy Wave Inc + * + * 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 +#include +#include +#include +#include +#include +#include +#include + + +#include +#include + + +GST_DEBUG_CATEGORY (gst_vp8enc_debug); +#define GST_CAT_DEFAULT gst_vp8enc_debug + +#define GST_TYPE_VP8_ENC \ + (gst_vp8_enc_get_type()) +#define GST_VP8_ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VP8_ENC,GstVP8Enc)) +#define GST_VP8_ENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VP8_ENC,GstVP8EncClass)) +#define GST_IS_GST_VP8_ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VP8_ENC)) +#define GST_IS_GST_VP8_ENC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VP8_ENC)) + +typedef struct _GstVP8Enc GstVP8Enc; +typedef struct _GstVP8EncClass GstVP8EncClass; + +struct _GstVP8Enc +{ + GstBaseVideoEncoder base_video_encoder; + + vpx_codec_ctx_t encoder; + + /* properties */ + int bitrate; + double quality; + gboolean error_resilient; + int max_latency; + int keyframe_interval; + int speed; + + /* state */ + + gboolean inited; + + int resolution_id; + int n_frames; + +}; + +struct _GstVP8EncClass +{ + GstBaseVideoEncoderClass base_video_encoder_class; +}; + +/* GstVP8Enc signals and args */ +enum +{ + LAST_SIGNAL +}; + +#define DEFAULT_BITRATE 0 +#define DEFAULT_QUALITY 5 +#define DEFAULT_ERROR_RESILIENT FALSE +#define DEFAULT_MAX_LATENCY 10 +#define DEFAULT_KEYFRAME_INTERVAL 60 +#define DEFAULT_SPEED 0 + +enum +{ + PROP_0, + PROP_BITRATE, + PROP_QUALITY, + PROP_ERROR_RESILIENT, + PROP_MAX_LATENCY, + PROP_KEYFRAME_INTERVAL, + PROP_SPEED +}; + +static void gst_vp8_enc_finalize (GObject * object); +static void gst_vp8_enc_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_vp8_enc_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_vp8_enc_start (GstBaseVideoEncoder * encoder); +static gboolean gst_vp8_enc_stop (GstBaseVideoEncoder * encoder); +static gboolean gst_vp8_enc_set_format (GstBaseVideoEncoder * + base_video_encoder, GstVideoState * state); +static gboolean gst_vp8_enc_finish (GstBaseVideoEncoder * base_video_encoder); +static gboolean gst_vp8_enc_handle_frame (GstBaseVideoEncoder * + base_video_encoder, GstVideoFrame * frame); +static GstFlowReturn gst_vp8_enc_shape_output (GstBaseVideoEncoder * encoder, + GstVideoFrame * frame); +static GstCaps *gst_vp8_enc_get_caps (GstBaseVideoEncoder * base_video_encoder); + +GType gst_vp8_enc_get_type (void); + +static const char *vpx_error_name (vpx_codec_err_t status); + + +static GstStaticPadTemplate gst_vp8_enc_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw-yuv,format=(fourcc)I420," + "width=[1,max],height=[1,max],framerate=(fraction)[0,max]," + "interlaced=(boolean){TRUE,FALSE}") + ); + +static GstStaticPadTemplate gst_vp8_enc_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-vp8") + ); + +GST_BOILERPLATE (GstVP8Enc, gst_vp8_enc, GstBaseVideoEncoder, + GST_TYPE_BASE_VIDEO_ENCODER); + +static void +gst_vp8_enc_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_vp8_enc_src_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_vp8_enc_sink_template)); + + gst_element_class_set_details_simple (element_class, + "On2 VP8 Encoder", + "Codec/Encoder/Video", + "Encode VP8 video streams", "David Schleef "); +} + +static void +gst_vp8_enc_class_init (GstVP8EncClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + GstBaseVideoEncoderClass *base_video_encoder_class; + + gobject_class = G_OBJECT_CLASS (klass); + element_class = GST_ELEMENT_CLASS (klass); + base_video_encoder_class = GST_BASE_VIDEO_ENCODER_CLASS (klass); + + gobject_class->set_property = gst_vp8_enc_set_property; + gobject_class->get_property = gst_vp8_enc_get_property; + gobject_class->finalize = gst_vp8_enc_finalize; + + base_video_encoder_class->start = gst_vp8_enc_start; + base_video_encoder_class->stop = gst_vp8_enc_stop; + base_video_encoder_class->handle_frame = gst_vp8_enc_handle_frame; + base_video_encoder_class->set_format = gst_vp8_enc_set_format; + base_video_encoder_class->finish = gst_vp8_enc_finish; + base_video_encoder_class->shape_output = gst_vp8_enc_shape_output; + base_video_encoder_class->get_caps = gst_vp8_enc_get_caps; + + g_object_class_install_property (gobject_class, PROP_BITRATE, + g_param_spec_int ("bitrate", "Bit rate", + "Bit rate", + 0, 1000000000, DEFAULT_BITRATE, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_QUALITY, + g_param_spec_double ("quality", "Quality", + "Quality", + 0, 10.0, DEFAULT_QUALITY, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_ERROR_RESILIENT, + g_param_spec_boolean ("error-resilient", "Error Resilient", + "Encode streams that are error resilient", + DEFAULT_ERROR_RESILIENT, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_MAX_LATENCY, + g_param_spec_int ("max-latency", "Max latency", + "Number of frames in encoder queue", + 0, 100, DEFAULT_MAX_LATENCY, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_KEYFRAME_INTERVAL, + g_param_spec_int ("keyframe-interval", "Key frame interval", + "Maximum distance between key frames", + 1, 1000, DEFAULT_KEYFRAME_INTERVAL, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_SPEED, + g_param_spec_int ("speed", "Speed", + "Speed", + 0, 2, DEFAULT_SPEED, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + +} + +static void +gst_vp8_enc_init (GstVP8Enc * gst_vp8_enc, GstVP8EncClass * klass) +{ + + GST_DEBUG ("init"); + + gst_vp8_enc->bitrate = DEFAULT_BITRATE; + gst_vp8_enc->quality = DEFAULT_QUALITY; + gst_vp8_enc->error_resilient = DEFAULT_ERROR_RESILIENT; + gst_vp8_enc->max_latency = DEFAULT_MAX_LATENCY; + gst_vp8_enc->keyframe_interval = DEFAULT_KEYFRAME_INTERVAL; +} + +static void +gst_vp8_enc_finalize (GObject * object) +{ + GstVP8Enc *gst_vp8_enc; + + GST_DEBUG ("finalize"); + + g_return_if_fail (GST_IS_GST_VP8_ENC (object)); + gst_vp8_enc = GST_VP8_ENC (object); + + G_OBJECT_CLASS (parent_class)->finalize (object); + +} + +static void +gst_vp8_enc_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstVP8Enc *gst_vp8_enc; + + g_return_if_fail (GST_IS_GST_VP8_ENC (object)); + gst_vp8_enc = GST_VP8_ENC (object); + + GST_DEBUG ("gst_vp8_enc_set_property"); + switch (prop_id) { + case PROP_BITRATE: + gst_vp8_enc->bitrate = g_value_get_int (value); + break; + case PROP_QUALITY: + gst_vp8_enc->quality = g_value_get_double (value); + break; + case PROP_ERROR_RESILIENT: + gst_vp8_enc->error_resilient = g_value_get_boolean (value); + break; + case PROP_MAX_LATENCY: + gst_vp8_enc->max_latency = g_value_get_int (value); + break; + case PROP_KEYFRAME_INTERVAL: + gst_vp8_enc->keyframe_interval = g_value_get_int (value); + break; + case PROP_SPEED: + gst_vp8_enc->speed = g_value_get_int (value); + break; + default: + break; + } +} + +static void +gst_vp8_enc_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstVP8Enc *gst_vp8_enc; + + g_return_if_fail (GST_IS_GST_VP8_ENC (object)); + gst_vp8_enc = GST_VP8_ENC (object); + + switch (prop_id) { + case PROP_BITRATE: + g_value_set_int (value, gst_vp8_enc->bitrate); + break; + case PROP_QUALITY: + g_value_set_double (value, gst_vp8_enc->quality); + break; + case PROP_ERROR_RESILIENT: + g_value_set_boolean (value, gst_vp8_enc->error_resilient); + break; + case PROP_MAX_LATENCY: + g_value_set_int (value, gst_vp8_enc->max_latency); + break; + case PROP_KEYFRAME_INTERVAL: + g_value_set_int (value, gst_vp8_enc->keyframe_interval); + break; + case PROP_SPEED: + g_value_set_int (value, gst_vp8_enc->speed); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static gboolean +gst_vp8_enc_start (GstBaseVideoEncoder * base_video_encoder) +{ + GstVP8Enc *encoder; + + GST_DEBUG ("start"); + + encoder = GST_VP8_ENC (base_video_encoder); + + + return TRUE; +} + +static gboolean +gst_vp8_enc_stop (GstBaseVideoEncoder * base_video_encoder) +{ + GstVP8Enc *encoder; + + encoder = GST_VP8_ENC (base_video_encoder); + + if (encoder->inited) { + vpx_codec_destroy (&encoder->encoder); + encoder->inited = FALSE; + } + + return TRUE; +} + +static gboolean +gst_vp8_enc_set_format (GstBaseVideoEncoder * base_video_encoder, + GstVideoState * state) +{ + GstVP8Enc *encoder; + + GST_DEBUG ("set_format"); + + encoder = GST_VP8_ENC (base_video_encoder); + + + return TRUE; +} + +static GstCaps * +gst_vp8_enc_get_caps (GstBaseVideoEncoder * base_video_encoder) +{ + GstCaps *caps; + const GstVideoState *state; + GstVP8Enc *encoder; + + encoder = GST_VP8_ENC (base_video_encoder); + + state = gst_base_video_encoder_get_state (base_video_encoder); + + caps = gst_caps_new_simple ("video/x-vp8", + "width", G_TYPE_INT, state->width, + "height", G_TYPE_INT, state->height, + "framerate", GST_TYPE_FRACTION, state->fps_n, + state->fps_d, + "pixel-aspect-ratio", GST_TYPE_FRACTION, state->par_n, + state->par_d, NULL); + + return caps; +} + +static gboolean +gst_vp8_enc_finish (GstBaseVideoEncoder * base_video_encoder) +{ + GstVP8Enc *encoder; + GstVideoFrame *frame; + int flags = 0; + int status; + vpx_codec_iter_t iter = NULL; + + GST_DEBUG ("finish"); + + encoder = GST_VP8_ENC (base_video_encoder); + + status = + vpx_codec_encode (&encoder->encoder, NULL, encoder->n_frames, 1, flags, + 0); + if (status != 0) { + GST_ERROR ("encode returned %d %s", status, vpx_error_name (status)); + } + + frame = gst_base_video_encoder_get_oldest_frame (base_video_encoder); + while (frame != NULL) { + const vpx_codec_cx_pkt_t *pkt; + + pkt = vpx_codec_get_cx_data (&encoder->encoder, &iter); + if (pkt) { + GST_DEBUG ("packet %d type %d", pkt->data.frame.sz, pkt->kind); + + frame->src_buffer = gst_buffer_new_and_alloc (pkt->data.frame.sz); + + memcpy (GST_BUFFER_DATA (frame->src_buffer), + pkt->data.frame.buf, pkt->data.frame.sz); + + if (frame->coder_hook) + g_free (frame->coder_hook); + + gst_base_video_encoder_finish_frame (base_video_encoder, frame); + } + + frame = gst_base_video_encoder_get_oldest_frame (base_video_encoder); + } + + return TRUE; +} + +static const char * +vpx_error_name (vpx_codec_err_t status) +{ + switch (status) { + case VPX_CODEC_OK: + return "OK"; + case VPX_CODEC_ERROR: + return "error"; + case VPX_CODEC_MEM_ERROR: + return "mem error"; + case VPX_CODEC_ABI_MISMATCH: + return "abi mismatch"; + case VPX_CODEC_INCAPABLE: + return "incapable"; + case VPX_CODEC_UNSUP_BITSTREAM: + return "unsupported bitstream"; + case VPX_CODEC_UNSUP_FEATURE: + return "unsupported feature"; + case VPX_CODEC_CORRUPT_FRAME: + return "corrupt frame"; + case VPX_CODEC_INVALID_PARAM: + return "invalid parameter"; + default: + return "unknown"; + } +} + +static const int speed_table[] = { + 0, + 100000, + 1, +}; + +static gboolean +gst_vp8_enc_handle_frame (GstBaseVideoEncoder * base_video_encoder, + GstVideoFrame * frame) +{ + GstVP8Enc *encoder; + const GstVideoState *state; + guint8 *src; + long status; + int flags = 0; + vpx_codec_iter_t iter = NULL; + const vpx_codec_cx_pkt_t *pkt; + vpx_image_t *image; + + GST_DEBUG ("handle_frame"); + + encoder = GST_VP8_ENC (base_video_encoder); + src = GST_BUFFER_DATA (frame->sink_buffer); + + state = gst_base_video_encoder_get_state (base_video_encoder); + encoder->n_frames++; + + GST_DEBUG ("res id %d size %d %d", encoder->resolution_id, + state->width, state->height); + + if (!encoder->inited) { + vpx_codec_enc_cfg_t cfg; + + vpx_codec_enc_config_default (&vpx_codec_vp8_cx_algo, &cfg, 0); + + cfg.g_w = base_video_encoder->state.width; + cfg.g_h = base_video_encoder->state.height; + //cfg.g_timebase.num = base_video_encoder->state.fps_n; + //cfg.g_timebase.den = base_video_encoder->state.fps_d; + + cfg.g_error_resilient = encoder->error_resilient; + cfg.g_pass = VPX_RC_ONE_PASS; + cfg.g_lag_in_frames = encoder->max_latency; + + if (encoder->bitrate) { + cfg.rc_end_usage = VPX_CBR; + cfg.rc_target_bitrate = encoder->bitrate / 1000; + } else { + cfg.rc_end_usage = VPX_VBR; + cfg.rc_min_quantizer = 63 - encoder->quality * 5.0; + cfg.rc_max_quantizer = 63 - encoder->quality * 5.0; + cfg.rc_target_bitrate = encoder->bitrate; + cfg.rc_buf_sz = 1000; // FIXME 1000 ms + cfg.rc_buf_initial_sz = 1000; // FIXME 1000 ms + } + + cfg.kf_mode = VPX_KF_AUTO; + cfg.kf_min_dist = 0; + cfg.kf_max_dist = encoder->keyframe_interval; + + status = vpx_codec_enc_init (&encoder->encoder, &vpx_codec_vp8_cx_algo, + &cfg, 0); + if (status) { + GST_ERROR ("encoder input error"); + return GST_FLOW_ERROR; + } + + encoder->inited = TRUE; + } + + image = g_malloc0 (sizeof (vpx_image_t)); + vpx_img_wrap (image, IMG_FMT_I420, + base_video_encoder->state.width, + base_video_encoder->state.height, 1, + GST_BUFFER_DATA (frame->sink_buffer)); + frame->coder_hook = image; + +#if 0 + if (encoder->force_keyframe) { + flags |= VPX_EFLAG_FORCE_KF; + } +#endif + + status = vpx_codec_encode (&encoder->encoder, image, + encoder->n_frames, 1, flags, speed_table[encoder->speed]); + if (status != 0) { + GST_ERROR ("encode returned %d %s", status, vpx_error_name (status)); + } + + pkt = vpx_codec_get_cx_data (&encoder->encoder, &iter); + if (pkt != NULL) { + GST_DEBUG ("packet %d type %d", pkt->data.frame.sz, pkt->kind); + + if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) { + GST_ERROR ("non frame pkt"); + } + + frame->src_buffer = gst_buffer_new_and_alloc (pkt->data.frame.sz); + + memcpy (GST_BUFFER_DATA (frame->src_buffer), + pkt->data.frame.buf, pkt->data.frame.sz); + } + //frame->is_sync_point = TRUE; + + if (frame->src_buffer) { + if (frame->coder_hook) + g_free (frame->coder_hook); + + gst_base_video_encoder_finish_frame (base_video_encoder, frame); + } + + return TRUE; +} + + + +static GstFlowReturn +gst_vp8_enc_shape_output (GstBaseVideoEncoder * base_video_encoder, + GstVideoFrame * frame) +{ + GstBuffer *buf = frame->src_buffer; + const GstVideoState *state; + GstFlowReturn ret; + + GST_DEBUG ("shape_output"); + + state = gst_base_video_encoder_get_state (base_video_encoder); + + GST_BUFFER_TIMESTAMP (buf) = gst_video_state_get_timestamp (state, + &base_video_encoder->segment, frame->presentation_frame_number); + GST_BUFFER_DURATION (buf) = gst_video_state_get_timestamp (state, + &base_video_encoder->segment, + frame->presentation_frame_number + 1) - GST_BUFFER_TIMESTAMP (buf); + GST_BUFFER_OFFSET_END (buf) = GST_CLOCK_TIME_NONE; + GST_BUFFER_OFFSET (buf) = GST_CLOCK_TIME_NONE; + + if (frame->is_sync_point) { + GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DELTA_UNIT); + } else { + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); + } + + gst_buffer_set_caps (buf, base_video_encoder->caps); + + ret = gst_pad_push (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), buf); + if (ret != GST_FLOW_OK) { + GST_ERROR ("flow error %d", ret); + } + + frame->src_buffer = NULL; + + return ret; +} diff --git a/ext/vp8/plugin.c b/ext/vp8/plugin.c new file mode 100644 index 0000000..97d7f38 --- /dev/null +++ b/ext/vp8/plugin.c @@ -0,0 +1,52 @@ +/* VP8 + * Copyright (C) 2006 David Schleef + * Copyright (C) 2010 Entropy Wave Inc + * + * 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 + +GType gst_vp8_dec_get_type (void); +GType gst_vp8_enc_get_type (void); + +GST_DEBUG_CATEGORY_EXTERN (gst_vp8dec_debug); +GST_DEBUG_CATEGORY_EXTERN (gst_vp8enc_debug); + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (gst_vp8dec_debug, "vp8dec", 0, "VP8 Decoder"); + GST_DEBUG_CATEGORY_INIT (gst_vp8enc_debug, "vp8enc", 0, "VP8 Encoder"); + + gst_element_register (plugin, "vp8dec", GST_RANK_PRIMARY, + gst_vp8_dec_get_type ()); + gst_element_register (plugin, "vp8enc", GST_RANK_PRIMARY, + gst_vp8_enc_get_type ()); + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "vp8", + "VP8 plugin", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) -- 2.7.4