Merge branch 'master' into 0.11
authorWim Taymans <wim.taymans@collabora.co.uk>
Tue, 6 Sep 2011 13:24:32 +0000 (15:24 +0200)
committerWim Taymans <wim.taymans@collabora.co.uk>
Tue, 6 Sep 2011 13:24:32 +0000 (15:24 +0200)
Conflicts:
gst-libs/gst/audio/audio.h
gst-libs/gst/audio/gstaudiodecoder.c
gst-libs/gst/audio/gstaudiodecoder.h
gst-libs/gst/audio/gstaudioencoder.c
gst-libs/gst/audio/gstbaseaudioencoder.h
gst/playback/Makefile.am
gst/playback/gstplaybin.c
gst/playback/gstplaysink.c
gst/playback/gstplaysinkvideoconvert.c
gst/playback/gstsubtitleoverlay.c
gst/videorate/gstvideorate.c
gst/videoscale/gstvideoscale.c
win32/common/libgstaudio.def

45 files changed:
1  2 
docs/libs/Makefile.am
docs/libs/gst-plugins-base-libs-docs.sgml
docs/libs/gst-plugins-base-libs-sections.txt
docs/libs/gst-plugins-base-libs.types
gst-libs/gst/audio/Makefile.am
gst-libs/gst/audio/audio.h
gst-libs/gst/audio/gstaudiodecoder.c
gst-libs/gst/audio/gstaudiodecoder.h
gst-libs/gst/audio/gstaudioencoder.c
gst-libs/gst/audio/gstaudioencoder.h
gst-libs/gst/audio/gstaudiofilterexample.c
gst-libs/gst/audio/gstaudioprocess.c
gst-libs/gst/audio/gstaudioprocess.h
gst-libs/gst/audio/gstaudioringbuffer.c
gst-libs/gst/audio/gstaudioringbuffer.h
gst-libs/gst/audio/gstbaseaudiosrc.c.orig
gst-libs/gst/audio/gstbaseaudiosrc.c.rej
gst-libs/gst/audio/gstringbufferthread.c
gst-libs/gst/audio/gstringbufferthread.h
gst-libs/gst/cdda/gst-plugins-base-sha1-2.patch
gst-libs/gst/cdda/gstcddabasesrc.c.orig
gst-libs/gst/rtp/gst-plugins-base-rtcp-feedback.patch
gst-libs/gst/rtp/gstbasertppayload.c.orig
gst-libs/gst/rtp/gstbasertppayload.c.rej
gst-libs/gst/rtp/gstrtpbuffer.c.new
gst-libs/gst/rtsp/gstrtspconnection.c.orig
gst-libs/gst/rtsp/rtsp-marshal.c
gst-libs/gst/rtsp/rtsp-marshal.h
gst-libs/gst/rtsp/rtspdefs.patch
gst-libs/gst/video/gstmetavideoclip.h
gst/encoding/gstencodebin.c
gst/playback/Makefile.am
gst/playback/gstdecodebin2.c
gst/playback/gstplaysink.c
gst/playback/gstplaysinkvideoconvert.c
gst/playback/gstsubtitleoverlay.c
gst/videorate/gstvideorate.c
gst/videorate/videorate-discont.patch
gst/videoscale/gstvideoscale.c
gst/videoscale/gstvideoscale.h
tests/check/libs/profile.c
tests/check/libs/tag.c
tests/check/libs/video.c
tests/examples/seek/seek.c
win32/common/libgstaudio.def

Simple merge
Simple merge
Simple merge
Simple merge
  #include "config.h"
  #endif
  
- #include "gstbaseaudiodecoder.h"
 -#define GST_USE_UNSTABLE_API
+ #include "gstaudiodecoder.h"
  #include <gst/pbutils/descriptions.h>
  
  #include <string.h>
@@@ -262,38 -263,39 +262,35 @@@ struct _GstAudioDecoderPrivat
  };
  
  
- static void gst_base_audio_decoder_finalize (GObject * object);
- static void gst_base_audio_decoder_set_property (GObject * object,
+ static void gst_audio_decoder_finalize (GObject * object);
+ static void gst_audio_decoder_set_property (GObject * object,
      guint prop_id, const GValue * value, GParamSpec * pspec);
- static void gst_base_audio_decoder_get_property (GObject * object,
+ static void gst_audio_decoder_get_property (GObject * object,
      guint prop_id, GValue * value, GParamSpec * pspec);
  
- static void gst_base_audio_decoder_clear_queues (GstBaseAudioDecoder * dec);
- static GstFlowReturn gst_base_audio_decoder_chain_reverse (GstBaseAudioDecoder *
+ static void gst_audio_decoder_clear_queues (GstAudioDecoder * dec);
+ static GstFlowReturn gst_audio_decoder_chain_reverse (GstAudioDecoder *
      dec, GstBuffer * buf);
  
- static GstStateChangeReturn gst_base_audio_decoder_change_state (GstElement *
+ static GstStateChangeReturn gst_audio_decoder_change_state (GstElement *
      element, GstStateChange transition);
- static gboolean gst_base_audio_decoder_sink_event (GstPad * pad,
-     GstEvent * event);
- static gboolean gst_base_audio_decoder_src_event (GstPad * pad,
-     GstEvent * event);
- static GstFlowReturn gst_base_audio_decoder_chain (GstPad * pad,
-     GstBuffer * buf);
- static gboolean gst_base_audio_decoder_src_query (GstPad * pad,
-     GstQuery * query);
- static gboolean gst_base_audio_decoder_sink_query (GstPad * pad,
-     GstQuery * query);
- static const GstQueryType *gst_base_audio_decoder_get_query_types (GstPad *
-     pad);
- static void gst_base_audio_decoder_reset (GstBaseAudioDecoder * dec,
-     gboolean full);
- #define gst_base_audio_decoder_parent_class parent_class
- G_DEFINE_TYPE (GstBaseAudioDecoder, gst_base_audio_decoder, GST_TYPE_ELEMENT);
+ static gboolean gst_audio_decoder_sink_event (GstPad * pad, GstEvent * event);
+ static gboolean gst_audio_decoder_src_event (GstPad * pad, GstEvent * event);
 -static gboolean gst_audio_decoder_sink_setcaps (GstPad * pad, GstCaps * caps);
 -static gboolean gst_audio_decoder_src_setcaps (GstPad * pad, GstCaps * caps);
++static gboolean gst_audio_decoder_sink_setcaps (GstAudioDecoder * dec,
++    GstCaps * caps);
++gboolean gst_audio_decoder_src_setcaps (GstAudioDecoder * dec, GstCaps * caps);
+ static GstFlowReturn gst_audio_decoder_chain (GstPad * pad, GstBuffer * buf);
+ static gboolean gst_audio_decoder_src_query (GstPad * pad, GstQuery * query);
+ static gboolean gst_audio_decoder_sink_query (GstPad * pad, GstQuery * query);
+ static const GstQueryType *gst_audio_decoder_get_query_types (GstPad * pad);
+ static void gst_audio_decoder_reset (GstAudioDecoder * dec, gboolean full);
 -GST_BOILERPLATE (GstAudioDecoder, gst_audio_decoder, GstElement,
 -    GST_TYPE_ELEMENT);
 -
 -static void
 -gst_audio_decoder_base_init (gpointer g_class)
 -{
 -}
++#define gst_audio_decoder_parent_class parent_class
++G_DEFINE_TYPE (GstAudioDecoder, gst_audio_decoder, GST_TYPE_ELEMENT);
  
  static void
- gst_base_audio_decoder_class_init (GstBaseAudioDecoderClass * klass)
+ gst_audio_decoder_class_init (GstAudioDecoderClass * klass)
  {
    GObjectClass *gobject_class;
    GstElementClass *element_class;
  }
  
  static void
- gst_base_audio_decoder_init (GstBaseAudioDecoder * dec)
 -gst_audio_decoder_init (GstAudioDecoder * dec, GstAudioDecoderClass * klass)
++gst_audio_decoder_init (GstAudioDecoder * dec)
  {
-   GstBaseAudioDecoderClass *klass = GST_BASE_AUDIO_DECODER_GET_CLASS (dec);
++  GstAudioDecoderClass *klass = GST_AUDIO_DECODER_GET_CLASS (dec);
    GstPadTemplate *pad_template;
  
-   GST_DEBUG_OBJECT (dec, "gst_base_audio_decoder_init");
+   GST_DEBUG_OBJECT (dec, "gst_audio_decoder_init");
  
-   dec->priv = GST_BASE_AUDIO_DECODER_GET_PRIVATE (dec);
+   dec->priv = GST_AUDIO_DECODER_GET_PRIVATE (dec);
  
    /* Setup sink pad */
    pad_template =
  
    dec->sinkpad = gst_pad_new_from_template (pad_template, "sink");
    gst_pad_set_event_function (dec->sinkpad,
-       GST_DEBUG_FUNCPTR (gst_base_audio_decoder_sink_event));
+       GST_DEBUG_FUNCPTR (gst_audio_decoder_sink_event));
 -  gst_pad_set_setcaps_function (dec->sinkpad,
 -      GST_DEBUG_FUNCPTR (gst_audio_decoder_sink_setcaps));
    gst_pad_set_chain_function (dec->sinkpad,
-       GST_DEBUG_FUNCPTR (gst_base_audio_decoder_chain));
+       GST_DEBUG_FUNCPTR (gst_audio_decoder_chain));
    gst_pad_set_query_function (dec->sinkpad,
-       GST_DEBUG_FUNCPTR (gst_base_audio_decoder_sink_query));
+       GST_DEBUG_FUNCPTR (gst_audio_decoder_sink_query));
    gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad);
    GST_DEBUG_OBJECT (dec, "sinkpad created");
  
    g_return_if_fail (pad_template != NULL);
  
    dec->srcpad = gst_pad_new_from_template (pad_template, "src");
 -  gst_pad_set_setcaps_function (dec->srcpad,
 -      GST_DEBUG_FUNCPTR (gst_audio_decoder_src_setcaps));
    gst_pad_set_event_function (dec->srcpad,
-       GST_DEBUG_FUNCPTR (gst_base_audio_decoder_src_event));
+       GST_DEBUG_FUNCPTR (gst_audio_decoder_src_event));
    gst_pad_set_query_function (dec->srcpad,
-       GST_DEBUG_FUNCPTR (gst_base_audio_decoder_src_query));
+       GST_DEBUG_FUNCPTR (gst_audio_decoder_src_query));
    gst_pad_set_query_type_function (dec->srcpad,
-       GST_DEBUG_FUNCPTR (gst_base_audio_decoder_get_query_types));
+       GST_DEBUG_FUNCPTR (gst_audio_decoder_get_query_types));
    gst_pad_use_fixed_caps (dec->srcpad);
    gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);
    GST_DEBUG_OBJECT (dec, "srcpad created");
@@@ -401,9 -406,9 +398,9 @@@ gst_audio_decoder_reset (GstAudioDecode
      dec->priv->samples_out = 0;
      dec->priv->agg = -1;
      dec->priv->error_count = 0;
-     gst_base_audio_decoder_clear_queues (dec);
+     gst_audio_decoder_clear_queues (dec);
  
 -    gst_audio_info_clear (&dec->priv->ctx.info);
 +    gst_audio_info_init (&dec->priv->ctx.info);
      memset (&dec->priv->ctx, 0, sizeof (dec->priv->ctx));
  
      if (dec->priv->taglist) {
@@@ -450,9 -455,10 +447,9 @@@ gst_audio_decoder_finalize (GObject * o
  
  /* automagically perform sanity checking of src caps;
   * also extracts output data format */
 -static gboolean
 -gst_audio_decoder_src_setcaps (GstPad * pad, GstCaps * caps)
 +gboolean
- gst_base_audio_decoder_src_setcaps (GstBaseAudioDecoder * dec, GstCaps * caps)
++gst_audio_decoder_src_setcaps (GstAudioDecoder * dec, GstCaps * caps)
  {
 -  GstAudioDecoder *dec;
    gboolean res = TRUE;
    guint old_rate;
  
@@@ -487,12 -495,14 +484,12 @@@ refuse_caps
  }
  
  static gboolean
- gst_base_audio_decoder_sink_setcaps (GstBaseAudioDecoder * dec, GstCaps * caps)
 -gst_audio_decoder_sink_setcaps (GstPad * pad, GstCaps * caps)
++gst_audio_decoder_sink_setcaps (GstAudioDecoder * dec, GstCaps * caps)
  {
-   GstBaseAudioDecoderClass *klass;
 -  GstAudioDecoder *dec;
+   GstAudioDecoderClass *klass;
    gboolean res = TRUE;
  
-   klass = GST_BASE_AUDIO_DECODER_GET_CLASS (dec);
 -  dec = GST_AUDIO_DECODER (gst_pad_get_parent (pad));
+   klass = GST_AUDIO_DECODER_GET_CLASS (dec);
  
    GST_DEBUG_OBJECT (dec, "caps: %" GST_PTR_FORMAT, caps);
  
@@@ -670,20 -683,19 +667,20 @@@ again
  }
  
  GstFlowReturn
- gst_base_audio_decoder_finish_frame (GstBaseAudioDecoder * dec, GstBuffer * buf,
+ gst_audio_decoder_finish_frame (GstAudioDecoder * dec, GstBuffer * buf,
      gint frames)
  {
-   GstBaseAudioDecoderPrivate *priv;
-   GstBaseAudioDecoderContext *ctx;
+   GstAudioDecoderPrivate *priv;
+   GstAudioDecoderContext *ctx;
    gint samples = 0;
    GstClockTime ts, next_ts;
 +  gsize size;
  
    /* subclass should know what it is producing by now */
 -  g_return_val_if_fail (buf == NULL || GST_PAD_CAPS (dec->srcpad) != NULL,
 +  g_return_val_if_fail (buf == NULL || gst_pad_has_current_caps (dec->srcpad),
        GST_FLOW_ERROR);
    /* subclass should not hand us no data */
 -  g_return_val_if_fail (buf == NULL || GST_BUFFER_SIZE (buf) > 0,
 +  g_return_val_if_fail (buf == NULL || gst_buffer_get_size (buf) > 0,
        GST_FLOW_ERROR);
    /* no dummy calls please */
    g_return_val_if_fail (frames != 0, GST_FLOW_ERROR);
@@@ -823,17 -836,17 +820,17 @@@ overflow
  }
  
  static GstFlowReturn
- gst_base_audio_decoder_handle_frame (GstBaseAudioDecoder * dec,
-     GstBaseAudioDecoderClass * klass, GstBuffer * buffer)
+ gst_audio_decoder_handle_frame (GstAudioDecoder * dec,
+     GstAudioDecoderClass * klass, GstBuffer * buffer)
  {
    if (G_LIKELY (buffer)) {
 +    gsize size = gst_buffer_get_size (buffer);
      /* keep around for admin */
      GST_LOG_OBJECT (dec, "tracking frame size %d, ts %" GST_TIME_FORMAT,
 -        GST_BUFFER_SIZE (buffer),
 -        GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
 +        size, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
      g_queue_push_tail (&dec->priv->frames, buffer);
      dec->priv->ctx.delay = dec->priv->frames.length;
 -    dec->priv->bytes_in += GST_BUFFER_SIZE (buffer);
 +    dec->priv->bytes_in += size;
    } else {
      GST_LOG_OBJECT (dec, "providing subclass with NULL frame");
    }
@@@ -1305,33 -1326,30 +1299,33 @@@ gst_audio_decoder_sink_eventfunc (GstAu
        }
  
        /* finish current segment */
-       gst_base_audio_decoder_drain (dec);
+       gst_audio_decoder_drain (dec);
  
 +#if 0
        if (update) {
          /* time progressed without data, see if we can fill the gap with
           * some concealment data */
          GST_DEBUG_OBJECT (dec,
 -            "segment update: plc %d, do_plc %d, last_stop %" GST_TIME_FORMAT,
 +            "segment update: plc %d, do_plc %d, position %" GST_TIME_FORMAT,
              dec->priv->plc, dec->priv->ctx.do_plc,
 -            GST_TIME_ARGS (dec->segment.last_stop));
 +            GST_TIME_ARGS (dec->segment.position));
          if (dec->priv->plc && dec->priv->ctx.do_plc &&
 -            dec->segment.rate > 0.0 && dec->segment.last_stop < start) {
 +            dec->segment.rate > 0.0 && dec->segment.position < start) {
-           GstBaseAudioDecoderClass *klass;
+           GstAudioDecoderClass *klass;
            GstBuffer *buf;
  
-           klass = GST_BASE_AUDIO_DECODER_GET_CLASS (dec);
+           klass = GST_AUDIO_DECODER_GET_CLASS (dec);
            /* hand subclass empty frame with duration that needs covering */
            buf = gst_buffer_new ();
 -          GST_BUFFER_DURATION (buf) = start - dec->segment.last_stop;
 +          GST_BUFFER_DURATION (buf) = start - dec->segment.position;
            /* best effort, not much error handling */
-           gst_base_audio_decoder_handle_frame (dec, klass, buf);
+           gst_audio_decoder_handle_frame (dec, klass, buf);
          }
 -      } else {
 +      } else
 +#endif
 +      {
          /* prepare for next one */
-         gst_base_audio_decoder_flush (dec, FALSE);
+         gst_audio_decoder_flush (dec, FALSE);
          /* and that's where we time from,
           * in case upstream does not come up with anything better
           * (e.g. upstream BYTE) */
        break;
  
      case GST_EVENT_EOS:
-       gst_base_audio_decoder_drain (dec);
+       gst_audio_decoder_drain (dec);
        break;
  
-       gst_base_audio_decoder_sink_setcaps (dec, caps);
 +    case GST_EVENT_CAPS:
 +    {
 +      GstCaps *caps;
 +
 +      gst_event_parse_caps (event, &caps);
++      gst_audio_decoder_sink_setcaps (dec, caps);
 +      gst_event_unref (event);
 +      handled = TRUE;
 +      break;
 +    }
      default:
        break;
    }
@@@ -1686,14 -1695,15 +1680,14 @@@ gst_audio_decoder_src_query (GstPad * p
  
        gst_query_parse_duration (query, &format, NULL);
        /* try answering TIME by converting from BYTE if subclass allows  */
-       if (format == GST_FORMAT_TIME && gst_base_audio_decoder_do_byte (dec)) {
+       if (format == GST_FORMAT_TIME && gst_audio_decoder_do_byte (dec)) {
          gint64 value;
  
 -        format = GST_FORMAT_BYTES;
 -        if (gst_pad_query_peer_duration (dec->sinkpad, &format, &value)) {
 +        if (gst_pad_query_peer_duration (dec->sinkpad, GST_FORMAT_BYTES,
 +                &value)) {
            GST_LOG_OBJECT (dec, "upstream size %" G_GINT64_FORMAT, value);
 -          format = GST_FORMAT_TIME;
            if (gst_pad_query_convert (dec->sinkpad, GST_FORMAT_BYTES, value,
 -                  &format, &value)) {
 +                  GST_FORMAT_TIME, &value)) {
              gst_query_set_duration (query, GST_FORMAT_TIME, value);
              res = TRUE;
            }
@@@ -224,35 -225,42 +225,42 @@@ struct _GstAudioDecoderClas
    gpointer       _gst_reserved[GST_PADDING_LARGE];
  };
  
- gboolean          gst_base_audio_decoder_src_setcaps  (GstBaseAudioDecoder * dec,
-                                                        GstCaps * caps);
- GstFlowReturn     gst_base_audio_decoder_finish_frame (GstBaseAudioDecoder * dec,
-                                                        GstBuffer * buf, gint frames);
+ GType             gst_audio_decoder_get_type (void);
+ GstFlowReturn     gst_audio_decoder_finish_frame (GstAudioDecoder * dec,
 -                                                       GstBuffer * buf, gint frames);
++                                                  GstBuffer * buf, gint frames);
  
  /* context parameters */
- GstAudioInfo    * gst_base_audio_decoder_get_audio_info (GstBaseAudioDecoder * dec);
+ GstAudioInfo    * gst_audio_decoder_get_audio_info (GstAudioDecoder * dec);
+ void              gst_audio_decoder_set_plc_aware  (GstAudioDecoder * dec,
+                                                     gboolean          plc);
+ gint              gst_audio_decoder_get_plc_aware  (GstAudioDecoder * dec);
+ void              gst_audio_decoder_set_byte_time  (GstAudioDecoder * dec,
+                                                     gboolean          enabled);
  
- void              gst_base_audio_decoder_set_plc_aware (GstBaseAudioDecoder * dec,
-                                                         gboolean plc);
- gint              gst_base_audio_decoder_get_plc_aware (GstBaseAudioDecoder * dec);
+ gint              gst_audio_decoder_get_byte_time  (GstAudioDecoder * dec);
  
- void              gst_base_audio_decoder_set_byte_time (GstBaseAudioDecoder * dec,
-                                                         gboolean enabled);
- gint              gst_base_audio_decoder_get_byte_time (GstBaseAudioDecoder * dec);
+ gint              gst_audio_decoder_get_delay      (GstAudioDecoder * dec);
  
- gint              gst_base_audio_decoder_get_delay (GstBaseAudioDecoder * dec);
+ void              gst_audio_decoder_set_max_errors (GstAudioDecoder * dec,
+                                                    gint               num);
  
- void              gst_base_audio_decoder_set_max_errors (GstBaseAudioDecoder * enc,
-                                                          gint num);
- gint              gst_base_audio_decoder_get_max_errors (GstBaseAudioDecoder * dec);
+ gint              gst_audio_decoder_get_max_errors (GstAudioDecoder * dec);
  
- void              gst_base_audio_decoder_set_latency (GstBaseAudioDecoder * dec,
-                                                       GstClockTime min, GstClockTime max);
- void              gst_base_audio_decoder_get_latency (GstBaseAudioDecoder * dec,
-                                                       GstClockTime * min, GstClockTime * max);
+ void              gst_audio_decoder_set_latency (GstAudioDecoder * dec,
+                                                  GstClockTime      min,
+                                                  GstClockTime      max);
  
- void              gst_base_audio_decoder_get_parse_state (GstBaseAudioDecoder * dec,
-                                                           gboolean * sync, gboolean * eos);
+ void              gst_audio_decoder_get_latency (GstAudioDecoder * dec,
+                                                  GstClockTime    * min,
+                                                  GstClockTime    * max);
+ void              gst_audio_decoder_get_parse_state (GstAudioDecoder * dec,
+                                                      gboolean        * sync,
+                                                      gboolean        * eos);
  
  
  /* object properties */
  #  include "config.h"
  #endif
  
- #include "gstbaseaudioencoder.h"
 -#define GST_USE_UNSTABLE_API
+ #include "gstaudioencoder.h"
  #include <gst/base/gstadapter.h>
  #include <gst/audio/audio.h>
  
@@@ -271,43 -271,37 +270,37 @@@ gst_audio_encoder_get_type (void
        NULL                      /* interface_data */
      };
  
-     base_audio_encoder_type = g_type_register_static (GST_TYPE_ELEMENT,
-         "GstBaseAudioEncoder", &base_audio_encoder_info, G_TYPE_FLAG_ABSTRACT);
+     audio_encoder_type = g_type_register_static (GST_TYPE_ELEMENT,
+         "GstAudioEncoder", &audio_encoder_info, G_TYPE_FLAG_ABSTRACT);
  
-     g_type_add_interface_static (base_audio_encoder_type, GST_TYPE_PRESET,
+     g_type_add_interface_static (audio_encoder_type, GST_TYPE_PRESET,
          &preset_interface_info);
    }
-   return base_audio_encoder_type;
+   return audio_encoder_type;
  }
  
- static void gst_base_audio_encoder_finalize (GObject * object);
- static void gst_base_audio_encoder_reset (GstBaseAudioEncoder * enc,
-     gboolean full);
+ static void gst_audio_encoder_finalize (GObject * object);
+ static void gst_audio_encoder_reset (GstAudioEncoder * enc, gboolean full);
  
- static void gst_base_audio_encoder_set_property (GObject * object,
+ static void gst_audio_encoder_set_property (GObject * object,
      guint prop_id, const GValue * value, GParamSpec * pspec);
- static void gst_base_audio_encoder_get_property (GObject * object,
+ static void gst_audio_encoder_get_property (GObject * object,
      guint prop_id, GValue * value, GParamSpec * pspec);
  
- static gboolean gst_base_audio_encoder_sink_activate_push (GstPad * pad,
+ static gboolean gst_audio_encoder_sink_activate_push (GstPad * pad,
      gboolean active);
  
- static gboolean gst_base_audio_encoder_sink_event (GstPad * pad,
-     GstEvent * event);
- static GstFlowReturn gst_base_audio_encoder_chain (GstPad * pad,
-     GstBuffer * buffer);
- static gboolean gst_base_audio_encoder_src_query (GstPad * pad,
-     GstQuery * query);
- static gboolean gst_base_audio_encoder_sink_query (GstPad * pad,
-     GstQuery * query);
- static const GstQueryType *gst_base_audio_encoder_get_query_types (GstPad *
-     pad);
- static GstCaps *gst_base_audio_encoder_sink_getcaps (GstPad * pad,
-     GstCaps * filter);
+ static gboolean gst_audio_encoder_sink_event (GstPad * pad, GstEvent * event);
 -static gboolean gst_audio_encoder_sink_setcaps (GstPad * pad, GstCaps * caps);
++static gboolean gst_audio_encoder_sink_setcaps (GstAudioEncoder * enc,
++    GstCaps * caps);
+ static GstFlowReturn gst_audio_encoder_chain (GstPad * pad, GstBuffer * buffer);
+ static gboolean gst_audio_encoder_src_query (GstPad * pad, GstQuery * query);
+ static gboolean gst_audio_encoder_sink_query (GstPad * pad, GstQuery * query);
+ static const GstQueryType *gst_audio_encoder_get_query_types (GstPad * pad);
 -static GstCaps *gst_audio_encoder_sink_getcaps (GstPad * pad);
 -
++static GstCaps *gst_audio_encoder_sink_getcaps (GstPad * pad, GstCaps * filter);
  
  static void
- gst_base_audio_encoder_class_init (GstBaseAudioEncoderClass * klass)
+ gst_audio_encoder_class_init (GstAudioEncoderClass * klass)
  {
    GObjectClass *gobject_class;
  
@@@ -360,15 -353,17 +352,15 @@@ gst_audio_encoder_init (GstAudioEncode
    g_return_if_fail (pad_template != NULL);
    enc->sinkpad = gst_pad_new_from_template (pad_template, "sink");
    gst_pad_set_event_function (enc->sinkpad,
-       GST_DEBUG_FUNCPTR (gst_base_audio_encoder_sink_event));
+       GST_DEBUG_FUNCPTR (gst_audio_encoder_sink_event));
 -  gst_pad_set_setcaps_function (enc->sinkpad,
 -      GST_DEBUG_FUNCPTR (gst_audio_encoder_sink_setcaps));
    gst_pad_set_getcaps_function (enc->sinkpad,
-       GST_DEBUG_FUNCPTR (gst_base_audio_encoder_sink_getcaps));
+       GST_DEBUG_FUNCPTR (gst_audio_encoder_sink_getcaps));
    gst_pad_set_query_function (enc->sinkpad,
-       GST_DEBUG_FUNCPTR (gst_base_audio_encoder_sink_query));
+       GST_DEBUG_FUNCPTR (gst_audio_encoder_sink_query));
    gst_pad_set_chain_function (enc->sinkpad,
-       GST_DEBUG_FUNCPTR (gst_base_audio_encoder_chain));
+       GST_DEBUG_FUNCPTR (gst_audio_encoder_chain));
    gst_pad_set_activatepush_function (enc->sinkpad,
-       GST_DEBUG_FUNCPTR (gst_base_audio_encoder_sink_activate_push));
+       GST_DEBUG_FUNCPTR (gst_audio_encoder_sink_activate_push));
    gst_element_add_pad (GST_ELEMENT (enc), enc->sinkpad);
  
    GST_DEBUG_OBJECT (enc, "sinkpad created");
@@@ -776,16 -764,15 +768,16 @@@ gst_audio_encoder_set_base_gp (GstAudio
  }
  
  static GstFlowReturn
- gst_base_audio_encoder_chain (GstPad * pad, GstBuffer * buffer)
+ gst_audio_encoder_chain (GstPad * pad, GstBuffer * buffer)
  {
-   GstBaseAudioEncoder *enc;
-   GstBaseAudioEncoderPrivate *priv;
-   GstBaseAudioEncoderContext *ctx;
+   GstAudioEncoder *enc;
+   GstAudioEncoderPrivate *priv;
+   GstAudioEncoderContext *ctx;
    GstFlowReturn ret = GST_FLOW_OK;
    gboolean discont;
 +  gsize size;
  
-   enc = GST_BASE_AUDIO_ENCODER (GST_OBJECT_PARENT (pad));
+   enc = GST_AUDIO_ENCODER (GST_OBJECT_PARENT (pad));
  
    priv = enc->priv;
    ctx = &enc->priv->ctx;
@@@ -962,15 -946,17 +954,15 @@@ audio_info_is_equal (GstAudioInfo * fro
  }
  
  static gboolean
- gst_base_audio_encoder_sink_setcaps (GstBaseAudioEncoder * enc, GstCaps * caps)
 -gst_audio_encoder_sink_setcaps (GstPad * pad, GstCaps * caps)
++gst_audio_encoder_sink_setcaps (GstAudioEncoder * enc, GstCaps * caps)
  {
-   GstBaseAudioEncoderClass *klass;
-   GstBaseAudioEncoderContext *ctx;
 -  GstAudioEncoder *enc;
+   GstAudioEncoderClass *klass;
+   GstAudioEncoderContext *ctx;
 -  GstAudioInfo *state, *old_state;
 +  GstAudioInfo state;
    gboolean res = TRUE, changed = FALSE;
    guint old_rate;
  
-   klass = GST_BASE_AUDIO_ENCODER_GET_CLASS (enc);
 -  enc = GST_AUDIO_ENCODER (GST_PAD_PARENT (pad));
+   klass = GST_AUDIO_ENCODER_GET_CLASS (enc);
  
    /* subclass must do something here ... */
    g_return_val_if_fail (klass->set_format != NULL, FALSE);
@@@ -1111,20 -1100,20 +1103,20 @@@ done
  }
  
  static GstCaps *
- gst_base_audio_encoder_sink_getcaps (GstPad * pad, GstCaps * filter)
 -gst_audio_encoder_sink_getcaps (GstPad * pad)
++gst_audio_encoder_sink_getcaps (GstPad * pad, GstCaps * filter)
  {
-   GstBaseAudioEncoder *enc;
-   GstBaseAudioEncoderClass *klass;
+   GstAudioEncoder *enc;
+   GstAudioEncoderClass *klass;
    GstCaps *caps;
  
-   enc = GST_BASE_AUDIO_ENCODER (gst_pad_get_parent (pad));
-   klass = GST_BASE_AUDIO_ENCODER_GET_CLASS (enc);
+   enc = GST_AUDIO_ENCODER (gst_pad_get_parent (pad));
+   klass = GST_AUDIO_ENCODER_GET_CLASS (enc);
    g_assert (pad == enc->sinkpad);
  
    if (klass->getcaps)
 -    caps = klass->getcaps (enc);
 +    caps = klass->getcaps (enc, filter);
    else
-     caps = gst_base_audio_encoder_proxy_getcaps (enc, NULL);
+     caps = gst_audio_encoder_proxy_getcaps (enc, NULL);
    gst_object_unref (enc);
  
    GST_LOG_OBJECT (enc, "returning caps %" GST_PTR_FORMAT, caps);
  }
  
  static gboolean
- gst_base_audio_encoder_sink_eventfunc (GstBaseAudioEncoder * enc,
-     GstEvent * event)
+ gst_audio_encoder_sink_eventfunc (GstAudioEncoder * enc, GstEvent * event)
  {
-   GstBaseAudioEncoderClass *klass;
+   GstAudioEncoderClass *klass;
    gboolean handled = FALSE;
  
-   klass = GST_BASE_AUDIO_ENCODER_GET_CLASS (enc);
+   klass = GST_AUDIO_ENCODER_GET_CLASS (enc);
  
    switch (GST_EVENT_TYPE (event)) {
 -    case GST_EVENT_NEWSEGMENT:
 +    case GST_EVENT_SEGMENT:
      {
 -      GstFormat format;
 -      gdouble rate, arate;
 -      gint64 start, stop, time;
 -      gboolean update;
 -
 -      gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
 -          &start, &stop, &time);
 -
 -      if (format == GST_FORMAT_TIME) {
 -        GST_DEBUG_OBJECT (enc, "received TIME NEW_SEGMENT %" GST_TIME_FORMAT
 -            " -- %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT
 -            ", rate %g, applied_rate %g",
 -            GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time),
 -            rate, arate);
 +      GstSegment seg;
 +
 +      gst_event_copy_segment (event, &seg);
 +
 +      if (seg.format == GST_FORMAT_TIME) {
 +        GST_DEBUG_OBJECT (enc, "received TIME SEGMENT %" GST_PTR_FORMAT, &seg);
        } else {
 -        GST_DEBUG_OBJECT (enc, "received NEW_SEGMENT %" G_GINT64_FORMAT
 -            " -- %" G_GINT64_FORMAT ", time %" G_GINT64_FORMAT
 -            ", rate %g, applied_rate %g", start, stop, time, rate, arate);
 +        GST_DEBUG_OBJECT (enc, "received SEGMENT %" GST_PTR_FORMAT, &seg);
          GST_DEBUG_OBJECT (enc, "unsupported format; ignoring");
          break;
        }
  
        /* finish current segment */
-       gst_base_audio_encoder_drain (enc);
+       gst_audio_encoder_drain (enc);
        /* reset partially for new segment */
-       gst_base_audio_encoder_reset (enc, FALSE);
+       gst_audio_encoder_reset (enc, FALSE);
        /* and follow along with segment */
 -      gst_segment_set_newsegment_full (&enc->segment, update, rate, arate,
 -          format, start, stop, time);
 +      enc->segment = seg;
        break;
      }
  
        break;
  
      case GST_EVENT_EOS:
-       gst_base_audio_encoder_drain (enc);
+       gst_audio_encoder_drain (enc);
        break;
  
-       gst_base_audio_encoder_sink_setcaps (enc, caps);
 +    case GST_EVENT_CAPS:
 +    {
 +      GstCaps *caps;
 +
 +      gst_event_parse_caps (event, &caps);
++      gst_audio_encoder_sink_setcaps (enc, caps);
 +      gst_event_unref (event);
 +      handled = TRUE;
 +      break;
 +    }
 +
      default:
        break;
    }
index 0000000,a8ff018..1a8c5af
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,243 +1,243 @@@
 -  GstCaps *     (*getcaps)            (GstAudioEncoder *enc);
+ /* GStreamer
+  * Copyright (C) 2011 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>.
+  * Copyright (C) 2011 Nokia Corporation. All rights reserved.
+  *   Contact: Stefan Kost <stefan.kost@nokia.com>
+  *
+  * 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_AUDIO_ENCODER_H__
+ #define __GST_AUDIO_ENCODER_H__
+ #ifndef GST_USE_UNSTABLE_API
+ #warning "GstAudioEncoder is unstable API and may change in future."
+ #warning "You can define GST_USE_UNSTABLE_API to avoid this warning."
+ #endif
+ #include <gst/gst.h>
+ #include <gst/audio/audio.h>
+ G_BEGIN_DECLS
+ #define GST_TYPE_AUDIO_ENCODER                   (gst_audio_encoder_get_type())
+ #define GST_AUDIO_ENCODER(obj)                   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_ENCODER,GstAudioEncoder))
+ #define GST_AUDIO_ENCODER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIO_ENCODER,GstAudioEncoderClass))
+ #define GST_AUDIO_ENCODER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_AUDIO_ENCODER,GstAudioEncoderClass))
+ #define GST_IS_AUDIO_ENCODER(obj)        (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_ENCODER))
+ #define GST_IS_AUDIO_ENCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIO_ENCODER))
+ #define GST_AUDIO_ENCODER_CAST(obj)   ((GstAudioEncoder *)(obj))
+ /**
+  * GST_AUDIO_ENCODER_SINK_NAME:
+  *
+  * the name of the templates for the sink pad
+  *
+  * Since: 0.10.36
+  */
+ #define GST_AUDIO_ENCODER_SINK_NAME   "sink"
+ /**
+  * GST_AUDIO_ENCODER_SRC_NAME:
+  *
+  * the name of the templates for the source pad
+  *
+  * Since: 0.10.36
+  */
+ #define GST_AUDIO_ENCODER_SRC_NAME            "src"
+ /**
+  * GST_AUDIO_ENCODER_SRC_PAD:
+  * @obj: base parse instance
+  *
+  * Gives the pointer to the source #GstPad object of the element.
+  *
+  * Since: 0.10.36
+  */
+ #define GST_AUDIO_ENCODER_SRC_PAD(obj)        (GST_AUDIO_ENCODER_CAST (obj)->srcpad)
+ /**
+  * GST_AUDIO_ENCODER_SINK_PAD:
+  * @obj: base parse instance
+  *
+  * Gives the pointer to the sink #GstPad object of the element.
+  *
+  * Since: 0.10.36
+  */
+ #define GST_AUDIO_ENCODER_SINK_PAD(obj)       (GST_AUDIO_ENCODER_CAST (obj)->sinkpad)
+ /**
+  * GST_AUDIO_ENCODER_SEGMENT:
+  * @obj: base parse instance
+  *
+  * Gives the segment of the element.
+  *
+  * Since: 0.10.36
+  */
+ #define GST_AUDIO_ENCODER_SEGMENT(obj)     (GST_AUDIO_ENCODER_CAST (obj)->segment)
+ typedef struct _GstAudioEncoder GstAudioEncoder;
+ typedef struct _GstAudioEncoderClass GstAudioEncoderClass;
+ typedef struct _GstAudioEncoderPrivate GstAudioEncoderPrivate;
+ /**
+  * GstAudioEncoder:
+  *
+  * The opaque #GstAudioEncoder data structure.
+  *
+  * Since: 0.10.36
+  */
+ struct _GstAudioEncoder {
+   GstElement     element;
+   /*< protected >*/
+   /* source and sink pads */
+   GstPad         *sinkpad;
+   GstPad         *srcpad;
+   /* MT-protected (with STREAM_LOCK) */
+   GstSegment      segment;
+   /*< private >*/
+   GstAudioEncoderPrivate *priv;
+   gpointer       _gst_reserved[GST_PADDING_LARGE];
+ };
+ /**
+  * GstAudioEncoderClass:
+  * @element_class:  The parent class structure
+  * @start:          Optional.
+  *                  Called when the element starts processing.
+  *                  Allows opening external resources.
+  * @stop:           Optional.
+  *                  Called when the element stops processing.
+  *                  Allows closing external resources.
+  * @set_format:     Notifies subclass of incoming data format.
+  *                  GstAudioInfo contains the format according to provided caps.
+  * @handle_frame:   Provides input samples (or NULL to clear any remaining data)
+  *                  according to directions as configured by the subclass
+  *                  using the API.  Input data ref management is performed
+  *                  by base class, subclass should not care or intervene.
+  * @flush:          Optional.
+  *                  Instructs subclass to clear any codec caches and discard
+  *                  any pending samples and not yet returned encoded data.
+  * @event:          Optional.
+  *                  Event handler on the sink pad. This function should return
+  *                  TRUE if the event was handled and should be discarded
+  *                  (i.e. not unref'ed).
+  * @pre_push:       Optional.
+  *                  Called just prior to pushing (encoded data) buffer downstream.
+  *                  Subclass has full discretionary access to buffer,
+  *                  and a not OK flow return will abort downstream pushing.
+  * @getcaps:        Optional.
+  *                  Allows for a custom sink getcaps implementation (e.g.
+  *                  for multichannel input specification).  If not implemented,
+  *                  default returns gst_audio_encoder_proxy_getcaps
+  *                  applied to sink template caps.
+  *
+  * Subclasses can override any of the available virtual methods or not, as
+  * needed. At minimum @set_format and @handle_frame needs to be overridden.
+  *
+  * Since: 0.10.36
+  */
+ struct _GstAudioEncoderClass {
+   GstElementClass element_class;
+   /*< public >*/
+   /* virtual methods for subclasses */
+   gboolean      (*start)              (GstAudioEncoder *enc);
+   gboolean      (*stop)               (GstAudioEncoder *enc);
+   gboolean      (*set_format)         (GstAudioEncoder *enc,
+                                        GstAudioInfo        *info);
+   GstFlowReturn (*handle_frame)       (GstAudioEncoder *enc,
+                                        GstBuffer *buffer);
+   void          (*flush)              (GstAudioEncoder *enc);
+   GstFlowReturn (*pre_push)           (GstAudioEncoder *enc,
+                                        GstBuffer **buffer);
+   gboolean      (*event)              (GstAudioEncoder *enc,
+                                        GstEvent *event);
++  GstCaps *     (*getcaps)            (GstAudioEncoder *enc, GstCaps *filter);
+   /*< private >*/
+   gpointer       _gst_reserved[GST_PADDING_LARGE];
+ };
+ GType           gst_audio_encoder_get_type         (void);
+ GstFlowReturn   gst_audio_encoder_finish_frame (GstAudioEncoder * enc,
+                                                 GstBuffer       * buffer,
+                                                 gint              samples);
+ GstCaps *       gst_audio_encoder_proxy_getcaps (GstAudioEncoder * enc,
+                                                  GstCaps         * caps);
+ /* context parameters */
+ GstAudioInfo  * gst_audio_encoder_get_audio_info (GstAudioEncoder * enc);
+ gint            gst_audio_encoder_get_frame_samples (GstAudioEncoder * enc);
+ void            gst_audio_encoder_set_frame_samples (GstAudioEncoder * enc, gint num);
+ gint            gst_audio_encoder_get_frame_max (GstAudioEncoder * enc);
+ void            gst_audio_encoder_set_frame_max (GstAudioEncoder * enc, gint num);
+ gint            gst_audio_encoder_get_lookahead (GstAudioEncoder * enc);
+ void            gst_audio_encoder_set_lookahead (GstAudioEncoder * enc, gint num);
+ void            gst_audio_encoder_get_latency (GstAudioEncoder * enc,
+                                                GstClockTime    * min,
+                                                GstClockTime    * max);
+ void            gst_audio_encoder_set_latency (GstAudioEncoder * enc,
+                                                GstClockTime      min,
+                                                GstClockTime      max);
+ /* object properties */
+ void            gst_audio_encoder_set_mark_granule (GstAudioEncoder * enc,
+                                                     gboolean enabled);
+ gboolean        gst_audio_encoder_get_mark_granule (GstAudioEncoder * enc);
+ void            gst_audio_encoder_set_perfect_timestamp (GstAudioEncoder * enc,
+                                                          gboolean          enabled);
+ gboolean        gst_audio_encoder_get_perfect_timestamp (GstAudioEncoder * enc);
+ void            gst_audio_encoder_set_hard_resync (GstAudioEncoder * enc,
+                                                    gboolean          enabled);
+ gboolean        gst_audio_encoder_get_hard_resync (GstAudioEncoder * enc);
+ void            gst_audio_encoder_set_tolerance (GstAudioEncoder * enc,
+                                                  gint64            tolerance);
+ gint64          gst_audio_encoder_get_tolerance (GstAudioEncoder * enc);
+ G_END_DECLS
+ #endif /* __GST_AUDIO_ENCODER_H__ */
index 0000000,0000000..9834715
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,262 @@@
++/* GStreamer
++ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
++ * Copyright (C) <2003> David Schleef <ds@schleef.org>
++ *
++ * 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.
++ */
++
++/*
++ * This file was (probably) generated from
++ * $Id$
++ * and
++ * $Id$
++ */
++
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include <gst/gst.h>
++#include <gst/audio/audio.h>
++#include <gst/audio/gstaudiofilter.h>
++#include <string.h>
++
++GST_DEBUG_CATEGORY_STATIC (audio_filter_template_debug);
++#define GST_CAT_DEFAULT audio_filter_template_debug
++
++static const GstElementDetails audio_filter_template_details =
++GST_ELEMENT_DETAILS ("Audio filter template",
++    "Filter/Effect/Audio",
++    "Filters audio",
++    "David Schleef <ds@schleef.org>");
++
++typedef struct _GstAudioFilterTemplate GstAudioFilterTemplate;
++typedef struct _GstAudioFilterTemplateClass GstAudioFilterTemplateClass;
++
++#define GST_TYPE_AUDIO_FILTER_TEMPLATE \
++  (gst_audio_filter_template_get_type())
++#define GST_AUDIO_FILTER_TEMPLATE(obj) \
++  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_FILTER_TEMPLATE,GstAudioFilterTemplate))
++#define GST_AUDIO_FILTER_TEMPLATE_CLASS(klass) \
++  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIO_FILTER_TEMPLATE,GstAudioFilterTemplateClass))
++#define GST_IS_AUDIO_FILTER_TEMPLATE(obj) \
++  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_FILTER_TEMPLATE))
++#define GST_IS_AUDIO_FILTER_TEMPLATE_CLASS(klass) \
++  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIO_FILTER_TEMPLATE))
++
++struct _GstAudioFilterTemplate
++{
++  GstAudioFilter audiofilter;
++};
++
++struct _GstAudioFilterTemplateClass
++{
++  GstAudioFilterClass parent_class;
++};
++
++
++enum
++{
++  /* FILL ME */
++  LAST_SIGNAL
++};
++
++enum
++{
++  ARG_0
++      /* FILL ME */
++};
++
++GST_BOILERPLATE (GstAudioFilterTemplate, gst_audio_filter_template,
++    GstAudioFilter, GST_TYPE_AUDIO_FILTER);
++
++static void gst_audio_filter_template_set_property (GObject * object,
++    guint prop_id, const GValue * value, GParamSpec * pspec);
++static void gst_audio_filter_template_get_property (GObject * object,
++    guint prop_id, GValue * value, GParamSpec * pspec);
++
++static gboolean gst_audio_filter_template_setup (GstAudioFilter * filter,
++    GstRingBufferSpec * spec);
++static GstFlowReturn gst_audio_filter_template_filter (GstBaseTransform * bt,
++    GstBuffer * outbuf, GstBuffer * inbuf);
++static GstFlowReturn
++gst_audio_filter_template_filter_inplace (GstBaseTransform * base_transform,
++    GstBuffer * buf);
++
++#define ALLOWED_CAPS_STRING \
++    GST_AUDIO_INT_STANDARD_PAD_TEMPLATE_CAPS
++
++static void
++gst_audio_filter_template_base_init (gpointer g_class)
++{
++  GstAudioFilterTemplateClass *klass = (GstAudioFilterTemplateClass *) g_class;
++  GstAudioFilterClass *audiofilter_class = GST_AUDIO_FILTER_CLASS (g_class);
++  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
++  GstCaps *caps;
++
++  gst_element_class_set_details (element_class, &audio_filter_template_details);
++
++  caps = gst_caps_from_string (ALLOWED_CAPS_STRING);
++  gst_audio_filter_class_add_pad_templates (audiofilter_class, caps);
++  gst_caps_unref (caps);
++}
++
++static void
++gst_audio_filter_template_class_init (GstAudioFilterTemplateClass * klass)
++{
++  GObjectClass *gobject_class;
++  GstBaseTransformClass *btrans_class;
++  GstAudioFilterClass *audio_filter_class;
++
++  gobject_class = (GObjectClass *) klass;
++  btrans_class = (GstBaseTransformClass *) klass;
++  audio_filter_class = (GstAudioFilterClass *) klass;
++
++#if 0
++  g_object_class_install_property (gobject_class, ARG_METHOD,
++      g_param_spec_enum ("method", "method", "method",
++          GST_TYPE_AUDIOTEMPLATE_METHOD, GST_AUDIOTEMPLATE_METHOD_1,
++          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++#endif
++
++  gobject_class->set_property = gst_audio_filter_template_set_property;
++  gobject_class->get_property = gst_audio_filter_template_get_property;
++
++  /* this function will be called whenever the format changes */
++  audio_filter_class->setup = gst_audio_filter_template_setup;
++
++  /* here you set up functions to process data (either in place, or from
++   * one input buffer to another output buffer); only one is required */
++  btrans_class->transform = gst_audio_filter_template_filter;
++  btrans_class->transform_ip = gst_audio_filter_template_filter_inplace;
++}
++
++static void
++gst_audio_filter_template_init (GstAudioFilterTemplate * audio_filter_template,
++    GstAudioFilterTemplateClass * g_class)
++{
++  GST_DEBUG ("init");
++
++  /* do stuff if you need to */
++}
++
++static void
++gst_audio_filter_template_set_property (GObject * object, guint prop_id,
++    const GValue * value, GParamSpec * pspec)
++{
++  GstAudioFilterTemplate *filter;
++
++  filter = GST_AUDIO_FILTER_TEMPLATE (object);
++
++  GST_DEBUG ("set  property %u", prop_id);
++
++  GST_OBJECT_LOCK (filter);
++  switch (prop_id) {
++    default:
++      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++      break;
++  }
++  GST_OBJECT_UNLOCK (filter);
++}
++
++static void
++gst_audio_filter_template_get_property (GObject * object, guint prop_id,
++    GValue * value, GParamSpec * pspec)
++{
++  GstAudioFilterTemplate *filter;
++
++  filter = GST_AUDIO_FILTER_TEMPLATE (object);
++
++  GST_DEBUG ("get  property %u", prop_id);
++
++  GST_OBJECT_LOCK (filter);
++  switch (prop_id) {
++    default:
++      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++      break;
++  }
++  GST_OBJECT_UNLOCK (filter);
++}
++
++static gboolean
++gst_audio_filter_template_setup (GstAudioFilter * filter,
++    GstRingBufferSpec * spec)
++{
++  GstAudioFilterTemplate *audio_filter_template;
++
++  audio_filter_template = GST_AUDIO_FILTER_TEMPLATE (filter);
++
++  /* if any setup needs to be done, do it here */
++
++  return TRUE;                  /* it's all good */
++}
++
++/* You may choose to implement either a copying filter or an
++ * in-place filter (or both).  Implementing only one will give
++ * full functionality, however, implementing both will cause
++ * audiofilter to use the optimal function in every situation,
++ * with a minimum of memory copies. */
++
++static GstFlowReturn
++gst_audio_filter_template_filter (GstBaseTransform * base_transform,
++    GstBuffer * inbuf, GstBuffer * outbuf)
++{
++  GstAudioFilterTemplate *audio_filter_template;
++  GstAudioFilter *audiofilter;
++
++  audiofilter = GST_AUDIO_FILTER (base_transform);
++  audio_filter_template = GST_AUDIO_FILTER_TEMPLATE (base_transform);
++
++  /* do something interesting here.  This simply copies the source
++   * to the destination. */
++
++  memcpy (GST_BUFFER_DATA (outbuf), GST_BUFFER_DATA (inbuf),
++      GST_BUFFER_SIZE (inbuf));
++
++  return GST_FLOW_OK;
++}
++
++static GstFlowReturn
++gst_audio_filter_template_filter_inplace (GstBaseTransform * base_transform,
++    GstBuffer * buf)
++{
++  GstAudioFilterTemplate *audio_filter_template;
++  GstAudioFilter *audiofilter;
++
++  audiofilter = GST_AUDIO_FILTER (base_transform);
++  audio_filter_template = GST_AUDIO_FILTER_TEMPLATE (base_transform);
++
++  /* do something interesting here.  This simply copies the source
++   * to the destination. */
++
++  return GST_FLOW_OK;
++}
++
++static gboolean
++plugin_init (GstPlugin * plugin)
++{
++  GST_DEBUG_CATEGORY_INIT (audio_filter_template_debug, "audiofilterexample",
++      0, "audiofilterexample");
++
++  return gst_element_register (plugin, "audiofilterexample", GST_RANK_NONE,
++      GST_TYPE_AUDIO_FILTER_TEMPLATE);
++}
++
++GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
++    GST_VERSION_MINOR,
++    "gstaudio_filter_template",
++    "Audio filter template",
++    plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
index 0000000,0000000..716df04
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,224 @@@
++/* GStreamer RTSP extension
++ * Copyright (C) 2007 Wim Taymans <wim.taymans@gmail.com>
++ *
++ * gstrtspextension.c: RTSP extension mechanism
++ *
++ * 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.
++ */
++
++/**
++ * SECTION:gstrtspextension
++ * @short_description: Interface for extending RTSP protocols
++ *
++ * <refsect2>
++ * <para>
++ *  This interface is implemented e.g. by the Windows Media Streaming RTSP
++ *  exentension (rtspwms) and the RealMedia RTSP extension (rtspreal).
++ * </para>
++ * </refsect2>
++ *
++ * Last reviewed on 2007-07-25 (0.10.14)
++ */
++
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include "gstrtsp-marshal.h"
++#include "gstrtsp-enumtypes.h"
++#include "gstrtspextension.h"
++
++static void gst_rtsp_extension_iface_init (GstRTSPExtension * iface);
++
++enum
++{
++  SIGNAL_SEND,
++  LAST_SIGNAL
++};
++
++static guint gst_rtsp_extension_signals[LAST_SIGNAL] = { 0 };
++
++GType
++gst_rtsp_extension_get_type (void)
++{
++  static GType gst_rtsp_extension_type = 0;
++
++  if (!gst_rtsp_extension_type) {
++    static const GTypeInfo gst_rtsp_extension_info = {
++      sizeof (GstRTSPExtensionInterface),
++      (GBaseInitFunc) gst_rtsp_extension_iface_init,
++      NULL,
++      NULL,
++      NULL,
++      NULL,
++      0,
++      0,
++      NULL,
++    };
++
++    gst_rtsp_extension_type = g_type_register_static (G_TYPE_INTERFACE,
++        "GstRTSPExtension", &gst_rtsp_extension_info, 0);
++  }
++  return gst_rtsp_extension_type;
++}
++
++static void
++gst_rtsp_extension_iface_init (GstRTSPExtension * iface)
++{
++  static gboolean initialized = FALSE;
++
++  if (!initialized) {
++    gst_rtsp_extension_signals[SIGNAL_SEND] =
++        g_signal_new ("send", G_TYPE_FROM_CLASS (iface),
++        G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPExtensionInterface,
++            send), NULL, NULL, gst_rtsp_marshal_ENUM__POINTER_POINTER,
++        GST_TYPE_RTSP_RESULT, 2, G_TYPE_POINTER, G_TYPE_POINTER);
++    initialized = TRUE;
++  }
++}
++
++gboolean
++gst_rtsp_extension_detect_server (GstRTSPExtension * ext, GstRTSPMessage * resp)
++{
++  GstRTSPExtensionInterface *iface;
++  gboolean res = TRUE;
++
++  iface = GST_RTSP_EXTENSION_GET_IFACE (ext);
++  if (iface->detect_server)
++    res = iface->detect_server (ext, resp);
++
++  return res;
++}
++
++GstRTSPResult
++gst_rtsp_extension_before_send (GstRTSPExtension * ext, GstRTSPMessage * req)
++{
++  GstRTSPExtensionInterface *iface;
++  GstRTSPResult res = GST_RTSP_OK;
++
++  iface = GST_RTSP_EXTENSION_GET_IFACE (ext);
++  if (iface->before_send)
++    res = iface->before_send (ext, req);
++
++  return res;
++}
++
++GstRTSPResult
++gst_rtsp_extension_after_send (GstRTSPExtension * ext, GstRTSPMessage * req,
++    GstRTSPMessage * resp)
++{
++  GstRTSPExtensionInterface *iface;
++  GstRTSPResult res = GST_RTSP_OK;
++
++  iface = GST_RTSP_EXTENSION_GET_IFACE (ext);
++  if (iface->after_send)
++    res = iface->after_send (ext, req, resp);
++
++  return res;
++}
++
++GstRTSPResult
++gst_rtsp_extension_parse_sdp (GstRTSPExtension * ext, GstSDPMessage * sdp,
++    GstStructure * s)
++{
++  GstRTSPExtensionInterface *iface;
++  GstRTSPResult res = GST_RTSP_OK;
++
++  iface = GST_RTSP_EXTENSION_GET_IFACE (ext);
++  if (iface->parse_sdp)
++    res = iface->parse_sdp (ext, sdp, s);
++
++  return res;
++}
++
++GstRTSPResult
++gst_rtsp_extension_setup_media (GstRTSPExtension * ext, GstSDPMedia * media)
++{
++  GstRTSPExtensionInterface *iface;
++  GstRTSPResult res = GST_RTSP_OK;
++
++  iface = GST_RTSP_EXTENSION_GET_IFACE (ext);
++  if (iface->setup_media)
++    res = iface->setup_media (ext, media);
++
++  return res;
++}
++
++gboolean
++gst_rtsp_extension_configure_stream (GstRTSPExtension * ext, GstCaps * caps)
++{
++  GstRTSPExtensionInterface *iface;
++  gboolean res = TRUE;
++
++  iface = GST_RTSP_EXTENSION_GET_IFACE (ext);
++  if (iface->configure_stream)
++    res = iface->configure_stream (ext, caps);
++
++  return res;
++}
++
++GstRTSPResult
++gst_rtsp_extension_get_transports (GstRTSPExtension * ext,
++    GstRTSPLowerTrans protocols, gchar ** transport)
++{
++  GstRTSPExtensionInterface *iface;
++  GstRTSPResult res = GST_RTSP_OK;
++
++  iface = GST_RTSP_EXTENSION_GET_IFACE (ext);
++  if (iface->get_transports)
++    res = iface->get_transports (ext, protocols, transport);
++
++  return res;
++}
++
++GstRTSPResult
++gst_rtsp_extension_stream_select (GstRTSPExtension * ext, GstRTSPUrl * url)
++{
++  GstRTSPExtensionInterface *iface;
++  GstRTSPResult res = GST_RTSP_OK;
++
++  iface = GST_RTSP_EXTENSION_GET_IFACE (ext);
++  if (iface->stream_select)
++    res = iface->stream_select (ext, url);
++
++  return res;
++}
++
++GstRTSPResult
++gst_rtsp_extension_receive_request (GstRTSPExtension * ext,
++    GstRTSPMessage * msg)
++{
++  GstRTSPExtensionInterface *iface;
++  GstRTSPResult res = GST_RTSP_ENOTIMPL;
++
++  iface = GST_RTSP_EXTENSION_GET_IFACE (ext);
++  if (iface->receive_request)
++    res = iface->receive_request (ext, msg);
++
++  return res;
++}
++
++GstRTSPResult
++gst_rtsp_extension_send (GstRTSPExtension * ext, GstRTSPMessage * req,
++    GstRTSPMessage * resp)
++{
++  GstRTSPResult res = GST_RTSP_OK;
++
++  g_signal_emit (ext, gst_rtsp_extension_signals[SIGNAL_SEND], 0,
++      req, resp, &res);
++
++  return res;
++}
index 0000000,0000000..8a73bfa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,61 @@@
++/* GStreamer Audio Process
++ * Copyright (C) 2010 Wim Taymans <wim.taymans@gmail.com>
++ *
++ * gstaudioprocess.h: Audio processing extension
++ *
++ * 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_AUDIO_PROCESS_H__
++#define __GST_AUDIO_PROCESS_H__
++
++#include <gst/gst.h>
++
++G_BEGIN_DECLS
++
++#define GST_TYPE_AUDIO_PROCESS \
++  (gst_audio_process_get_type ())
++#define GST_AUDIO_PROCESS(obj) \
++  (GST_IMPLEMENTS_INTERFACE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_AUDIO_PROCESS, GstAudioProcess))
++#define GST_IS_AUDIO_PROCESS(obj) \
++  (GST_IMPLEMENTS_INTERFACE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_AUDIO_PROCESS))
++#define GST_AUDIO_PROCESS_GET_IFACE(inst) \
++  (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GST_TYPE_AUDIO_PROCESS, GstAudioProcessInterface))
++
++typedef struct _GstAudioProcess GstAudioProcess;
++typedef struct _GstAudioProcessInterface GstAudioProcessInterface;
++
++struct _GstAudioProcessInterface {
++  GTypeInterface parent;
++
++  /* vfunctions */
++  gint          (*activate)       (GstAudioProcess *process, gboolean active);
++  gint          (*process)        (GstAudioProcess *process, gpointer src_in, gpointer sink_in,
++                                   gpointer src_out, guint length);
++
++  /*< private >*/
++  gpointer                 _gst_reserved[GST_PADDING];
++};
++
++GType           gst_audio_process_get_type          (void);
++
++/* invoke vfunction on interface */
++gint    gst_audio_process_process   (GstAudioProcess *ext, gpointer src_in, gpointer sink_in,
++                                     gpointer src_out, guint length);
++
++G_END_DECLS
++
++#endif /* __GST_AUDIO_PROCESS_H__ */
index 0000000,0000000..19e9070
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,216 @@@
++/* GStreamer
++ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
++ *                    2005 Wim Taymans <wim@fluendo.com>
++ *
++ * gstaudioringbuffer.c: simple audio ringbuffer base class
++ *
++ * 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.
++ */
++
++
++#include <string.h>
++
++#include "gstaudioringbuffer.h"
++
++GST_DEBUG_CATEGORY_STATIC (gst_audio_ring_buffer_debug);
++#define GST_CAT_DEFAULT gst_audio_ring_buffer_debug
++
++static void gst_audio_ring_buffer_class_init (GstAudioRingBufferClass * klass);
++static void gst_audio_ring_buffer_init (GstAudioRingBuffer * ringbuffer,
++    GstAudioRingBufferClass * klass);
++static void gst_audio_ring_buffer_dispose (GObject * object);
++static void gst_audio_ring_buffer_finalize (GObject * object);
++
++static GstRingBufferClass *ring_parent_class = NULL;
++
++static gboolean gst_audio_ring_buffer_start (GstRingBuffer * buf);
++static gboolean gst_audio_ring_buffer_pause (GstRingBuffer * buf);
++static gboolean gst_audio_ring_buffer_stop (GstRingBuffer * buf);
++static gboolean gst_audio_ring_buffer_activate (GstRingBuffer * buf,
++    gboolean active);
++
++/* ringbuffer abstract base class */
++GType
++gst_audio_ring_buffer_get_type (void)
++{
++  static GType ringbuffer_type = 0;
++
++  if (!ringbuffer_type) {
++    static const GTypeInfo ringbuffer_info = {
++      sizeof (GstAudioRingBufferClass),
++      NULL,
++      NULL,
++      (GClassInitFunc) gst_audio_ring_buffer_class_init,
++      NULL,
++      NULL,
++      sizeof (GstAudioRingBuffer),
++      0,
++      (GInstanceInitFunc) gst_audio_ring_buffer_init,
++      NULL
++    };
++
++    ringbuffer_type =
++        g_type_register_static (GST_TYPE_RING_BUFFER, "GstAudioSinkRingBuffer",
++        &ringbuffer_info, G_TYPE_FLAG_ABSTRACT);
++
++    GST_DEBUG_CATEGORY_INIT (gst_audio_ring_buffer_debug, "audioringbuffer", 0,
++        "audio ringbuffer");
++  }
++  return ringbuffer_type;
++}
++
++static void
++gst_audio_ring_buffer_class_init (GstAudioRingBufferClass * klass)
++{
++  GObjectClass *gobject_class;
++  GstRingBufferClass *gstringbuffer_class;
++
++  gobject_class = (GObjectClass *) klass;
++  gstringbuffer_class = (GstRingBufferClass *) klass;
++
++  ring_parent_class = g_type_class_peek_parent (klass);
++
++  gobject_class->dispose = gst_audio_ring_buffer_dispose;
++  gobject_class->finalize = gst_audio_ring_buffer_finalize;
++
++  gstringbuffer_class->start = GST_DEBUG_FUNCPTR (gst_audio_ring_buffer_start);
++  gstringbuffer_class->pause = GST_DEBUG_FUNCPTR (gst_audio_ring_buffer_pause);
++  gstringbuffer_class->resume = GST_DEBUG_FUNCPTR (gst_audio_ring_buffer_start);
++  gstringbuffer_class->stop = GST_DEBUG_FUNCPTR (gst_audio_ring_buffer_stop);
++
++  gstringbuffer_class->activate =
++      GST_DEBUG_FUNCPTR (gst_audio_ring_buffer_activate);
++}
++
++static void
++gst_audio_ring_buffer_init (GstAudioRingBuffer * ringbuffer,
++    GstAudioRingBufferClass * g_class)
++{
++}
++
++static void
++gst_audio_ring_buffer_dispose (GObject * object)
++{
++  G_OBJECT_CLASS (ring_parent_class)->dispose (object);
++}
++
++static void
++gst_audio_ring_buffer_finalize (GObject * object)
++{
++  G_OBJECT_CLASS (ring_parent_class)->finalize (object);
++}
++
++static gboolean
++gst_audio_ring_buffer_activate (GstRingBuffer * buf, gboolean active)
++{
++  GstAudioRingBuffer *abuf;
++  gboolean res;
++
++  abuf = GST_AUDIO_RING_BUFFER_CAST (buf);
++
++  GST_OBJECT_UNLOCK (buf);
++  res = gst_ring_buffer_thread_activate (abuf->thread, active);
++  GST_OBJECT_LOCK (buf);
++
++  return res;
++}
++
++gboolean
++gst_audio_ring_buffer_set_thread (GstAudioRingBuffer * buf,
++    GstRingBufferThread * thread)
++{
++  GstRingBufferThread *old;
++
++  g_return_val_if_fail (GST_IS_AUDIO_RING_BUFFER (buf), FALSE);
++
++  old = buf->thread;
++  if (thread)
++    gst_object_ref (thread);
++  buf->thread = thread;
++  if (old)
++    gst_object_unref (old);
++
++  if (thread)
++    gst_ring_buffer_thread_set_ringbuffer (thread, buf);
++
++  return TRUE;
++}
++
++gboolean
++gst_audio_ring_buffer_link (GstAudioRingBuffer * buf1,
++    GstAudioRingBuffer * buf2)
++{
++  buf1->link = buf2;
++  buf2->link = buf1;
++
++  return TRUE;
++}
++
++static gboolean
++gst_audio_ring_buffer_start (GstRingBuffer * buf)
++{
++  GstAudioRingBuffer *abuf;
++
++  abuf = GST_AUDIO_RING_BUFFER_CAST (buf);
++
++  GST_DEBUG_OBJECT (buf, "start, sending signal");
++
++  return gst_ring_buffer_thread_start (abuf->thread);
++}
++
++static gboolean
++gst_audio_ring_buffer_pause (GstRingBuffer * buf)
++{
++  GstAudioRingBuffer *abuf;
++  GstAudioRingBufferClass *cbuf;
++
++  abuf = GST_AUDIO_RING_BUFFER_CAST (buf);
++  cbuf = GST_AUDIO_RING_BUFFER_GET_CLASS (abuf);
++
++  /* unblock any pending writes to the audio device */
++  if (cbuf->reset) {
++    GST_DEBUG_OBJECT (abuf, "reset...");
++    cbuf->reset (abuf);
++    GST_DEBUG_OBJECT (abuf, "reset done");
++  }
++  return TRUE;
++}
++
++static gboolean
++gst_audio_ring_buffer_stop (GstRingBuffer * buf)
++{
++  GstAudioRingBuffer *abuf;
++  GstAudioRingBufferClass *cbuf;
++
++  abuf = GST_AUDIO_RING_BUFFER_CAST (buf);
++  cbuf = GST_AUDIO_RING_BUFFER_GET_CLASS (abuf);
++
++  /* unblock any pending writes to the audio device */
++  if (cbuf->reset) {
++    GST_DEBUG_OBJECT (abuf, "reset...");
++    cbuf->reset (abuf);
++    GST_DEBUG_OBJECT (abuf, "reset done");
++  }
++#if 0
++  if (abuf->running) {
++    GST_DEBUG_OBJECT (sink, "stop, waiting...");
++    GST_AUDIO_RING_BUFFER_WAIT (buf);
++    GST_DEBUG_OBJECT (sink, "stopped");
++  }
++#endif
++
++  return TRUE;
++}
index 0000000,0000000..c643e84
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,98 @@@
++/* GStreamer
++ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
++ *                    2005 Wim Taymans <wim@fluendo.com>
++ *
++ * gstaudioringbuffer.h:
++ *
++ * 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_AUDIO_RING_BUFFER_H__
++#define __GST_AUDIO_RING_BUFFER_H__
++
++#include <gst/gst.h>
++#include <gst/audio/gstringbuffer.h>
++
++G_BEGIN_DECLS
++
++#define GST_TYPE_AUDIO_RING_BUFFER             (gst_audio_ring_buffer_get_type())
++#define GST_AUDIO_RING_BUFFER(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_RING_BUFFER,GstAudioRingBuffer))
++#define GST_AUDIO_RING_BUFFER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIO_RING_BUFFER,GstAudioRingBufferClass))
++#define GST_AUDIO_RING_BUFFER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_AUDIO_RING_BUFFER,GstAudioRingBufferClass))
++#define GST_IS_AUDIO_RING_BUFFER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_RING_BUFFER))
++#define GST_IS_AUDIO_RING_BUFFER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIO_RING_BUFFER))
++#define GST_AUDIO_RING_BUFFER_CAST(obj)        ((GstAudioRingBuffer *)obj)
++
++typedef struct _GstAudioRingBuffer GstAudioRingBuffer;
++typedef struct _GstAudioRingBufferClass GstAudioRingBufferClass;
++
++#include <gst/audio/gstringbufferthread.h>
++
++typedef enum {
++  GST_AUDIO_RING_BUFFER_MODE_UNKNOWN,
++  GST_AUDIO_RING_BUFFER_MODE_PLAYBACK,
++  GST_AUDIO_RING_BUFFER_MODE_CAPTURE
++} GstAudioRingBufferMode;
++/**
++ * GstAudioRingBuffer:
++ *
++ * Opaque #GstAudioRingBuffer.
++ */
++struct _GstAudioRingBuffer {
++  GstRingBuffer       element;
++
++  /*< protected >*/
++  GstAudioRingBufferMode  mode;
++  GstRingBufferThread    *thread;
++
++  GstAudioRingBuffer     *link;
++
++  /*< private >*/
++  gpointer _gst_reserved[GST_PADDING];
++};
++
++/**
++ * GstAudioRingBufferClass:
++ * @parent_class: the parent class structure.
++ * @process: Write/Read data to/from the device.
++ * @reset: Returns as quickly as possible from a write/read and flush any pending
++ *         samples from the device.
++ *
++ * #GstAudioRingBuffer class. Override the vmethods to implement functionality.
++ */
++struct _GstAudioRingBufferClass {
++  GstRingBufferClass parent_class;
++
++  /* vtable */
++
++  /* write/read samples to the device */
++  gint      (*process)   (GstAudioRingBuffer *buf, gpointer data, guint length);
++  /* reset the audio device, unblock from a read/write */
++  void      (*reset)     (GstAudioRingBuffer *buf);
++
++  /*< private >*/
++  gpointer _gst_reserved[GST_PADDING];
++};
++
++GType gst_audio_ring_buffer_get_type(void);
++
++gboolean gst_audio_ring_buffer_link       (GstAudioRingBuffer *buf1, GstAudioRingBuffer *buf2);
++
++gboolean gst_audio_ring_buffer_set_thread (GstAudioRingBuffer *buf, GstRingBufferThread *thread);
++
++G_END_DECLS
++
++#endif /* __GST_AUDIO_RING_BUFFER_H__ */
index 0000000,0000000..1c2c72c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1129 @@@
++/* GStreamer
++ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
++ *                    2005 Wim Taymans <wim@fluendo.com>
++ *
++ * gstbaseaudiosrc.c: 
++ *
++ * 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.
++ */
++
++/**
++ * SECTION:gstbaseaudiosrc
++ * @short_description: Base class for audio sources
++ * @see_also: #GstAudioSrc, #GstRingBuffer.
++ *
++ * This is the base class for audio sources. Subclasses need to implement the
++ * ::create_ringbuffer vmethod. This base class will then take care of
++ * reading samples from the ringbuffer, synchronisation and flushing.
++ *
++ * Last reviewed on 2006-09-27 (0.10.12)
++ */
++
++#ifdef HAVE_CONFIG_H
++#  include "config.h"
++#endif
++
++#include <string.h>
++
++#include "gstbaseaudiosrc.h"
++
++#include "gst/gst-i18n-plugin.h"
++
++GST_DEBUG_CATEGORY_STATIC (gst_base_audio_src_debug);
++#define GST_CAT_DEFAULT gst_base_audio_src_debug
++
++GType
++gst_base_audio_src_slave_method_get_type (void)
++{
++  static GType slave_method_type = 0;
++  static const GEnumValue slave_method[] = {
++    {GST_BASE_AUDIO_SRC_SLAVE_RESAMPLE, "Resampling slaving", "resample"},
++    {GST_BASE_AUDIO_SRC_SLAVE_RETIMESTAMP, "Re-timestamp", "re-timestamp"},
++    {GST_BASE_AUDIO_SRC_SLAVE_SKEW, "Skew", "skew"},
++    {GST_BASE_AUDIO_SRC_SLAVE_NONE, "No slaving", "none"},
++    {0, NULL, NULL},
++  };
++
++  if (!slave_method_type) {
++    slave_method_type =
++        g_enum_register_static ("GstBaseAudioSrcSlaveMethod", slave_method);
++  }
++  return slave_method_type;
++}
++
++#define GST_BASE_AUDIO_SRC_GET_PRIVATE(obj)  \
++   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BASE_AUDIO_SRC, GstBaseAudioSrcPrivate))
++
++struct _GstBaseAudioSrcPrivate
++{
++  gboolean provide_clock;
++
++  /* the clock slaving algorithm in use */
++  GstBaseAudioSrcSlaveMethod slave_method;
++};
++
++/* BaseAudioSrc signals and args */
++enum
++{
++  /* FILL ME */
++  LAST_SIGNAL
++};
++
++#define DEFAULT_BUFFER_TIME     ((200 * GST_MSECOND) / GST_USECOND)
++#define DEFAULT_LATENCY_TIME    ((10 * GST_MSECOND) / GST_USECOND)
++#define DEFAULT_ACTUAL_BUFFER_TIME     -1
++#define DEFAULT_ACTUAL_LATENCY_TIME    -1
++#define DEFAULT_PROVIDE_CLOCK   TRUE
++#define DEFAULT_SLAVE_METHOD    GST_BASE_AUDIO_SRC_SLAVE_SKEW
++
++enum
++{
++  PROP_0,
++  PROP_BUFFER_TIME,
++  PROP_LATENCY_TIME,
++  PROP_ACTUAL_BUFFER_TIME,
++  PROP_ACTUAL_LATENCY_TIME,
++  PROP_PROVIDE_CLOCK,
++  PROP_SLAVE_METHOD,
++  PROP_LAST
++};
++
++static void
++_do_init (GType type)
++{
++  GST_DEBUG_CATEGORY_INIT (gst_base_audio_src_debug, "baseaudiosrc", 0,
++      "baseaudiosrc element");
++
++#ifdef ENABLE_NLS
++  GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
++      LOCALEDIR);
++  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
++  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
++#endif /* ENABLE_NLS */
++}
++
++GST_BOILERPLATE_FULL (GstBaseAudioSrc, gst_base_audio_src, GstPushSrc,
++    GST_TYPE_PUSH_SRC, _do_init);
++
++static void gst_base_audio_src_set_property (GObject * object, guint prop_id,
++    const GValue * value, GParamSpec * pspec);
++static void gst_base_audio_src_get_property (GObject * object, guint prop_id,
++    GValue * value, GParamSpec * pspec);
++static void gst_base_audio_src_dispose (GObject * object);
++
++static GstStateChangeReturn gst_base_audio_src_change_state (GstElement *
++    element, GstStateChange transition);
++
++static GstClock *gst_base_audio_src_provide_clock (GstElement * elem);
++static GstClockTime gst_base_audio_src_get_time (GstClock * clock,
++    GstBaseAudioSrc * src);
++
++static GstFlowReturn gst_base_audio_src_create (GstBaseSrc * bsrc,
++    guint64 offset, guint length, GstBuffer ** buf);
++static gboolean gst_base_audio_src_check_get_range (GstBaseSrc * bsrc);
++
++static gboolean gst_base_audio_src_event (GstBaseSrc * bsrc, GstEvent * event);
++static void gst_base_audio_src_get_times (GstBaseSrc * bsrc,
++    GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
++static gboolean gst_base_audio_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps);
++static gboolean gst_base_audio_src_query (GstBaseSrc * bsrc, GstQuery * query);
++static void gst_base_audio_src_fixate (GstBaseSrc * bsrc, GstCaps * caps);
++
++/* static guint gst_base_audio_src_signals[LAST_SIGNAL] = { 0 }; */
++
++static void
++gst_base_audio_src_base_init (gpointer g_class)
++{
++}
++
++static void
++gst_base_audio_src_class_init (GstBaseAudioSrcClass * klass)
++{
++  GObjectClass *gobject_class;
++  GstElementClass *gstelement_class;
++  GstBaseSrcClass *gstbasesrc_class;
++
++  gobject_class = (GObjectClass *) klass;
++  gstelement_class = (GstElementClass *) klass;
++  gstbasesrc_class = (GstBaseSrcClass *) klass;
++
++  g_type_class_add_private (klass, sizeof (GstBaseAudioSrcPrivate));
++
++  gobject_class->set_property =
++      GST_DEBUG_FUNCPTR (gst_base_audio_src_set_property);
++  gobject_class->get_property =
++      GST_DEBUG_FUNCPTR (gst_base_audio_src_get_property);
++  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_base_audio_src_dispose);
++
++  g_object_class_install_property (gobject_class, PROP_BUFFER_TIME,
++      g_param_spec_int64 ("buffer-time", "Buffer Time",
++          "Size of audio buffer in microseconds", 1,
++          G_MAXINT64, DEFAULT_BUFFER_TIME,
++          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++
++  g_object_class_install_property (gobject_class, PROP_LATENCY_TIME,
++      g_param_spec_int64 ("latency-time", "Latency Time",
++          "Audio latency in microseconds", 1,
++          G_MAXINT64, DEFAULT_LATENCY_TIME,
++          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++
++  /**
++   * GstBaseAudioSrc:actual-buffer-time:
++   *
++   * Actual configured size of audio buffer in microseconds.
++   *
++   * Since: 0.10.20
++   **/
++  g_object_class_install_property (gobject_class, PROP_ACTUAL_BUFFER_TIME,
++      g_param_spec_int64 ("actual-buffer-time", "Actual Buffer Time",
++          "Actual configured size of audio buffer in microseconds",
++          DEFAULT_ACTUAL_BUFFER_TIME, G_MAXINT64, DEFAULT_ACTUAL_BUFFER_TIME,
++          G_PARAM_READABLE));
++
++  /**
++   * GstBaseAudioSrc:actual-latency-time:
++   *
++   * Actual configured audio latency in microseconds.
++   *
++   * Since: 0.10.20
++   **/
++  g_object_class_install_property (gobject_class, PROP_ACTUAL_LATENCY_TIME,
++      g_param_spec_int64 ("actual-latency-time", "Actual Latency Time",
++          "Actual configured audio latency in microseconds",
++          DEFAULT_ACTUAL_LATENCY_TIME, G_MAXINT64, DEFAULT_ACTUAL_LATENCY_TIME,
++          G_PARAM_READABLE));
++
++  g_object_class_install_property (gobject_class, PROP_PROVIDE_CLOCK,
++      g_param_spec_boolean ("provide-clock", "Provide Clock",
++          "Provide a clock to be used as the global pipeline clock",
++          DEFAULT_PROVIDE_CLOCK, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++
++  g_object_class_install_property (gobject_class, PROP_SLAVE_METHOD,
++      g_param_spec_enum ("slave-method", "Slave Method",
++          "Algorithm to use to match the rate of the masterclock",
++          GST_TYPE_BASE_AUDIO_SRC_SLAVE_METHOD, DEFAULT_SLAVE_METHOD,
++          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++
++  gstelement_class->change_state =
++      GST_DEBUG_FUNCPTR (gst_base_audio_src_change_state);
++  gstelement_class->provide_clock =
++      GST_DEBUG_FUNCPTR (gst_base_audio_src_provide_clock);
++
++  gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_base_audio_src_setcaps);
++  gstbasesrc_class->event = GST_DEBUG_FUNCPTR (gst_base_audio_src_event);
++  gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_base_audio_src_query);
++  gstbasesrc_class->get_times =
++      GST_DEBUG_FUNCPTR (gst_base_audio_src_get_times);
++  gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_base_audio_src_create);
++  gstbasesrc_class->check_get_range =
++      GST_DEBUG_FUNCPTR (gst_base_audio_src_check_get_range);
++  gstbasesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_base_audio_src_fixate);
++
++  /* ref class from a thread-safe context to work around missing bit of
++   * thread-safety in GObject */
++  g_type_class_ref (GST_TYPE_AUDIO_CLOCK);
++  g_type_class_ref (GST_TYPE_RING_BUFFER);
++}
++
++static void
++gst_base_audio_src_init (GstBaseAudioSrc * baseaudiosrc,
++    GstBaseAudioSrcClass * g_class)
++{
++  baseaudiosrc->priv = GST_BASE_AUDIO_SRC_GET_PRIVATE (baseaudiosrc);
++
++  baseaudiosrc->buffer_time = DEFAULT_BUFFER_TIME;
++  baseaudiosrc->latency_time = DEFAULT_LATENCY_TIME;
++  baseaudiosrc->priv->provide_clock = DEFAULT_PROVIDE_CLOCK;
++  baseaudiosrc->priv->slave_method = DEFAULT_SLAVE_METHOD;
++  /* reset blocksize we use latency time to calculate a more useful 
++   * value based on negotiated format. */
++  GST_BASE_SRC (baseaudiosrc)->blocksize = 0;
++
++  baseaudiosrc->clock = gst_audio_clock_new ("GstAudioSrcClock",
++      (GstAudioClockGetTimeFunc) gst_base_audio_src_get_time, baseaudiosrc);
++
++  /* we are always a live source */
++  gst_base_src_set_live (GST_BASE_SRC (baseaudiosrc), TRUE);
++  /* we operate in time */
++  gst_base_src_set_format (GST_BASE_SRC (baseaudiosrc), GST_FORMAT_TIME);
++}
++
++static void
++gst_base_audio_src_dispose (GObject * object)
++{
++  GstBaseAudioSrc *src;
++
++  src = GST_BASE_AUDIO_SRC (object);
++
++  GST_OBJECT_LOCK (src);
++  if (src->clock)
++    gst_object_unref (src->clock);
++  src->clock = NULL;
++
++  if (src->ringbuffer) {
++    gst_object_unparent (GST_OBJECT_CAST (src->ringbuffer));
++    src->ringbuffer = NULL;
++  }
++  GST_OBJECT_UNLOCK (src);
++
++  G_OBJECT_CLASS (parent_class)->dispose (object);
++}
++
++static GstClock *
++gst_base_audio_src_provide_clock (GstElement * elem)
++{
++  GstBaseAudioSrc *src;
++  GstClock *clock;
++
++  src = GST_BASE_AUDIO_SRC (elem);
++
++  /* we have no ringbuffer (must be NULL state) */
++  if (src->ringbuffer == NULL)
++    goto wrong_state;
++
++  if (!gst_ring_buffer_is_acquired (src->ringbuffer))
++    goto wrong_state;
++
++  GST_OBJECT_LOCK (src);
++  if (!src->priv->provide_clock)
++    goto clock_disabled;
++
++  clock = GST_CLOCK_CAST (gst_object_ref (src->clock));
++  GST_OBJECT_UNLOCK (src);
++
++  return clock;
++
++  /* ERRORS */
++wrong_state:
++  {
++    GST_DEBUG_OBJECT (src, "ringbuffer not acquired");
++    return NULL;
++  }
++clock_disabled:
++  {
++    GST_DEBUG_OBJECT (src, "clock provide disabled");
++    GST_OBJECT_UNLOCK (src);
++    return NULL;
++  }
++}
++
++static GstClockTime
++gst_base_audio_src_get_time (GstClock * clock, GstBaseAudioSrc * src)
++{
++  guint64 raw, samples;
++  guint delay;
++  GstClockTime result;
++
++  if (G_UNLIKELY (src->ringbuffer == NULL || src->ringbuffer->spec.rate == 0))
++    return GST_CLOCK_TIME_NONE;
++
++  raw = samples = gst_ring_buffer_samples_done (src->ringbuffer);
++
++  /* the number of samples not yet processed, this is still queued in the
++   * device (not yet read for capture). */
++  delay = gst_ring_buffer_delay (src->ringbuffer);
++
++  samples += delay;
++
++  result = gst_util_uint64_scale_int (samples, GST_SECOND,
++      src->ringbuffer->spec.rate);
++
++  GST_DEBUG_OBJECT (src,
++      "processed samples: raw %llu, delay %u, real %llu, time %"
++      GST_TIME_FORMAT, raw, delay, samples, GST_TIME_ARGS (result));
++
++  return result;
++}
++
++static gboolean
++gst_base_audio_src_check_get_range (GstBaseSrc * bsrc)
++{
++  /* we allow limited pull base operation of which the details
++   * will eventually exposed in an as of yet non-existing query.
++   * Basically pulling can be done on any number of bytes as long
++   * as the offset is -1 or sequentially increasing. */
++  return TRUE;
++}
++
++/**
++ * gst_base_audio_src_set_provide_clock:
++ * @src: a #GstBaseAudioSrc
++ * @provide: new state
++ *
++ * Controls whether @src will provide a clock or not. If @provide is %TRUE, 
++ * gst_element_provide_clock() will return a clock that reflects the datarate
++ * of @src. If @provide is %FALSE, gst_element_provide_clock() will return NULL.
++ *
++ * Since: 0.10.16
++ */
++void
++gst_base_audio_src_set_provide_clock (GstBaseAudioSrc * src, gboolean provide)
++{
++  g_return_if_fail (GST_IS_BASE_AUDIO_SRC (src));
++
++  GST_OBJECT_LOCK (src);
++  src->priv->provide_clock = provide;
++  GST_OBJECT_UNLOCK (src);
++}
++
++/**
++ * gst_base_audio_src_get_provide_clock:
++ * @src: a #GstBaseAudioSrc
++ *
++ * Queries whether @src will provide a clock or not. See also
++ * gst_base_audio_src_set_provide_clock.
++ *
++ * Returns: %TRUE if @src will provide a clock.
++ *
++ * Since: 0.10.16
++ */
++gboolean
++gst_base_audio_src_get_provide_clock (GstBaseAudioSrc * src)
++{
++  gboolean result;
++
++  g_return_val_if_fail (GST_IS_BASE_AUDIO_SRC (src), FALSE);
++
++  GST_OBJECT_LOCK (src);
++  result = src->priv->provide_clock;
++  GST_OBJECT_UNLOCK (src);
++
++  return result;
++}
++
++/**
++ * gst_base_audio_src_set_slave_method:
++ * @src: a #GstBaseAudioSrc
++ * @method: the new slave method
++ *
++ * Controls how clock slaving will be performed in @src. 
++ *
++ * Since: 0.10.20
++ */
++void
++gst_base_audio_src_set_slave_method (GstBaseAudioSrc * src,
++    GstBaseAudioSrcSlaveMethod method)
++{
++  g_return_if_fail (GST_IS_BASE_AUDIO_SRC (src));
++
++  GST_OBJECT_LOCK (src);
++  src->priv->slave_method = method;
++  GST_OBJECT_UNLOCK (src);
++}
++
++/**
++ * gst_base_audio_src_get_slave_method:
++ * @src: a #GstBaseAudioSrc
++ *
++ * Get the current slave method used by @src.
++ *
++ * Returns: The current slave method used by @src.
++ *
++ * Since: 0.10.20
++ */
++GstBaseAudioSrcSlaveMethod
++gst_base_audio_src_get_slave_method (GstBaseAudioSrc * src)
++{
++  GstBaseAudioSrcSlaveMethod result;
++
++  g_return_val_if_fail (GST_IS_BASE_AUDIO_SRC (src), -1);
++
++  GST_OBJECT_LOCK (src);
++  result = src->priv->slave_method;
++  GST_OBJECT_UNLOCK (src);
++
++  return result;
++}
++
++static void
++gst_base_audio_src_set_property (GObject * object, guint prop_id,
++    const GValue * value, GParamSpec * pspec)
++{
++  GstBaseAudioSrc *src;
++
++  src = GST_BASE_AUDIO_SRC (object);
++
++  switch (prop_id) {
++    case PROP_BUFFER_TIME:
++      src->buffer_time = g_value_get_int64 (value);
++      break;
++    case PROP_LATENCY_TIME:
++      src->latency_time = g_value_get_int64 (value);
++      break;
++    case PROP_PROVIDE_CLOCK:
++      gst_base_audio_src_set_provide_clock (src, g_value_get_boolean (value));
++      break;
++    case PROP_SLAVE_METHOD:
++      gst_base_audio_src_set_slave_method (src, g_value_get_enum (value));
++      break;
++    default:
++      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++      break;
++  }
++}
++
++static void
++gst_base_audio_src_get_property (GObject * object, guint prop_id,
++    GValue * value, GParamSpec * pspec)
++{
++  GstBaseAudioSrc *src;
++
++  src = GST_BASE_AUDIO_SRC (object);
++
++  switch (prop_id) {
++    case PROP_BUFFER_TIME:
++      g_value_set_int64 (value, src->buffer_time);
++      break;
++    case PROP_LATENCY_TIME:
++      g_value_set_int64 (value, src->latency_time);
++      break;
++    case PROP_ACTUAL_BUFFER_TIME:
++      GST_OBJECT_LOCK (src);
++      if (src->ringbuffer && src->ringbuffer->acquired)
++        g_value_set_int64 (value, src->ringbuffer->spec.buffer_time);
++      else
++        g_value_set_int64 (value, DEFAULT_ACTUAL_BUFFER_TIME);
++      GST_OBJECT_UNLOCK (src);
++      break;
++    case PROP_ACTUAL_LATENCY_TIME:
++      GST_OBJECT_LOCK (src);
++      if (src->ringbuffer && src->ringbuffer->acquired)
++        g_value_set_int64 (value, src->ringbuffer->spec.latency_time);
++      else
++        g_value_set_int64 (value, DEFAULT_ACTUAL_LATENCY_TIME);
++      GST_OBJECT_UNLOCK (src);
++      break;
++    case PROP_PROVIDE_CLOCK:
++      g_value_set_boolean (value, gst_base_audio_src_get_provide_clock (src));
++      break;
++    case PROP_SLAVE_METHOD:
++      g_value_set_enum (value, gst_base_audio_src_get_slave_method (src));
++      break;
++    default:
++      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++      break;
++  }
++}
++
++static void
++gst_base_audio_src_fixate (GstBaseSrc * bsrc, GstCaps * caps)
++{
++  GstStructure *s;
++  gint width, depth;
++
++  s = gst_caps_get_structure (caps, 0);
++
++  /* fields for all formats */
++  gst_structure_fixate_field_nearest_int (s, "rate", 44100);
++  gst_structure_fixate_field_nearest_int (s, "channels", 2);
++  gst_structure_fixate_field_nearest_int (s, "width", 16);
++
++  /* fields for int */
++  if (gst_structure_has_field (s, "depth")) {
++    gst_structure_get_int (s, "width", &width);
++    /* round width to nearest multiple of 8 for the depth */
++    depth = GST_ROUND_UP_8 (width);
++    gst_structure_fixate_field_nearest_int (s, "depth", depth);
++  }
++  if (gst_structure_has_field (s, "signed"))
++    gst_structure_fixate_field_boolean (s, "signed", TRUE);
++  if (gst_structure_has_field (s, "endianness"))
++    gst_structure_fixate_field_nearest_int (s, "endianness", G_BYTE_ORDER);
++}
++
++static gboolean
++gst_base_audio_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps)
++{
++  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (bsrc);
++  GstRingBufferSpec *spec;
++
++  spec = &src->ringbuffer->spec;
++
++  spec->buffer_time = src->buffer_time;
++  spec->latency_time = src->latency_time;
++
++  if (!gst_ring_buffer_parse_caps (spec, caps))
++    goto parse_error;
++
++  /* calculate suggested segsize and segtotal */
++  spec->segsize =
++      spec->rate * spec->bytes_per_sample * spec->latency_time / GST_MSECOND;
++  spec->segtotal = spec->buffer_time / spec->latency_time;
++
++  GST_DEBUG ("release old ringbuffer");
++
++  gst_ring_buffer_release (src->ringbuffer);
++
++  gst_ring_buffer_debug_spec_buff (spec);
++
++  GST_DEBUG ("acquire new ringbuffer");
++
++  if (!gst_ring_buffer_acquire (src->ringbuffer, spec))
++    goto acquire_error;
++
++  /* calculate actual latency and buffer times */
++  spec->latency_time =
++      spec->segsize * GST_MSECOND / (spec->rate * spec->bytes_per_sample);
++  spec->buffer_time =
++      spec->segtotal * spec->segsize * GST_MSECOND / (spec->rate *
++      spec->bytes_per_sample);
++
++  gst_ring_buffer_debug_spec_buff (spec);
++
++  g_object_notify (G_OBJECT (src), "actual-buffer-time");
++  g_object_notify (G_OBJECT (src), "actual-latency-time");
++
++  return TRUE;
++
++  /* ERRORS */
++parse_error:
++  {
++    GST_DEBUG ("could not parse caps");
++    return FALSE;
++  }
++acquire_error:
++  {
++    GST_DEBUG ("could not acquire ringbuffer");
++    return FALSE;
++  }
++}
++
++static void
++gst_base_audio_src_get_times (GstBaseSrc * bsrc, GstBuffer * buffer,
++    GstClockTime * start, GstClockTime * end)
++{
++  /* no need to sync to a clock here, we schedule the samples based
++   * on our own clock for the moment. */
++  *start = GST_CLOCK_TIME_NONE;
++  *end = GST_CLOCK_TIME_NONE;
++}
++
++static gboolean
++gst_base_audio_src_query (GstBaseSrc * bsrc, GstQuery * query)
++{
++  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (bsrc);
++  gboolean res = FALSE;
++
++  switch (GST_QUERY_TYPE (query)) {
++    case GST_QUERY_LATENCY:
++    {
++      GstClockTime min_latency, max_latency;
++      GstRingBufferSpec *spec;
++
++      if (G_UNLIKELY (src->ringbuffer == NULL
++              || src->ringbuffer->spec.rate == 0))
++        goto done;
++
++      spec = &src->ringbuffer->spec;
++
++      /* we have at least 1 segment of latency */
++      min_latency =
++          gst_util_uint64_scale_int (spec->segsize, GST_SECOND,
++          spec->rate * spec->bytes_per_sample);
++      /* we cannot delay more than the buffersize else we lose data */
++      max_latency =
++          gst_util_uint64_scale_int (spec->segtotal * spec->segsize, GST_SECOND,
++          spec->rate * spec->bytes_per_sample);
++
++      GST_DEBUG_OBJECT (src,
++          "report latency min %" GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
++          GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
++
++      /* we are always live, the min latency is 1 segment and the max latency is
++       * the complete buffer of segments. */
++      gst_query_set_latency (query, TRUE, min_latency, max_latency);
++
++      res = TRUE;
++      break;
++    }
++    default:
++      res = GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query);
++      break;
++  }
++done:
++  return res;
++}
++
++static gboolean
++gst_base_audio_src_event (GstBaseSrc * bsrc, GstEvent * event)
++{
++  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (bsrc);
++  gboolean res;
++
++  res = TRUE;
++
++  switch (GST_EVENT_TYPE (event)) {
++    case GST_EVENT_FLUSH_START:
++      GST_DEBUG_OBJECT (bsrc, "flush-start");
++      gst_ring_buffer_pause (src->ringbuffer);
++      gst_ring_buffer_clear_all (src->ringbuffer);
++      break;
++    case GST_EVENT_FLUSH_STOP:
++      GST_DEBUG_OBJECT (bsrc, "flush-stop");
++      /* always resync on sample after a flush */
++      src->next_sample = -1;
++      gst_ring_buffer_clear_all (src->ringbuffer);
++      break;
++    case GST_EVENT_SEEK:
++      GST_DEBUG_OBJECT (bsrc, "refuse to seek");
++      res = FALSE;
++      break;
++    default:
++      GST_DEBUG_OBJECT (bsrc, "dropping event %p", event);
++      break;
++  }
++  return res;
++}
++
++/* get the next offset in the ringbuffer for reading samples.
++ * If the next sample is too far away, this function will position itself to the
++ * next most recent sample, creating discontinuity */
++static guint64
++gst_base_audio_src_get_offset (GstBaseAudioSrc * src)
++{
++  guint64 sample;
++  gint readseg, segdone, segtotal, sps;
++  gint diff;
++
++  /* assume we can append to the previous sample */
++  sample = src->next_sample;
++  /* no previous sample, try to read from position 0 */
++  if (sample == -1)
++    sample = 0;
++
++  sps = src->ringbuffer->samples_per_seg;
++  segtotal = src->ringbuffer->spec.segtotal;
++
++  /* figure out the segment and the offset inside the segment where
++   * the sample should be read from. */
++  readseg = sample / sps;
++
++  /* get the currently processed segment */
++  segdone = g_atomic_int_get (&src->ringbuffer->segdone)
++      - src->ringbuffer->segbase;
++
++  GST_DEBUG_OBJECT (src, "reading from %d, we are at %d", readseg, segdone);
++
++  /* see how far away it is from the read segment, normally segdone (where new
++   * data is written in the ringbuffer) is bigger than readseg (where we are
++   * reading). */
++  diff = segdone - readseg;
++  if (diff >= segtotal) {
++    GST_DEBUG_OBJECT (src, "dropped, align to segment %d", segdone);
++    /* sample would be dropped, position to next playable position */
++    sample = ((guint64) (segdone)) * sps;
++  }
++
++  return sample;
++}
++
++static GstFlowReturn
++gst_base_audio_src_create (GstBaseSrc * bsrc, guint64 offset, guint length,
++    GstBuffer ** outbuf)
++{
++  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (bsrc);
++  GstBuffer *buf;
++  guchar *data;
++  guint samples, total_samples;
++  guint64 sample;
++  gint bps;
++  GstRingBuffer *ringbuffer;
++  GstRingBufferSpec *spec;
++  guint read;
++  GstClockTime timestamp, duration;
++  GstClock *clock;
++
++  ringbuffer = src->ringbuffer;
++  spec = &ringbuffer->spec;
++
++  if (G_UNLIKELY (!gst_ring_buffer_is_acquired (ringbuffer)))
++    goto wrong_state;
++
++  bps = spec->bytes_per_sample;
++
++  if ((length == 0 && bsrc->blocksize == 0) || length == -1)
++    /* no length given, use the default segment size */
++    length = spec->segsize;
++  else
++    /* make sure we round down to an integral number of samples */
++    length -= length % bps;
++
++  /* figure out the offset in the ringbuffer */
++  if (G_UNLIKELY (offset != -1)) {
++    sample = offset / bps;
++    /* if a specific offset was given it must be the next sequential
++     * offset we expect or we fail for now. */
++    if (src->next_sample != -1 && sample != src->next_sample)
++      goto wrong_offset;
++  } else {
++    /* calculate the sequentially next sample we need to read. This can jump and
++     * create a DISCONT. */
++    sample = gst_base_audio_src_get_offset (src);
++  }
++
++  GST_DEBUG_OBJECT (src, "reading from sample %" G_GUINT64_FORMAT, sample);
++
++  /* get the number of samples to read */
++  total_samples = samples = length / bps;
++
++  /* FIXME, using a bufferpool would be nice here */
++  buf = gst_buffer_new_and_alloc (length);
++  data = GST_BUFFER_DATA (buf);
++
++  do {
++    read = gst_ring_buffer_read (ringbuffer, sample, data, samples);
++    GST_DEBUG_OBJECT (src, "read %u of %u", read, samples);
++    /* if we read all, we're done */
++    if (read == samples)
++      break;
++
++    /* else something interrupted us and we wait for playing again. */
++    GST_DEBUG_OBJECT (src, "wait playing");
++    if (gst_base_src_wait_playing (bsrc) != GST_FLOW_OK)
++      goto stopped;
++
++    GST_DEBUG_OBJECT (src, "continue playing");
++
++    /* read next samples */
++    sample += read;
++    samples -= read;
++    data += read * bps;
++  } while (TRUE);
++
++  /* mark discontinuity if needed */
++  if (G_UNLIKELY (sample != src->next_sample) && src->next_sample != -1) {
++    GST_WARNING_OBJECT (src,
++        "create DISCONT of %" G_GUINT64_FORMAT " samples at sample %"
++        G_GUINT64_FORMAT, sample - src->next_sample, sample);
++    GST_ELEMENT_WARNING (src, CORE, CLOCK,
++        (_("Can't record audio fast enough")),
++        ("Dropped %" G_GUINT64_FORMAT " samples. This is most likely because "
++            "downstream can't keep up and is consuming samples too slowly.",
++            sample - src->next_sample));
++    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
++  }
++
++  src->next_sample = sample + samples;
++
++  /* get the normal timestamp to get the duration. */
++  timestamp = gst_util_uint64_scale_int (sample, GST_SECOND, spec->rate);
++  duration = gst_util_uint64_scale_int (src->next_sample, GST_SECOND,
++      spec->rate) - timestamp;
++
++  GST_OBJECT_LOCK (src);
++  if (!(clock = GST_ELEMENT_CLOCK (src)))
++    goto no_sync;
++
++  if (clock != src->clock) {
++    /* we are slaved, check how to handle this */
++    switch (src->priv->slave_method) {
++      case GST_BASE_AUDIO_SRC_SLAVE_RESAMPLE:
++        /* not implemented, use skew algorithm. This algorithm should
++         * work on the readout pointer and produces more or less samples based
++         * on the clock drift */
++      case GST_BASE_AUDIO_SRC_SLAVE_SKEW:
++      {
++        GstClockTime running_time;
++        GstClockTime base_time;
++        GstClockTime current_time;
++        guint64 running_time_sample;
++        gint running_time_segment;
++        gint current_segment;
++        gint segment_skew;
++        gint sps;
++
++        /* samples per segment */
++        sps = ringbuffer->samples_per_seg;
++
++        /* get the current time */
++        current_time = gst_clock_get_time (clock);
++
++        /* get the basetime */
++        base_time = GST_ELEMENT_CAST (src)->base_time;
++
++        /* get the running_time */
++        running_time = current_time - base_time;
++
++        /* the running_time converted to a sample (relative to the ringbuffer) */
++        running_time_sample =
++            gst_util_uint64_scale_int (running_time, spec->rate, GST_SECOND);
++
++        /* the segmentnr corrensponding to running_time, round down */
++        running_time_segment = running_time_sample / sps;
++
++        /* the segment currently read from the ringbuffer */
++        current_segment = sample / sps;
++
++        /* the skew we have between running_time and the ringbuffertime */
++        segment_skew = running_time_segment - current_segment;
++
++        GST_DEBUG_OBJECT (bsrc, "\n running_time = %" GST_TIME_FORMAT
++            "\n timestamp     = %" GST_TIME_FORMAT
++            "\n running_time_segment = %d"
++            "\n current_segment      = %d"
++            "\n segment_skew         = %d",
++            GST_TIME_ARGS (running_time),
++            GST_TIME_ARGS (timestamp),
++            running_time_segment, current_segment, segment_skew);
++
++        /* Resync the ringbuffer if:
++         * 1. We get one segment into the future.
++         *    This is clearly a lie, because we can't
++         *    possibly have a buffer with timestamp 1 at
++         *    time 0. (unless it has time-travelled...)
++         *
++         * 2. We are more than the length of the ringbuffer behind.
++         *    The length of the ringbuffer then gets to dictate
++         *    the threshold for what is concidered "too late"
++         *
++         * 3. If this is our first buffer.
++         *    We know that we should catch up to running_time
++         *    the first time we are ran.
++         */
++        if ((segment_skew < 0) ||
++            (segment_skew >= ringbuffer->spec.segtotal) ||
++            (current_segment == 0)) {
++          gint segments_written;
++          gint first_segment;
++          gint last_segment;
++          gint new_last_segment;
++          gint segment_diff;
++          gint new_first_segment;
++          guint64 new_sample;
++
++          /* we are going to say that the last segment was captured at the current time
++             (running_time), minus one segment of creation-latency in the ringbuffer.
++             This can be thought of as: The segment arrived in the ringbuffer at time X, and
++             that means it was created at time X - (one segment). */
++          new_last_segment = running_time_segment - 1;
++
++          /* for better readablity */
++          first_segment = current_segment;
++
++          /* get the amount of segments written from the device by now */
++          segments_written = g_atomic_int_get (&ringbuffer->segdone);
++
++          /* subtract the base to segments_written to get the number of the
++             last written segment in the ringbuffer (one segment written = segment 0) */
++          last_segment = segments_written - ringbuffer->segbase - 1;
++
++          /* we see how many segments the ringbuffer was timeshifted */
++          segment_diff = new_last_segment - last_segment;
++
++          /* we move the first segment an equal amount */
++          new_first_segment = first_segment + segment_diff;
++
++          /* and we also move the segmentbase the same amount */
++          ringbuffer->segbase -= segment_diff;
++
++          /* we calculate the new sample value */
++          new_sample = ((guint64) new_first_segment) * sps;
++
++          /* and get the relative time to this -> our new timestamp */
++          timestamp =
++              gst_util_uint64_scale_int (new_sample, GST_SECOND, spec->rate);
++
++          /* we update the next sample accordingly */
++          src->next_sample = new_sample + samples;
++
++          GST_DEBUG_OBJECT (bsrc,
++              "Timeshifted the ringbuffer with %d segments: "
++              "Updating the timestamp to %" GST_TIME_FORMAT ", "
++              "and src->next_sample to %" G_GUINT64_FORMAT, segment_diff,
++              GST_TIME_ARGS (timestamp), src->next_sample);
++        }
++        break;
++      }
++      case GST_BASE_AUDIO_SRC_SLAVE_RETIMESTAMP:
++      {
++        GstClockTime base_time, latency;
++
++        /* We are slaved to another clock, take running time of the pipeline clock and
++         * timestamp against it. Somebody else in the pipeline should figure out the
++         * clock drift. We keep the duration we calculated above. */
++        timestamp = gst_clock_get_time (clock);
++        base_time = GST_ELEMENT_CAST (src)->base_time;
++
++        if (timestamp > base_time)
++          timestamp -= base_time;
++        else
++          timestamp = 0;
++
++        /* subtract latency */
++        latency =
++            gst_util_uint64_scale_int (total_samples, GST_SECOND, spec->rate);
++        if (timestamp > latency)
++          timestamp -= latency;
++        else
++          timestamp = 0;
++      }
++      case GST_BASE_AUDIO_SRC_SLAVE_NONE:
++        break;
++    }
++  } else {
++    GstClockTime base_time;
++
++    /* to get the timestamp against the clock we also need to add our offset */
++    timestamp = gst_audio_clock_adjust (clock, timestamp);
++
++    /* we are not slaved, subtract base_time */
++    base_time = GST_ELEMENT_CAST (src)->base_time;
++
++    if (timestamp > base_time) {
++      timestamp -= base_time;
++      GST_LOG_OBJECT (src,
++          "buffer timestamp %" GST_TIME_FORMAT " (base_time %" GST_TIME_FORMAT
++          ")", GST_TIME_ARGS (timestamp), GST_TIME_ARGS (base_time));
++    } else {
++      GST_LOG_OBJECT (src,
++          "buffer timestamp 0, ts %" GST_TIME_FORMAT " <= base_time %"
++          GST_TIME_FORMAT, GST_TIME_ARGS (timestamp),
++          GST_TIME_ARGS (base_time));
++      timestamp = 0;
++    }
++  }
++
++no_sync:
++  GST_OBJECT_UNLOCK (src);
++
++  GST_BUFFER_TIMESTAMP (buf) = timestamp;
++  GST_BUFFER_DURATION (buf) = duration;
++  GST_BUFFER_OFFSET (buf) = sample;
++  GST_BUFFER_OFFSET_END (buf) = sample + samples;
++
++  *outbuf = buf;
++
++  return GST_FLOW_OK;
++
++  /* ERRORS */
++wrong_state:
++  {
++    GST_DEBUG_OBJECT (src, "ringbuffer in wrong state");
++    return GST_FLOW_WRONG_STATE;
++  }
++wrong_offset:
++  {
++    GST_ELEMENT_ERROR (src, RESOURCE, SEEK,
++        (NULL), ("resource can only be operated on sequentially but offset %"
++            G_GUINT64_FORMAT " was given", offset));
++    return GST_FLOW_ERROR;
++  }
++stopped:
++  {
++    gst_buffer_unref (buf);
++    GST_DEBUG_OBJECT (src, "ringbuffer stopped");
++    return GST_FLOW_WRONG_STATE;
++  }
++}
++
++/**
++ * gst_base_audio_src_create_ringbuffer:
++ * @src: a #GstBaseAudioSrc.
++ *
++ * Create and return the #GstRingBuffer for @src. This function will call the
++ * ::create_ringbuffer vmethod and will set @src as the parent of the returned
++ * buffer (see gst_object_set_parent()).
++ *
++ * Returns: The new ringbuffer of @src.
++ */
++GstRingBuffer *
++gst_base_audio_src_create_ringbuffer (GstBaseAudioSrc * src)
++{
++  GstBaseAudioSrcClass *bclass;
++  GstRingBuffer *buffer = NULL;
++
++  bclass = GST_BASE_AUDIO_SRC_GET_CLASS (src);
++  if (bclass->create_ringbuffer)
++    buffer = bclass->create_ringbuffer (src);
++
++  if (G_LIKELY (buffer))
++    gst_object_set_parent (GST_OBJECT_CAST (buffer), GST_OBJECT_CAST (src));
++
++  return buffer;
++}
++
++static GstStateChangeReturn
++gst_base_audio_src_change_state (GstElement * element,
++    GstStateChange transition)
++{
++  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
++  GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (element);
++
++  switch (transition) {
++    case GST_STATE_CHANGE_NULL_TO_READY:
++      GST_DEBUG_OBJECT (src, "NULL->READY");
++      GST_OBJECT_LOCK (src);
++      if (src->ringbuffer == NULL) {
++        gst_audio_clock_reset (GST_AUDIO_CLOCK (src->clock), 0);
++        src->ringbuffer = gst_base_audio_src_create_ringbuffer (src);
++      }
++      GST_OBJECT_UNLOCK (src);
++      if (!gst_ring_buffer_open_device (src->ringbuffer))
++        goto open_failed;
++      break;
++    case GST_STATE_CHANGE_READY_TO_PAUSED:
++      GST_DEBUG_OBJECT (src, "READY->PAUSED");
++      src->next_sample = -1;
++      gst_ring_buffer_set_flushing (src->ringbuffer, FALSE);
++      gst_ring_buffer_may_start (src->ringbuffer, FALSE);
++      break;
++    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
++      GST_DEBUG_OBJECT (src, "PAUSED->PLAYING");
++      gst_ring_buffer_may_start (src->ringbuffer, TRUE);
++      break;
++    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
++      GST_DEBUG_OBJECT (src, "PLAYING->PAUSED");
++      gst_ring_buffer_may_start (src->ringbuffer, FALSE);
++      gst_ring_buffer_pause (src->ringbuffer);
++      break;
++    case GST_STATE_CHANGE_PAUSED_TO_READY:
++      GST_DEBUG_OBJECT (src, "PAUSED->READY");
++      gst_ring_buffer_set_flushing (src->ringbuffer, TRUE);
++      break;
++    default:
++      break;
++  }
++
++  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
++
++  switch (transition) {
++    case GST_STATE_CHANGE_PAUSED_TO_READY:
++      GST_DEBUG_OBJECT (src, "PAUSED->READY");
++      gst_ring_buffer_release (src->ringbuffer);
++      break;
++    case GST_STATE_CHANGE_READY_TO_NULL:
++      GST_DEBUG_OBJECT (src, "READY->NULL");
++      gst_ring_buffer_close_device (src->ringbuffer);
++      GST_OBJECT_LOCK (src);
++      gst_object_unparent (GST_OBJECT_CAST (src->ringbuffer));
++      src->ringbuffer = NULL;
++      GST_OBJECT_UNLOCK (src);
++      break;
++    default:
++      break;
++  }
++
++  return ret;
++
++  /* ERRORS */
++open_failed:
++  {
++    /* subclass must post a meaningfull error message */
++    GST_DEBUG_OBJECT (src, "open failed");
++    return GST_STATE_CHANGE_FAILURE;
++  }
++
++}
index 0000000,0000000..773c6e8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,127 @@@
++*************** gst_base_audio_src_create (GstBaseSrc * bsrc, guint64 offset, guint length,
++*** 865,935 ****
++          running_time_segment = running_time_sample / sps;
++  
++          /* the segment currently read from the ringbuffer */
++-         current_segment = sample / sps;
++- 
++-         /* the skew we have between running_time and the ringbuffertime */
++-         segment_skew = running_time_segment - current_segment;
++- 
++-         GST_DEBUG_OBJECT (bsrc, "\n running_time = %" GST_TIME_FORMAT
++-             "\n timestamp     = %" GST_TIME_FORMAT
++-             "\n running_time_segment = %d"
++-             "\n current_segment      = %d"
++-             "\n segment_skew         = %d",
++              GST_TIME_ARGS (running_time),
++              GST_TIME_ARGS (timestamp),
++-             running_time_segment, current_segment, segment_skew);
++  
++          /* Resync the ringbuffer if:
++-          * 1. We are more than the length of the ringbuffer in front.
++-          *    The length of the ringbuffer then gets to dictate
++-          *    the threshold for what is concidered "too far ahead"
++-          *
++-          * 2. We are more than the length of the ringbuffer behind.
++           *    The length of the ringbuffer then gets to dictate
++           *    the threshold for what is concidered "too late"
++           *
++-          * 3. If this is our first buffer.
++           *    We know that we should catch up to running_time
++           *    the first time we are ran.
++           */
++-         if ((segment_skew <= -ringbuffer->spec.segtotal) ||
++-             (segment_skew >= ringbuffer->spec.segtotal) ||
++-             (current_segment == 0)) {
++-           gint segments_written;
++-           gint first_segment;
++-           gint last_segment;
++-           gint new_last_segment;
++            gint segment_diff;
++-           gint new_first_segment;
++            guint64 new_sample;
++  
++-           /* we are going to say that the last segment was captured at the current time
++-              (running_time), minus one segment of creation-latency in the ringbuffer.
++-              This can be thought of as: The segment arrived in the ringbuffer at time X, and
++-              that means it was created at time X - (one segment). */
++-           new_last_segment = running_time_segment - 1;
++- 
++-           /* for better readablity */
++-           first_segment = current_segment;
++- 
++-           /* get the amount of segments written from the device by now */
++-           segments_written = g_atomic_int_get (&ringbuffer->segdone);
++- 
++-           /* subtract the base to segments_written to get the number of the
++-              last written segment in the ringbuffer (one segment written = segment 0) */
++-           last_segment = segments_written - ringbuffer->segbase - 1;
++- 
++-           /* we see how many segments the ringbuffer was timeshifted */
++-           segment_diff = new_last_segment - last_segment;
++  
++-           /* we move the first segment an equal amount */
++-           new_first_segment = first_segment + segment_diff;
++  
++-           /* and we also move the segmentbase the same amount */
++-           ringbuffer->segbase -= segment_diff;
++  
++            /* we calculate the new sample value */
++-           new_sample = ((guint64) new_first_segment) * sps;
++  
++            /* and get the relative time to this -> our new timestamp */
++            timestamp =
++--- 874,926 ----
++          running_time_segment = running_time_sample / sps;
++  
++          /* the segment currently read from the ringbuffer */
+++         last_read_segment = sample / sps;
+++ 
+++         /* the skew we have between running_time and the ringbuffertime (last written to) */
+++         segment_skew = running_time_segment - last_written_segment;
+++ 
+++         GST_DEBUG_OBJECT (bsrc,
+++              "\n running_time                                              = %" GST_TIME_FORMAT
+++             "\n timestamp                                                  = %" GST_TIME_FORMAT
+++             "\n running_time_segment                                       = %d"
+++             "\n last_written_segment                                       = %d"
+++             "\n segment_skew (running time segment - last_written_segment) = %d"
+++             "\n last_read_segment                                          = %d",
++              GST_TIME_ARGS (running_time),
++              GST_TIME_ARGS (timestamp),
+++             running_time_segment,
+++             last_written_segment,
+++             segment_skew,
+++             last_read_segment);
++  
++          /* Resync the ringbuffer if:
+++ 
+++          * 1. We are more than the length of the ringbuffer behind.
++           *    The length of the ringbuffer then gets to dictate
++           *    the threshold for what is concidered "too late"
++           *
+++          * 2. If this is our first buffer.
++           *    We know that we should catch up to running_time
++           *    the first time we are ran.
++           */
+++         if ((segment_skew >= ringbuffer->spec.segtotal) ||
+++             (last_read_segment == 0))
+++         {
+++           gint new_read_segment;
++            gint segment_diff;
++            guint64 new_sample;
++  
+++           /* the difference between running_time and the last written segment */
+++           segment_diff = running_time_segment - last_written_segment;
++  
+++           /* advance the ringbuffer */
+++           gst_ring_buffer_advance(ringbuffer, segment_diff);
++  
+++            /* we move the  new read segment to the last known written segment */
+++           new_read_segment = g_atomic_int_get (&ringbuffer->segdone) - ringbuffer->segbase;
++  
++            /* we calculate the new sample value */
+++           new_sample = ((guint64) new_read_segment) * sps;
++  
++            /* and get the relative time to this -> our new timestamp */
++            timestamp =
index 0000000,0000000..6d23e19
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,362 @@@
++/* GStreamer
++ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
++ *                    2005 Wim Taymans <wim@fluendo.com>
++ *
++ * gstaudioringbuffer.c: simple audio ringbuffer base class
++ *
++ * 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.
++ */
++
++
++#include <string.h>
++
++#include "gstringbufferthread.h"
++
++GST_DEBUG_CATEGORY_STATIC (gst_ring_buffer_thread_debug);
++#define GST_CAT_DEFAULT gst_ring_buffer_thread_debug
++
++static void gst_ring_buffer_thread_class_init (GstRingBufferThreadClass *
++    klass);
++static void gst_ring_buffer_thread_init (GstRingBufferThread * ringbuffer,
++    GstRingBufferThreadClass * klass);
++static void gst_ring_buffer_thread_dispose (GObject * object);
++static void gst_ring_buffer_thread_finalize (GObject * object);
++
++static GstRingBufferClass *ring_parent_class = NULL;
++
++GType
++gst_ring_buffer_thread_get_type (void)
++{
++  static GType ringbuffer_type = 0;
++
++  if (!ringbuffer_type) {
++    static const GTypeInfo ringbuffer_info = {
++      sizeof (GstRingBufferThreadClass),
++      NULL,
++      NULL,
++      (GClassInitFunc) gst_ring_buffer_thread_class_init,
++      NULL,
++      NULL,
++      sizeof (GstRingBufferThread),
++      0,
++      (GInstanceInitFunc) gst_ring_buffer_thread_init,
++      NULL
++    };
++
++    ringbuffer_type =
++        g_type_register_static (GST_TYPE_OBJECT, "GstRingBufferThread",
++        &ringbuffer_info, 0);
++
++    GST_DEBUG_CATEGORY_INIT (gst_ring_buffer_thread_debug, "ringbufferthread",
++        0, "ringbuffer thread");
++  }
++  return ringbuffer_type;
++}
++
++static void
++gst_ring_buffer_thread_class_init (GstRingBufferThreadClass * klass)
++{
++  GObjectClass *gobject_class;
++
++  gobject_class = (GObjectClass *) klass;
++
++  ring_parent_class = g_type_class_peek_parent (klass);
++
++  gobject_class->dispose = gst_ring_buffer_thread_dispose;
++  gobject_class->finalize = gst_ring_buffer_thread_finalize;
++}
++
++typedef gint (*ProcessFunc) (GstAudioRingBuffer * buf, gpointer data,
++    guint length);
++
++/* this internal thread does nothing else but write samples to the audio device.
++ * It will write each segment in the ringbuffer and will update the play
++ * pointer.
++ * The start/stop methods control the thread.
++ */
++static void
++ring_buffer_thread_thread_func (GstRingBufferThread * thread)
++{
++  GstElement *parent = NULL;
++  GstMessage *message;
++  GValue val = { 0 };
++  GstAudioRingBuffer *capture, *playback;
++  ProcessFunc writefunc = NULL, readfunc = NULL;
++  gint preroll = 1;
++
++  GST_DEBUG_OBJECT (thread, "enter thread");
++
++  GST_OBJECT_LOCK (thread);
++  GST_DEBUG_OBJECT (thread, "signal wait");
++  GST_RING_BUFFER_THREAD_SIGNAL (thread);
++  if ((capture = thread->capture))
++    gst_object_ref (capture);
++  if ((playback = thread->playback))
++    gst_object_ref (playback);
++  GST_OBJECT_UNLOCK (thread);
++
++  if (capture)
++    readfunc = GST_AUDIO_RING_BUFFER_GET_CLASS (capture)->process;
++  if (playback)
++    writefunc = GST_AUDIO_RING_BUFFER_GET_CLASS (playback)->process;
++
++  if (parent) {
++    g_value_init (&val, G_TYPE_POINTER);
++    g_value_set_pointer (&val, thread->thread);
++    message = gst_message_new_stream_status (GST_OBJECT_CAST (thread),
++        GST_STREAM_STATUS_TYPE_ENTER, NULL);
++    gst_message_set_stream_status_object (message, &val);
++    GST_DEBUG_OBJECT (thread, "posting ENTER stream status");
++    gst_element_post_message (parent, message);
++  }
++
++  while (TRUE) {
++    gint left, processed;
++    guint8 *read_ptr, *write_ptr;
++    gint read_seg, write_seg;
++    gint read_len, write_len;
++    gboolean read_active, write_active;
++
++    if (playback)
++      write_active =
++          gst_ring_buffer_prepare_read (GST_RING_BUFFER_CAST (playback),
++          &write_seg, &write_ptr, &write_len);
++    else
++      write_active = FALSE;
++
++    if (playback) {
++      if (!write_active) {
++        write_ptr = GST_RING_BUFFER_CAST (playback)->empty_seg;
++        write_len = GST_RING_BUFFER_CAST (playback)->spec.segsize;
++      }
++
++      left = write_len;
++      do {
++        processed = writefunc (playback, write_ptr, left);
++        GST_LOG_OBJECT (thread, "written %d bytes of %d from segment %d",
++            processed, left, write_seg);
++        if (processed < 0 || processed > left) {
++          /* might not be critical, it e.g. happens when aborting playback */
++          GST_WARNING_OBJECT (thread,
++              "error writing data in %s (reason: %s), skipping segment (left: %d, processed: %d)",
++              GST_DEBUG_FUNCPTR_NAME (writefunc),
++              (errno > 1 ? g_strerror (errno) : "unknown"), left, processed);
++          break;
++        }
++        left -= processed;
++        write_ptr += processed;
++      } while (left > 0);
++
++      /* we wrote one segment */
++      gst_ring_buffer_advance (GST_RING_BUFFER_CAST (playback), 1);
++
++      if (preroll > 0) {
++        /* do not start reading until we have read enough data */
++        preroll--;
++        GST_DEBUG_OBJECT (thread, "need more preroll");
++        continue;
++      }
++    }
++
++
++    if (capture)
++      read_active =
++          gst_ring_buffer_prepare_read (GST_RING_BUFFER_CAST (capture),
++          &read_seg, &read_ptr, &read_len);
++    else
++      read_active = FALSE;
++
++    if (capture) {
++      left = read_len;
++      do {
++        processed = readfunc (capture, read_ptr, left);
++        GST_LOG_OBJECT (thread, "read %d bytes of %d from segment %d",
++            processed, left, read_seg);
++        if (processed < 0 || processed > left) {
++          /* might not be critical, it e.g. happens when aborting playback */
++          GST_WARNING_OBJECT (thread,
++              "error reading data in %s (reason: %s), skipping segment (left: %d, processed: %d)",
++              GST_DEBUG_FUNCPTR_NAME (readfunc),
++              (errno > 1 ? g_strerror (errno) : "unknown"), left, processed);
++          break;
++        }
++        left -= processed;
++        read_ptr += processed;
++      } while (left > 0);
++
++      if (read_active)
++        /* we read one segment */
++        gst_ring_buffer_advance (GST_RING_BUFFER_CAST (capture), 1);
++    }
++
++    if (!read_active && !write_active) {
++      GST_OBJECT_LOCK (thread);
++      if (!thread->running)
++        goto stop_running;
++      GST_DEBUG_OBJECT (thread, "signal wait");
++      GST_RING_BUFFER_THREAD_SIGNAL (thread);
++      GST_DEBUG_OBJECT (thread, "wait for action");
++      GST_RING_BUFFER_THREAD_WAIT (thread);
++      GST_DEBUG_OBJECT (thread, "got signal");
++      if (!thread->running)
++        goto stop_running;
++      GST_DEBUG_OBJECT (thread, "continue running");
++      GST_OBJECT_UNLOCK (thread);
++    }
++  }
++
++  /* Will never be reached */
++  g_assert_not_reached ();
++  return;
++
++  /* ERROR */
++stop_running:
++  {
++    GST_OBJECT_UNLOCK (thread);
++    GST_DEBUG_OBJECT (thread, "stop running, exit thread");
++    if (parent) {
++      message = gst_message_new_stream_status (GST_OBJECT_CAST (thread),
++          GST_STREAM_STATUS_TYPE_LEAVE, GST_ELEMENT_CAST (thread));
++      gst_message_set_stream_status_object (message, &val);
++      GST_DEBUG_OBJECT (thread, "posting LEAVE stream status");
++      gst_element_post_message (parent, message);
++    }
++    return;
++  }
++}
++
++static void
++gst_ring_buffer_thread_init (GstRingBufferThread * thread,
++    GstRingBufferThreadClass * g_class)
++{
++  thread->running = FALSE;
++  thread->cond = g_cond_new ();
++}
++
++static void
++gst_ring_buffer_thread_dispose (GObject * object)
++{
++  GstRingBufferThread *thread = GST_RING_BUFFER_THREAD_CAST (object);
++
++  GST_OBJECT_LOCK (thread);
++  if (thread->playback) {
++    gst_object_unref (thread->playback);
++    thread->playback = NULL;
++  }
++  if (thread->capture) {
++    gst_object_unref (thread->capture);
++    thread->capture = NULL;
++  }
++  GST_OBJECT_UNLOCK (thread);
++
++  G_OBJECT_CLASS (ring_parent_class)->dispose (object);
++}
++
++static void
++gst_ring_buffer_thread_finalize (GObject * object)
++{
++  GstRingBufferThread *thread = GST_RING_BUFFER_THREAD_CAST (object);
++
++  g_cond_free (thread->cond);
++
++  G_OBJECT_CLASS (ring_parent_class)->finalize (object);
++}
++
++gboolean
++gst_ring_buffer_thread_activate (GstRingBufferThread * thread, gboolean active)
++{
++  GError *error = NULL;
++
++  GST_OBJECT_LOCK (thread);
++  if (active) {
++    if (thread->active_count == 0) {
++      thread->running = TRUE;
++      GST_DEBUG_OBJECT (thread, "starting thread");
++      thread->thread =
++          g_thread_create ((GThreadFunc) ring_buffer_thread_thread_func, thread,
++          TRUE, &error);
++      if (!thread->thread || error != NULL)
++        goto thread_failed;
++
++      GST_DEBUG_OBJECT (thread, "waiting for thread");
++      /* the object lock is taken */
++      GST_RING_BUFFER_THREAD_WAIT (thread);
++      GST_DEBUG_OBJECT (thread, "thread is started");
++    }
++    thread->active_count++;
++  } else {
++    if (thread->active_count == 1) {
++      thread->running = FALSE;
++      GST_DEBUG_OBJECT (thread, "signal wait");
++      GST_RING_BUFFER_THREAD_SIGNAL (thread);
++      GST_OBJECT_UNLOCK (thread);
++
++      /* join the thread */
++      g_thread_join (thread->thread);
++
++      GST_OBJECT_LOCK (thread);
++    }
++    thread->active_count--;
++  }
++  GST_OBJECT_UNLOCK (thread);
++
++  return TRUE;
++
++  /* ERRORS */
++thread_failed:
++  {
++    if (error)
++      GST_ERROR_OBJECT (thread, "could not create thread %s", error->message);
++    else
++      GST_ERROR_OBJECT (thread, "could not create thread for unknown reason");
++    thread->running = FALSE;
++    GST_OBJECT_UNLOCK (thread);
++    return FALSE;
++  }
++}
++
++gboolean
++gst_ring_buffer_thread_set_ringbuffer (GstRingBufferThread * thread,
++    GstAudioRingBuffer * buf)
++{
++  GstAudioRingBuffer *old, **new;
++
++  g_return_val_if_fail (GST_IS_RING_BUFFER_THREAD (thread), FALSE);
++
++  if (buf->mode == GST_AUDIO_RING_BUFFER_MODE_PLAYBACK)
++    new = &thread->playback;
++  else
++    new = &thread->capture;
++
++  old = *new;
++  if (buf)
++    gst_object_ref (buf);
++  *new = buf;
++  if (old)
++    gst_object_unref (old);
++
++  return TRUE;
++}
++
++gboolean
++gst_ring_buffer_thread_start (GstRingBufferThread * thread)
++{
++  g_return_val_if_fail (GST_IS_RING_BUFFER_THREAD (thread), FALSE);
++
++  GST_RING_BUFFER_THREAD_SIGNAL (thread);
++
++  return TRUE;
++}
index 0000000,0000000..70498c5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,93 @@@
++/* GStreamer
++ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
++ *                    2005 Wim Taymans <wim@fluendo.com>
++ *
++ * gstaudioringbuffer.h:
++ *
++ * 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_RING_BUFFER_THREAD_H__
++#define __GST_RING_BUFFER_THREAD_H__
++
++#include <gst/gst.h>
++
++G_BEGIN_DECLS
++
++#define GST_TYPE_RING_BUFFER_THREAD             (gst_ring_buffer_thread_get_type())
++#define GST_RING_BUFFER_THREAD(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RING_BUFFER_THREAD,GstRingBufferThread))
++#define GST_RING_BUFFER_THREAD_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RING_BUFFER_THREAD,GstRingBufferThreadClass))
++#define GST_RING_BUFFER_THREAD_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_RING_BUFFER_THREAD,GstRingBufferThreadClass))
++#define GST_IS_RING_BUFFER_THREAD(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RING_BUFFER_THREAD))
++#define GST_IS_RING_BUFFER_THREAD_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RING_BUFFER_THREAD))
++#define GST_RING_BUFFER_THREAD_CAST(obj)        ((GstRingBufferThread *)obj)
++
++typedef struct _GstRingBufferThread GstRingBufferThread;
++typedef struct _GstRingBufferThreadClass GstRingBufferThreadClass;
++
++#include <gst/audio/gstaudioringbuffer.h>
++
++#define GST_RING_BUFFER_THREAD_GET_COND(buf) (((GstRingBufferThread *)buf)->cond)
++#define GST_RING_BUFFER_THREAD_WAIT(buf)     (g_cond_wait (GST_RING_BUFFER_THREAD_GET_COND (buf), GST_OBJECT_GET_LOCK (buf)))
++#define GST_RING_BUFFER_THREAD_SIGNAL(buf)   (g_cond_signal (GST_RING_BUFFER_THREAD_GET_COND (buf)))
++#define GST_RING_BUFFER_THREAD_BROADCAST(buf)(g_cond_broadcast (GST_RING_BUFFER_THREAD_GET_COND (buf)))
++
++/**
++ * GstRingBufferThread:
++ *
++ * Opaque #GstRingBufferThread.
++ */
++struct _GstRingBufferThread {
++  GstObject       parent;
++
++  gint       active_count;
++
++  /*< private >*/ /* with LOCK */
++  GThread   *thread;
++  gboolean   running;
++  GCond     *cond;
++
++  GstAudioRingBuffer *playback;
++  GstAudioRingBuffer *capture;
++
++  /*< private >*/
++  gpointer _gst_reserved[GST_PADDING];
++};
++
++/**
++ * GstRingBufferThreadClass:
++ * @parent_class: the parent class structure.
++ *
++ * #GstRingBufferThread class. Override the vmethods to implement functionality.
++ */
++struct _GstRingBufferThreadClass {
++  GstObjectClass parent_class;
++
++  /*< private >*/
++  gpointer _gst_reserved[GST_PADDING];
++};
++
++GType gst_ring_buffer_thread_get_type(void);
++
++gboolean gst_ring_buffer_thread_set_ringbuffer (GstRingBufferThread *thread, GstAudioRingBuffer *buf);
++
++gboolean gst_ring_buffer_thread_activate (GstRingBufferThread *thread, gboolean active);
++
++gboolean gst_ring_buffer_thread_start (GstRingBufferThread *thread);
++
++G_END_DECLS
++
++#endif /* __GST_RING_BUFFER_THREAD_H__ */
index 0000000,0000000..0a2041a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,617 @@@
++? foo
++Index: Makefile.am
++===================================================================
++RCS file: /cvs/gstreamer/gst-plugins-base/gst-libs/gst/cdda/Makefile.am,v
++retrieving revision 1.4
++diff -u -p -u -p -r1.4 Makefile.am
++--- Makefile.am       3 Apr 2008 06:39:27 -0000       1.4
+++++ Makefile.am       21 Aug 2008 14:17:21 -0000
++@@ -1,9 +1,7 @@
++ lib_LTLIBRARIES = libgstcdda-@GST_MAJORMINOR@.la
++ 
++ libgstcdda_@GST_MAJORMINOR@_la_SOURCES = \
++-     gstcddabasesrc.c \
++-     sha1.c \
++-     sha1.h
+++     gstcddabasesrc.c
++ 
++ libgstcdda_@GST_MAJORMINOR@includedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/cdda
++ libgstcdda_@GST_MAJORMINOR@include_HEADERS = \
++Index: gstcddabasesrc.c
++===================================================================
++RCS file: /cvs/gstreamer/gst-plugins-base/gst-libs/gst/cdda/gstcddabasesrc.c,v
++retrieving revision 1.19
++diff -u -p -u -p -r1.19 gstcddabasesrc.c
++--- gstcddabasesrc.c  28 May 2008 15:48:33 -0000      1.19
+++++ gstcddabasesrc.c  21 Aug 2008 14:17:21 -0000
++@@ -1084,36 +1084,35 @@ cddb_sum (gint n)
++   return ret;
++ }
++ 
++-#include "sha1.h"
++-
++ static void
++ gst_cddabasesrc_calculate_musicbrainz_discid (GstCddaBaseSrc * src)
++ {
++   GString *s;
++-  SHA_INFO sha;
+++  GChecksum *sha;
++   guchar digest[20];
++   gchar *ptr;
++   gchar tmp[9];
++   gulong i;
++   guint leadout_sector;
+++  gsize digest_len;
++ 
++   s = g_string_new (NULL);
++ 
++   leadout_sector = src->tracks[src->num_tracks - 1].end + 1 + CD_MSF_OFFSET;
++ 
++   /* generate SHA digest */
++-  sha_init (&sha);
+++  sha = g_checksum_new (G_CHECKSUM_SHA1);
++   g_snprintf (tmp, sizeof (tmp), "%02X", src->tracks[0].num);
++   g_string_append_printf (s, "%02X", src->tracks[0].num);
++-  sha_update (&sha, (SHA_BYTE *) tmp, 2);
+++  g_checksum_update (sha, (guchar *) tmp, 2);
++ 
++   g_snprintf (tmp, sizeof (tmp), "%02X", src->tracks[src->num_tracks - 1].num);
++   g_string_append_printf (s, " %02X", src->tracks[src->num_tracks - 1].num);
++-  sha_update (&sha, (SHA_BYTE *) tmp, 2);
+++  g_checksum_update (sha, (guchar *) tmp, 2);
++ 
++   g_snprintf (tmp, sizeof (tmp), "%08X", leadout_sector);
++   g_string_append_printf (s, " %08X", leadout_sector);
++-  sha_update (&sha, (SHA_BYTE *) tmp, 8);
+++  g_checksum_update (sha, (guchar *) tmp, 8);
++ 
++   for (i = 0; i < 99; i++) {
++     if (i < src->num_tracks) {
++@@ -1121,15 +1120,17 @@ gst_cddabasesrc_calculate_musicbrainz_di
++ 
++       g_snprintf (tmp, sizeof (tmp), "%08X", frame_offset);
++       g_string_append_printf (s, " %08X", frame_offset);
++-      sha_update (&sha, (SHA_BYTE *) tmp, 8);
+++      g_checksum_update (sha, (guchar *) tmp, 8);
++     } else {
++-      sha_update (&sha, (SHA_BYTE *) "00000000", 8);
+++      g_checksum_update (sha, (guchar *) "00000000", 8);
++     }
++   }
++-  sha_final (digest, &sha);
+++  digest_len = 20;
+++  g_checksum_get_digest (sha, (guint8 *) &digest, &digest_len);
++ 
++   /* re-encode to base64 */
++-  ptr = g_base64_encode (digest, 20);
+++  ptr = g_base64_encode (digest, digest_len);
+++  g_checksum_free (sha);
++   i = strlen (ptr);
++ 
++   g_assert (i < sizeof (src->mb_discid) + 1);
++Index: sha1.c
++===================================================================
++RCS file: sha1.c
++diff -N sha1.c
++--- sha1.c    27 Feb 2008 10:42:08 -0000      1.2
+++++ /dev/null 1 Jan 1970 00:00:00 -0000
++@@ -1,450 +0,0 @@
++-/* (PD) 2001 The Bitzi Corporation
++- * Please see file COPYING or http://bitzi.com/publicdomain 
++- * for more info.
++- *
++- * NIST Secure Hash Algorithm 
++- * heavily modified by Uwe Hollerbach <uh@alumni.caltech edu> 
++- * from Peter C. Gutmann's implementation as found in 
++- * Applied Cryptography by Bruce Schneier 
++- * Further modifications to include the "UNRAVEL" stuff, below 
++- *
++- * This code is in the public domain 
++- *
++- * $Id: sha1.c,v 1.2 2008-02-27 10:42:08 slomo Exp $
++- */
++-
++-#ifdef HAVE_CONFIG_H
++-#include "config.h"
++-#endif
++-#include <glib.h>
++-#define SHA_BYTE_ORDER G_BYTE_ORDER
++-
++-#include <string.h>
++-#include "sha1.h"
++-
++-/* UNRAVEL should be fastest & biggest */
++-/* UNROLL_LOOPS should be just as big, but slightly slower */
++-/* both undefined should be smallest and slowest */
++-
++-#define UNRAVEL
++-/* #define UNROLL_LOOPS */
++-
++-/* SHA f()-functions */
++-
++-#define f1(x,y,z)    ((x & y) | (~x & z))
++-#define f2(x,y,z)    (x ^ y ^ z)
++-#define f3(x,y,z)    ((x & y) | (x & z) | (y & z))
++-#define f4(x,y,z)    (x ^ y ^ z)
++-
++-/* SHA constants */
++-
++-#define CONST1               0x5a827999L
++-#define CONST2               0x6ed9eba1L
++-#define CONST3               0x8f1bbcdcL
++-#define CONST4               0xca62c1d6L
++-
++-/* truncate to 32 bits -- should be a null op on 32-bit machines */
++-
++-#define T32(x)       ((x) & 0xffffffffL)
++-
++-/* 32-bit rotate */
++-
++-#define R32(x,n)     T32(((x << n) | (x >> (32 - n))))
++-
++-/* the generic case, for when the overall rotation is not unraveled */
++-
++-#define FG(n)        \
++-    T = T32(R32(A,5) + f##n(B,C,D) + E + *WP++ + CONST##n);  \
++-    E = D; D = C; C = R32(B,30); B = A; A = T
++-
++-/* specific cases, for when the overall rotation is unraveled */
++-
++-#define FA(n)        \
++-    T = T32(R32(A,5) + f##n(B,C,D) + E + *WP++ + CONST##n); B = R32(B,30)
++-
++-#define FB(n)        \
++-    E = T32(R32(T,5) + f##n(A,B,C) + D + *WP++ + CONST##n); A = R32(A,30)
++-
++-#define FC(n)        \
++-    D = T32(R32(E,5) + f##n(T,A,B) + C + *WP++ + CONST##n); T = R32(T,30)
++-
++-#define FD(n)        \
++-    C = T32(R32(D,5) + f##n(E,T,A) + B + *WP++ + CONST##n); E = R32(E,30)
++-
++-#define FE(n)        \
++-    B = T32(R32(C,5) + f##n(D,E,T) + A + *WP++ + CONST##n); D = R32(D,30)
++-
++-#define FT(n)        \
++-    A = T32(R32(B,5) + f##n(C,D,E) + T + *WP++ + CONST##n); C = R32(C,30)
++-
++-/* do SHA transformation */
++-
++-static void
++-sha_transform (SHA_INFO * sha_info)
++-{
++-  int i;
++-  SHA_BYTE *dp;
++-  SHA_LONG T, A, B, C, D, E, W[80], *WP;
++-
++-  dp = sha_info->data;
++-
++-/*
++-the following makes sure that at least one code block below is
++-traversed or an error is reported, without the necessity for nested
++-preprocessor if/else/endif blocks, which are a great pain in the
++-nether regions of the anatomy...
++-*/
++-#undef SWAP_DONE
++-
++-#if (SHA_BYTE_ORDER == 1234)
++-#define SWAP_DONE
++-  for (i = 0; i < 16; ++i) {
++-    memcpy (&T, dp, sizeof (SHA_LONG));
++-    dp += 4;
++-    W[i] = ((T << 24) & 0xff000000) | ((T << 8) & 0x00ff0000) |
++-        ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff);
++-  }
++-#endif /* SHA_BYTE_ORDER == 1234 */
++-
++-#if (SHA_BYTE_ORDER == 4321)
++-#define SWAP_DONE
++-  for (i = 0; i < 16; ++i) {
++-    memcpy (&T, dp, sizeof (SHA_LONG));
++-    dp += 4;
++-    W[i] = T32 (T);
++-  }
++-#endif /* SHA_BYTE_ORDER == 4321 */
++-
++-#if (SHA_BYTE_ORDER == 12345678)
++-#define SWAP_DONE
++-  for (i = 0; i < 16; i += 2) {
++-    memcpy (&T, dp, sizeof (SHA_LONG));
++-    dp += 8;
++-    W[i] = ((T << 24) & 0xff000000) | ((T << 8) & 0x00ff0000) |
++-        ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff);
++-    T >>= 32;
++-    W[i + 1] = ((T << 24) & 0xff000000) | ((T << 8) & 0x00ff0000) |
++-        ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff);
++-  }
++-#endif /* SHA_BYTE_ORDER == 12345678 */
++-
++-#if (SHA_BYTE_ORDER == 87654321)
++-#define SWAP_DONE
++-  for (i = 0; i < 16; i += 2) {
++-    memcpy (&T, dp, sizeof (SHA_LONG));
++-    dp += 8;
++-    W[i] = T32 (T >> 32);
++-    W[i + 1] = T32 (T);
++-  }
++-#endif /* SHA_BYTE_ORDER == 87654321 */
++-
++-#ifndef SWAP_DONE
++-#error Unknown byte order -- you need to add code here
++-#endif /* SWAP_DONE */
++-
++-  for (i = 16; i < 80; ++i) {
++-    W[i] = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16];
++-#if (SHA_VERSION == 1)
++-    W[i] = R32 (W[i], 1);
++-#endif /* SHA_VERSION */
++-  }
++-  A = sha_info->digest[0];
++-  B = sha_info->digest[1];
++-  C = sha_info->digest[2];
++-  D = sha_info->digest[3];
++-  E = sha_info->digest[4];
++-  WP = W;
++-#ifdef UNRAVEL
++-  FA (1);
++-  FB (1);
++-  FC (1);
++-  FD (1);
++-  FE (1);
++-  FT (1);
++-  FA (1);
++-  FB (1);
++-  FC (1);
++-  FD (1);
++-  FE (1);
++-  FT (1);
++-  FA (1);
++-  FB (1);
++-  FC (1);
++-  FD (1);
++-  FE (1);
++-  FT (1);
++-  FA (1);
++-  FB (1);
++-  FC (2);
++-  FD (2);
++-  FE (2);
++-  FT (2);
++-  FA (2);
++-  FB (2);
++-  FC (2);
++-  FD (2);
++-  FE (2);
++-  FT (2);
++-  FA (2);
++-  FB (2);
++-  FC (2);
++-  FD (2);
++-  FE (2);
++-  FT (2);
++-  FA (2);
++-  FB (2);
++-  FC (2);
++-  FD (2);
++-  FE (3);
++-  FT (3);
++-  FA (3);
++-  FB (3);
++-  FC (3);
++-  FD (3);
++-  FE (3);
++-  FT (3);
++-  FA (3);
++-  FB (3);
++-  FC (3);
++-  FD (3);
++-  FE (3);
++-  FT (3);
++-  FA (3);
++-  FB (3);
++-  FC (3);
++-  FD (3);
++-  FE (3);
++-  FT (3);
++-  FA (4);
++-  FB (4);
++-  FC (4);
++-  FD (4);
++-  FE (4);
++-  FT (4);
++-  FA (4);
++-  FB (4);
++-  FC (4);
++-  FD (4);
++-  FE (4);
++-  FT (4);
++-  FA (4);
++-  FB (4);
++-  FC (4);
++-  FD (4);
++-  FE (4);
++-  FT (4);
++-  FA (4);
++-  FB (4);
++-  sha_info->digest[0] = T32 (sha_info->digest[0] + E);
++-  sha_info->digest[1] = T32 (sha_info->digest[1] + T);
++-  sha_info->digest[2] = T32 (sha_info->digest[2] + A);
++-  sha_info->digest[3] = T32 (sha_info->digest[3] + B);
++-  sha_info->digest[4] = T32 (sha_info->digest[4] + C);
++-#else /* !UNRAVEL */
++-#ifdef UNROLL_LOOPS
++-  FG (1);
++-  FG (1);
++-  FG (1);
++-  FG (1);
++-  FG (1);
++-  FG (1);
++-  FG (1);
++-  FG (1);
++-  FG (1);
++-  FG (1);
++-  FG (1);
++-  FG (1);
++-  FG (1);
++-  FG (1);
++-  FG (1);
++-  FG (1);
++-  FG (1);
++-  FG (1);
++-  FG (1);
++-  FG (1);
++-  FG (2);
++-  FG (2);
++-  FG (2);
++-  FG (2);
++-  FG (2);
++-  FG (2);
++-  FG (2);
++-  FG (2);
++-  FG (2);
++-  FG (2);
++-  FG (2);
++-  FG (2);
++-  FG (2);
++-  FG (2);
++-  FG (2);
++-  FG (2);
++-  FG (2);
++-  FG (2);
++-  FG (2);
++-  FG (2);
++-  FG (3);
++-  FG (3);
++-  FG (3);
++-  FG (3);
++-  FG (3);
++-  FG (3);
++-  FG (3);
++-  FG (3);
++-  FG (3);
++-  FG (3);
++-  FG (3);
++-  FG (3);
++-  FG (3);
++-  FG (3);
++-  FG (3);
++-  FG (3);
++-  FG (3);
++-  FG (3);
++-  FG (3);
++-  FG (3);
++-  FG (4);
++-  FG (4);
++-  FG (4);
++-  FG (4);
++-  FG (4);
++-  FG (4);
++-  FG (4);
++-  FG (4);
++-  FG (4);
++-  FG (4);
++-  FG (4);
++-  FG (4);
++-  FG (4);
++-  FG (4);
++-  FG (4);
++-  FG (4);
++-  FG (4);
++-  FG (4);
++-  FG (4);
++-  FG (4);
++-#else /* !UNROLL_LOOPS */
++-  for (i = 0; i < 20; ++i) {
++-    FG (1);
++-  }
++-  for (i = 20; i < 40; ++i) {
++-    FG (2);
++-  }
++-  for (i = 40; i < 60; ++i) {
++-    FG (3);
++-  }
++-  for (i = 60; i < 80; ++i) {
++-    FG (4);
++-  }
++-#endif /* !UNROLL_LOOPS */
++-  sha_info->digest[0] = T32 (sha_info->digest[0] + A);
++-  sha_info->digest[1] = T32 (sha_info->digest[1] + B);
++-  sha_info->digest[2] = T32 (sha_info->digest[2] + C);
++-  sha_info->digest[3] = T32 (sha_info->digest[3] + D);
++-  sha_info->digest[4] = T32 (sha_info->digest[4] + E);
++-#endif /* !UNRAVEL */
++-}
++-
++-/* initialize the SHA digest */
++-
++-void
++-sha_init (SHA_INFO * sha_info)
++-{
++-  sha_info->digest[0] = 0x67452301L;
++-  sha_info->digest[1] = 0xefcdab89L;
++-  sha_info->digest[2] = 0x98badcfeL;
++-  sha_info->digest[3] = 0x10325476L;
++-  sha_info->digest[4] = 0xc3d2e1f0L;
++-  sha_info->count_lo = 0L;
++-  sha_info->count_hi = 0L;
++-  sha_info->local = 0;
++-}
++-
++-/* update the SHA digest */
++-
++-void
++-sha_update (SHA_INFO * sha_info, SHA_BYTE * buffer, int count)
++-{
++-  int i;
++-  SHA_LONG clo;
++-
++-  clo = T32 (sha_info->count_lo + ((SHA_LONG) count << 3));
++-  if (clo < sha_info->count_lo) {
++-    ++sha_info->count_hi;
++-  }
++-  sha_info->count_lo = clo;
++-  sha_info->count_hi += (SHA_LONG) count >> 29;
++-  if (sha_info->local) {
++-    i = SHA_BLOCKSIZE - sha_info->local;
++-    if (i > count) {
++-      i = count;
++-    }
++-    memcpy (((SHA_BYTE *) sha_info->data) + sha_info->local, buffer, i);
++-    count -= i;
++-    buffer += i;
++-    sha_info->local += i;
++-    if (sha_info->local == SHA_BLOCKSIZE) {
++-      sha_transform (sha_info);
++-    } else {
++-      return;
++-    }
++-  }
++-  while (count >= SHA_BLOCKSIZE) {
++-    memcpy (sha_info->data, buffer, SHA_BLOCKSIZE);
++-    buffer += SHA_BLOCKSIZE;
++-    count -= SHA_BLOCKSIZE;
++-    sha_transform (sha_info);
++-  }
++-  memcpy (sha_info->data, buffer, count);
++-  sha_info->local = count;
++-}
++-
++-/* finish computing the SHA digest */
++-
++-void
++-sha_final (unsigned char digest[20], SHA_INFO * sha_info)
++-{
++-  int count;
++-  SHA_LONG lo_bit_count, hi_bit_count;
++-
++-  lo_bit_count = sha_info->count_lo;
++-  hi_bit_count = sha_info->count_hi;
++-  count = (int) ((lo_bit_count >> 3) & 0x3f);
++-  ((SHA_BYTE *) sha_info->data)[count++] = 0x80;
++-  if (count > SHA_BLOCKSIZE - 8) {
++-    memset (((SHA_BYTE *) sha_info->data) + count, 0, SHA_BLOCKSIZE - count);
++-    sha_transform (sha_info);
++-    memset ((SHA_BYTE *) sha_info->data, 0, SHA_BLOCKSIZE - 8);
++-  } else {
++-    memset (((SHA_BYTE *) sha_info->data) + count, 0,
++-        SHA_BLOCKSIZE - 8 - count);
++-  }
++-  sha_info->data[56] = (unsigned char) ((hi_bit_count >> 24) & 0xff);
++-  sha_info->data[57] = (unsigned char) ((hi_bit_count >> 16) & 0xff);
++-  sha_info->data[58] = (unsigned char) ((hi_bit_count >> 8) & 0xff);
++-  sha_info->data[59] = (unsigned char) ((hi_bit_count >> 0) & 0xff);
++-  sha_info->data[60] = (unsigned char) ((lo_bit_count >> 24) & 0xff);
++-  sha_info->data[61] = (unsigned char) ((lo_bit_count >> 16) & 0xff);
++-  sha_info->data[62] = (unsigned char) ((lo_bit_count >> 8) & 0xff);
++-  sha_info->data[63] = (unsigned char) ((lo_bit_count >> 0) & 0xff);
++-  sha_transform (sha_info);
++-  digest[0] = (unsigned char) ((sha_info->digest[0] >> 24) & 0xff);
++-  digest[1] = (unsigned char) ((sha_info->digest[0] >> 16) & 0xff);
++-  digest[2] = (unsigned char) ((sha_info->digest[0] >> 8) & 0xff);
++-  digest[3] = (unsigned char) ((sha_info->digest[0]) & 0xff);
++-  digest[4] = (unsigned char) ((sha_info->digest[1] >> 24) & 0xff);
++-  digest[5] = (unsigned char) ((sha_info->digest[1] >> 16) & 0xff);
++-  digest[6] = (unsigned char) ((sha_info->digest[1] >> 8) & 0xff);
++-  digest[7] = (unsigned char) ((sha_info->digest[1]) & 0xff);
++-  digest[8] = (unsigned char) ((sha_info->digest[2] >> 24) & 0xff);
++-  digest[9] = (unsigned char) ((sha_info->digest[2] >> 16) & 0xff);
++-  digest[10] = (unsigned char) ((sha_info->digest[2] >> 8) & 0xff);
++-  digest[11] = (unsigned char) ((sha_info->digest[2]) & 0xff);
++-  digest[12] = (unsigned char) ((sha_info->digest[3] >> 24) & 0xff);
++-  digest[13] = (unsigned char) ((sha_info->digest[3] >> 16) & 0xff);
++-  digest[14] = (unsigned char) ((sha_info->digest[3] >> 8) & 0xff);
++-  digest[15] = (unsigned char) ((sha_info->digest[3]) & 0xff);
++-  digest[16] = (unsigned char) ((sha_info->digest[4] >> 24) & 0xff);
++-  digest[17] = (unsigned char) ((sha_info->digest[4] >> 16) & 0xff);
++-  digest[18] = (unsigned char) ((sha_info->digest[4] >> 8) & 0xff);
++-  digest[19] = (unsigned char) ((sha_info->digest[4]) & 0xff);
++-}
++Index: sha1.h
++===================================================================
++RCS file: sha1.h
++diff -N sha1.h
++--- sha1.h    13 Dec 2007 10:10:35 -0000      1.2
+++++ /dev/null 1 Jan 1970 00:00:00 -0000
++@@ -1,62 +0,0 @@
++-/* NIST Secure Hash Algorithm */
++-/* heavily modified by Uwe Hollerbach <uh@alumni.caltech edu> */
++-/* from Peter C. Gutmann's implementation as found in */
++-/* Applied Cryptography by Bruce Schneier */
++-/* This code is in the public domain */
++-/* $Id: sha1.h,v 1.2 2007-12-13 10:10:35 tpm Exp $ */
++-
++-#ifndef __GST_CDDA_SHA_H__
++-#define __GST_CDDA_SHA_H__
++-
++-#include <stdlib.h>
++-#include <stdio.h>
++-
++-/* Useful defines & typedefs */
++-typedef unsigned char SHA_BYTE;      /* 8-bit quantity */
++-typedef unsigned long SHA_LONG;      /* 32-or-more-bit quantity */
++-
++-#define SHA_BLOCKSIZE                64
++-#define SHA_DIGESTSIZE               20
++-
++-typedef struct {
++-    SHA_LONG digest[5];              /* message digest */
++-    SHA_LONG count_lo, count_hi;     /* 64-bit bit count */
++-    SHA_BYTE data[SHA_BLOCKSIZE];    /* SHA data buffer */
++-    int local;                       /* unprocessed amount in data */
++-} SHA_INFO;
++-
++-#define sha_init   __gst_cdda_sha_init
++-#define sha_update __gst_cdda_sha_update
++-#define sha_final  __gst_cdda_sha_final
++-
++-void sha_init(SHA_INFO *);
++-void sha_update(SHA_INFO *, SHA_BYTE *, int);
++-void sha_final(unsigned char [20], SHA_INFO *);
++-
++-#define SHA_VERSION 1
++-
++-#ifdef HAVE_CONFIG_H 
++-#include "config.h"
++-
++-
++-#ifdef WORDS_BIGENDIAN
++-#  if SIZEOF_LONG == 4
++-#    define SHA_BYTE_ORDER  4321
++-#  elif SIZEOF_LONG == 8
++-#    define SHA_BYTE_ORDER  87654321
++-#  endif
++-#else
++-#  if SIZEOF_LONG == 4
++-#    define SHA_BYTE_ORDER  1234
++-#  elif SIZEOF_LONG == 8
++-#    define SHA_BYTE_ORDER  12345678
++-#  endif
++-#endif
++-
++-#else
++-
++-#define SHA_BYTE_ORDER 1234
++-
++-#endif
++-
++-#endif /* __GST_CDDA_SHA_H__ */
index 0000000,0000000..3a0bea8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1594 @@@
++/* GStreamer
++ * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
++ * Copyright (C) 2005 Tim-Philipp Müller <tim centricular net>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
++ * Boston, MA 02111-1307, USA.
++ */
++
++/* TODO:
++ *
++ *  - in ::start(), we want to post a tags message with an array or a list
++ *    of tagslists of all tracks, so that applications know at least the
++ *    number of tracks and all track durations immediately without having
++ *    to do any querying. We have to decide what type and name to use for
++ *    this array of track taglists.
++ *
++ *  - FIX cddb discid calculation algorithm for mixed mode CDs - do we use
++ *    offsets and duration of ALL tracks (data + audio) for the CDDB ID
++ *    calculation, or only audio tracks?
++ *
++ *  - Do we really need properties for the TOC bias/offset stuff? Wouldn't
++ *    environment variables make much more sense? Do we need this at all
++ *    (does it only affect ancient hardware?)
++ */
++
++/**
++ * SECTION:gstcddabasesrc
++ * @short_description: Base class for CD digital audio (CDDA) sources
++ *
++ * <refsect2>
++ * <para>
++ * Provides a base class for CDDA sources, which handles things like seeking,
++ * querying, discid calculation, tags, and buffer timestamping.
++ * </para>
++ * <title>Using GstCddaBaseSrc-based elements in applications</title>
++ * <para>
++ * GstCddaBaseSrc registers two #GstFormat<!-- -->s of its own, namely
++ * the "track" format and the "sector" format. Applications will usually
++ * only find the "track" format interesting. You can retrieve that #GstFormat
++ * for use in seek events or queries with gst_format_get_by_nick("track").
++ * </para>
++ * <para>
++ * In order to query the number of tracks, for example, an application would
++ * set the CDDA source element to READY or PAUSED state and then query the
++ * the number of tracks via gst_element_query_duration() using the track
++ * format acquired above. Applications can query the currently playing track
++ * in the same way.
++ * </para>
++ * <para>
++ * Alternatively, applications may retrieve the currently playing track and
++ * the total number of tracks from the taglist that will posted on the bus
++ * whenever the CD is opened or the currently playing track changes. The
++ * taglist will contain GST_TAG_TRACK_NUMBER and GST_TAG_TRACK_COUNT tags.
++ * </para>
++ * <para>
++ * Applications playing back CD audio using playbin and cdda://n URIs should
++ * issue a seek command in track format to change between tracks, rather than
++ * setting a new cdda://n+1 URI on playbin (as setting a new URI on playbin
++ * involves closing and re-opening the CD device, which is much much slower).
++ * </para>
++ * <title>Tags and meta-information</title>
++ * <para>
++ * CDDA sources will automatically emit a number of tags, details about which
++ * can be found in the libgsttag documentation. Those tags are:
++ * #GST_TAG_CDDA_CDDB_DISCID, #GST_TAG_CDDA_CDDB_DISCID_FULL,
++ * #GST_TAG_CDDA_MUSICBRAINZ_DISCID, #GST_TAG_CDDA_MUSICBRAINZ_DISCID_FULL,
++ * among others.
++ * </para>
++ * </refsect2>
++ */
++
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include <string.h>
++#include <stdlib.h>             /* for strtol */
++
++#include "gstcddabasesrc.h"
++#include "gst/gst-i18n-plugin.h"
++
++GST_DEBUG_CATEGORY_STATIC (gst_cdda_base_src_debug);
++#define GST_CAT_DEFAULT gst_cdda_base_src_debug
++
++#define DEFAULT_DEVICE                       "/dev/cdrom"
++
++#define CD_FRAMESIZE_RAW                     (2352)
++
++#define SECTORS_PER_SECOND                   (75)
++#define SECTORS_PER_MINUTE                   (75*60)
++#define SAMPLES_PER_SECTOR                   (CD_FRAMESIZE_RAW >> 2)
++#define TIME_INTERVAL_FROM_SECTORS(sectors)  ((SAMPLES_PER_SECTOR * sectors * GST_SECOND) / 44100)
++#define SECTORS_FROM_TIME_INTERVAL(dtime)    (dtime * 44100 / (SAMPLES_PER_SECTOR * GST_SECOND))
++
++enum
++{
++  ARG_0,
++  ARG_MODE,
++  ARG_DEVICE,
++  ARG_TRACK,
++  ARG_TOC_OFFSET,
++  ARG_TOC_BIAS
++};
++
++static void gst_cdda_base_src_get_property (GObject * object, guint prop_id,
++    GValue * value, GParamSpec * pspec);
++static void gst_cdda_base_src_set_property (GObject * object, guint prop_id,
++    const GValue * value, GParamSpec * pspec);
++static void gst_cdda_base_src_finalize (GObject * obj);
++static const GstQueryType *gst_cdda_base_src_get_query_types (GstPad * pad);
++static gboolean gst_cdda_base_src_query (GstBaseSrc * src, GstQuery * query);
++static gboolean gst_cdda_base_src_handle_event (GstBaseSrc * basesrc,
++    GstEvent * event);
++static gboolean gst_cdda_base_src_do_seek (GstBaseSrc * basesrc,
++    GstSegment * segment);
++static void gst_cdda_base_src_setup_interfaces (GType type);
++static gboolean gst_cdda_base_src_start (GstBaseSrc * basesrc);
++static gboolean gst_cdda_base_src_stop (GstBaseSrc * basesrc);
++static GstFlowReturn gst_cdda_base_src_create (GstPushSrc * pushsrc,
++    GstBuffer ** buf);
++static gboolean gst_cdda_base_src_is_seekable (GstBaseSrc * basesrc);
++static void gst_cdda_base_src_update_duration (GstCddaBaseSrc * src);
++static void gst_cdda_base_src_set_index (GstElement * src, GstIndex * index);
++static GstIndex *gst_cdda_base_src_get_index (GstElement * src);
++
++GST_BOILERPLATE_FULL (GstCddaBaseSrc, gst_cdda_base_src, GstPushSrc,
++    GST_TYPE_PUSH_SRC, gst_cdda_base_src_setup_interfaces);
++
++#define SRC_CAPS \
++  "audio/x-raw-int, "               \
++  "endianness = (int) BYTE_ORDER, " \
++  "signed = (boolean) true, "       \
++  "width = (int) 16, "              \
++  "depth = (int) 16, "              \
++  "rate = (int) 44100, "            \
++  "channels = (int) 2"              \
++
++static GstStaticPadTemplate gst_cdda_base_src_src_template =
++GST_STATIC_PAD_TEMPLATE ("src",
++    GST_PAD_SRC,
++    GST_PAD_ALWAYS,
++    GST_STATIC_CAPS (SRC_CAPS)
++    );
++
++/* our two formats */
++static GstFormat track_format;
++static GstFormat sector_format;
++
++GType
++gst_cdda_base_src_mode_get_type (void)
++{
++  static GType mode_type;       /* 0 */
++  static const GEnumValue modes[] = {
++    {GST_CDDA_BASE_SRC_MODE_NORMAL, "Stream consists of a single track",
++        "normal"},
++    {GST_CDDA_BASE_SRC_MODE_CONTINUOUS, "Stream consists of the whole disc",
++        "continuous"},
++    {0, NULL, NULL}
++  };
++
++  if (mode_type == 0)
++    mode_type = g_enum_register_static ("GstCddaBaseSrcMode", modes);
++
++  return mode_type;
++}
++
++static void
++gst_cdda_base_src_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_cdda_base_src_src_template));
++
++  /* our very own formats */
++  track_format = gst_format_register ("track", "CD track");
++  sector_format = gst_format_register ("sector", "CD sector");
++
++  /* register CDDA tags */
++  gst_tag_register_musicbrainz_tags ();
++
++#if 0
++  ///// FIXME: what type to use here? ///////
++  gst_tag_register (GST_TAG_CDDA_TRACK_TAGS, GST_TAG_FLAG_META, GST_TYPE_TAG_LIST, "track-tags", "CDDA taglist for one track", gst_tag_merge_use_first);        ///////////// FIXME: right function??? ///////
++#endif
++
++  GST_DEBUG_CATEGORY_INIT (gst_cdda_base_src_debug, "cddabasesrc", 0,
++      "CDDA Base Source");
++}
++
++static void
++gst_cdda_base_src_class_init (GstCddaBaseSrcClass * klass)
++{
++  GstElementClass *element_class;
++  GstPushSrcClass *pushsrc_class;
++  GstBaseSrcClass *basesrc_class;
++  GObjectClass *gobject_class;
++
++  gobject_class = (GObjectClass *) klass;
++  element_class = (GstElementClass *) klass;
++  basesrc_class = (GstBaseSrcClass *) klass;
++  pushsrc_class = (GstPushSrcClass *) klass;
++
++  gobject_class->set_property = gst_cdda_base_src_set_property;
++  gobject_class->get_property = gst_cdda_base_src_get_property;
++  gobject_class->finalize = gst_cdda_base_src_finalize;
++
++  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DEVICE,
++      g_param_spec_string ("device", "Device", "CD device location",
++          NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MODE,
++      g_param_spec_enum ("mode", "Mode", "Mode", GST_TYPE_CDDA_BASE_SRC_MODE,
++          GST_CDDA_BASE_SRC_MODE_NORMAL,
++          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++
++  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TRACK,
++      g_param_spec_uint ("track", "Track", "Track", 1, 99, 1,
++          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++
++#if 0
++  /* Do we really need this toc adjustment stuff as properties? does the user
++   * have a chance to set it in practice, e.g. when using sound-juicer, rb,
++   * totem, whatever? Shouldn't we rather use environment variables
++   * for this? (tpm) */
++
++  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TOC_OFFSET,
++      g_param_spec_int ("toc-offset", "Table of contents offset",
++          "Add <n> sectors to the values reported", G_MININT, G_MAXINT, 0,
++          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TOC_BIAS,
++      g_param_spec_boolean ("toc-bias", "Table of contents bias",
++          "Assume that the beginning offset of track 1 as reported in the TOC "
++          "will be addressed as LBA 0.  Necessary for some Toshiba drives to "
++          "get track boundaries", FALSE,
++          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++#endif
++
++  element_class->set_index = GST_DEBUG_FUNCPTR (gst_cdda_base_src_set_index);
++  element_class->get_index = GST_DEBUG_FUNCPTR (gst_cdda_base_src_get_index);
++
++  basesrc_class->start = GST_DEBUG_FUNCPTR (gst_cdda_base_src_start);
++  basesrc_class->stop = GST_DEBUG_FUNCPTR (gst_cdda_base_src_stop);
++  basesrc_class->query = GST_DEBUG_FUNCPTR (gst_cdda_base_src_query);
++  basesrc_class->event = GST_DEBUG_FUNCPTR (gst_cdda_base_src_handle_event);
++  basesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_cdda_base_src_do_seek);
++  basesrc_class->is_seekable =
++      GST_DEBUG_FUNCPTR (gst_cdda_base_src_is_seekable);
++
++  pushsrc_class->create = GST_DEBUG_FUNCPTR (gst_cdda_base_src_create);
++}
++
++static void
++gst_cdda_base_src_init (GstCddaBaseSrc * src, GstCddaBaseSrcClass * klass)
++{
++  gst_pad_set_query_type_function (GST_BASE_SRC_PAD (src),
++      GST_DEBUG_FUNCPTR (gst_cdda_base_src_get_query_types));
++
++  /* we're not live and we operate in time */
++  gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME);
++  gst_base_src_set_live (GST_BASE_SRC (src), FALSE);
++
++  src->device = NULL;
++  src->mode = GST_CDDA_BASE_SRC_MODE_NORMAL;
++  src->uri_track = -1;
++}
++
++static void
++gst_cdda_base_src_finalize (GObject * obj)
++{
++  GstCddaBaseSrc *cddasrc = GST_CDDA_BASE_SRC (obj);
++
++  g_free (cddasrc->uri);
++  g_free (cddasrc->device);
++
++  G_OBJECT_CLASS (parent_class)->finalize (obj);
++}
++
++static void
++gst_cdda_base_src_set_property (GObject * object, guint prop_id,
++    const GValue * value, GParamSpec * pspec)
++{
++  GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (object);
++
++  GST_OBJECT_LOCK (src);
++
++  switch (prop_id) {
++    case ARG_MODE:{
++      src->mode = g_value_get_enum (value);
++      break;
++    }
++    case ARG_DEVICE:{
++      const gchar *dev = g_value_get_string (value);
++
++      g_free (src->device);
++      if (dev && *dev) {
++        src->device = g_strdup (dev);
++      } else {
++        src->device = NULL;
++      }
++      break;
++    }
++    case ARG_TRACK:{
++      guint track = g_value_get_uint (value);
++
++      if (src->num_tracks > 0 && track > src->num_tracks) {
++        g_warning ("Invalid track %u", track);
++      } else if (track > 0 && src->tracks != NULL) {
++        src->cur_sector = src->tracks[track - 1].start;
++        src->uri_track = track;
++      } else {
++        src->uri_track = track; /* seek will be done in start() */
++      }
++      break;
++    }
++    case ARG_TOC_OFFSET:{
++      src->toc_offset = g_value_get_int (value);
++      break;
++    }
++    case ARG_TOC_BIAS:{
++      src->toc_bias = g_value_get_boolean (value);
++      break;
++    }
++    default:{
++      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++      break;
++    }
++  }
++
++  GST_OBJECT_UNLOCK (src);
++}
++
++static void
++gst_cdda_base_src_get_property (GObject * object, guint prop_id,
++    GValue * value, GParamSpec * pspec)
++{
++  GstCddaBaseSrcClass *klass = GST_CDDA_BASE_SRC_GET_CLASS (object);
++  GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (object);
++
++  GST_OBJECT_LOCK (src);
++
++  switch (prop_id) {
++    case ARG_MODE:
++      g_value_set_enum (value, src->mode);
++      break;
++    case ARG_DEVICE:{
++      if (src->device == NULL && klass->get_default_device != NULL) {
++        gchar *d = klass->get_default_device (src);
++
++        if (d != NULL) {
++          g_value_set_string (value, DEFAULT_DEVICE);
++          g_free (d);
++          break;
++        }
++      }
++      if (src->device == NULL)
++        g_value_set_string (value, DEFAULT_DEVICE);
++      else
++        g_value_set_string (value, src->device);
++      break;
++    }
++    case ARG_TRACK:{
++      if (src->num_tracks <= 0 && src->uri_track > 0) {
++        g_value_set_uint (value, src->uri_track);
++      } else {
++        g_value_set_uint (value, src->cur_track + 1);
++      }
++      break;
++    }
++    case ARG_TOC_OFFSET:
++      g_value_set_int (value, src->toc_offset);
++      break;
++    case ARG_TOC_BIAS:
++      g_value_set_boolean (value, src->toc_bias);
++      break;
++    default:{
++      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++      break;
++    }
++  }
++
++  GST_OBJECT_UNLOCK (src);
++}
++
++static gint
++gst_cdda_base_src_get_track_from_sector (GstCddaBaseSrc * src, gint sector)
++{
++  gint i;
++
++  for (i = 0; i < src->num_tracks; ++i) {
++    if (sector >= src->tracks[i].start && sector <= src->tracks[i].end)
++      return i;
++  }
++  return -1;
++}
++
++static const GstQueryType *
++gst_cdda_base_src_get_query_types (GstPad * pad)
++{
++  static const GstQueryType src_query_types[] = {
++    GST_QUERY_DURATION,
++    GST_QUERY_POSITION,
++    GST_QUERY_CONVERT,
++    0
++  };
++
++  return src_query_types;
++}
++
++static gboolean
++gst_cdda_base_src_convert (GstCddaBaseSrc * src, GstFormat src_format,
++    gint64 src_val, GstFormat dest_format, gint64 * dest_val)
++{
++  gboolean started;
++
++  GST_LOG_OBJECT (src, "converting value %" G_GINT64_FORMAT " from %s into %s",
++      src_val, gst_format_get_name (src_format),
++      gst_format_get_name (dest_format));
++
++  if (src_format == dest_format) {
++    *dest_val = src_val;
++    return TRUE;
++  }
++
++  started = GST_OBJECT_FLAG_IS_SET (GST_BASE_SRC (src), GST_BASE_SRC_STARTED);
++
++  if (src_format == track_format) {
++    if (!started)
++      goto not_started;
++    if (src_val < 0 || src_val >= src->num_tracks) {
++      GST_DEBUG_OBJECT (src, "track number %d out of bounds", (gint) src_val);
++      goto wrong_value;
++    }
++    src_format = GST_FORMAT_DEFAULT;
++    src_val = src->tracks[src_val].start * SAMPLES_PER_SECTOR;
++  } else if (src_format == sector_format) {
++    src_format = GST_FORMAT_DEFAULT;
++    src_val = src_val * SAMPLES_PER_SECTOR;
++  }
++
++  if (src_format == dest_format) {
++    *dest_val = src_val;
++    goto done;
++  }
++
++  switch (src_format) {
++    case GST_FORMAT_BYTES:
++      /* convert to samples (4 bytes per sample) */
++      src_val = src_val >> 2;
++      /* fallthrough */
++    case GST_FORMAT_DEFAULT:{
++      switch (dest_format) {
++        case GST_FORMAT_BYTES:{
++          if (src_val < 0) {
++            GST_DEBUG_OBJECT (src, "sample source value negative");
++            goto wrong_value;
++          }
++          *dest_val = src_val << 2;     /* 4 bytes per sample */
++          break;
++        }
++        case GST_FORMAT_TIME:{
++          *dest_val = gst_util_uint64_scale_int (src_val, GST_SECOND, 44100);
++          break;
++        }
++        default:{
++          gint64 sector = src_val / SAMPLES_PER_SECTOR;
++
++          if (dest_format == sector_format) {
++            *dest_val = sector;
++          } else if (dest_format == track_format) {
++            if (!started)
++              goto not_started;
++            *dest_val = gst_cdda_base_src_get_track_from_sector (src, sector);
++          } else {
++            goto unknown_format;
++          }
++          break;
++        }
++      }
++      break;
++    }
++    case GST_FORMAT_TIME:{
++      gint64 sample_offset;
++
++      if (src_val == GST_CLOCK_TIME_NONE) {
++        GST_DEBUG_OBJECT (src, "source time value invalid");
++        goto wrong_value;
++      }
++
++      sample_offset = gst_util_uint64_scale_int (src_val, 44100, GST_SECOND);
++      switch (dest_format) {
++        case GST_FORMAT_BYTES:{
++          *dest_val = sample_offset << 2;       /* 4 bytes per sample */
++          break;
++        }
++        case GST_FORMAT_DEFAULT:{
++          *dest_val = sample_offset;
++          break;
++        }
++        default:{
++          gint64 sector = sample_offset / SAMPLES_PER_SECTOR;
++
++          if (dest_format == sector_format) {
++            *dest_val = sector;
++          } else if (dest_format == track_format) {
++            if (!started)
++              goto not_started;
++            *dest_val = gst_cdda_base_src_get_track_from_sector (src, sector);
++          } else {
++            goto unknown_format;
++          }
++          break;
++        }
++      }
++      break;
++    }
++    default:{
++      goto unknown_format;
++    }
++  }
++
++done:
++  {
++    GST_LOG_OBJECT (src, "returning %" G_GINT64_FORMAT, *dest_val);
++    return TRUE;
++  }
++
++unknown_format:
++  {
++    GST_DEBUG_OBJECT (src, "conversion failed: %s", "unsupported format");
++    return FALSE;
++  }
++
++wrong_value:
++  {
++    GST_DEBUG_OBJECT (src, "conversion failed: %s",
++        "source value not within allowed range");
++    return FALSE;
++  }
++
++not_started:
++  {
++    GST_DEBUG_OBJECT (src, "conversion failed: %s",
++        "cannot do this conversion, device not open");
++    return FALSE;
++  }
++}
++
++static gboolean
++gst_cdda_base_src_query (GstBaseSrc * basesrc, GstQuery * query)
++{
++  GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (basesrc);
++  gboolean started;
++
++  started = GST_OBJECT_FLAG_IS_SET (basesrc, GST_BASE_SRC_STARTED);
++
++  GST_LOG_OBJECT (src, "handling %s query",
++      gst_query_type_get_name (GST_QUERY_TYPE (query)));
++
++  switch (GST_QUERY_TYPE (query)) {
++    case GST_QUERY_DURATION:{
++      GstFormat dest_format;
++      gint64 dest_val;
++      guint sectors;
++
++      gst_query_parse_duration (query, &dest_format, NULL);
++
++      if (!started)
++        return FALSE;
++
++      g_assert (src->tracks != NULL);
++
++      if (dest_format == track_format) {
++        GST_LOG_OBJECT (src, "duration: %d tracks", src->num_tracks);
++        gst_query_set_duration (query, track_format, src->num_tracks);
++        return TRUE;
++      }
++
++      if (src->cur_track < 0 || src->cur_track >= src->num_tracks)
++        return FALSE;
++
++      if (src->mode == GST_CDDA_BASE_SRC_MODE_NORMAL) {
++        sectors = src->tracks[src->cur_track].end -
++            src->tracks[src->cur_track].start + 1;
++      } else {
++        sectors = src->tracks[src->num_tracks - 1].end -
++            src->tracks[0].start + 1;
++      }
++
++      /* ... and convert into final format */
++      if (!gst_cdda_base_src_convert (src, sector_format, sectors,
++              dest_format, &dest_val)) {
++        return FALSE;
++      }
++
++      gst_query_set_duration (query, dest_format, dest_val);
++
++      GST_LOG ("duration: %u sectors, %" G_GINT64_FORMAT " in format %s",
++          sectors, dest_val, gst_format_get_name (dest_format));
++      break;
++    }
++    case GST_QUERY_POSITION:{
++      GstFormat dest_format;
++      gint64 pos_sector;
++      gint64 dest_val;
++
++      gst_query_parse_position (query, &dest_format, NULL);
++
++      if (!started)
++        return FALSE;
++
++      g_assert (src->tracks != NULL);
++
++      if (dest_format == track_format) {
++        GST_LOG_OBJECT (src, "position: track %d", src->cur_track);
++        gst_query_set_position (query, track_format, src->cur_track);
++        return TRUE;
++      }
++
++      if (src->cur_track < 0 || src->cur_track >= src->num_tracks)
++        return FALSE;
++
++      if (src->mode == GST_CDDA_BASE_SRC_MODE_NORMAL) {
++        pos_sector = src->cur_sector - src->tracks[src->cur_track].start;
++      } else {
++        pos_sector = src->cur_sector - src->tracks[0].start;
++      }
++
++      if (!gst_cdda_base_src_convert (src, sector_format, pos_sector,
++              dest_format, &dest_val)) {
++        return FALSE;
++      }
++
++      gst_query_set_position (query, dest_format, dest_val);
++
++      GST_LOG ("position: sector %u, %" G_GINT64_FORMAT " in format %s",
++          (guint) pos_sector, dest_val, gst_format_get_name (dest_format));
++      break;
++    }
++    case GST_QUERY_CONVERT:{
++      GstFormat src_format, dest_format;
++      gint64 src_val, dest_val;
++
++      gst_query_parse_convert (query, &src_format, &src_val, &dest_format,
++          NULL);
++
++      if (!gst_cdda_base_src_convert (src, src_format, src_val, dest_format,
++              &dest_val)) {
++        return FALSE;
++      }
++
++      gst_query_set_convert (query, src_format, src_val, dest_format, dest_val);
++      break;
++    }
++    default:{
++      GST_DEBUG_OBJECT (src, "unhandled query, chaining up to parent class");
++      return GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
++    }
++  }
++
++  return TRUE;
++}
++
++static gboolean
++gst_cdda_base_src_is_seekable (GstBaseSrc * basesrc)
++{
++  return TRUE;
++}
++
++static gboolean
++gst_cdda_base_src_do_seek (GstBaseSrc * basesrc, GstSegment * segment)
++{
++  GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (basesrc);
++  gint64 seek_sector;
++
++  GST_DEBUG_OBJECT (src, "segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
++      GST_TIME_ARGS (segment->start), GST_TIME_ARGS (segment->stop));
++
++  if (!gst_cdda_base_src_convert (src, GST_FORMAT_TIME, segment->start,
++          sector_format, &seek_sector)) {
++    GST_WARNING_OBJECT (src, "conversion failed");
++    return FALSE;
++  }
++
++  /* we should only really be called when open */
++  g_assert (src->cur_track >= 0 && src->cur_track < src->num_tracks);
++
++  switch (src->mode) {
++    case GST_CDDA_BASE_SRC_MODE_NORMAL:
++      seek_sector += src->tracks[src->cur_track].start;
++      break;
++    case GST_CDDA_BASE_SRC_MODE_CONTINUOUS:
++      seek_sector += src->tracks[0].start;
++      break;
++    default:
++      g_return_val_if_reached (FALSE);
++  }
++
++  src->cur_sector = (gint) seek_sector;
++
++  GST_DEBUG_OBJECT (src, "seek'd to sector %d", src->cur_sector);
++
++  return TRUE;
++}
++
++static gboolean
++gst_cdda_base_src_handle_track_seek (GstCddaBaseSrc * src, gdouble rate,
++    GstSeekFlags flags, GstSeekType start_type, gint64 start,
++    GstSeekType stop_type, gint64 stop)
++{
++  GstBaseSrc *basesrc = GST_BASE_SRC (src);
++  GstEvent *event;
++
++  if ((flags & GST_SEEK_FLAG_SEGMENT) == GST_SEEK_FLAG_SEGMENT) {
++    gint64 start_time = -1;
++    gint64 stop_time = -1;
++
++    if (src->mode != GST_CDDA_BASE_SRC_MODE_CONTINUOUS) {
++      GST_DEBUG_OBJECT (src, "segment seek in track format is only "
++          "supported in CONTINUOUS mode, not in mode %d", src->mode);
++      return FALSE;
++    }
++
++    switch (start_type) {
++      case GST_SEEK_TYPE_SET:
++        if (!gst_cdda_base_src_convert (src, track_format, start,
++                GST_FORMAT_TIME, &start_time)) {
++          GST_DEBUG_OBJECT (src, "cannot convert track %d to time",
++              (gint) start);
++          return FALSE;
++        }
++        break;
++      case GST_SEEK_TYPE_END:
++        if (!gst_cdda_base_src_convert (src, track_format,
++                src->num_tracks - start - 1, GST_FORMAT_TIME, &start_time)) {
++          GST_DEBUG_OBJECT (src, "cannot convert track %d to time",
++              (gint) start);
++          return FALSE;
++        }
++        start_type = GST_SEEK_TYPE_SET;
++        break;
++      case GST_SEEK_TYPE_NONE:
++        start_time = -1;
++        break;
++      default:
++        g_return_val_if_reached (FALSE);
++    }
++
++    switch (stop_type) {
++      case GST_SEEK_TYPE_SET:
++        if (!gst_cdda_base_src_convert (src, track_format, stop,
++                GST_FORMAT_TIME, &stop_time)) {
++          GST_DEBUG_OBJECT (src, "cannot convert track %d to time",
++              (gint) stop);
++          return FALSE;
++        }
++        break;
++      case GST_SEEK_TYPE_END:
++        if (!gst_cdda_base_src_convert (src, track_format,
++                src->num_tracks - stop - 1, GST_FORMAT_TIME, &stop_time)) {
++          GST_DEBUG_OBJECT (src, "cannot convert track %d to time",
++              (gint) stop);
++          return FALSE;
++        }
++        stop_type = GST_SEEK_TYPE_SET;
++        break;
++      case GST_SEEK_TYPE_NONE:
++        stop_time = -1;
++        break;
++      default:
++        g_return_val_if_reached (FALSE);
++    }
++
++    GST_LOG_OBJECT (src, "seek segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
++        GST_TIME_ARGS (start_time), GST_TIME_ARGS (stop_time));
++
++    /* send fake segment seek event in TIME format to
++     * base class, which will hopefully handle the rest */
++
++    event = gst_event_new_seek (rate, GST_FORMAT_TIME, flags, start_type,
++        start_time, stop_type, stop_time);
++
++    return GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event);
++  }
++
++  /* not a segment seek */
++
++  if (start_type == GST_SEEK_TYPE_NONE) {
++    GST_LOG_OBJECT (src, "start seek type is NONE, nothing to do");
++    return TRUE;
++  }
++
++  if (stop_type != GST_SEEK_TYPE_NONE) {
++    GST_WARNING_OBJECT (src, "ignoring stop seek type (expected NONE)");
++  }
++
++  if (start < 0 || start >= src->num_tracks) {
++    GST_DEBUG_OBJECT (src, "invalid track %" G_GINT64_FORMAT, start);
++    return FALSE;
++  }
++
++  GST_DEBUG_OBJECT (src, "seeking to track %" G_GINT64_FORMAT, start + 1);
++
++  src->cur_sector = src->tracks[start].start;
++  GST_DEBUG_OBJECT (src, "starting at sector %d", src->cur_sector);
++
++  if (src->cur_track != start) {
++    src->cur_track = (gint) start;
++    src->uri_track = -1;
++    src->prev_track = -1;
++
++    gst_cdda_base_src_update_duration (src);
++  } else {
++    GST_DEBUG_OBJECT (src, "is current track, just seeking back to start");
++  }
++
++  /* send fake segment seek event in TIME format to
++   * base class (so we get a newsegment etc.) */
++  event = gst_event_new_seek (rate, GST_FORMAT_TIME, flags,
++      GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, -1);
++
++  return GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event);
++}
++
++static gboolean
++gst_cdda_base_src_handle_event (GstBaseSrc * basesrc, GstEvent * event)
++{
++  GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (basesrc);
++  gboolean ret = FALSE;
++
++  GST_LOG_OBJECT (src, "handling %s event", GST_EVENT_TYPE_NAME (event));
++
++  switch (GST_EVENT_TYPE (event)) {
++    case GST_EVENT_SEEK:{
++      GstSeekType start_type, stop_type;
++      GstSeekFlags flags;
++      GstFormat format;
++      gdouble rate;
++      gint64 start, stop;
++
++      if (!GST_OBJECT_FLAG_IS_SET (basesrc, GST_BASE_SRC_STARTED)) {
++        GST_DEBUG_OBJECT (src, "seek failed: device not open");
++        break;
++      }
++
++      gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
++          &stop_type, &stop);
++
++      if (format == sector_format) {
++        GST_DEBUG_OBJECT (src, "seek in sector format not supported");
++        break;
++      }
++
++      if (format == track_format) {
++        ret = gst_cdda_base_src_handle_track_seek (src, rate, flags,
++            start_type, start, stop_type, stop);
++      } else {
++        GST_LOG_OBJECT (src, "let base class handle seek in %s format",
++            gst_format_get_name (format));
++        event = gst_event_ref (event);
++        ret = GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event);
++      }
++      break;
++    }
++    default:{
++      GST_LOG_OBJECT (src, "let base class handle event");
++      ret = GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event);
++      break;
++    }
++  }
++
++  return ret;
++}
++
++static GstURIType
++gst_cdda_base_src_uri_get_type (void)
++{
++  return GST_URI_SRC;
++}
++
++static gchar **
++gst_cdda_base_src_uri_get_protocols (void)
++{
++  static gchar *protocols[] = { "cdda", NULL };
++
++  return protocols;
++}
++
++static const gchar *
++gst_cdda_base_src_uri_get_uri (GstURIHandler * handler)
++{
++  GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (handler);
++
++  GST_OBJECT_LOCK (src);
++
++  g_free (src->uri);
++
++  if (GST_OBJECT_FLAG_IS_SET (GST_BASE_SRC (src), GST_BASE_SRC_STARTED)) {
++    src->uri = g_strdup_printf ("cdda://%d", src->uri_track);
++  } else {
++    src->uri = g_strdup ("cdda://1");
++  }
++
++  GST_OBJECT_UNLOCK (src);
++
++  return src->uri;
++}
++
++/* Note: gst_element_make_from_uri() might call us with just 'cdda://' as
++ * URI and expects us to return TRUE then (and this might be in any state) */
++
++static gboolean
++gst_cdda_base_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
++{
++  GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (handler);
++  gchar *protocol, *location;
++
++  GST_OBJECT_LOCK (src);
++
++  protocol = gst_uri_get_protocol (uri);
++  if (!protocol || strcmp (protocol, "cdda") != 0) {
++    g_free (protocol);
++    goto failed;
++  }
++  g_free (protocol);
++
++  location = gst_uri_get_location (uri);
++  if (location == NULL || *location == '\0') {
++    g_free (location);
++    location = g_strdup ("1");
++  }
++
++  src->uri_track = strtol (location, NULL, 10);
++  g_free (location);
++
++  if (src->uri_track == 0)
++    goto failed;
++
++  if (src->num_tracks > 0
++      && src->tracks != NULL && src->uri_track > src->num_tracks)
++    goto failed;
++
++  if (src->uri_track > 0 && src->tracks != NULL) {
++    GST_OBJECT_UNLOCK (src);
++
++    gst_pad_send_event (GST_BASE_SRC_PAD (src),
++        gst_event_new_seek (1.0, track_format, GST_SEEK_FLAG_FLUSH,
++            GST_SEEK_TYPE_SET, src->uri_track - 1, GST_SEEK_TYPE_NONE, -1));
++  } else {
++    /* seek will be done in start() */
++    GST_OBJECT_UNLOCK (src);
++  }
++
++  GST_LOG_OBJECT (handler, "successfully handled uri '%s'", uri);
++
++  return TRUE;
++
++failed:
++  {
++    GST_OBJECT_UNLOCK (src);
++    GST_DEBUG_OBJECT (src, "cannot handle URI '%s'", uri);
++    return FALSE;
++  }
++}
++
++static void
++gst_cdda_base_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
++{
++  GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
++
++  iface->get_type = gst_cdda_base_src_uri_get_type;
++  iface->get_uri = gst_cdda_base_src_uri_get_uri;
++  iface->set_uri = gst_cdda_base_src_uri_set_uri;
++  iface->get_protocols = gst_cdda_base_src_uri_get_protocols;
++}
++
++static void
++gst_cdda_base_src_setup_interfaces (GType type)
++{
++  static const GInterfaceInfo urihandler_info = {
++    gst_cdda_base_src_uri_handler_init,
++    NULL,
++    NULL,
++  };
++
++  g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &urihandler_info);
++}
++
++/**
++ * gst_cdda_base_src_add_track:
++ * @src: a #GstCddaBaseSrc
++ * @track: address of #GstCddaBaseSrcTrack to add
++ * 
++ * CDDA sources use this function from their start vfunc to announce the
++ * available data and audio tracks to the base source class. The caller
++ * should allocate @track on the stack, the base source will do a shallow
++ * copy of the structure (and take ownership of the taglist if there is one).
++ *
++ * Returns: FALSE on error, otherwise TRUE.
++ */
++
++gboolean
++gst_cdda_base_src_add_track (GstCddaBaseSrc * src, GstCddaBaseSrcTrack * track)
++{
++  g_return_val_if_fail (GST_IS_CDDA_BASE_SRC (src), FALSE);
++  g_return_val_if_fail (track != NULL, FALSE);
++  g_return_val_if_fail (track->num > 0, FALSE);
++
++  GST_DEBUG_OBJECT (src, "adding track %2u (%2u) [%6u-%6u] [%5s], tags: %"
++      GST_PTR_FORMAT, src->num_tracks + 1, track->num, track->start,
++      track->end, (track->is_audio) ? "AUDIO" : "DATA ", track->tags);
++
++  if (src->num_tracks > 0) {
++    guint end_of_previous_track = src->tracks[src->num_tracks - 1].end;
++
++    if (track->start <= end_of_previous_track) {
++      GST_WARNING ("track %2u overlaps with previous tracks", track->num);
++      return FALSE;
++    }
++  }
++
++  GST_OBJECT_LOCK (src);
++
++  ++src->num_tracks;
++  src->tracks = g_renew (GstCddaBaseSrcTrack, src->tracks, src->num_tracks);
++  src->tracks[src->num_tracks - 1] = *track;
++
++  GST_OBJECT_UNLOCK (src);
++
++  return TRUE;
++}
++
++static void
++gst_cdda_base_src_update_duration (GstCddaBaseSrc * src)
++{
++  GstBaseSrc *basesrc;
++  GstFormat format;
++  gint64 duration;
++
++  basesrc = GST_BASE_SRC (src);
++
++  format = GST_FORMAT_TIME;
++  if (gst_pad_query_duration (GST_BASE_SRC_PAD (src), &format, &duration)) {
++    gst_segment_set_duration (&basesrc->segment, GST_FORMAT_TIME, duration);
++  } else {
++    gst_segment_set_duration (&basesrc->segment, GST_FORMAT_TIME, -1);
++    duration = GST_CLOCK_TIME_NONE;
++  }
++
++  gst_element_post_message (GST_ELEMENT (src),
++      gst_message_new_duration (GST_OBJECT (src), GST_FORMAT_TIME, -1));
++
++  GST_LOG_OBJECT (src, "duration updated to %" GST_TIME_FORMAT,
++      GST_TIME_ARGS (duration));
++}
++
++#define CD_MSF_OFFSET 150
++
++/* the cddb hash function */
++static guint
++cddb_sum (gint n)
++{
++  guint ret;
++
++  ret = 0;
++  while (n > 0) {
++    ret += (n % 10);
++    n /= 10;
++  }
++  return ret;
++}
++
++#include "sha1.h"
++
++static void
++gst_cddabasesrc_calculate_musicbrainz_discid (GstCddaBaseSrc * src)
++{
++  GString *s;
++  SHA_INFO sha;
++  guchar digest[20];
++  gchar *ptr;
++  gchar tmp[9];
++  gulong i;
++  guint leadout_sector;
++
++  s = g_string_new (NULL);
++
++  leadout_sector = src->tracks[src->num_tracks - 1].end + 1 + CD_MSF_OFFSET;
++
++  /* generate SHA digest */
++  sha_init (&sha);
++  g_snprintf (tmp, sizeof (tmp), "%02X", src->tracks[0].num);
++  g_string_append_printf (s, "%02X", src->tracks[0].num);
++  sha_update (&sha, (SHA_BYTE *) tmp, 2);
++
++  g_snprintf (tmp, sizeof (tmp), "%02X", src->tracks[src->num_tracks - 1].num);
++  g_string_append_printf (s, " %02X", src->tracks[src->num_tracks - 1].num);
++  sha_update (&sha, (SHA_BYTE *) tmp, 2);
++
++  g_snprintf (tmp, sizeof (tmp), "%08X", leadout_sector);
++  g_string_append_printf (s, " %08X", leadout_sector);
++  sha_update (&sha, (SHA_BYTE *) tmp, 8);
++
++  for (i = 0; i < 99; i++) {
++    if (i < src->num_tracks) {
++      guint frame_offset = src->tracks[i].start + CD_MSF_OFFSET;
++
++      g_snprintf (tmp, sizeof (tmp), "%08X", frame_offset);
++      g_string_append_printf (s, " %08X", frame_offset);
++      sha_update (&sha, (SHA_BYTE *) tmp, 8);
++    } else {
++      sha_update (&sha, (SHA_BYTE *) "00000000", 8);
++    }
++  }
++  sha_final (digest, &sha);
++
++  /* re-encode to base64 */
++  ptr = g_base64_encode (digest, 20);
++  i = strlen (ptr);
++
++  g_assert (i < sizeof (src->mb_discid) + 1);
++  memcpy (src->mb_discid, ptr, i);
++  src->mb_discid[i] = '\0';
++  free (ptr);
++
++  /* Replace '/', '+' and '=' by '_', '.' and '-' as specified on
++   * http://musicbrainz.org/doc/DiscIDCalculation
++   */
++  for (ptr = src->mb_discid; *ptr != '\0'; ptr++) {
++    if (*ptr == '/')
++      *ptr = '_';
++    else if (*ptr == '+')
++      *ptr = '.';
++    else if (*ptr == '=')
++      *ptr = '-';
++  }
++
++  GST_DEBUG_OBJECT (src, "musicbrainz-discid      = %s", src->mb_discid);
++  GST_DEBUG_OBJECT (src, "musicbrainz-discid-full = %s", s->str);
++
++  gst_tag_list_add (src->tags, GST_TAG_MERGE_REPLACE,
++      GST_TAG_CDDA_MUSICBRAINZ_DISCID, src->mb_discid,
++      GST_TAG_CDDA_MUSICBRAINZ_DISCID_FULL, s->str, NULL);
++
++  g_string_free (s, TRUE);
++}
++
++static void
++lba_to_msf (guint sector, guint * p_m, guint * p_s, guint * p_f, guint * p_secs)
++{
++  guint m, s, f;
++
++  m = sector / SECTORS_PER_MINUTE;
++  sector = sector % SECTORS_PER_MINUTE;
++  s = sector / SECTORS_PER_SECOND;
++  f = sector % SECTORS_PER_SECOND;
++
++  if (p_m)
++    *p_m = m;
++  if (p_s)
++    *p_s = s;
++  if (p_f)
++    *p_f = f;
++  if (p_secs)
++    *p_secs = s + (m * 60);
++}
++
++static void
++gst_cdda_base_src_calculate_cddb_id (GstCddaBaseSrc * src)
++{
++  GString *s;
++  guint first_sector = 0, last_sector = 0;
++  guint start_secs, end_secs, secs, len_secs;
++  guint total_secs, num_audio_tracks;
++  guint id, t, i;
++
++  id = 0;
++  total_secs = 0;
++  num_audio_tracks = 0;
++
++  /* FIXME: do we use offsets and duration of ALL tracks (data + audio)
++   * for the CDDB ID calculation, or only audio tracks? */
++  for (i = 0; i < src->num_tracks; ++i) {
++    if (1) {                    /* src->tracks[i].is_audio) { */
++      if (num_audio_tracks == 0) {
++        first_sector = src->tracks[i].start + CD_MSF_OFFSET;
++      }
++      last_sector = src->tracks[i].end + CD_MSF_OFFSET + 1;
++      ++num_audio_tracks;
++
++      lba_to_msf (src->tracks[i].start + CD_MSF_OFFSET, NULL, NULL, NULL,
++          &secs);
++
++      len_secs = (src->tracks[i].end - src->tracks[i].start + 1) / 75;
++
++      GST_DEBUG_OBJECT (src, "track %02u: lsn %6u (%02u:%02u), "
++          "length: %u seconds (%02u:%02u)",
++          num_audio_tracks, src->tracks[i].start + CD_MSF_OFFSET,
++          secs / 60, secs % 60, len_secs, len_secs / 60, len_secs % 60);
++
++      id += cddb_sum (secs);
++      total_secs += len_secs;
++    }
++  }
++
++  /* first_sector = src->tracks[0].start + CD_MSF_OFFSET; */
++  lba_to_msf (first_sector, NULL, NULL, NULL, &start_secs);
++
++  /* last_sector = src->tracks[src->num_tracks-1].end + CD_MSF_OFFSET; */
++  lba_to_msf (last_sector, NULL, NULL, NULL, &end_secs);
++
++  GST_DEBUG_OBJECT (src, "first_sector = %u = %u secs (%02u:%02u)",
++      first_sector, start_secs, start_secs / 60, start_secs % 60);
++  GST_DEBUG_OBJECT (src, "last_sector  = %u = %u secs (%02u:%02u)",
++      last_sector, end_secs, end_secs / 60, end_secs % 60);
++
++  t = end_secs - start_secs;
++
++  GST_DEBUG_OBJECT (src, "total length = %u secs (%02u:%02u), added title "
++      "lengths = %u seconds (%02u:%02u)", t, t / 60, t % 60, total_secs,
++      total_secs / 60, total_secs % 60);
++
++  src->discid = ((id % 0xff) << 24 | t << 8 | num_audio_tracks);
++
++  s = g_string_new (NULL);
++  g_string_append_printf (s, "%08x", src->discid);
++
++  gst_tag_list_add (src->tags, GST_TAG_MERGE_REPLACE,
++      GST_TAG_CDDA_CDDB_DISCID, s->str, NULL);
++
++  g_string_append_printf (s, " %u", src->num_tracks);
++  for (i = 0; i < src->num_tracks; ++i) {
++    g_string_append_printf (s, " %u", src->tracks[i].start + CD_MSF_OFFSET);
++  }
++  g_string_append_printf (s, " %u", t);
++
++  gst_tag_list_add (src->tags, GST_TAG_MERGE_REPLACE,
++      GST_TAG_CDDA_CDDB_DISCID_FULL, s->str, NULL);
++
++  GST_DEBUG_OBJECT (src, "cddb discid = %s", s->str);
++
++  g_string_free (s, TRUE);
++}
++
++static void
++gst_cdda_base_src_add_tags (GstCddaBaseSrc * src)
++{
++  gint i;
++
++  /* fill in details for each track */
++  for (i = 0; i < src->num_tracks; ++i) {
++    gint64 duration;
++    guint num_sectors;
++
++    if (src->tracks[i].tags == NULL)
++      src->tracks[i].tags = gst_tag_list_new ();
++
++    num_sectors = src->tracks[i].end - src->tracks[i].start + 1;
++    gst_cdda_base_src_convert (src, sector_format, num_sectors,
++        GST_FORMAT_TIME, &duration);
++
++    gst_tag_list_add (src->tracks[i].tags,
++        GST_TAG_MERGE_REPLACE,
++        GST_TAG_TRACK_NUMBER, i + 1,
++        GST_TAG_TRACK_COUNT, src->num_tracks, GST_TAG_DURATION, duration, NULL);
++  }
++
++  /* now fill in per-album tags and include each track's tags
++   * in the album tags, so that interested parties can retrieve
++   * the relevant details for each track in one go */
++
++  /* /////////////////////////////// FIXME should we rather insert num_tracks
++   * tags by the name of 'track-tags' and have the caller use
++   * gst_tag_list_get_value_index() rather than use tag names incl.
++   * the track number ?? *////////////////////////////////////////
++
++  gst_tag_list_add (src->tags, GST_TAG_MERGE_REPLACE,
++      GST_TAG_TRACK_COUNT, src->num_tracks, NULL);
++#if 0
++  for (i = 0; i < src->num_tracks; ++i) {
++    gst_tag_list_add (src->tags, GST_TAG_MERGE_APPEND,
++        GST_TAG_CDDA_TRACK_TAGS, src->tracks[i].tags, NULL);
++  }
++#endif
++
++  GST_DEBUG ("src->tags = %" GST_PTR_FORMAT, src->tags);
++}
++
++static void
++gst_cdda_base_src_add_index_associations (GstCddaBaseSrc * src)
++{
++  gint i;
++
++  for (i = 0; i < src->num_tracks; i++) {
++    gint64 sector;
++
++    sector = src->tracks[i].start;
++    gst_index_add_association (src->index, src->index_id, GST_ASSOCIATION_FLAG_KEY_UNIT, track_format, i,       /* here we count from 0 */
++        sector_format, sector,
++        GST_FORMAT_TIME,
++        (gint64) (((CD_FRAMESIZE_RAW >> 2) * sector * GST_SECOND) / 44100),
++        GST_FORMAT_BYTES, (gint64) (sector << 2), GST_FORMAT_DEFAULT,
++        (gint64) ((CD_FRAMESIZE_RAW >> 2) * sector), NULL);
++  }
++}
++
++static void
++gst_cdda_base_src_set_index (GstElement * element, GstIndex * index)
++{
++  GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (element);
++
++  src->index = index;
++
++  gst_index_get_writer_id (index, GST_OBJECT (src), &src->index_id);
++  gst_index_add_format (index, src->index_id, track_format);
++  gst_index_add_format (index, src->index_id, sector_format);
++}
++
++
++static GstIndex *
++gst_cdda_base_src_get_index (GstElement * element)
++{
++  GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (element);
++
++  return src->index;
++}
++
++static gint
++gst_cdda_base_src_track_sort_func (gconstpointer a, gconstpointer b,
++    gpointer foo)
++{
++  GstCddaBaseSrcTrack *track_a = ((GstCddaBaseSrcTrack *) a);
++  GstCddaBaseSrcTrack *track_b = ((GstCddaBaseSrcTrack *) b);
++
++  /* sort data tracks to the end, and audio tracks by track number */
++  if (track_a->is_audio == track_b->is_audio)
++    return (gint) track_a->num - (gint) track_b->num;
++
++  if (track_a->is_audio) {
++    return -1;
++  } else {
++    return 1;
++  }
++}
++
++static gboolean
++gst_cdda_base_src_start (GstBaseSrc * basesrc)
++{
++  GstCddaBaseSrcClass *klass = GST_CDDA_BASE_SRC_GET_CLASS (basesrc);
++  GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (basesrc);
++  gboolean ret;
++  gchar *device = NULL;
++
++  src->discid = 0;
++  src->mb_discid[0] = '\0';
++
++  g_assert (klass->open != NULL);
++
++  if (src->device != NULL) {
++    device = g_strdup (src->device);
++  } else if (klass->get_default_device != NULL) {
++    device = klass->get_default_device (src);
++  }
++
++  if (device == NULL)
++    device = g_strdup (DEFAULT_DEVICE);
++
++  GST_LOG_OBJECT (basesrc, "opening device %s", device);
++
++  src->tags = gst_tag_list_new ();
++
++  ret = klass->open (src, device);
++  g_free (device);
++  device = NULL;
++
++  if (!ret) {
++    GST_DEBUG_OBJECT (basesrc, "failed to open device");
++    /* subclass (should have) posted an error message with the details */
++    gst_cdda_base_src_stop (basesrc);
++    return FALSE;
++  }
++
++  if (src->num_tracks == 0 || src->tracks == NULL) {
++    GST_DEBUG_OBJECT (src, "no tracks");
++    GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
++        (_("This CD has no audio tracks")), (NULL));
++    gst_cdda_base_src_stop (basesrc);
++    return FALSE;
++  }
++
++  /* need to calculate disc IDs before we ditch the data tracks */
++  gst_cdda_base_src_calculate_cddb_id (src);
++  gst_cddabasesrc_calculate_musicbrainz_discid (src);
++
++#if 0
++  /* adjust sector offsets if necessary */
++  if (src->toc_bias) {
++    src->toc_offset -= src->tracks[0].start;
++  }
++  for (i = 0; i < src->num_tracks; ++i) {
++    src->tracks[i].start += src->toc_offset;
++    src->tracks[i].end += src->toc_offset;
++  }
++#endif
++
++  /* now that we calculated the various disc IDs,
++   * sort the data tracks to end and ignore them */
++  src->num_all_tracks = src->num_tracks;
++
++  g_qsort_with_data (src->tracks, src->num_tracks,
++      sizeof (GstCddaBaseSrcTrack), gst_cdda_base_src_track_sort_func, NULL);
++
++  while (src->num_tracks > 0 && !src->tracks[src->num_tracks - 1].is_audio)
++    --src->num_tracks;
++
++  if (src->num_tracks == 0) {
++    GST_DEBUG_OBJECT (src, "no audio tracks");
++    GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
++        (_("This CD has no audio tracks")), (NULL));
++    gst_cdda_base_src_stop (basesrc);
++    return FALSE;
++  }
++
++  gst_cdda_base_src_add_tags (src);
++
++  if (src->index && GST_INDEX_IS_WRITABLE (src->index))
++    gst_cdda_base_src_add_index_associations (src);
++
++  src->cur_track = 0;
++  src->prev_track = -1;
++
++  if (src->uri_track > 0 && src->uri_track <= src->num_tracks) {
++    GST_LOG_OBJECT (src, "seek to track %d", src->uri_track);
++    src->cur_track = src->uri_track - 1;
++    src->uri_track = -1;
++    src->mode = GST_CDDA_BASE_SRC_MODE_NORMAL;
++  }
++
++  src->cur_sector = src->tracks[src->cur_track].start;
++  GST_LOG_OBJECT (src, "starting at sector %d", src->cur_sector);
++
++  gst_cdda_base_src_update_duration (src);
++
++  return TRUE;
++}
++
++static void
++gst_cdda_base_src_clear_tracks (GstCddaBaseSrc * src)
++{
++  if (src->tracks != NULL) {
++    gint i;
++
++    for (i = 0; i < src->num_all_tracks; ++i) {
++      if (src->tracks[i].tags)
++        gst_tag_list_free (src->tracks[i].tags);
++    }
++
++    g_free (src->tracks);
++    src->tracks = NULL;
++  }
++  src->num_tracks = 0;
++  src->num_all_tracks = 0;
++}
++
++static gboolean
++gst_cdda_base_src_stop (GstBaseSrc * basesrc)
++{
++  GstCddaBaseSrcClass *klass = GST_CDDA_BASE_SRC_GET_CLASS (basesrc);
++  GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (basesrc);
++
++  g_assert (klass->close != NULL);
++
++  klass->close (src);
++
++  gst_cdda_base_src_clear_tracks (src);
++
++  if (src->tags) {
++    gst_tag_list_free (src->tags);
++    src->tags = NULL;
++  }
++
++  src->prev_track = -1;
++  src->cur_track = -1;
++
++  return TRUE;
++}
++
++
++static GstFlowReturn
++gst_cdda_base_src_create (GstPushSrc * pushsrc, GstBuffer ** buffer)
++{
++  GstCddaBaseSrcClass *klass = GST_CDDA_BASE_SRC_GET_CLASS (pushsrc);
++  GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (pushsrc);
++  GstBuffer *buf;
++  GstFormat format;
++  gboolean eos;
++
++  GstClockTime position = GST_CLOCK_TIME_NONE;
++  GstClockTime duration = GST_CLOCK_TIME_NONE;
++  gint64 qry_position;
++
++  g_assert (klass->read_sector != NULL);
++
++  switch (src->mode) {
++    case GST_CDDA_BASE_SRC_MODE_NORMAL:
++      eos = (src->cur_sector > src->tracks[src->cur_track].end);
++      break;
++    case GST_CDDA_BASE_SRC_MODE_CONTINUOUS:
++      eos = (src->cur_sector > src->tracks[src->num_tracks - 1].end);
++      src->cur_track = gst_cdda_base_src_get_track_from_sector (src,
++          src->cur_sector);
++      break;
++    default:
++      g_return_val_if_reached (GST_FLOW_ERROR);
++  }
++
++  if (eos) {
++    src->prev_track = -1;
++    GST_DEBUG_OBJECT (src, "EOS at sector %d, cur_track=%d, mode=%d",
++        src->cur_sector, src->cur_track, src->mode);
++    /* base class will send EOS for us */
++    return GST_FLOW_UNEXPECTED;
++  }
++
++  if (src->prev_track != src->cur_track) {
++    GstTagList *tags;
++
++    tags = gst_tag_list_merge (src->tags, src->tracks[src->cur_track].tags,
++        GST_TAG_MERGE_REPLACE);
++    GST_LOG_OBJECT (src, "announcing tags: %" GST_PTR_FORMAT, tags);
++    gst_element_found_tags_for_pad (GST_ELEMENT (src),
++        GST_BASE_SRC_PAD (src), tags);
++    src->prev_track = src->cur_track;
++
++    gst_cdda_base_src_update_duration (src);
++
++    g_object_notify (G_OBJECT (src), "track");
++  }
++
++  GST_LOG_OBJECT (src, "asking for sector %u", src->cur_sector);
++
++  buf = klass->read_sector (src, src->cur_sector);
++
++  if (buf == NULL) {
++    GST_WARNING_OBJECT (src, "failed to read sector %u", src->cur_sector);
++    return GST_FLOW_ERROR;
++  }
++
++  if (GST_BUFFER_CAPS (buf) == NULL) {
++    gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (src)));
++  }
++
++  format = GST_FORMAT_TIME;
++  if (gst_pad_query_position (GST_BASE_SRC_PAD (src), &format, &qry_position)) {
++    gint64 next_ts = 0;
++
++    position = (GstClockTime) qry_position;
++
++    ++src->cur_sector;
++    if (gst_pad_query_position (GST_BASE_SRC_PAD (src), &format, &next_ts)) {
++      duration = (GstClockTime) (next_ts - qry_position);
++    }
++    --src->cur_sector;
++  }
++
++  /* fallback duration: 4 bytes per sample, 44100 samples per second */
++  if (duration == GST_CLOCK_TIME_NONE) {
++    duration = gst_util_uint64_scale_int (GST_BUFFER_SIZE (buf) >> 2,
++        GST_SECOND, 44100);
++  }
++
++  GST_BUFFER_TIMESTAMP (buf) = position;
++  GST_BUFFER_DURATION (buf) = duration;
++
++  GST_LOG_OBJECT (src, "pushing sector %d with timestamp %" GST_TIME_FORMAT,
++      src->cur_sector, GST_TIME_ARGS (position));
++
++  ++src->cur_sector;
++
++  *buffer = buf;
++
++  return GST_FLOW_OK;
++}
index 0000000,0000000..ad50816
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,224 @@@
++diff --git a/gst-libs/gst/rtp/gstrtcpbuffer.c b/gst-libs/gst/rtp/gstrtcpbuffer.c
++index ab77c8a..fb35a92 100644
++--- a/gst-libs/gst/rtp/gstrtcpbuffer.c
+++++ b/gst-libs/gst/rtp/gstrtcpbuffer.c
++@@ -449,6 +449,11 @@ gst_rtcp_buffer_add_packet (GstBuffer * buffer, GstRTCPType type,
++     case GST_RTCP_TYPE_APP:
++       len = 12;
++       break;
+++    case GST_RTCP_TYPE_RTPFB:
+++      len = 12;
+++      break;
+++    case GST_RTCP_TYPE_PSFB:
+++      len = 12;
++     default:
++       goto unknown_type;
++   }
++@@ -1637,6 +1642,147 @@ no_space:
++ }
++ 
++ /**
+++ * gst_rtcp_packet_fb_get_sender_ssrc:
+++ * @packet: a valid RTPFB or PSFB #GstRTCPPacket
+++ *
+++ * Get the sender SSRC field of the RTPFB or PSFB @packet.
+++ *
+++ * Returns: the sender SSRC.
+++ */
+++guint32
+++gst_rtcp_packet_fb_get_sender_ssrc (GstRTCPPacket * packet)
+++{
+++  guint8 *data;
+++  guint32 ssrc;
+++
+++  g_return_val_if_fail (packet != NULL, 0);
+++  g_return_val_if_fail ((packet->type == GST_RTCP_TYPE_RTPFB ||
+++      packet->type == GST_RTCP_TYPE_PSFB), 0);
+++  g_return_val_if_fail (GST_IS_BUFFER (packet->buffer), 0);
+++
+++  data = GST_BUFFER_DATA (packet->buffer);
+++
+++  /* skip header */
+++  data += packet->offset + 4;
+++  ssrc = GST_READ_UINT32_BE (data);
+++
+++  return ssrc;
+++}
+++
+++/**
+++ * gst_rtcp_packet_fb_set_sender_ssrc:
+++ * @packet: a valid RTPFB or PSFB #GstRTCPPacket
+++ *
+++ * Set the sender SSRC field of the RTPFB or PSFB @packet.
+++ */
+++void
+++gst_rtcp_packet_fb_set_sender_ssrc (GstRTCPPacket *packet, guint32 ssrc)
+++{
+++  guint8 *data;
+++
+++  g_return_if_fail (packet != NULL);
+++  g_return_if_fail (packet->type == GST_RTCP_TYPE_RTPFB ||
+++      packet->type == GST_RTCP_TYPE_PSFB);
+++  g_return_if_fail (GST_IS_BUFFER (packet->buffer));
+++
+++  data = GST_BUFFER_DATA (packet->buffer);
+++
+++  /* skip header */
+++  data += packet->offset + 4;
+++  GST_WRITE_UINT32_BE (data, ssrc);
+++}
+++
+++/**
+++ * gst_rtcp_packet_fb_get_media_ssrc:
+++ * @packet: a valid RTPFB or PSFB #GstRTCPPacket
+++ *
+++ * Get the media SSRC field of the RTPFB or PSFB @packet.
+++ *
+++ * Returns: the media SSRC.
+++ */
+++guint32
+++gst_rtcp_packet_fb_get_media_ssrc (GstRTCPPacket * packet)
+++{
+++  guint8 *data;
+++  guint32 ssrc;
+++
+++  g_return_val_if_fail (packet != NULL, 0);
+++  g_return_val_if_fail ((packet->type == GST_RTCP_TYPE_RTPFB ||
+++      packet->type == GST_RTCP_TYPE_PSFB), 0);
+++  g_return_val_if_fail (GST_IS_BUFFER (packet->buffer), 0);
+++
+++  data = GST_BUFFER_DATA (packet->buffer);
+++
+++  /* skip header and sender ssrc */
+++  data += packet->offset + 8;
+++  ssrc = GST_READ_UINT32_BE (data);
+++
+++  return ssrc;
+++}
+++
+++/**
+++ * gst_rtcp_packet_fb_set_media_ssrc:
+++ * @packet: a valid RTPFB or PSFB #GstRTCPPacket
+++ *
+++ * Set the media SSRC field of the RTPFB or PSFB @packet.
+++ */
+++void
+++gst_rtcp_packet_fb_set_media_ssrc (GstRTCPPacket *packet, guint32 ssrc)
+++{
+++  guint8 *data;
+++
+++  g_return_if_fail (packet != NULL);
+++  g_return_if_fail (packet->type == GST_RTCP_TYPE_RTPFB ||
+++      packet->type == GST_RTCP_TYPE_PSFB);
+++  g_return_if_fail (GST_IS_BUFFER (packet->buffer));
+++
+++  data = GST_BUFFER_DATA (packet->buffer);
+++
+++  /* skip header and sender ssrc */
+++  data += packet->offset + 8;
+++  GST_WRITE_UINT32_BE (data, ssrc);
+++}
+++
+++/**
+++ * gst_rtcp_packet_psfb_get_type:
+++ * @packet: a valid PSFB #GstRTCPPacket
+++ *
+++ * Get the feedback message type of the PSFB @packet.
+++ *
+++ * Returns: The feedback message type.
+++ */
+++GstRTCPPSFBType
+++gst_rtcp_packet_psfb_get_type (GstRTCPPacket *packet)
+++{
+++  g_return_val_if_fail (packet != NULL, GST_RTCP_PSFB_TYPE_INVALID);
+++  g_return_val_if_fail (packet->type == GST_RTCP_TYPE_PSFB,
+++      GST_RTCP_PSFB_TYPE_INVALID);
+++
+++  return packet->count;
+++}
+++
+++/**
+++ * gst_rtcp_packet_psfb_set_type:
+++ * @packet: a valid PSFB #GstRTCPPacket
+++ * @type: the #GstRTCPPSFBType to set
+++ *
+++ * Set the feedback message type of the PSFB @packet.
+++ */
+++void
+++gst_rtcp_packet_psfb_set_type (GstRTCPPacket *packet, GstRTCPPSFBType type)
+++{
+++  guint8 *data;
+++
+++  g_return_if_fail (packet != NULL);
+++  g_return_if_fail (packet->type == GST_RTCP_TYPE_PSFB);
+++  g_return_if_fail (GST_IS_BUFFER (packet->buffer));
+++
+++  data = GST_BUFFER_DATA (packet->buffer);
+++
+++  data[packet->offset] = (data[packet->offset] & 0xE0) | type;
+++}
+++
+++/**
++  * gst_rtcp_ntp_to_unix:
++  * @ntptime: an NTP timestamp
++  *
++diff --git a/gst-libs/gst/rtp/gstrtcpbuffer.h b/gst-libs/gst/rtp/gstrtcpbuffer.h
++index 9c908a8..bb247c9 100644
++--- a/gst-libs/gst/rtp/gstrtcpbuffer.h
+++++ b/gst-libs/gst/rtp/gstrtcpbuffer.h
++@@ -42,6 +42,8 @@ G_BEGIN_DECLS
++  * @GST_RTCP_TYPE_SDES: Source description
++  * @GST_RTCP_TYPE_BYE: Goodbye
++  * @GST_RTCP_TYPE_APP: Application defined
+++ * @GST_RTCP_TYPE_RTPFB: Transport layer feedback
+++ * @GST_RTCP_TYPE_PSFB: Payload-specific feedback
++  *
++  * Different RTCP packet types.
++  */
++@@ -52,9 +54,28 @@ typedef enum
++   GST_RTCP_TYPE_RR      = 201,
++   GST_RTCP_TYPE_SDES    = 202,
++   GST_RTCP_TYPE_BYE     = 203,
++-  GST_RTCP_TYPE_APP     = 204
+++  GST_RTCP_TYPE_APP     = 204,
+++  GST_RTCP_TYPE_RTPFB   = 205,
+++  GST_RTCP_TYPE_PSFB    = 206
++ } GstRTCPType;
++ 
+++/**
+++ * GstRTCPPSFBType:
+++ * @GST_RTCP_PSFB_TYPE_INVALID: Invalid type
+++ * @GST_RTCP_PSFB_TYPE_PLI: Picture Loss Indication
+++ * @GST_RTCP_PSFB_TYPE_SLI: Slice Loss Indication
+++ * @GST_RTCP_PSFB_TYPE_RPSI: Reference Picture Selection Indication
+++ * @GST_RTCP_PSFB_TYPE_AFB: Application layer Feedback
+++ */
+++typedef enum
+++{
+++  GST_RTCP_PSFB_TYPE_INVALID = 0,
+++  GST_RTCP_PSFB_TYPE_PLI     = 1,
+++  GST_RTCP_PSFB_TYPE_SLI     = 2,
+++  GST_RTCP_PSFB_TYPE_RPSI    = 3,
+++  GST_RTCP_PSFB_TYPE_AFB     = 15
+++} GstRTCPPSFBType;
+++
++ /** 
++  * GstRTCPSDESType:
++  * @GST_RTCP_SDES_INVALID: Invalid SDES entry
++@@ -232,6 +253,16 @@ guint8          gst_rtcp_packet_bye_get_reason_len    (GstRTCPPacket *packet);
++ gchar*          gst_rtcp_packet_bye_get_reason        (GstRTCPPacket *packet);
++ gboolean        gst_rtcp_packet_bye_set_reason        (GstRTCPPacket *packet, const gchar *reason);
++ 
+++/* feedback packets */
+++guint32         gst_rtcp_packet_fb_get_sender_ssrc    (GstRTCPPacket *packet);
+++void            gst_rtcp_packet_fb_set_sender_ssrc    (GstRTCPPacket *packet, guint32 ssrc);
+++guint32         gst_rtcp_packet_fb_get_media_ssrc     (GstRTCPPacket *packet);
+++void            gst_rtcp_packet_fb_set_media_ssrc     (GstRTCPPacket *packet, guint32 ssrc);
+++
+++/* psfb packets */
+++GstRTCPPSFBType gst_rtcp_packet_psfb_get_type         (GstRTCPPacket *packet);
+++void            gst_rtcp_packet_psfb_set_type         (GstRTCPPacket *packet, GstRTCPPSFBType type);
+++
++ /* helper functions */
++ guint64         gst_rtcp_ntp_to_unix                  (guint64 ntptime);
++ guint64         gst_rtcp_unix_to_ntp                  (guint64 unixtime);
index 0000000,0000000..e1cc721
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1040 @@@
++/* GStreamer
++ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
++ *
++ * 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
++ */
++
++/**
++ * SECTION:gstbasertppayload
++ * @short_description: Base class for RTP payloader
++ *
++ * <refsect2>
++ * <para>
++ * Provides a base class for RTP payloaders
++ * </para>
++ * </refsect2>
++ */
++
++#ifdef HAVE_CONFIG_H
++#  include "config.h"
++#endif
++
++#include <string.h>
++
++#include <gst/rtp/gstrtpbuffer.h>
++
++#include "gstbasertppayload.h"
++
++GST_DEBUG_CATEGORY_STATIC (basertppayload_debug);
++#define GST_CAT_DEFAULT (basertppayload_debug)
++
++#define GST_BASE_RTP_PAYLOAD_GET_PRIVATE(obj)  \
++   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BASE_RTP_PAYLOAD, GstBaseRTPPayloadPrivate))
++
++struct _GstBaseRTPPayloadPrivate
++{
++  gboolean ts_offset_random;
++  gboolean seqnum_offset_random;
++  gboolean ssrc_random;
++  guint16 next_seqnum;
++  gboolean perfect_rtptime;
++
++  gint64 prop_max_ptime;
++  gint64 caps_max_ptime;
++};
++
++/* BaseRTPPayload signals and args */
++enum
++{
++  /* FILL ME */
++  LAST_SIGNAL
++};
++
++/* FIXME 0.11, a better default is the Ethernet MTU of
++ * 1500 - sizeof(headers) as pointed out by marcelm in IRC:
++ * So an Ethernet MTU of 1500, minus 60 for the max IP, minus 8 for UDP, gives
++ * 1432 bytes or so.  And that should be adjusted downward further for other
++ * encapsulations like PPPoE, so 1400 at most.
++ */
++#define DEFAULT_MTU                     1400
++#define DEFAULT_PT                      96
++#define DEFAULT_SSRC                    -1
++#define DEFAULT_TIMESTAMP_OFFSET        -1
++#define DEFAULT_SEQNUM_OFFSET           -1
++#define DEFAULT_MAX_PTIME               -1
++#define DEFAULT_MIN_PTIME               0
++#define DEFAULT_PERFECT_RTPTIME         TRUE
++#define DEFAULT_PTIME_MULTIPLE          0
++
++enum
++{
++  PROP_0,
++  PROP_MTU,
++  PROP_PT,
++  PROP_SSRC,
++  PROP_TIMESTAMP_OFFSET,
++  PROP_SEQNUM_OFFSET,
++  PROP_MAX_PTIME,
++  PROP_MIN_PTIME,
++  PROP_TIMESTAMP,
++  PROP_SEQNUM,
++  PROP_PERFECT_RTPTIME,
++  PROP_PTIME_MULTIPLE,
++  PROP_LAST
++};
++
++static void gst_basertppayload_class_init (GstBaseRTPPayloadClass * klass);
++static void gst_basertppayload_base_init (GstBaseRTPPayloadClass * klass);
++static void gst_basertppayload_init (GstBaseRTPPayload * basertppayload,
++    gpointer g_class);
++static void gst_basertppayload_finalize (GObject * object);
++
++static gboolean gst_basertppayload_sink_setcaps (GstPad * pad, GstCaps * caps);
++static GstCaps *gst_basertppayload_sink_getcaps (GstPad * pad);
++static gboolean gst_basertppayload_event (GstPad * pad, GstEvent * event);
++static GstFlowReturn gst_basertppayload_chain (GstPad * pad,
++    GstBuffer * buffer);
++
++static void gst_basertppayload_set_property (GObject * object, guint prop_id,
++    const GValue * value, GParamSpec * pspec);
++static void gst_basertppayload_get_property (GObject * object, guint prop_id,
++    GValue * value, GParamSpec * pspec);
++
++static GstStateChangeReturn gst_basertppayload_change_state (GstElement *
++    element, GstStateChange transition);
++
++static GstElementClass *parent_class = NULL;
++
++/* FIXME 0.11: API should be changed to gst_base_typ_payload_xyz */
++
++GType
++gst_basertppayload_get_type (void)
++{
++  static GType basertppayload_type = 0;
++
++  if (!basertppayload_type) {
++    static const GTypeInfo basertppayload_info = {
++      sizeof (GstBaseRTPPayloadClass),
++      (GBaseInitFunc) gst_basertppayload_base_init,
++      NULL,
++      (GClassInitFunc) gst_basertppayload_class_init,
++      NULL,
++      NULL,
++      sizeof (GstBaseRTPPayload),
++      0,
++      (GInstanceInitFunc) gst_basertppayload_init,
++    };
++
++    basertppayload_type =
++        g_type_register_static (GST_TYPE_ELEMENT, "GstBaseRTPPayload",
++        &basertppayload_info, G_TYPE_FLAG_ABSTRACT);
++  }
++  return basertppayload_type;
++}
++
++static void
++gst_basertppayload_base_init (GstBaseRTPPayloadClass * klass)
++{
++}
++
++static void
++gst_basertppayload_class_init (GstBaseRTPPayloadClass * klass)
++{
++  GObjectClass *gobject_class;
++  GstElementClass *gstelement_class;
++
++  gobject_class = (GObjectClass *) klass;
++  gstelement_class = (GstElementClass *) klass;
++
++  g_type_class_add_private (klass, sizeof (GstBaseRTPPayloadPrivate));
++
++  parent_class = g_type_class_peek_parent (klass);
++
++  gobject_class->finalize = gst_basertppayload_finalize;
++
++  gobject_class->set_property = gst_basertppayload_set_property;
++  gobject_class->get_property = gst_basertppayload_get_property;
++
++  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MTU,
++      g_param_spec_uint ("mtu", "MTU",
++          "Maximum size of one packet",
++          28, G_MAXUINT, DEFAULT_MTU,
++          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PT,
++      g_param_spec_uint ("pt", "payload type",
++          "The payload type of the packets", 0, 0x80, DEFAULT_PT,
++          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SSRC,
++      g_param_spec_uint ("ssrc", "SSRC",
++          "The SSRC of the packets (default == random)", 0, G_MAXUINT32,
++          DEFAULT_SSRC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++  g_object_class_install_property (G_OBJECT_CLASS (klass),
++      PROP_TIMESTAMP_OFFSET, g_param_spec_uint ("timestamp-offset",
++          "Timestamp Offset",
++          "Offset to add to all outgoing timestamps (default = random)", 0,
++          G_MAXUINT32, DEFAULT_TIMESTAMP_OFFSET,
++          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SEQNUM_OFFSET,
++      g_param_spec_int ("seqnum-offset", "Sequence number Offset",
++          "Offset to add to all outgoing seqnum (-1 = random)", -1, G_MAXUINT16,
++          DEFAULT_SEQNUM_OFFSET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MAX_PTIME,
++      g_param_spec_int64 ("max-ptime", "Max packet time",
++          "Maximum duration of the packet data in ns (-1 = unlimited up to MTU)",
++          -1, G_MAXINT64, DEFAULT_MAX_PTIME,
++          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++  /**
++   * GstBaseRTPAudioPayload:min-ptime:
++   *
++   * Minimum duration of the packet data in ns (can't go above MTU)
++   *
++   * Since: 0.10.13
++   **/
++  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MIN_PTIME,
++      g_param_spec_int64 ("min-ptime", "Min packet time",
++          "Minimum duration of the packet data in ns (can't go above MTU)",
++          0, G_MAXINT64, DEFAULT_MIN_PTIME,
++          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++
++  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TIMESTAMP,
++      g_param_spec_uint ("timestamp", "Timestamp",
++          "The RTP timestamp of the last processed packet",
++          0, G_MAXUINT32, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
++  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SEQNUM,
++      g_param_spec_uint ("seqnum", "Sequence number",
++          "The RTP sequence number of the last processed packet",
++          0, G_MAXUINT16, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
++
++  /**
++   * GstBaseRTPAudioPayload:perfect-rtptime:
++   *
++   * Try to use the offset fields to generate perfect RTP timestamps. when this
++   * option is disabled, RTP timestamps are generated from the GStreamer
++   * timestamps, which could result in RTP timestamps that don't increment with
++   * the amount of data in the packet.
++   *
++   * Since: 0.10.25
++   */
++  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PERFECT_RTPTIME,
++      g_param_spec_boolean ("perfect-rtptime", "Perfect RTP Time",
++          "Generate perfect RTP timestamps when possible",
++          DEFAULT_PERFECT_RTPTIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++  /**
++   * GstBaseRTPAudioPayload:ptime-multiple:
++   *
++   * Force buffers to be multiples of this duration in ns (0 disables)
++   *
++   * Since: 0.10.29
++   **/
++  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PTIME_MULTIPLE,
++      g_param_spec_int64 ("ptime-multiple", "Packet time multiple",
++          "Force buffers to be multiples of this duration in ns (0 disables)",
++          0, G_MAXINT64, DEFAULT_PTIME_MULTIPLE,
++          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++
++  gstelement_class->change_state = gst_basertppayload_change_state;
++
++  GST_DEBUG_CATEGORY_INIT (basertppayload_debug, "basertppayload", 0,
++      "Base class for RTP Payloaders");
++}
++
++static void
++gst_basertppayload_init (GstBaseRTPPayload * basertppayload, gpointer g_class)
++{
++  GstPadTemplate *templ;
++  GstBaseRTPPayloadPrivate *priv;
++
++  basertppayload->priv = priv =
++      GST_BASE_RTP_PAYLOAD_GET_PRIVATE (basertppayload);
++
++  templ =
++      gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
++  g_return_if_fail (templ != NULL);
++
++  basertppayload->srcpad = gst_pad_new_from_template (templ, "src");
++  gst_element_add_pad (GST_ELEMENT (basertppayload), basertppayload->srcpad);
++
++  templ =
++      gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "sink");
++  g_return_if_fail (templ != NULL);
++
++  basertppayload->sinkpad = gst_pad_new_from_template (templ, "sink");
++  gst_pad_set_setcaps_function (basertppayload->sinkpad,
++      gst_basertppayload_sink_setcaps);
++  gst_pad_set_getcaps_function (basertppayload->sinkpad,
++      gst_basertppayload_sink_getcaps);
++  gst_pad_set_event_function (basertppayload->sinkpad,
++      gst_basertppayload_event);
++  gst_pad_set_chain_function (basertppayload->sinkpad,
++      gst_basertppayload_chain);
++  gst_element_add_pad (GST_ELEMENT (basertppayload), basertppayload->sinkpad);
++
++  basertppayload->seq_rand = g_rand_new_with_seed (g_random_int ());
++  basertppayload->ssrc_rand = g_rand_new_with_seed (g_random_int ());
++  basertppayload->ts_rand = g_rand_new_with_seed (g_random_int ());
++
++  basertppayload->mtu = DEFAULT_MTU;
++  basertppayload->pt = DEFAULT_PT;
++  basertppayload->seqnum_offset = DEFAULT_SEQNUM_OFFSET;
++  basertppayload->ssrc = DEFAULT_SSRC;
++  basertppayload->ts_offset = DEFAULT_TIMESTAMP_OFFSET;
++  priv->seqnum_offset_random = (basertppayload->seqnum_offset == -1);
++  priv->ts_offset_random = (basertppayload->ts_offset == -1);
++  priv->ssrc_random = (basertppayload->ssrc == -1);
++
++  basertppayload->max_ptime = DEFAULT_MAX_PTIME;
++  basertppayload->min_ptime = DEFAULT_MIN_PTIME;
++  basertppayload->priv->perfect_rtptime = DEFAULT_PERFECT_RTPTIME;
++  basertppayload->abidata.ABI.ptime_multiple = DEFAULT_PTIME_MULTIPLE;
++
++  basertppayload->media = NULL;
++  basertppayload->encoding_name = NULL;
++
++  basertppayload->clock_rate = 0;
++
++  basertppayload->priv->caps_max_ptime = DEFAULT_MAX_PTIME;
++  basertppayload->priv->prop_max_ptime = DEFAULT_MAX_PTIME;
++}
++
++static void
++gst_basertppayload_finalize (GObject * object)
++{
++  GstBaseRTPPayload *basertppayload;
++
++  basertppayload = GST_BASE_RTP_PAYLOAD (object);
++
++  g_rand_free (basertppayload->seq_rand);
++  basertppayload->seq_rand = NULL;
++  g_rand_free (basertppayload->ssrc_rand);
++  basertppayload->ssrc_rand = NULL;
++  g_rand_free (basertppayload->ts_rand);
++  basertppayload->ts_rand = NULL;
++
++  g_free (basertppayload->media);
++  basertppayload->media = NULL;
++  g_free (basertppayload->encoding_name);
++  basertppayload->encoding_name = NULL;
++
++  G_OBJECT_CLASS (parent_class)->finalize (object);
++}
++
++static gboolean
++gst_basertppayload_sink_setcaps (GstPad * pad, GstCaps * caps)
++{
++  GstBaseRTPPayload *basertppayload;
++  GstBaseRTPPayloadClass *basertppayload_class;
++  gboolean ret = TRUE;
++
++  GST_DEBUG_OBJECT (pad, "setting caps %" GST_PTR_FORMAT, caps);
++  basertppayload = GST_BASE_RTP_PAYLOAD (gst_pad_get_parent (pad));
++  basertppayload_class = GST_BASE_RTP_PAYLOAD_GET_CLASS (basertppayload);
++
++  if (basertppayload_class->set_caps)
++    ret = basertppayload_class->set_caps (basertppayload, caps);
++
++  gst_object_unref (basertppayload);
++
++  return ret;
++}
++
++static GstCaps *
++gst_basertppayload_sink_getcaps (GstPad * pad)
++{
++  GstBaseRTPPayload *basertppayload;
++  GstBaseRTPPayloadClass *basertppayload_class;
++  GstCaps *caps = NULL;
++
++  GST_DEBUG_OBJECT (pad, "getting caps");
++
++  basertppayload = GST_BASE_RTP_PAYLOAD (gst_pad_get_parent (pad));
++  basertppayload_class = GST_BASE_RTP_PAYLOAD_GET_CLASS (basertppayload);
++
++  if (basertppayload_class->get_caps)
++    caps = basertppayload_class->get_caps (basertppayload, pad);
++
++  if (!caps) {
++    caps = GST_PAD_TEMPLATE_CAPS (GST_PAD_PAD_TEMPLATE (pad));
++    GST_DEBUG_OBJECT (pad,
++        "using pad template %p with caps %p %" GST_PTR_FORMAT,
++        GST_PAD_PAD_TEMPLATE (pad), caps, caps);
++
++    caps = gst_caps_ref (caps);
++  }
++
++  gst_object_unref (basertppayload);
++
++  return caps;
++}
++
++static gboolean
++gst_basertppayload_event (GstPad * pad, GstEvent * event)
++{
++  GstBaseRTPPayload *basertppayload;
++  GstBaseRTPPayloadClass *basertppayload_class;
++  gboolean res;
++
++  basertppayload = GST_BASE_RTP_PAYLOAD (gst_pad_get_parent (pad));
++  basertppayload_class = GST_BASE_RTP_PAYLOAD_GET_CLASS (basertppayload);
++
++  if (basertppayload_class->handle_event) {
++    res = basertppayload_class->handle_event (pad, event);
++    if (res)
++      goto done;
++  }
++
++  switch (GST_EVENT_TYPE (event)) {
++    case GST_EVENT_FLUSH_START:
++      res = gst_pad_event_default (pad, event);
++      break;
++    case GST_EVENT_FLUSH_STOP:
++      res = gst_pad_event_default (pad, event);
++      gst_segment_init (&basertppayload->segment, GST_FORMAT_UNDEFINED);
++      break;
++    case GST_EVENT_NEWSEGMENT:
++    {
++      gboolean update;
++      gdouble rate;
++      GstFormat fmt;
++      gint64 start, stop, position;
++
++      gst_event_parse_new_segment (event, &update, &rate, &fmt, &start, &stop,
++          &position);
++      gst_segment_set_newsegment (&basertppayload->segment, update, rate, fmt,
++          start, stop, position);
++
++      /* fallthrough */
++    }
++    default:
++      res = gst_pad_event_default (pad, event);
++      break;
++  }
++
++done:
++  gst_object_unref (basertppayload);
++
++  return res;
++}
++
++
++static GstFlowReturn
++gst_basertppayload_chain (GstPad * pad, GstBuffer * buffer)
++{
++  GstBaseRTPPayload *basertppayload;
++  GstBaseRTPPayloadClass *basertppayload_class;
++  GstFlowReturn ret;
++
++  basertppayload = GST_BASE_RTP_PAYLOAD (gst_pad_get_parent (pad));
++  basertppayload_class = GST_BASE_RTP_PAYLOAD_GET_CLASS (basertppayload);
++
++  if (!basertppayload_class->handle_buffer)
++    goto no_function;
++
++  ret = basertppayload_class->handle_buffer (basertppayload, buffer);
++
++  gst_object_unref (basertppayload);
++
++  return ret;
++
++  /* ERRORS */
++no_function:
++  {
++    GST_ELEMENT_ERROR (basertppayload, STREAM, NOT_IMPLEMENTED, (NULL),
++        ("subclass did not implement handle_buffer function"));
++    gst_object_unref (basertppayload);
++    gst_buffer_unref (buffer);
++    return GST_FLOW_ERROR;
++  }
++}
++
++/**
++ * gst_basertppayload_set_options:
++ * @payload: a #GstBaseRTPPayload
++ * @media: the media type (typically "audio" or "video")
++ * @dynamic: if the payload type is dynamic
++ * @encoding_name: the encoding name
++ * @clock_rate: the clock rate of the media
++ *
++ * Set the rtp options of the payloader. These options will be set in the caps
++ * of the payloader. Subclasses must call this method before calling
++ * gst_basertppayload_push() or gst_basertppayload_set_outcaps().
++ */
++void
++gst_basertppayload_set_options (GstBaseRTPPayload * payload,
++    const gchar * media, gboolean dynamic, const gchar * encoding_name,
++    guint32 clock_rate)
++{
++  g_return_if_fail (payload != NULL);
++  g_return_if_fail (clock_rate != 0);
++
++  g_free (payload->media);
++  payload->media = g_strdup (media);
++  payload->dynamic = dynamic;
++  g_free (payload->encoding_name);
++  payload->encoding_name = g_strdup (encoding_name);
++  payload->clock_rate = clock_rate;
++}
++
++static gboolean
++copy_fixed (GQuark field_id, const GValue * value, GstStructure * dest)
++{
++  if (gst_value_is_fixed (value)) {
++    gst_structure_id_set_value (dest, field_id, value);
++  }
++  return TRUE;
++}
++
++static void
++update_max_ptime (GstBaseRTPPayload * basertppayload)
++{
++  if (basertppayload->priv->caps_max_ptime != -1 &&
++      basertppayload->priv->prop_max_ptime != -1)
++    basertppayload->max_ptime = MIN (basertppayload->priv->caps_max_ptime,
++        basertppayload->priv->prop_max_ptime);
++  else if (basertppayload->priv->caps_max_ptime != -1)
++    basertppayload->max_ptime = basertppayload->priv->caps_max_ptime;
++  else if (basertppayload->priv->prop_max_ptime != -1)
++    basertppayload->max_ptime = basertppayload->priv->prop_max_ptime;
++  else
++    basertppayload->max_ptime = DEFAULT_MAX_PTIME;
++}
++
++/**
++ * gst_basertppayload_set_outcaps:
++ * @payload: a #GstBaseRTPPayload
++ * @fieldname: the first field name or %NULL
++ * @...: field values
++ *
++ * Configure the output caps with the optional parameters.
++ *
++ * Variable arguments should be in the form field name, field type
++ * (as a GType), value(s).  The last variable argument should be NULL.
++ *
++ * Returns: %TRUE if the caps could be set.
++ */
++gboolean
++gst_basertppayload_set_outcaps (GstBaseRTPPayload * payload,
++    const gchar * fieldname, ...)
++{
++  GstCaps *srccaps, *peercaps;
++  gboolean res;
++
++  /* fill in the defaults, their properties cannot be negotiated. */
++  srccaps = gst_caps_new_simple ("application/x-rtp",
++      "media", G_TYPE_STRING, payload->media,
++      "clock-rate", G_TYPE_INT, payload->clock_rate,
++      "encoding-name", G_TYPE_STRING, payload->encoding_name, NULL);
++
++  GST_DEBUG_OBJECT (payload, "defaults: %" GST_PTR_FORMAT, srccaps);
++
++  if (fieldname) {
++    va_list varargs;
++
++    /* override with custom properties */
++    va_start (varargs, fieldname);
++    gst_caps_set_simple_valist (srccaps, fieldname, varargs);
++    va_end (varargs);
++
++    GST_DEBUG_OBJECT (payload, "custom added: %" GST_PTR_FORMAT, srccaps);
++  }
++
++  payload->priv->caps_max_ptime = DEFAULT_MAX_PTIME;
++  payload->abidata.ABI.ptime = 0;
++
++  /* the peer caps can override some of the defaults */
++  peercaps = gst_pad_peer_get_caps (payload->srcpad);
++  if (peercaps == NULL) {
++    /* no peer caps, just add the other properties */
++    gst_caps_set_simple (srccaps,
++        "payload", G_TYPE_INT, GST_BASE_RTP_PAYLOAD_PT (payload),
++        "ssrc", G_TYPE_UINT, payload->current_ssrc,
++        "clock-base", G_TYPE_UINT, payload->ts_base,
++        "seqnum-base", G_TYPE_UINT, payload->seqnum_base, NULL);
++
++    GST_DEBUG_OBJECT (payload, "no peer caps: %" GST_PTR_FORMAT, srccaps);
++  } else {
++    GstCaps *temp;
++    GstStructure *s, *d;
++    const GValue *value;
++    gint pt;
++    guint max_ptime, ptime;
++
++    /* peer provides caps we can use to fixate, intersect. This always returns a
++     * writable caps. */
++    temp = gst_caps_intersect (srccaps, peercaps);
++    gst_caps_unref (srccaps);
++    gst_caps_unref (peercaps);
++
++    if (gst_caps_is_empty (temp)) {
++      gst_caps_unref (temp);
++      return FALSE;
++    }
++
++    /* now fixate, start by taking the first caps */
++    gst_caps_truncate (temp);
++
++    /* get first structure */
++    s = gst_caps_get_structure (temp, 0);
++
++    if (gst_structure_get_uint (s, "maxptime", &max_ptime))
++      payload->priv->caps_max_ptime = max_ptime * GST_MSECOND;
++
++    if (gst_structure_get_uint (s, "ptime", &ptime))
++      payload->abidata.ABI.ptime = ptime * GST_MSECOND;
++
++    if (gst_structure_get_int (s, "payload", &pt)) {
++      /* use peer pt */
++      GST_BASE_RTP_PAYLOAD_PT (payload) = pt;
++      GST_LOG_OBJECT (payload, "using peer pt %d", pt);
++    } else {
++      if (gst_structure_has_field (s, "payload")) {
++        /* can only fixate if there is a field */
++        gst_structure_fixate_field_nearest_int (s, "payload",
++            GST_BASE_RTP_PAYLOAD_PT (payload));
++        gst_structure_get_int (s, "payload", &pt);
++        GST_LOG_OBJECT (payload, "using peer pt %d", pt);
++      } else {
++        /* no pt field, use the internal pt */
++        pt = GST_BASE_RTP_PAYLOAD_PT (payload);
++        gst_structure_set (s, "payload", G_TYPE_INT, pt, NULL);
++        GST_LOG_OBJECT (payload, "using internal pt %d", pt);
++      }
++    }
++
++    if (gst_structure_has_field_typed (s, "ssrc", G_TYPE_UINT)) {
++      value = gst_structure_get_value (s, "ssrc");
++      payload->current_ssrc = g_value_get_uint (value);
++      GST_LOG_OBJECT (payload, "using peer ssrc %08x", payload->current_ssrc);
++    } else {
++      /* FIXME, fixate_nearest_uint would be even better */
++      gst_structure_set (s, "ssrc", G_TYPE_UINT, payload->current_ssrc, NULL);
++      GST_LOG_OBJECT (payload, "using internal ssrc %08x",
++          payload->current_ssrc);
++    }
++
++    if (gst_structure_has_field_typed (s, "clock-base", G_TYPE_UINT)) {
++      value = gst_structure_get_value (s, "clock-base");
++      payload->ts_base = g_value_get_uint (value);
++      GST_LOG_OBJECT (payload, "using peer clock-base %u", payload->ts_base);
++    } else {
++      /* FIXME, fixate_nearest_uint would be even better */
++      gst_structure_set (s, "clock-base", G_TYPE_UINT, payload->ts_base, NULL);
++      GST_LOG_OBJECT (payload, "using internal clock-base %u",
++          payload->ts_base);
++    }
++    if (gst_structure_has_field_typed (s, "seqnum-base", G_TYPE_UINT)) {
++      value = gst_structure_get_value (s, "seqnum-base");
++      payload->seqnum_base = g_value_get_uint (value);
++      GST_LOG_OBJECT (payload, "using peer seqnum-base %u",
++          payload->seqnum_base);
++    } else {
++      /* FIXME, fixate_nearest_uint would be even better */
++      gst_structure_set (s, "seqnum-base", G_TYPE_UINT, payload->seqnum_base,
++          NULL);
++      GST_LOG_OBJECT (payload, "using internal seqnum-base %u",
++          payload->seqnum_base);
++    }
++
++    /* make the target caps by copying over all the fixed caps, removing the
++     * unfixed caps. */
++    srccaps = gst_caps_new_simple (gst_structure_get_name (s), NULL);
++    d = gst_caps_get_structure (srccaps, 0);
++
++    gst_structure_foreach (s, (GstStructureForeachFunc) copy_fixed, d);
++
++    gst_caps_unref (temp);
++
++    GST_DEBUG_OBJECT (payload, "with peer caps: %" GST_PTR_FORMAT, srccaps);
++  }
++
++  update_max_ptime (payload);
++
++  res = gst_pad_set_caps (GST_BASE_RTP_PAYLOAD_SRCPAD (payload), srccaps);
++  gst_caps_unref (srccaps);
++
++  return res;
++}
++
++/**
++ * gst_basertppayload_is_filled:
++ * @payload: a #GstBaseRTPPayload
++ * @size: the size of the packet
++ * @duration: the duration of the packet
++ *
++ * Check if the packet with @size and @duration would exceed the configured
++ * maximum size.
++ *
++ * Returns: %TRUE if the packet of @size and @duration would exceed the
++ * configured MTU or max_ptime.
++ */
++gboolean
++gst_basertppayload_is_filled (GstBaseRTPPayload * payload,
++    guint size, GstClockTime duration)
++{
++  if (size > payload->mtu)
++    return TRUE;
++
++  if (payload->max_ptime != -1 && duration >= payload->max_ptime)
++    return TRUE;
++
++  return FALSE;
++}
++
++typedef struct
++{
++  GstBaseRTPPayload *payload;
++  guint32 ssrc;
++  guint16 seqnum;
++  guint8 pt;
++  GstCaps *caps;
++  GstClockTime timestamp;
++  guint64 offset;
++  guint32 rtptime;
++} HeaderData;
++
++static GstBufferListItem
++find_timestamp (GstBuffer ** buffer, guint group, guint idx, HeaderData * data)
++{
++  data->timestamp = GST_BUFFER_TIMESTAMP (*buffer);
++  data->offset = GST_BUFFER_OFFSET (*buffer);
++
++  /* stop when we find a timestamp. We take whatever offset is associated with
++   * the timestamp (if any) to do perfect timestamps when we need to. */
++  if (data->timestamp != -1)
++    return GST_BUFFER_LIST_END;
++  else
++    return GST_BUFFER_LIST_CONTINUE;
++}
++
++static GstBufferListItem
++set_headers (GstBuffer ** buffer, guint group, guint idx, HeaderData * data)
++{
++  gst_rtp_buffer_set_ssrc (*buffer, data->ssrc);
++  gst_rtp_buffer_set_payload_type (*buffer, data->pt);
++  gst_rtp_buffer_set_seq (*buffer, data->seqnum);
++  gst_rtp_buffer_set_timestamp (*buffer, data->rtptime);
++  gst_buffer_set_caps (*buffer, data->caps);
++  /* increment the seqnum for each buffer */
++  data->seqnum++;
++
++  return GST_BUFFER_LIST_SKIP_GROUP;
++}
++
++/* Updates the SSRC, payload type, seqnum and timestamp of the RTP buffer
++ * before the buffer is pushed. */
++static GstFlowReturn
++gst_basertppayload_prepare_push (GstBaseRTPPayload * payload,
++    gpointer obj, gboolean is_list)
++{
++  GstBaseRTPPayloadPrivate *priv;
++  HeaderData data;
++
++  if (payload->clock_rate == 0)
++    goto no_rate;
++
++  priv = payload->priv;
++
++  /* update first, so that the property is set to the last
++   * seqnum pushed */
++  payload->seqnum = priv->next_seqnum;
++
++  /* fill in the fields we want to set on all headers */
++  data.payload = payload;
++  data.seqnum = payload->seqnum;
++  data.ssrc = payload->current_ssrc;
++  data.pt = payload->pt;
++  data.caps = GST_PAD_CAPS (payload->srcpad);
++
++  /* find the first buffer with a timestamp */
++  if (is_list) {
++    data.timestamp = -1;
++    data.offset = GST_BUFFER_OFFSET_NONE;
++    gst_buffer_list_foreach (GST_BUFFER_LIST_CAST (obj),
++        (GstBufferListFunc) find_timestamp, &data);
++  } else {
++    data.timestamp = GST_BUFFER_TIMESTAMP (GST_BUFFER_CAST (obj));
++    data.offset = GST_BUFFER_OFFSET (GST_BUFFER_CAST (obj));
++  }
++
++  /* convert to RTP time */
++  if (priv->perfect_rtptime && data.offset != GST_BUFFER_OFFSET_NONE) {
++    /* if we have an offset, use that for making an RTP timestamp */
++    data.rtptime = payload->ts_base + data.offset;
++    GST_LOG_OBJECT (payload,
++        "Using offset %" G_GUINT64_FORMAT " for RTP timestamp", data.offset);
++  } else if (GST_CLOCK_TIME_IS_VALID (data.timestamp)) {
++    gint64 rtime;
++
++    /* no offset, use the gstreamer timestamp */
++    rtime = gst_segment_to_running_time (&payload->segment, GST_FORMAT_TIME,
++        data.timestamp);
++
++    GST_LOG_OBJECT (payload,
++        "Using running_time %" GST_TIME_FORMAT " for RTP timestamp",
++        GST_TIME_ARGS (rtime));
++
++    rtime = gst_util_uint64_scale_int (rtime, payload->clock_rate, GST_SECOND);
++
++    /* add running_time in clock-rate units to the base timestamp */
++    data.rtptime = payload->ts_base + rtime;
++  } else {
++    GST_LOG_OBJECT (payload,
++        "Using previous RTP timestamp %" G_GUINT32_FORMAT, payload->timestamp);
++    /* no timestamp to convert, take previous timestamp */
++    data.rtptime = payload->timestamp;
++  }
++
++  /* set ssrc, payload type, seq number, caps and rtptime */
++  if (is_list) {
++    gst_buffer_list_foreach (GST_BUFFER_LIST_CAST (obj),
++        (GstBufferListFunc) set_headers, &data);
++  } else {
++    GstBuffer *buf = GST_BUFFER_CAST (obj);
++    set_headers (&buf, 0, 0, &data);
++  }
++
++  priv->next_seqnum = data.seqnum;
++  payload->timestamp = data.rtptime;
++
++  GST_LOG_OBJECT (payload,
++      "Preparing to push packet with size %d, seq=%d, rtptime=%u, timestamp %"
++      GST_TIME_FORMAT, (is_list) ? -1 :
++      GST_BUFFER_SIZE (GST_BUFFER (obj)), payload->seqnum, data.rtptime,
++      GST_TIME_ARGS (data.timestamp));
++
++  return GST_FLOW_OK;
++
++  /* ERRORS */
++no_rate:
++  {
++    GST_ELEMENT_ERROR (payload, STREAM, NOT_IMPLEMENTED, (NULL),
++        ("subclass did not specify clock-rate"));
++    return GST_FLOW_ERROR;
++  }
++}
++
++/**
++ * gst_basertppayload_push_list:
++ * @payload: a #GstBaseRTPPayload
++ * @list: a #GstBufferList
++ *
++ * Push @list to the peer element of the payloader. The SSRC, payload type,
++ * seqnum and timestamp of the RTP buffer will be updated first.
++ *
++ * This function takes ownership of @list.
++ *
++ * Returns: a #GstFlowReturn.
++ *
++ * Since: 0.10.24
++ */
++GstFlowReturn
++gst_basertppayload_push_list (GstBaseRTPPayload * payload, GstBufferList * list)
++{
++  GstFlowReturn res;
++
++  res = gst_basertppayload_prepare_push (payload, list, TRUE);
++
++  if (G_LIKELY (res == GST_FLOW_OK))
++    res = gst_pad_push_list (payload->srcpad, list);
++  else
++    gst_buffer_list_unref (list);
++
++  return res;
++}
++
++/**
++ * gst_basertppayload_push:
++ * @payload: a #GstBaseRTPPayload
++ * @buffer: a #GstBuffer
++ *
++ * Push @buffer to the peer element of the payloader. The SSRC, payload type,
++ * seqnum and timestamp of the RTP buffer will be updated first.
++ *
++ * This function takes ownership of @buffer.
++ *
++ * Returns: a #GstFlowReturn.
++ */
++GstFlowReturn
++gst_basertppayload_push (GstBaseRTPPayload * payload, GstBuffer * buffer)
++{
++  GstFlowReturn res;
++
++  res = gst_basertppayload_prepare_push (payload, buffer, FALSE);
++
++  if (G_LIKELY (res == GST_FLOW_OK))
++    res = gst_pad_push (payload->srcpad, buffer);
++  else
++    gst_buffer_unref (buffer);
++
++  return res;
++}
++
++static void
++gst_basertppayload_set_property (GObject * object, guint prop_id,
++    const GValue * value, GParamSpec * pspec)
++{
++  GstBaseRTPPayload *basertppayload;
++  GstBaseRTPPayloadPrivate *priv;
++  gint64 val;
++
++  basertppayload = GST_BASE_RTP_PAYLOAD (object);
++  priv = basertppayload->priv;
++
++  switch (prop_id) {
++    case PROP_MTU:
++      basertppayload->mtu = g_value_get_uint (value);
++      break;
++    case PROP_PT:
++      basertppayload->pt = g_value_get_uint (value);
++      break;
++    case PROP_SSRC:
++      val = g_value_get_uint (value);
++      basertppayload->ssrc = val;
++      priv->ssrc_random = FALSE;
++      break;
++    case PROP_TIMESTAMP_OFFSET:
++      val = g_value_get_uint (value);
++      basertppayload->ts_offset = val;
++      priv->ts_offset_random = FALSE;
++      break;
++    case PROP_SEQNUM_OFFSET:
++      val = g_value_get_int (value);
++      basertppayload->seqnum_offset = val;
++      priv->seqnum_offset_random = (val == -1);
++      GST_DEBUG_OBJECT (basertppayload, "seqnum offset 0x%04x, random %d",
++          basertppayload->seqnum_offset, priv->seqnum_offset_random);
++      break;
++    case PROP_MAX_PTIME:
++      basertppayload->priv->prop_max_ptime = g_value_get_int64 (value);
++      update_max_ptime (basertppayload);
++      break;
++    case PROP_MIN_PTIME:
++      basertppayload->min_ptime = g_value_get_int64 (value);
++      break;
++    case PROP_PERFECT_RTPTIME:
++      priv->perfect_rtptime = g_value_get_boolean (value);
++      break;
++    case PROP_PTIME_MULTIPLE:
++      basertppayload->abidata.ABI.ptime_multiple = g_value_get_int64 (value);
++      break;
++    default:
++      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++      break;
++  }
++}
++
++static void
++gst_basertppayload_get_property (GObject * object, guint prop_id,
++    GValue * value, GParamSpec * pspec)
++{
++  GstBaseRTPPayload *basertppayload;
++  GstBaseRTPPayloadPrivate *priv;
++
++  basertppayload = GST_BASE_RTP_PAYLOAD (object);
++  priv = basertppayload->priv;
++
++  switch (prop_id) {
++    case PROP_MTU:
++      g_value_set_uint (value, basertppayload->mtu);
++      break;
++    case PROP_PT:
++      g_value_set_uint (value, basertppayload->pt);
++      break;
++    case PROP_SSRC:
++      if (priv->ssrc_random)
++        g_value_set_uint (value, -1);
++      else
++        g_value_set_uint (value, basertppayload->ssrc);
++      break;
++    case PROP_TIMESTAMP_OFFSET:
++      if (priv->ts_offset_random)
++        g_value_set_uint (value, -1);
++      else
++        g_value_set_uint (value, (guint32) basertppayload->ts_offset);
++      break;
++    case PROP_SEQNUM_OFFSET:
++      if (priv->seqnum_offset_random)
++        g_value_set_int (value, -1);
++      else
++        g_value_set_int (value, (guint16) basertppayload->seqnum_offset);
++      break;
++    case PROP_MAX_PTIME:
++      g_value_set_int64 (value, basertppayload->max_ptime);
++      break;
++    case PROP_MIN_PTIME:
++      g_value_set_int64 (value, basertppayload->min_ptime);
++      break;
++    case PROP_TIMESTAMP:
++      g_value_set_uint (value, basertppayload->timestamp);
++      break;
++    case PROP_SEQNUM:
++      g_value_set_uint (value, basertppayload->seqnum);
++      break;
++    case PROP_PERFECT_RTPTIME:
++      g_value_set_boolean (value, priv->perfect_rtptime);
++      break;
++    case PROP_PTIME_MULTIPLE:
++      g_value_set_int64 (value, basertppayload->abidata.ABI.ptime_multiple);
++      break;
++    default:
++      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++      break;
++  }
++}
++
++static GstStateChangeReturn
++gst_basertppayload_change_state (GstElement * element,
++    GstStateChange transition)
++{
++  GstBaseRTPPayload *basertppayload;
++  GstBaseRTPPayloadPrivate *priv;
++  GstStateChangeReturn ret;
++
++  basertppayload = GST_BASE_RTP_PAYLOAD (element);
++  priv = basertppayload->priv;
++
++  switch (transition) {
++    case GST_STATE_CHANGE_NULL_TO_READY:
++      break;
++    case GST_STATE_CHANGE_READY_TO_PAUSED:
++      gst_segment_init (&basertppayload->segment, GST_FORMAT_UNDEFINED);
++
++      if (priv->seqnum_offset_random)
++        basertppayload->seqnum_base = g_random_int_range (0, G_MAXUINT16);
++      else
++        basertppayload->seqnum_base = basertppayload->seqnum_offset;
++      priv->next_seqnum = basertppayload->seqnum_base;
++      basertppayload->seqnum = basertppayload->seqnum_base;
++
++      if (priv->ssrc_random)
++        basertppayload->current_ssrc = g_random_int ();
++      else
++        basertppayload->current_ssrc = basertppayload->ssrc;
++
++      if (priv->ts_offset_random)
++        basertppayload->ts_base = g_random_int ();
++      else
++        basertppayload->ts_base = basertppayload->ts_offset;
++      basertppayload->timestamp = basertppayload->ts_base;
++      break;
++    default:
++      break;
++  }
++
++  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
++
++  switch (transition) {
++    case GST_STATE_CHANGE_READY_TO_NULL:
++      break;
++    default:
++      break;
++  }
++  return ret;
++}
index 0000000,0000000..adeb449
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++*************** struct _GstBaseRTPPayloadPrivate
++*** 48,53 ****
++    guint16 next_seqnum;
++    gboolean perfect_rtptime;
++    gboolean timestamp_valid;
++  
++    gint64 prop_max_ptime;
++    gint64 caps_max_ptime;
++--- 48,54 ----
++    guint16 next_seqnum;
++    gboolean perfect_rtptime;
++    gboolean timestamp_valid;
+++   gboolean notified_first_timestamp;
++  
++    gint64 prop_max_ptime;
++    gint64 caps_max_ptime;
index 0000000,0000000..87b36d4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1403 @@@
++/* GStreamer
++ * Copyright (C) <2005> Philippe Khalaf <burger@speedy.org>
++ * Copyright (C) <2006> Wim Taymans <wim@fluendo.com>
++ *
++ * 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.
++ */
++
++/**
++ * SECTION:gstrtpbuffer
++ * @short_description: Helper methods for dealing with RTP buffers
++ * @see_also: #GstBaseRTPPayload, #GstBaseRTPDepayload, gstrtcpbuffer
++ *
++ * <refsect2>
++ * <para>
++ * The GstRTPBuffer helper functions makes it easy to parse and create regular 
++ * #GstBuffer objects that contain RTP payloads. These buffers are typically of
++ * 'application/x-rtp' #GstCaps.
++ * </para>
++ * </refsect2>
++ *
++ * Last reviewed on 2006-07-17 (0.10.10)
++ */
++
++#include "gstrtpbuffer.h"
++
++#include <stdlib.h>
++#include <string.h>
++
++#define GST_RTP_HEADER_LEN 12
++
++/* Note: we use bitfields here to make sure the compiler doesn't add padding
++ * between fields on certain architectures; can't assume aligned access either
++ */
++typedef struct _GstRTPHeader
++{
++#if G_BYTE_ORDER == G_LITTLE_ENDIAN
++  unsigned int csrc_count:4;    /* CSRC count */
++  unsigned int extension:1;     /* header extension flag */
++  unsigned int padding:1;       /* padding flag */
++  unsigned int version:2;       /* protocol version */
++  unsigned int payload_type:7;  /* payload type */
++  unsigned int marker:1;        /* marker bit */
++#elif G_BYTE_ORDER == G_BIG_ENDIAN
++  unsigned int version:2;       /* protocol version */
++  unsigned int padding:1;       /* padding flag */
++  unsigned int extension:1;     /* header extension flag */
++  unsigned int csrc_count:4;    /* CSRC count */
++  unsigned int marker:1;        /* marker bit */
++  unsigned int payload_type:7;  /* payload type */
++#else
++#error "G_BYTE_ORDER should be big or little endian."
++#endif
++  unsigned int seq:16;          /* sequence number */
++  unsigned int timestamp:32;    /* timestamp */
++  unsigned int ssrc:32;         /* synchronization source */
++  guint8 csrclist[4];           /* optional CSRC list, 32 bits each */
++} GstRTPHeader;
++
++#define GST_RTP_HEADER_VERSION(data)      (((GstRTPHeader *)(data))->version)
++#define GST_RTP_HEADER_PADDING(data)      (((GstRTPHeader *)(data))->padding)
++#define GST_RTP_HEADER_EXTENSION(data)    (((GstRTPHeader *)(data))->extension)
++#define GST_RTP_HEADER_CSRC_COUNT(data)   (((GstRTPHeader *)(data))->csrc_count)
++#define GST_RTP_HEADER_MARKER(data)       (((GstRTPHeader *)(data))->marker)
++#define GST_RTP_HEADER_PAYLOAD_TYPE(data) (((GstRTPHeader *)(data))->payload_type)
++#define GST_RTP_HEADER_SEQ(data)          (((GstRTPHeader *)(data))->seq)
++#define GST_RTP_HEADER_TIMESTAMP(data)    (((GstRTPHeader *)(data))->timestamp)
++#define GST_RTP_HEADER_SSRC(data)         (((GstRTPHeader *)(data))->ssrc)
++#define GST_RTP_HEADER_CSRC_LIST_OFFSET(data,i)        \
++    data + G_STRUCT_OFFSET(GstRTPHeader, csrclist) +   \
++    ((i) * sizeof(guint32))
++#define GST_RTP_HEADER_CSRC_SIZE(data)   (GST_RTP_HEADER_CSRC_COUNT(data) * sizeof (guint32))
++
++typedef enum
++{
++  PAYLOAD_TYPE,
++  SEQ,
++  TIMESTAMP,
++  SSRC,
++  NO_MORE
++} rtp_header_data_type;
++
++static gboolean validate_data (guint8 * data, guint len, guint8 * payload,
++    guint payload_len);
++static guint8 *gst_rtp_buffer_list_get_data (GstBufferList * list);
++static void gst_rtp_buffer_list_set_rtp_headers (GstBufferList * list,
++    gpointer data, rtp_header_data_type type);
++static void gst_rtp_buffer_list_set_data (guint8 * rtp_header, gpointer data,
++    rtp_header_data_type type);
++
++/**
++ * gst_rtp_buffer_allocate_data:
++ * @buffer: a #GstBuffer
++ * @payload_len: the length of the payload
++ * @pad_len: the amount of padding
++ * @csrc_count: the number of CSRC entries
++ *
++ * Allocate enough data in @buffer to hold an RTP packet with @csrc_count CSRCs,
++ * a payload length of @payload_len and padding of @pad_len.
++ * MALLOCDATA of @buffer will be overwritten and will not be freed. 
++ * All other RTP header fields will be set to 0/FALSE.
++ */
++void
++gst_rtp_buffer_allocate_data (GstBuffer * buffer, guint payload_len,
++    guint8 pad_len, guint8 csrc_count)
++{
++  guint len;
++  guint8 *data;
++
++  g_return_if_fail (csrc_count <= 15);
++  g_return_if_fail (GST_IS_BUFFER (buffer));
++
++  len = GST_RTP_HEADER_LEN + csrc_count * sizeof (guint32)
++      + payload_len + pad_len;
++
++  data = g_malloc (len);
++  GST_BUFFER_MALLOCDATA (buffer) = data;
++  GST_BUFFER_DATA (buffer) = data;
++  GST_BUFFER_SIZE (buffer) = len;
++
++  /* fill in defaults */
++  GST_RTP_HEADER_VERSION (data) = GST_RTP_VERSION;
++  GST_RTP_HEADER_PADDING (data) = FALSE;
++  GST_RTP_HEADER_EXTENSION (data) = FALSE;
++  GST_RTP_HEADER_CSRC_COUNT (data) = csrc_count;
++  memset (GST_RTP_HEADER_CSRC_LIST_OFFSET (data, 0), 0,
++      csrc_count * sizeof (guint32));
++  GST_RTP_HEADER_MARKER (data) = FALSE;
++  GST_RTP_HEADER_PAYLOAD_TYPE (data) = 0;
++  GST_RTP_HEADER_SEQ (data) = 0;
++  GST_RTP_HEADER_TIMESTAMP (data) = 0;
++  GST_RTP_HEADER_SSRC (data) = 0;
++}
++
++/**
++ * gst_rtp_buffer_new_take_data:
++ * @data: data for the new buffer
++ * @len: the length of data
++ *
++ * Create a new buffer and set the data and size of the buffer to @data and @len
++ * respectively. @data will be freed when the buffer is unreffed, so this
++ * function transfers ownership of @data to the new buffer.
++ *
++ * Returns: A newly allocated buffer with @data and of size @len.
++ */
++GstBuffer *
++gst_rtp_buffer_new_take_data (gpointer data, guint len)
++{
++  GstBuffer *result;
++
++  g_return_val_if_fail (data != NULL, NULL);
++  g_return_val_if_fail (len > 0, NULL);
++
++  result = gst_buffer_new ();
++
++  GST_BUFFER_MALLOCDATA (result) = data;
++  GST_BUFFER_DATA (result) = data;
++  GST_BUFFER_SIZE (result) = len;
++
++  return result;
++}
++
++/**
++ * gst_rtp_buffer_new_copy_data:
++ * @data: data for the new buffer
++ * @len: the length of data
++ *
++ * Create a new buffer and set the data to a copy of @len
++ * bytes of @data and the size to @len. The data will be freed when the buffer
++ * is freed.
++ *
++ * Returns: A newly allocated buffer with a copy of @data and of size @len.
++ */
++GstBuffer *
++gst_rtp_buffer_new_copy_data (gpointer data, guint len)
++{
++  return gst_rtp_buffer_new_take_data (g_memdup (data, len), len);
++}
++
++/**
++ * gst_rtp_buffer_new_allocate:
++ * @payload_len: the length of the payload
++ * @pad_len: the amount of padding
++ * @csrc_count: the number of CSRC entries
++ *
++ * Allocate a new #Gstbuffer with enough data to hold an RTP packet with @csrc_count CSRCs,
++ * a payload length of @payload_len and padding of @pad_len.
++ * All other RTP header fields will be set to 0/FALSE.
++ *
++ * Returns: A newly allocated buffer that can hold an RTP packet with given
++ * parameters.
++ */
++GstBuffer *
++gst_rtp_buffer_new_allocate (guint payload_len, guint8 pad_len,
++    guint8 csrc_count)
++{
++  GstBuffer *result;
++
++  g_return_val_if_fail (csrc_count <= 15, NULL);
++
++  result = gst_buffer_new ();
++  gst_rtp_buffer_allocate_data (result, payload_len, pad_len, csrc_count);
++
++  return result;
++}
++
++/**
++ * gst_rtp_buffer_new_allocate_len:
++ * @packet_len: the total length of the packet
++ * @pad_len: the amount of padding
++ * @csrc_count: the number of CSRC entries
++ *
++ * Create a new #GstBuffer that can hold an RTP packet that is exactly
++ * @packet_len long. The length of the payload depends on @pad_len and
++ * @csrc_count and can be calculated with gst_rtp_buffer_calc_payload_len().
++ * All RTP header fields will be set to 0/FALSE.
++ *
++ * Returns: A newly allocated buffer that can hold an RTP packet of @packet_len.
++ */
++GstBuffer *
++gst_rtp_buffer_new_allocate_len (guint packet_len, guint8 pad_len,
++    guint8 csrc_count)
++{
++  guint len;
++
++  g_return_val_if_fail (csrc_count <= 15, NULL);
++
++  len = gst_rtp_buffer_calc_payload_len (packet_len, pad_len, csrc_count);
++
++  return gst_rtp_buffer_new_allocate (len, pad_len, csrc_count);
++}
++
++/**
++ * gst_rtp_buffer_calc_header_len:
++ * @csrc_count: the number of CSRC entries
++ *
++ * Calculate the header length of an RTP packet with @csrc_count CSRC entries.
++ * An RTP packet can have at most 15 CSRC entries.
++ *
++ * Returns: The length of an RTP header with @csrc_count CSRC entries.
++ */
++guint
++gst_rtp_buffer_calc_header_len (guint8 csrc_count)
++{
++  g_return_val_if_fail (csrc_count <= 15, 0);
++
++  return GST_RTP_HEADER_LEN + (csrc_count * sizeof (guint32));
++}
++
++/**
++ * gst_rtp_buffer_calc_packet_len:
++ * @payload_len: the length of the payload
++ * @pad_len: the amount of padding
++ * @csrc_count: the number of CSRC entries
++ *
++ * Calculate the total length of an RTP packet with a payload size of @payload_len,
++ * a padding of @pad_len and a @csrc_count CSRC entries.
++ *
++ * Returns: The total length of an RTP header with given parameters.
++ */
++guint
++gst_rtp_buffer_calc_packet_len (guint payload_len, guint8 pad_len,
++    guint8 csrc_count)
++{
++  g_return_val_if_fail (csrc_count <= 15, 0);
++
++  return payload_len + GST_RTP_HEADER_LEN + (csrc_count * sizeof (guint32))
++      + pad_len;
++}
++
++/**
++ * gst_rtp_buffer_calc_payload_len:
++ * @packet_len: the length of the total RTP packet
++ * @pad_len: the amount of padding
++ * @csrc_count: the number of CSRC entries
++ *
++ * Calculate the length of the payload of an RTP packet with size @packet_len,
++ * a padding of @pad_len and a @csrc_count CSRC entries.
++ *
++ * Returns: The length of the payload of an RTP packet  with given parameters.
++ */
++guint
++gst_rtp_buffer_calc_payload_len (guint packet_len, guint8 pad_len,
++    guint8 csrc_count)
++{
++  g_return_val_if_fail (csrc_count <= 15, 0);
++
++  return packet_len - GST_RTP_HEADER_LEN - (csrc_count * sizeof (guint32))
++      - pad_len;
++}
++
++/**
++ * gst_rtp_buffer_validate_data:
++ * @data: the data to validate
++ * @len: the length of @data to validate
++ *
++ * Check if the @data and @size point to the data of a valid RTP packet.
++ * This function checks the length, version and padding of the packet data.
++ * Use this function to validate a packet before using the other functions in
++ * this module.
++ *
++ * Returns: TRUE if the data points to a valid RTP packet.
++ */
++gboolean
++gst_rtp_buffer_validate_data (guint8 * data, guint len)
++{
++  return validate_data (data, len, NULL, 0);
++}
++
++/**
++ * gst_rtp_buffer_validate:
++ * @buffer: the buffer to validate
++ *
++ * Check if the data pointed to by @buffer is a valid RTP packet using
++ * validate_data().
++ * Use this function to validate a packet before using the other functions in
++ * this module.
++ *
++ * Returns: TRUE if @buffer is a valid RTP packet.
++ */
++gboolean
++gst_rtp_buffer_validate (GstBuffer * buffer)
++{
++  guint8 *data;
++  guint len;
++
++  g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
++
++  data = GST_BUFFER_DATA (buffer);
++  len = GST_BUFFER_SIZE (buffer);
++
++  return validate_data (data, len, NULL, 0);
++}
++
++/**
++ * gst_rtp_buffer_list_validate:
++ * @list: the buffer list to validate
++ *
++ * Check if all RTP packets in the @list are valid using validate_data().
++ * Use this function to validate an list before using the other functions in
++ * this module.
++ *
++ * Returns: TRUE if @list consists only of valid RTP packets.
++ */
++gboolean
++gst_rtp_buffer_list_validate (GstBufferList * list)
++{
++  guint16 prev_seqnum = 0;
++  GstBufferListIterator *it;
++  guint i = 0;
++
++  g_return_val_if_fail (GST_IS_BUFFER_LIST (list), FALSE);
++
++  it = gst_buffer_list_iterate (list);
++  g_return_val_if_fail (it != NULL, FALSE);
++
++  /* iterate through all the RTP packets in the list */
++  while (gst_buffer_list_iterator_next_group (it)) {
++    GstBuffer *rtpbuf;
++    GstBuffer *paybuf;
++    guint8 *packet_header;
++    guint8 *packet_payload;
++    guint payload_size;
++    guint packet_size;
++
++    /* each group should consists of 2 buffers: one containing the RTP header
++     * and the other one the payload */
++    if (gst_buffer_list_iterator_n_buffers (it) != 2)
++      goto invalid_list;
++
++    /* get the RTP header */
++    rtpbuf = gst_buffer_list_iterator_next (it);
++    packet_header = GST_BUFFER_DATA (rtpbuf);
++    if (packet_header == NULL)
++      goto invalid_list;
++
++    /* get the payload */
++    paybuf = gst_buffer_list_iterator_next (it);
++    packet_payload = GST_BUFFER_DATA (paybuf);
++    if (packet_payload == NULL) {
++      goto invalid_list;
++    }
++    payload_size = GST_BUFFER_SIZE (paybuf);
++    if (payload_size == 0) {
++      goto invalid_list;
++    }
++
++    /* the size of the RTP packet within the current group */
++    packet_size = GST_BUFFER_SIZE (rtpbuf) + payload_size;
++
++    /* check the sequence number */
++    if (G_UNLIKELY (i == 0)) {
++      prev_seqnum = g_ntohs (GST_RTP_HEADER_SEQ (packet_header));
++      i++;
++    } else {
++      if (++prev_seqnum != g_ntohs (GST_RTP_HEADER_SEQ (packet_header)))
++        goto invalid_list;
++    }
++
++    /* validate packet */
++    if (!validate_data (packet_header, packet_size, packet_payload,
++            payload_size)) {
++      goto invalid_list;
++    }
++  }
++
++  gst_buffer_list_iterator_free (it);
++  return TRUE;
++
++invalid_list:
++  gst_buffer_list_iterator_free (it);
++  g_return_val_if_reached (FALSE);
++}
++
++/**
++ * validate_data:
++ * @data: the data to validate
++ * @len: the length of @data to validate
++ * @payload: the payload if @data represents the header only
++ * @payload_len: the len of the payload
++ *
++ * Checks if @data is a valid RTP packet.
++ *
++ * Returns: TRUE if @data is a valid RTP packet
++ */
++static gboolean
++validate_data (guint8 * data, guint len, guint8 * payload, guint payload_len)
++{
++  guint8 padding;
++  guint8 csrc_count;
++  guint header_len;
++  guint8 version;
++
++  g_return_val_if_fail (data != NULL, FALSE);
++
++  header_len = GST_RTP_HEADER_LEN;
++  if (G_UNLIKELY (len < header_len))
++    goto wrong_length;
++
++  /* check version */
++  version = (data[0] & 0xc0);
++  if (G_UNLIKELY (version != (GST_RTP_VERSION << 6)))
++    goto wrong_version;
++
++  /* calc header length with csrc */
++  csrc_count = (data[0] & 0x0f);
++  header_len += csrc_count * sizeof (guint32);
++
++  /* calc extension length when present. */
++  if (data[0] & 0x10) {
++    guint8 *extpos;
++    guint16 extlen;
++
++    /* this points to the extenstion bits and header length */
++    extpos = &data[header_len];
++
++    /* skip the header and check that we have enough space */
++    header_len += 4;
++    if (G_UNLIKELY (len < header_len))
++      goto wrong_length;
++
++    /* skip id */
++    extpos += 2;
++    /* read length as the number of 32 bits words */
++    extlen = GST_READ_UINT16_BE (extpos);
++
++    header_len += extlen * sizeof (guint32);
++  }
++
++  /* check for padding */
++  if (data[0] & 0x20) {
++    if (payload)
++      padding = payload[payload_len - 1];
++    else
++      padding = data[len - 1];
++  } else {
++    padding = 0;
++  }
++
++  /* check if padding and header not bigger than packet length */
++  if (G_UNLIKELY (len < padding + header_len))
++    goto wrong_padding;
++
++  return TRUE;
++
++  /* ERRORS */
++wrong_length:
++  {
++    GST_DEBUG ("len < header_len check failed (%d < %d)", len, header_len);
++    return FALSE;
++  }
++wrong_version:
++  {
++    GST_DEBUG ("version check failed (%d != %d)", version, GST_RTP_VERSION);
++    return FALSE;
++  }
++wrong_padding:
++  {
++    GST_DEBUG ("padding check failed (%d - %d < %d)", len, header_len, padding);
++    return FALSE;
++  }
++}
++
++/**
++ * gst_rtp_buffer_set_packet_len:
++ * @buffer: the buffer
++ * @len: the new packet length
++ *
++ * Set the total @buffer size to @len. The data in the buffer will be made
++ * larger if needed. Any padding will be removed from the packet. 
++ */
++void
++gst_rtp_buffer_set_packet_len (GstBuffer * buffer, guint len)
++{
++  guint oldlen;
++  guint8 *data;
++
++  oldlen = GST_BUFFER_SIZE (buffer);
++  data = GST_BUFFER_DATA (buffer);
++
++  if (oldlen < len) {
++    data = g_realloc (GST_BUFFER_MALLOCDATA (buffer), len);
++    GST_BUFFER_MALLOCDATA (buffer) = data;
++    GST_BUFFER_DATA (buffer) = data;
++  }
++  GST_BUFFER_SIZE (buffer) = len;
++
++  /* remove any padding */
++  GST_RTP_HEADER_PADDING (data) = FALSE;
++}
++
++/**
++ * gst_rtp_buffer_get_packet_len:
++ * @buffer: the buffer
++ *
++ * Return the total length of the packet in @buffer.
++ *
++ * Returns: The total length of the packet in @buffer.
++ */
++guint
++gst_rtp_buffer_get_packet_len (GstBuffer * buffer)
++{
++  return GST_BUFFER_SIZE (buffer);
++}
++
++/**
++ * gst_rtp_buffer_get_header_len:
++ * @buffer: the buffer
++ *
++ * Return the total length of the header in @buffer. This include the length of
++ * the fixed header, the CSRC list and the extension header.
++ *
++ * Returns: The total length of the header in @buffer.
++ */
++guint
++gst_rtp_buffer_get_header_len (GstBuffer * buffer)
++{
++  guint len;
++  guint8 *data;
++
++  data = GST_BUFFER_DATA (buffer);
++
++  len = GST_RTP_HEADER_LEN + GST_RTP_HEADER_CSRC_SIZE (data);
++  if (GST_RTP_HEADER_EXTENSION (data))
++    len += GST_READ_UINT16_BE (data + len + 2) * 4 + 4;
++
++  return len;
++}
++
++/**
++ * gst_rtp_buffer_get_version:
++ * @buffer: the buffer
++ *
++ * Get the version number of the RTP packet in @buffer.
++ *
++ * Returns: The version of @buffer.
++ */
++guint8
++gst_rtp_buffer_get_version (GstBuffer * buffer)
++{
++  return GST_RTP_HEADER_VERSION (GST_BUFFER_DATA (buffer));
++}
++
++/**
++ * gst_rtp_buffer_set_version:
++ * @buffer: the buffer
++ * @version: the new version
++ *
++ * Set the version of the RTP packet in @buffer to @version.
++ */
++void
++gst_rtp_buffer_set_version (GstBuffer * buffer, guint8 version)
++{
++  g_return_if_fail (version < 0x04);
++
++  GST_RTP_HEADER_VERSION (GST_BUFFER_DATA (buffer)) = version;
++}
++
++/**
++ * gst_rtp_buffer_get_padding:
++ * @buffer: the buffer
++ *
++ * Check if the padding bit is set on the RTP packet in @buffer.
++ *
++ * Returns: TRUE if @buffer has the padding bit set.
++ */
++gboolean
++gst_rtp_buffer_get_padding (GstBuffer * buffer)
++{
++  return GST_RTP_HEADER_PADDING (GST_BUFFER_DATA (buffer));
++}
++
++/**
++ * gst_rtp_buffer_set_padding:
++ * @buffer: the buffer
++ * @padding: the new padding
++ *
++ * Set the padding bit on the RTP packet in @buffer to @padding.
++ */
++void
++gst_rtp_buffer_set_padding (GstBuffer * buffer, gboolean padding)
++{
++  GST_RTP_HEADER_PADDING (GST_BUFFER_DATA (buffer)) = padding;
++}
++
++/**
++ * gst_rtp_buffer_pad_to:
++ * @buffer: the buffer
++ * @len: the new amount of padding
++ *
++ * Set the amount of padding in the RTP packet in @buffer to
++ * @len. If @len is 0, the padding is removed.
++ *
++ * NOTE: This function does not work correctly.
++ */
++void
++gst_rtp_buffer_pad_to (GstBuffer * buffer, guint len)
++{
++  guint8 *data;
++
++  data = GST_BUFFER_DATA (buffer);
++
++  if (len > 0)
++    GST_RTP_HEADER_PADDING (data) = TRUE;
++  else
++    GST_RTP_HEADER_PADDING (data) = FALSE;
++
++  /* FIXME, set the padding byte at the end of the payload data */
++}
++
++/**
++ * gst_rtp_buffer_get_extension:
++ * @buffer: the buffer
++ *
++ * Check if the extension bit is set on the RTP packet in @buffer.
++ * 
++ * Returns: TRUE if @buffer has the extension bit set.
++ */
++gboolean
++gst_rtp_buffer_get_extension (GstBuffer * buffer)
++{
++  return GST_RTP_HEADER_EXTENSION (GST_BUFFER_DATA (buffer));
++}
++
++/**
++ * gst_rtp_buffer_set_extension:
++ * @buffer: the buffer
++ * @extension: the new extension
++ *
++ * Set the extension bit on the RTP packet in @buffer to @extension.
++ */
++void
++gst_rtp_buffer_set_extension (GstBuffer * buffer, gboolean extension)
++{
++  GST_RTP_HEADER_EXTENSION (GST_BUFFER_DATA (buffer)) = extension;
++}
++
++/**
++ * gst_rtp_buffer_get_extension_data:
++ * @buffer: the buffer
++ * @bits: location for result bits
++ * @data: location for data
++ * @wordlen: location for length of @data in 32 bits words
++ *
++ * Get the extension data. @bits will contain the extension 16 bits of custom
++ * data. @data will point to the data in the extension and @wordlen will contain
++ * the length of @data in 32 bits words.
++ *
++ * If @buffer did not contain an extension, this function will return %FALSE
++ * with @bits, @data and @wordlen unchanged.
++ * 
++ * Returns: TRUE if @buffer had the extension bit set.
++ *
++ * Since: 0.10.15
++ */
++gboolean
++gst_rtp_buffer_get_extension_data (GstBuffer * buffer, guint16 * bits,
++    gpointer * data, guint * wordlen)
++{
++  guint len;
++  guint8 *pdata;
++
++  pdata = GST_BUFFER_DATA (buffer);
++
++  if (!GST_RTP_HEADER_EXTENSION (pdata))
++    return FALSE;
++
++  /* move to the extension */
++  len = GST_RTP_HEADER_LEN + GST_RTP_HEADER_CSRC_SIZE (pdata);
++  pdata += len;
++
++  if (bits)
++    *bits = GST_READ_UINT16_BE (pdata);
++  if (wordlen)
++    *wordlen = GST_READ_UINT16_BE (pdata + 2);
++  if (data)
++    *data = pdata + 4;
++
++  return TRUE;
++}
++
++/**
++ * gst_rtp_buffer_set_extension_data:
++ * @buffer: the buffer
++ * @bits: the bits specific for the extension
++ * @length: the length that counts the number of 32-bit words in
++ * the extension, excluding the extension header ( therefore zero is a valid length)
++ *
++ * Set the extension bit of the rtp buffer and fill in the @bits and @length of the
++ * extension header. It will refuse to set the extension data if the buffer is not
++ * large enough.
++ *
++ * Returns: True if done.
++ *
++ * Since : 0.10.18
++ */
++gboolean
++gst_rtp_buffer_set_extension_data (GstBuffer * buffer, guint16 bits,
++    guint16 length)
++{
++  guint32 min_size = 0;
++  guint8 *data;
++
++  data = GST_BUFFER_DATA (buffer);
++
++  /* check if the buffer is big enough to hold the extension */
++  min_size =
++      GST_RTP_HEADER_LEN + GST_RTP_HEADER_CSRC_SIZE (data) + 4 +
++      length * sizeof (guint32);
++  if (G_UNLIKELY (min_size > GST_BUFFER_SIZE (buffer)))
++    goto too_small;
++
++  /* now we can set the extension bit */
++  gst_rtp_buffer_set_extension (buffer, TRUE);
++
++  data += GST_RTP_HEADER_LEN + GST_RTP_HEADER_CSRC_SIZE (data);
++  GST_WRITE_UINT16_BE (data, bits);
++  GST_WRITE_UINT16_BE (data + 2, length);
++
++  return TRUE;
++
++  /* ERRORS */
++too_small:
++  {
++    g_warning
++        ("rtp buffer too small: need more than %d bytes but only have %d bytes",
++        min_size, GST_BUFFER_SIZE (buffer));
++    return FALSE;
++  }
++}
++
++/**
++ * gst_rtp_buffer_get_ssrc:
++ * @buffer: the buffer
++ *
++ * Get the SSRC of the RTP packet in @buffer.
++ * 
++ * Returns: the SSRC of @buffer in host order.
++ */
++guint32
++gst_rtp_buffer_get_ssrc (GstBuffer * buffer)
++{
++  return g_ntohl (GST_RTP_HEADER_SSRC (GST_BUFFER_DATA (buffer)));
++}
++
++/**
++ * gst_rtp_buffer_list_get_ssrc:
++ * @list: the list
++ *
++ * Get the SSRC of the first RTP packet in @list.
++ * All RTP packets within @list have the same SSRC.
++ *
++ * Returns: the SSRC of @list in host order.
++ */
++guint32
++gst_rtp_buffer_list_get_ssrc (GstBufferList * list)
++{
++  guint8 *data;
++  data = gst_rtp_buffer_list_get_data (list);
++  g_return_val_if_fail (data != NULL, 0);
++  return g_ntohl (GST_RTP_HEADER_SSRC (data));
++}
++
++/**
++ * gst_rtp_buffer_set_ssrc:
++ * @buffer: the buffer
++ * @ssrc: the new SSRC
++ *
++ * Set the SSRC on the RTP packet in @buffer to @ssrc.
++ */
++void
++gst_rtp_buffer_set_ssrc (GstBuffer * buffer, guint32 ssrc)
++{
++  GST_RTP_HEADER_SSRC (GST_BUFFER_DATA (buffer)) = g_htonl (ssrc);
++}
++
++/**
++ * gst_rtp_buffer_list_set_ssrc:
++ * @list: the buffer list
++ * @ssrc: the new SSRC
++ *
++ * Set the SSRC on each RTP packet in @list to @ssrc.
++ */
++void
++gst_rtp_buffer_list_set_ssrc (GstBufferList * list, guint32 ssrc)
++{
++  gst_rtp_buffer_list_set_rtp_headers (list, &ssrc, SSRC);
++}
++
++/**
++ * gst_rtp_buffer_get_csrc_count:
++ * @buffer: the buffer
++ *
++ * Get the CSRC count of the RTP packet in @buffer.
++ * 
++ * Returns: the CSRC count of @buffer.
++ */
++guint8
++gst_rtp_buffer_get_csrc_count (GstBuffer * buffer)
++{
++  return GST_RTP_HEADER_CSRC_COUNT (GST_BUFFER_DATA (buffer));
++}
++
++/**
++ * gst_rtp_buffer_get_csrc:
++ * @buffer: the buffer
++ * @idx: the index of the CSRC to get
++ *
++ * Get the CSRC at index @idx in @buffer.
++ * 
++ * Returns: the CSRC at index @idx in host order.
++ */
++guint32
++gst_rtp_buffer_get_csrc (GstBuffer * buffer, guint8 idx)
++{
++  guint8 *data;
++
++  data = GST_BUFFER_DATA (buffer);
++
++  g_return_val_if_fail (idx < GST_RTP_HEADER_CSRC_COUNT (data), 0);
++
++  return GST_READ_UINT32_BE (GST_RTP_HEADER_CSRC_LIST_OFFSET (data, idx));
++}
++
++/**
++ * gst_rtp_buffer_set_csrc:
++ * @buffer: the buffer
++ * @idx: the CSRC index to set
++ * @csrc: the CSRC in host order to set at @idx
++ *
++ * Modify the CSRC at index @idx in @buffer to @csrc.
++ */
++void
++gst_rtp_buffer_set_csrc (GstBuffer * buffer, guint8 idx, guint32 csrc)
++{
++  guint8 *data;
++
++  data = GST_BUFFER_DATA (buffer);
++
++  g_return_if_fail (idx < GST_RTP_HEADER_CSRC_COUNT (data));
++
++  GST_WRITE_UINT32_BE (GST_RTP_HEADER_CSRC_LIST_OFFSET (data, idx), csrc);
++}
++
++/**
++ * gst_rtp_buffer_get_marker:
++ * @buffer: the buffer
++ *
++ * Check if the marker bit is set on the RTP packet in @buffer.
++ *
++ * Returns: TRUE if @buffer has the marker bit set.
++ */
++gboolean
++gst_rtp_buffer_get_marker (GstBuffer * buffer)
++{
++  return GST_RTP_HEADER_MARKER (GST_BUFFER_DATA (buffer));
++}
++
++/**
++ * gst_rtp_buffer_set_marker:
++ * @buffer: the buffer
++ * @marker: the new marker
++ *
++ * Set the marker bit on the RTP packet in @buffer to @marker.
++ */
++void
++gst_rtp_buffer_set_marker (GstBuffer * buffer, gboolean marker)
++{
++  GST_RTP_HEADER_MARKER (GST_BUFFER_DATA (buffer)) = marker;
++}
++
++/**
++ * gst_rtp_buffer_get_payload_type:
++ * @buffer: the buffer
++ *
++ * Get the payload type of the RTP packet in @buffer.
++ *
++ * Returns: The payload type.
++ */
++guint8
++gst_rtp_buffer_get_payload_type (GstBuffer * buffer)
++{
++  return GST_RTP_HEADER_PAYLOAD_TYPE (GST_BUFFER_DATA (buffer));
++}
++
++/**
++ * gst_rtp_buffer_list_get_payload_type:
++ * @list: the list
++ *
++ * Get the payload type of the first RTP packet in @list.
++ * All packets in @list should have the same payload type.
++ *
++ * Returns: The payload type.
++ */
++guint8
++gst_rtp_buffer_list_get_payload_type (GstBufferList * list)
++{
++  guint8 *data;
++  data = gst_rtp_buffer_list_get_data (list);
++  g_return_val_if_fail (data != NULL, 0);
++  return GST_RTP_HEADER_PAYLOAD_TYPE (data);
++}
++
++/**
++ * gst_rtp_buffer_set_payload_type:
++ * @buffer: the buffer
++ * @payload_type: the new type
++ *
++ * Set the payload type of the RTP packet in @buffer to @payload_type.
++ */
++void
++gst_rtp_buffer_set_payload_type (GstBuffer * buffer, guint8 payload_type)
++{
++  g_return_if_fail (payload_type < 0x80);
++
++  GST_RTP_HEADER_PAYLOAD_TYPE (GST_BUFFER_DATA (buffer)) = payload_type;
++}
++
++/**
++ * gst_rtp_buffer_list_set_payload_type:
++ * @list: the buffer list
++ * @payload_type: the new type
++ *
++ * Set the payload type of each RTP packet in @list to @payload_type.
++ */
++void
++gst_rtp_buffer_list_set_payload_type (GstBufferList * list, guint8 payload_type)
++{
++  g_return_if_fail (payload_type < 0x80);
++
++  gst_rtp_buffer_list_set_rtp_headers (list, &payload_type, PAYLOAD_TYPE);
++}
++
++/**
++ * gst_rtp_buffer_get_seq:
++ * @buffer: the buffer
++ *
++ * Get the sequence number of the RTP packet in @buffer.
++ *
++ * Returns: The sequence number in host order.
++ */
++guint16
++gst_rtp_buffer_get_seq (GstBuffer * buffer)
++{
++  return g_ntohs (GST_RTP_HEADER_SEQ (GST_BUFFER_DATA (buffer)));
++}
++
++/**
++ * gst_rtp_buffer_set_seq:
++ * @buffer: the buffer
++ * @seq: the new sequence number
++ *
++ * Set the sequence number of the RTP packet in @buffer to @seq.
++ */
++void
++gst_rtp_buffer_set_seq (GstBuffer * buffer, guint16 seq)
++{
++  GST_RTP_HEADER_SEQ (GST_BUFFER_DATA (buffer)) = g_htons (seq);
++}
++
++/**
++ * gst_rtp_buffer_list_set_seq:
++ * @list: the buffer list
++ * @seq: the new sequence number
++ *
++ * Set the sequence number of each RTP packet in @list to @seq.
++ *
++ * Returns: The seq number of the last packet in the list + 1.
++ */
++guint16
++gst_rtp_buffer_list_set_seq (GstBufferList * list, guint16 seq)
++{
++  gst_rtp_buffer_list_set_rtp_headers (list, &seq, SEQ);
++  return seq;
++}
++
++/**
++ * gst_rtp_buffer_get_timestamp:
++ * @buffer: the buffer
++ *
++ * Get the timestamp of the RTP packet in @buffer.
++ *
++ * Returns: The timestamp in host order.
++ */
++guint32
++gst_rtp_buffer_get_timestamp (GstBuffer * buffer)
++{
++  return g_ntohl (GST_RTP_HEADER_TIMESTAMP (GST_BUFFER_DATA (buffer)));
++}
++
++/**
++ * gst_rtp_buffer_list_get_timestamp:
++ * @list: the list
++ *
++ * Get the timestamp of the first RTP packet in @list.
++ * All packets within @list have the same timestamp.
++ *
++ * Returns: The timestamp in host order.
++ */
++guint32
++gst_rtp_buffer_list_get_timestamp (GstBufferList * list)
++{
++  guint8 *data;
++  data = gst_rtp_buffer_list_get_data (list);
++  g_return_val_if_fail (data != NULL, 0);
++  return g_ntohl (GST_RTP_HEADER_TIMESTAMP (data));
++}
++
++/**
++ * gst_rtp_buffer_set_timestamp:
++ * @buffer: the buffer
++ * @timestamp: the new timestamp
++ *
++ * Set the timestamp of the RTP packet in @buffer to @timestamp.
++ */
++void
++gst_rtp_buffer_set_timestamp (GstBuffer * buffer, guint32 timestamp)
++{
++  GST_RTP_HEADER_TIMESTAMP (GST_BUFFER_DATA (buffer)) = g_htonl (timestamp);
++}
++
++/**
++ * gst_rtp_buffer_list_set_timestamp:
++ * @list: the buffer list
++ * @timestamp: the new timestamp
++ *
++ * Set the timestamp of each RTP packet in @list to @timestamp.
++ */
++void
++gst_rtp_buffer_list_set_timestamp (GstBufferList * list, guint32 timestamp)
++{
++  gst_rtp_buffer_list_set_rtp_headers (list, &timestamp, TIMESTAMP);
++}
++
++/**
++ * gst_rtp_buffer_get_payload_subbuffer:
++ * @buffer: the buffer
++ * @offset: the offset in the payload
++ * @len: the length in the payload
++ *
++ * Create a subbuffer of the payload of the RTP packet in @buffer. @offset bytes
++ * are skipped in the payload and the subbuffer will be of size @len.
++ * If @len is -1 the total payload starting from @offset if subbuffered.
++ *
++ * Returns: A new buffer with the specified data of the payload.
++ *
++ * Since: 0.10.10
++ */
++GstBuffer *
++gst_rtp_buffer_get_payload_subbuffer (GstBuffer * buffer, guint offset,
++    guint len)
++{
++  guint poffset, plen;
++
++  plen = gst_rtp_buffer_get_payload_len (buffer);
++  /* we can't go past the length */
++  if (G_UNLIKELY (offset >= plen))
++    goto wrong_offset;
++
++  /* apply offset */
++  poffset = gst_rtp_buffer_get_header_len (buffer) + offset;
++  plen -= offset;
++
++  /* see if we need to shrink the buffer based on @len */
++  if (len != -1 && len < plen)
++    plen = len;
++
++  return gst_buffer_create_sub (buffer, poffset, plen);
++
++  /* ERRORS */
++wrong_offset:
++  {
++    g_warning ("offset=%u should be less then plen=%u", offset, plen);
++    return NULL;
++  }
++}
++
++/**
++ * gst_rtp_buffer_get_payload_buffer:
++ * @buffer: the buffer
++ *
++ * Create a buffer of the payload of the RTP packet in @buffer. This function
++ * will internally create a subbuffer of @buffer so that a memcpy can be
++ * avoided.
++ *
++ * Returns: A new buffer with the data of the payload.
++ */
++GstBuffer *
++gst_rtp_buffer_get_payload_buffer (GstBuffer * buffer)
++{
++  return gst_rtp_buffer_get_payload_subbuffer (buffer, 0, -1);
++}
++
++/**
++ * gst_rtp_buffer_get_payload_len:
++ * @buffer: the buffer
++ *
++ * Get the length of the payload of the RTP packet in @buffer.
++ *
++ * Returns: The length of the payload in @buffer.
++ */
++guint
++gst_rtp_buffer_get_payload_len (GstBuffer * buffer)
++{
++  guint len, size;
++  guint8 *data;
++
++  size = GST_BUFFER_SIZE (buffer);
++  data = GST_BUFFER_DATA (buffer);
++
++  len = size - gst_rtp_buffer_get_header_len (buffer);
++
++  if (GST_RTP_HEADER_PADDING (data))
++    len -= data[size - 1];
++
++  return len;
++}
++
++/**
++ * gst_rtp_buffer_list_get_payload_len:
++ * @buffer: the buffer
++ *
++ * Get the length of the payload of the RTP packet in @list.
++ *
++ * Returns: The length of the payload in @list.
++ */
++guint
++gst_rtp_buffer_list_get_payload_len (GstBufferList * list)
++{
++  guint len = 0;
++  GstBufferListIterator *it;
++  it = gst_buffer_list_iterate (list);
++
++  while (gst_buffer_list_iterator_next_group (it)) {
++    guint i;
++    GstBuffer *buf;
++
++    i = 0;
++    while ((buf = gst_buffer_list_iterator_next (it))) {
++      /* skip the RTP header */
++      if (!i++)
++        continue;
++      /* take the size of the current buffer */
++      len += GST_BUFFER_SIZE (buf);
++    }
++  }
++
++  gst_buffer_list_iterator_free (it);
++
++  return len;
++}
++
++/**
++ * gst_rtp_buffer_get_payload:
++ * @buffer: the buffer
++ *
++ * Get a pointer to the payload data in @buffer. This pointer is valid as long
++ * as a reference to @buffer is held.
++ *
++ * Returns: A pointer to the payload data in @buffer.
++ */
++gpointer
++gst_rtp_buffer_get_payload (GstBuffer * buffer)
++{
++  return GST_BUFFER_DATA (buffer) + gst_rtp_buffer_get_header_len (buffer);
++}
++
++/**
++ * gst_rtp_buffer_default_clock_rate:
++ * @payload_type: the static payload type
++ *
++ * Get the default clock-rate for the static payload type @payload_type.
++ *
++ * Returns: the default clock rate or -1 if the payload type is not static or
++ * the clock-rate is undefined.
++ *
++ * Since: 0.10.13
++ */
++guint32
++gst_rtp_buffer_default_clock_rate (guint8 payload_type)
++{
++  const GstRTPPayloadInfo *info;
++  guint32 res;
++
++  info = gst_rtp_payload_info_for_pt (payload_type);
++  if (!info)
++    return -1;
++
++  res = info->clock_rate;
++  /* 0 means unknown so we have to return -1 from this function */
++  if (res == 0)
++    res = -1;
++
++  return res;
++}
++
++/**
++ * gst_rtp_buffer_compare_seqnum:
++ * @seqnum1: a sequence number
++ * @seqnum2: a sequence number
++ *
++ * Compare two sequence numbers, taking care of wraparounds. This function
++ * returns the difference between @seqnum1 and @seqnum2.
++ *
++ * Returns: a negative value if @seqnum1 is bigger than @seqnum2, 0 if they
++ * are equal or a positive value if @seqnum1 is smaller than @segnum2.
++ *
++ * Since: 0.10.15
++ */
++gint
++gst_rtp_buffer_compare_seqnum (guint16 seqnum1, guint16 seqnum2)
++{
++  return (gint16) (seqnum2 - seqnum1);
++}
++
++/**
++ * gst_rtp_buffer_ext_timestamp:
++ * @exttimestamp: a previous extended timestamp
++ * @timestamp: a new timestamp
++ *
++ * Update the @exttimestamp field with @timestamp. For the first call of the
++ * method, @exttimestamp should point to a location with a value of -1.
++ *
++ * This function makes sure that the returned value is a constantly increasing
++ * value even in the case where there is a timestamp wraparound.
++ *
++ * Returns: The extended timestamp of @timestamp.
++ *
++ * Since: 0.10.15
++ */
++guint64
++gst_rtp_buffer_ext_timestamp (guint64 * exttimestamp, guint32 timestamp)
++{
++  guint64 result, diff, ext;
++
++  g_return_val_if_fail (exttimestamp != NULL, -1);
++
++  ext = *exttimestamp;
++
++  if (ext == -1) {
++    result = timestamp;
++  } else {
++    /* pick wraparound counter from previous timestamp and add to new timestamp */
++    result = timestamp + (ext & ~(G_GINT64_CONSTANT (0xffffffff)));
++
++    /* check for timestamp wraparound */
++    if (result < ext)
++      diff = ext - result;
++    else
++      diff = result - ext;
++
++    if (diff > G_MAXINT32) {
++      /* timestamp went backwards more than allowed, we wrap around and get
++       * updated extended timestamp. */
++      result += (G_GINT64_CONSTANT (1) << 32);
++    }
++  }
++  *exttimestamp = result;
++
++  return result;
++}
++
++/**
++ * gst_rtp_buffer_list_get_data:
++ * @list: a buffer list
++ *
++ * Returns ponter to the RTP header of the first packet within the list
++ *
++ * Returns: pointer to the first RTP header
++ */
++static guint8 *
++gst_rtp_buffer_list_get_data (GstBufferList * list)
++{
++  GstBufferListIterator *it;
++  GstBuffer *rtpbuf;
++
++  it = gst_buffer_list_iterate (list);
++  if (!gst_buffer_list_iterator_next_group (it))
++    goto invalid_list;
++  rtpbuf = gst_buffer_list_iterator_next (it);
++  if (!rtpbuf)
++    goto invalid_list;
++
++  gst_buffer_list_iterator_free (it);
++  return GST_BUFFER_DATA (rtpbuf);
++
++invalid_list:
++  gst_buffer_list_iterator_free (it);
++  g_return_val_if_reached (FALSE);
++}
++
++/**
++ * gst_rtp_buffer_list_set_rtp_headers:
++ * @list: a buffer list
++ * @data: data to be set
++ * @type: which field in the header to be set
++ *
++ * Sets the field specified by @type to @data.
++ * This function updates all RTP headers within @list.
++ */
++static void
++gst_rtp_buffer_list_set_rtp_headers (GstBufferList * list,
++    gpointer data, rtp_header_data_type type)
++{
++  GstBufferListIterator *it;
++  it = gst_buffer_list_iterate (list);
++
++  while (gst_buffer_list_iterator_next_group (it)) {
++    GstBuffer *rtpbuf;
++    guint8 *rtp_header;
++    rtpbuf = gst_buffer_list_iterator_next (it);
++    rtp_header = GST_BUFFER_DATA (rtpbuf);
++    gst_rtp_buffer_list_set_data (rtp_header, data, type);
++  }
++
++  gst_buffer_list_iterator_free (it);
++}
++
++/**
++ * gst_rtp_buffer_list_set_data:
++ * @rtp_header: rtp header to be updated
++ * @data: data to be set
++ * @type: which field in the header to be set
++ *
++ * Sets the field specified by @type to @data.
++ * When setting SEQ number, this function will also increase
++ * @data by one.
++ */
++static void
++gst_rtp_buffer_list_set_data (guint8 * rtp_header,
++    gpointer data, rtp_header_data_type type)
++{
++  switch (type) {
++    case PAYLOAD_TYPE:
++      GST_RTP_HEADER_PAYLOAD_TYPE (rtp_header) = *(guint8 *) data;
++      break;
++    case SEQ:
++      GST_RTP_HEADER_SEQ (rtp_header) = g_htons (*(guint16 *) data);
++      (*(guint16 *) data)++;
++      break;
++    case SSRC:
++      GST_RTP_HEADER_SSRC (rtp_header) = g_htonl (*(guint32 *) data);
++      break;
++    case TIMESTAMP:
++      GST_RTP_HEADER_TIMESTAMP (rtp_header) = g_htonl (*(guint32 *) data);
++      break;
++    default:
++      g_warning ("Unknown data type");
++      break;
++  }
++}
index 0000000,0000000..088a67e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3519 @@@
++/* GStreamer
++ * Copyright (C) <2005-2009> Wim Taymans <wim.taymans@gmail.com>
++ *
++ * 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.
++ */
++/*
++ * Unless otherwise indicated, Source Code is licensed under MIT license.
++ * See further explanation attached in License Statement (distributed in the file
++ * LICENSE).
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy of
++ * this software and associated documentation files (the "Software"), to deal in
++ * the Software without restriction, including without limitation the rights to
++ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
++ * of the Software, and to permit persons to whom the Software is furnished to do
++ * so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in all
++ * copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ */
++
++/**
++ * SECTION:gstrtspconnection
++ * @short_description: manage RTSP connections
++ * @see_also: gstrtspurl
++ *  
++ * <refsect2>
++ * <para>
++ * This object manages the RTSP connection to the server. It provides function
++ * to receive and send bytes and messages.
++ * </para>
++ * </refsect2>
++ *  
++ * Last reviewed on 2007-07-24 (0.10.14)
++ */
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif
++
++#include <stdio.h>
++#include <errno.h>
++#include <stdlib.h>
++#include <string.h>
++#include <time.h>
++
++#ifdef HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++
++/* we include this here to get the G_OS_* defines */
++#include <glib.h>
++#include <gst/gst.h>
++
++#ifdef G_OS_WIN32
++/* ws2_32.dll has getaddrinfo and freeaddrinfo on Windows XP and later.
++ * minwg32 headers check WINVER before allowing the use of these */
++#ifndef WINVER
++#define WINVER 0x0501
++#endif
++#include <winsock2.h>
++#include <ws2tcpip.h>
++#define EINPROGRESS WSAEINPROGRESS
++#else
++#include <sys/ioctl.h>
++#include <netdb.h>
++#include <sys/socket.h>
++#include <fcntl.h>
++#include <netinet/in.h>
++#endif
++
++#ifdef HAVE_FIONREAD_IN_SYS_FILIO
++#include <sys/filio.h>
++#endif
++
++#include "gstrtspconnection.h"
++#include "gstrtspbase64.h"
++
++union gst_sockaddr
++{
++  struct sockaddr sa;
++  struct sockaddr_in sa_in;
++  struct sockaddr_in6 sa_in6;
++  struct sockaddr_storage sa_stor;
++};
++
++typedef struct
++{
++  gint state;
++  guint save;
++  guchar out[3];                /* the size must be evenly divisible by 3 */
++  guint cout;
++  guint coutl;
++} DecodeCtx;
++
++#ifdef G_OS_WIN32
++#define READ_SOCKET(fd, buf, len) recv (fd, (char *)buf, len, 0)
++#define WRITE_SOCKET(fd, buf, len) send (fd, (const char *)buf, len, 0)
++#define SETSOCKOPT(sock, level, name, val, len) setsockopt (sock, level, name, (const char *)val, len)
++#define CLOSE_SOCKET(sock) closesocket (sock)
++#define ERRNO_IS_EAGAIN (WSAGetLastError () == WSAEWOULDBLOCK)
++#define ERRNO_IS_EINTR (WSAGetLastError () == WSAEINTR)
++/* According to Microsoft's connect() documentation this one returns
++ * WSAEWOULDBLOCK and not WSAEINPROGRESS. */
++#define ERRNO_IS_EINPROGRESS (WSAGetLastError () == WSAEWOULDBLOCK)
++#else
++#define READ_SOCKET(fd, buf, len) read (fd, buf, len)
++#define WRITE_SOCKET(fd, buf, len) write (fd, buf, len)
++#define SETSOCKOPT(sock, level, name, val, len) setsockopt (sock, level, name, val, len)
++#define CLOSE_SOCKET(sock) close (sock)
++#define ERRNO_IS_EAGAIN (errno == EAGAIN)
++#define ERRNO_IS_EINTR (errno == EINTR)
++#define ERRNO_IS_EINPROGRESS (errno == EINPROGRESS)
++#endif
++
++#define ADD_POLLFD(fdset, pfd, fd)        \
++G_STMT_START {                            \
++  (pfd)->fd = fd;                         \
++  gst_poll_add_fd (fdset, pfd);           \
++} G_STMT_END
++
++#define REMOVE_POLLFD(fdset, pfd)          \
++G_STMT_START {                             \
++  if ((pfd)->fd != -1) {                   \
++    GST_DEBUG ("remove fd %d", (pfd)->fd); \
++    gst_poll_remove_fd (fdset, pfd);       \
++    CLOSE_SOCKET ((pfd)->fd);              \
++    (pfd)->fd = -1;                        \
++  }                                        \
++} G_STMT_END
++
++typedef enum
++{
++  TUNNEL_STATE_NONE,
++  TUNNEL_STATE_GET,
++  TUNNEL_STATE_POST,
++  TUNNEL_STATE_COMPLETE
++} GstRTSPTunnelState;
++
++#define TUNNELID_LEN   24
++
++struct _GstRTSPConnection
++{
++  /*< private > */
++  /* URL for the connection */
++  GstRTSPUrl *url;
++
++  /* connection state */
++  GstPollFD fd0;
++  GstPollFD fd1;
++
++  GstPollFD *readfd;
++  GstPollFD *writefd;
++
++  gboolean manual_http;
++
++  gchar tunnelid[TUNNELID_LEN];
++  gboolean tunneled;
++  GstRTSPTunnelState tstate;
++
++  GstPoll *fdset;
++  gchar *ip;
++
++  gint read_ahead;
++
++  gchar *initial_buffer;
++  gsize initial_buffer_offset;
++
++  /* Session state */
++  gint cseq;                    /* sequence number */
++  gchar session_id[512];        /* session id */
++  gint timeout;                 /* session timeout in seconds */
++  GTimer *timer;                /* timeout timer */
++
++  /* Authentication */
++  GstRTSPAuthMethod auth_method;
++  gchar *username;
++  gchar *passwd;
++  GHashTable *auth_params;
++
++  DecodeCtx ctx;
++  DecodeCtx *ctxp;
++
++  gchar *proxy_host;
++  guint proxy_port;
++};
++
++enum
++{
++  STATE_START = 0,
++  STATE_DATA_HEADER,
++  STATE_DATA_BODY,
++  STATE_READ_LINES,
++  STATE_END,
++  STATE_LAST
++};
++
++enum
++{
++  READ_AHEAD_EOH = -1,          /* end of headers */
++  READ_AHEAD_CRLF = -2,
++  READ_AHEAD_CRLFCR = -3
++};
++
++/* a structure for constructing RTSPMessages */
++typedef struct
++{
++  gint state;
++  GstRTSPResult status;
++  guint8 buffer[4096];
++  guint offset;
++
++  guint line;
++  guint8 *body_data;
++  glong body_len;
++} GstRTSPBuilder;
++
++static void
++build_reset (GstRTSPBuilder * builder)
++{
++  g_free (builder->body_data);
++  memset (builder, 0, sizeof (GstRTSPBuilder));
++}
++
++/**
++ * gst_rtsp_connection_create:
++ * @url: a #GstRTSPUrl 
++ * @conn: storage for a #GstRTSPConnection
++ *
++ * Create a newly allocated #GstRTSPConnection from @url and store it in @conn.
++ * The connection will not yet attempt to connect to @url, use
++ * gst_rtsp_connection_connect().
++ *
++ * A copy of @url will be made.
++ *
++ * Returns: #GST_RTSP_OK when @conn contains a valid connection.
++ */
++GstRTSPResult
++gst_rtsp_connection_create (const GstRTSPUrl * url, GstRTSPConnection ** conn)
++{
++  GstRTSPConnection *newconn;
++#ifdef G_OS_WIN32
++  WSADATA w;
++  int error;
++#endif
++
++  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
++
++#ifdef G_OS_WIN32
++  error = WSAStartup (0x0202, &w);
++
++  if (error)
++    goto startup_error;
++
++  if (w.wVersion != 0x0202)
++    goto version_error;
++#endif
++
++  newconn = g_new0 (GstRTSPConnection, 1);
++
++  if ((newconn->fdset = gst_poll_new (TRUE)) == NULL)
++    goto no_fdset;
++
++  newconn->url = gst_rtsp_url_copy (url);
++  newconn->fd0.fd = -1;
++  newconn->fd1.fd = -1;
++  newconn->timer = g_timer_new ();
++  newconn->timeout = 60;
++  newconn->cseq = 1;
++
++  newconn->auth_method = GST_RTSP_AUTH_NONE;
++  newconn->username = NULL;
++  newconn->passwd = NULL;
++  newconn->auth_params = NULL;
++
++  *conn = newconn;
++
++  return GST_RTSP_OK;
++
++  /* ERRORS */
++#ifdef G_OS_WIN32
++startup_error:
++  {
++    g_warning ("Error %d on WSAStartup", error);
++    return GST_RTSP_EWSASTART;
++  }
++version_error:
++  {
++    g_warning ("Windows sockets are not version 0x202 (current 0x%x)",
++        w.wVersion);
++    WSACleanup ();
++    return GST_RTSP_EWSAVERSION;
++  }
++#endif
++no_fdset:
++  {
++    g_free (newconn);
++#ifdef G_OS_WIN32
++    WSACleanup ();
++#endif
++    return GST_RTSP_ESYS;
++  }
++}
++
++/**
++ * gst_rtsp_connection_create_from_fd:
++ * @fd: a file descriptor
++ * @ip: the IP address of the other end
++ * @port: the port used by the other end
++ * @initial_buffer: data already read from @fd
++ * @conn: storage for a #GstRTSPConnection
++ *
++ * Create a new #GstRTSPConnection for handling communication on the existing
++ * file descriptor @fd. The @initial_buffer contains any data already read from
++ * @fd which should be used before starting to read new data.
++ *
++ * Returns: #GST_RTSP_OK when @conn contains a valid connection.
++ *
++ * Since: 0.10.25
++ */
++GstRTSPResult
++gst_rtsp_connection_create_from_fd (gint fd, const gchar * ip, guint16 port,
++    const gchar * initial_buffer, GstRTSPConnection ** conn)
++{
++  GstRTSPConnection *newconn = NULL;
++  GstRTSPUrl *url;
++#ifdef G_OS_WIN32
++  gulong flags = 1;
++#endif
++  GstRTSPResult res;
++
++  g_return_val_if_fail (fd >= 0, GST_RTSP_EINVAL);
++  g_return_val_if_fail (ip != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
++
++  /* set to non-blocking mode so that we can cancel the communication */
++#ifndef G_OS_WIN32
++  fcntl (fd, F_SETFL, O_NONBLOCK);
++#else
++  ioctlsocket (fd, FIONBIO, &flags);
++#endif /* G_OS_WIN32 */
++
++  /* create a url for the client address */
++  url = g_new0 (GstRTSPUrl, 1);
++  url->host = g_strdup (ip);
++  url->port = port;
++
++  /* now create the connection object */
++  GST_RTSP_CHECK (gst_rtsp_connection_create (url, &newconn), newconn_failed);
++  gst_rtsp_url_free (url);
++
++  ADD_POLLFD (newconn->fdset, &newconn->fd0, fd);
++
++  /* both read and write initially */
++  newconn->readfd = &newconn->fd0;
++  newconn->writefd = &newconn->fd0;
++
++  newconn->ip = g_strdup (ip);
++
++  newconn->initial_buffer = g_strdup (initial_buffer);
++
++  *conn = newconn;
++
++  return GST_RTSP_OK;
++
++  /* ERRORS */
++newconn_failed:
++  {
++    gst_rtsp_url_free (url);
++    return res;
++  }
++}
++
++/**
++ * gst_rtsp_connection_accept:
++ * @sock: a socket
++ * @conn: storage for a #GstRTSPConnection
++ *
++ * Accept a new connection on @sock and create a new #GstRTSPConnection for
++ * handling communication on new socket.
++ *
++ * Returns: #GST_RTSP_OK when @conn contains a valid connection.
++ *
++ * Since: 0.10.23
++ */
++GstRTSPResult
++gst_rtsp_connection_accept (gint sock, GstRTSPConnection ** conn)
++{
++  int fd;
++  union gst_sockaddr sa;
++  socklen_t slen = sizeof (sa);
++  gchar ip[INET6_ADDRSTRLEN];
++  guint16 port;
++
++  g_return_val_if_fail (sock >= 0, GST_RTSP_EINVAL);
++  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
++
++  memset (&sa, 0, slen);
++
++#ifndef G_OS_WIN32
++  fd = accept (sock, &sa.sa, &slen);
++#else
++  fd = accept (sock, &sa.sa, (gint *) & slen);
++#endif /* G_OS_WIN32 */
++  if (fd == -1)
++    goto accept_failed;
++
++  if (getnameinfo (&sa.sa, slen, ip, sizeof (ip), NULL, 0, NI_NUMERICHOST) != 0)
++    goto getnameinfo_failed;
++
++  if (sa.sa.sa_family == AF_INET)
++    port = sa.sa_in.sin_port;
++  else if (sa.sa.sa_family == AF_INET6)
++    port = sa.sa_in6.sin6_port;
++  else
++    goto wrong_family;
++
++  return gst_rtsp_connection_create_from_fd (fd, ip, port, NULL, conn);
++
++  /* ERRORS */
++accept_failed:
++  {
++    return GST_RTSP_ESYS;
++  }
++getnameinfo_failed:
++wrong_family:
++  {
++    CLOSE_SOCKET (fd);
++    return GST_RTSP_ERROR;
++  }
++}
++
++static gchar *
++do_resolve (const gchar * host)
++{
++  static gchar ip[INET6_ADDRSTRLEN];
++  struct addrinfo *aires;
++  struct addrinfo *ai;
++  gint aierr;
++
++  aierr = getaddrinfo (host, NULL, NULL, &aires);
++  if (aierr != 0)
++    goto no_addrinfo;
++
++  for (ai = aires; ai; ai = ai->ai_next) {
++    if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) {
++      break;
++    }
++  }
++  if (ai == NULL)
++    goto no_family;
++
++  aierr = getnameinfo (ai->ai_addr, ai->ai_addrlen, ip, sizeof (ip), NULL, 0,
++      NI_NUMERICHOST | NI_NUMERICSERV);
++  if (aierr != 0)
++    goto no_address;
++
++  freeaddrinfo (aires);
++
++  return g_strdup (ip);
++
++  /* ERRORS */
++no_addrinfo:
++  {
++    GST_ERROR ("no addrinfo found for %s: %s", host, gai_strerror (aierr));
++    return NULL;
++  }
++no_family:
++  {
++    GST_ERROR ("no family found for %s", host);
++    freeaddrinfo (aires);
++    return NULL;
++  }
++no_address:
++  {
++    GST_ERROR ("no address found for %s: %s", host, gai_strerror (aierr));
++    freeaddrinfo (aires);
++    return NULL;
++  }
++}
++
++static GstRTSPResult
++do_connect (const gchar * ip, guint16 port, GstPollFD * fdout,
++    GstPoll * fdset, GTimeVal * timeout)
++{
++  gint fd;
++  struct addrinfo hints;
++  struct addrinfo *aires;
++  struct addrinfo *ai;
++  gint aierr;
++  gchar service[NI_MAXSERV];
++  gint ret;
++#ifdef G_OS_WIN32
++  unsigned long flags = 1;
++#endif /* G_OS_WIN32 */
++  GstClockTime to;
++  gint retval;
++
++  memset (&hints, 0, sizeof hints);
++  hints.ai_flags = AI_NUMERICHOST;
++  hints.ai_family = AF_UNSPEC;
++  hints.ai_socktype = SOCK_STREAM;
++  g_snprintf (service, sizeof (service) - 1, "%hu", port);
++  service[sizeof (service) - 1] = '\0';
++
++  aierr = getaddrinfo (ip, service, &hints, &aires);
++  if (aierr != 0)
++    goto no_addrinfo;
++
++  for (ai = aires; ai; ai = ai->ai_next) {
++    if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) {
++      break;
++    }
++  }
++  if (ai == NULL)
++    goto no_family;
++
++  fd = socket (ai->ai_family, SOCK_STREAM, 0);
++  if (fd == -1)
++    goto no_socket;
++
++  /* set to non-blocking mode so that we can cancel the connect */
++#ifndef G_OS_WIN32
++  fcntl (fd, F_SETFL, O_NONBLOCK);
++#else
++  ioctlsocket (fd, FIONBIO, &flags);
++#endif /* G_OS_WIN32 */
++
++  /* add the socket to our fdset */
++  ADD_POLLFD (fdset, fdout, fd);
++
++  /* we are going to connect ASYNC now */
++  ret = connect (fd, ai->ai_addr, ai->ai_addrlen);
++  if (ret == 0)
++    goto done;
++  if (!ERRNO_IS_EINPROGRESS)
++    goto sys_error;
++
++  /* wait for connect to complete up to the specified timeout or until we got
++   * interrupted. */
++  gst_poll_fd_ctl_write (fdset, fdout, TRUE);
++
++  to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
++
++  do {
++    retval = gst_poll_wait (fdset, to);
++  } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
++
++  if (retval == 0)
++    goto timeout;
++  else if (retval == -1)
++    goto sys_error;
++
++  /* we can still have an error connecting on windows */
++  if (gst_poll_fd_has_error (fdset, fdout)) {
++    socklen_t len = sizeof (errno);
++#ifndef G_OS_WIN32
++    getsockopt (fd, SOL_SOCKET, SO_ERROR, &errno, &len);
++#else
++    getsockopt (fd, SOL_SOCKET, SO_ERROR, (char *) &errno, &len);
++#endif
++    goto sys_error;
++  }
++
++  gst_poll_fd_ignored (fdset, fdout);
++
++done:
++  freeaddrinfo (aires);
++
++  return GST_RTSP_OK;
++
++  /* ERRORS */
++no_addrinfo:
++  {
++    GST_ERROR ("no addrinfo found for %s: %s", ip, gai_strerror (aierr));
++    return GST_RTSP_ERROR;
++  }
++no_family:
++  {
++    GST_ERROR ("no family found for %s", ip);
++    freeaddrinfo (aires);
++    return GST_RTSP_ERROR;
++  }
++no_socket:
++  {
++    GST_ERROR ("no socket %d (%s)", errno, g_strerror (errno));
++    freeaddrinfo (aires);
++    return GST_RTSP_ESYS;
++  }
++sys_error:
++  {
++    GST_ERROR ("system error %d (%s)", errno, g_strerror (errno));
++    REMOVE_POLLFD (fdset, fdout);
++    freeaddrinfo (aires);
++    return GST_RTSP_ESYS;
++  }
++timeout:
++  {
++    GST_ERROR ("timeout");
++    REMOVE_POLLFD (fdset, fdout);
++    freeaddrinfo (aires);
++    return GST_RTSP_ETIMEOUT;
++  }
++}
++
++static GstRTSPResult
++setup_tunneling (GstRTSPConnection * conn, GTimeVal * timeout)
++{
++  gint i;
++  GstRTSPResult res;
++  gchar *ip;
++  gchar *uri;
++  gchar *value;
++  guint16 port, url_port;
++  GstRTSPUrl *url;
++  gchar *hostparam;
++  GstRTSPMessage *msg;
++  GstRTSPMessage response;
++  gboolean old_http;
++
++  memset (&response, 0, sizeof (response));
++  gst_rtsp_message_init (&response);
++
++  /* create a random sessionid */
++  for (i = 0; i < TUNNELID_LEN; i++)
++    conn->tunnelid[i] = g_random_int_range ('a', 'z');
++  conn->tunnelid[TUNNELID_LEN - 1] = '\0';
++
++  url = conn->url;
++  /* get the port from the url */
++  gst_rtsp_url_get_port (url, &url_port);
++
++  if (conn->proxy_host) {
++    uri = g_strdup_printf ("http://%s:%d%s%s%s", url->host, url_port,
++        url->abspath, url->query ? "?" : "", url->query ? url->query : "");
++    hostparam = g_strdup_printf ("%s:%d", url->host, url_port);
++    ip = conn->proxy_host;
++    port = conn->proxy_port;
++  } else {
++    uri = g_strdup_printf ("%s%s%s", url->abspath, url->query ? "?" : "",
++        url->query ? url->query : "");
++    hostparam = NULL;
++    ip = conn->ip;
++    port = url_port;
++  }
++
++  /* create the GET request for the read connection */
++  GST_RTSP_CHECK (gst_rtsp_message_new_request (&msg, GST_RTSP_GET, uri),
++      no_message);
++  msg->type = GST_RTSP_MESSAGE_HTTP_REQUEST;
++
++  if (hostparam != NULL)
++    gst_rtsp_message_add_header (msg, GST_RTSP_HDR_HOST, hostparam);
++  gst_rtsp_message_add_header (msg, GST_RTSP_HDR_X_SESSIONCOOKIE,
++      conn->tunnelid);
++  gst_rtsp_message_add_header (msg, GST_RTSP_HDR_ACCEPT,
++      "application/x-rtsp-tunnelled");
++  gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CACHE_CONTROL, "no-cache");
++  gst_rtsp_message_add_header (msg, GST_RTSP_HDR_PRAGMA, "no-cache");
++
++  /* we start by writing to this fd */
++  conn->writefd = &conn->fd0;
++
++  /* we need to temporarily set conn->tunneled to FALSE to prevent the HTTP
++   * request from being base64 encoded */
++  conn->tunneled = FALSE;
++  GST_RTSP_CHECK (gst_rtsp_connection_send (conn, msg, timeout), write_failed);
++  gst_rtsp_message_free (msg);
++  conn->tunneled = TRUE;
++
++  /* receive the response to the GET request */
++  /* we need to temporarily set manual_http to TRUE since
++   * gst_rtsp_connection_receive() will treat the HTTP response as a parsing
++   * failure otherwise */
++  old_http = conn->manual_http;
++  conn->manual_http = TRUE;
++  GST_RTSP_CHECK (gst_rtsp_connection_receive (conn, &response, timeout),
++      read_failed);
++  conn->manual_http = old_http;
++
++  if (response.type != GST_RTSP_MESSAGE_HTTP_RESPONSE ||
++      response.type_data.response.code != GST_RTSP_STS_OK)
++    goto wrong_result;
++
++  if (gst_rtsp_message_get_header (&response, GST_RTSP_HDR_X_SERVER_IP_ADDRESS,
++          &value, 0) != GST_RTSP_OK) {
++    if (conn->proxy_host) {
++      /* if we use a proxy we need to change the destination url */
++      g_free (url->host);
++      url->host = g_strdup (value);
++      g_free (hostparam);
++      hostparam = g_strdup_printf ("%s:%d", url->host, url_port);
++    } else {
++      /* and resolve the new ip address */
++      if (!(ip = do_resolve (conn->ip)))
++        goto not_resolved;
++      g_free (conn->ip);
++      conn->ip = ip;
++    }
++  }
++
++  /* connect to the host/port */
++  res = do_connect (ip, port, &conn->fd1, conn->fdset, timeout);
++  if (res != GST_RTSP_OK)
++    goto connect_failed;
++
++  /* this is now our writing socket */
++  conn->writefd = &conn->fd1;
++
++  /* create the POST request for the write connection */
++  GST_RTSP_CHECK (gst_rtsp_message_new_request (&msg, GST_RTSP_POST, uri),
++      no_message);
++  msg->type = GST_RTSP_MESSAGE_HTTP_REQUEST;
++
++  if (hostparam != NULL)
++    gst_rtsp_message_add_header (msg, GST_RTSP_HDR_HOST, hostparam);
++  gst_rtsp_message_add_header (msg, GST_RTSP_HDR_X_SESSIONCOOKIE,
++      conn->tunnelid);
++  gst_rtsp_message_add_header (msg, GST_RTSP_HDR_ACCEPT,
++      "application/x-rtsp-tunnelled");
++  gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CACHE_CONTROL, "no-cache");
++  gst_rtsp_message_add_header (msg, GST_RTSP_HDR_PRAGMA, "no-cache");
++  gst_rtsp_message_add_header (msg, GST_RTSP_HDR_EXPIRES,
++      "Sun, 9 Jan 1972 00:00:00 GMT");
++  gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CONTENT_LENGTH, "32767");
++
++  /* we need to temporarily set conn->tunneled to FALSE to prevent the HTTP
++   * request from being base64 encoded */
++  conn->tunneled = FALSE;
++  GST_RTSP_CHECK (gst_rtsp_connection_send (conn, msg, timeout), write_failed);
++  gst_rtsp_message_free (msg);
++  conn->tunneled = TRUE;
++
++exit:
++  gst_rtsp_message_unset (&response);
++  g_free (hostparam);
++  g_free (uri);
++
++  return res;
++
++  /* ERRORS */
++no_message:
++  {
++    GST_ERROR ("failed to create request (%d)", res);
++    goto exit;
++  }
++write_failed:
++  {
++    GST_ERROR ("write failed (%d)", res);
++    gst_rtsp_message_free (msg);
++    conn->tunneled = TRUE;
++    goto exit;
++  }
++read_failed:
++  {
++    GST_ERROR ("read failed (%d)", res);
++    conn->manual_http = FALSE;
++    goto exit;
++  }
++wrong_result:
++  {
++    GST_ERROR ("got failure response %d %s", response.type_data.response.code,
++        response.type_data.response.reason);
++    res = GST_RTSP_ERROR;
++    goto exit;
++  }
++not_resolved:
++  {
++    GST_ERROR ("could not resolve %s", conn->ip);
++    res = GST_RTSP_ENET;
++    goto exit;
++  }
++connect_failed:
++  {
++    GST_ERROR ("failed to connect");
++    goto exit;
++  }
++}
++
++/**
++ * gst_rtsp_connection_connect:
++ * @conn: a #GstRTSPConnection 
++ * @timeout: a #GTimeVal timeout
++ *
++ * Attempt to connect to the url of @conn made with
++ * gst_rtsp_connection_create(). If @timeout is #NULL this function can block
++ * forever. If @timeout contains a valid timeout, this function will return
++ * #GST_RTSP_ETIMEOUT after the timeout expired.
++ *
++ * This function can be cancelled with gst_rtsp_connection_flush().
++ *
++ * Returns: #GST_RTSP_OK when a connection could be made.
++ */
++GstRTSPResult
++gst_rtsp_connection_connect (GstRTSPConnection * conn, GTimeVal * timeout)
++{
++  GstRTSPResult res;
++  gchar *ip;
++  guint16 port;
++  GstRTSPUrl *url;
++
++  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (conn->url != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (conn->fd0.fd < 0, GST_RTSP_EINVAL);
++
++  url = conn->url;
++
++  if (conn->proxy_host && conn->tunneled) {
++    if (!(ip = do_resolve (conn->proxy_host))) {
++      GST_ERROR ("could not resolve %s", conn->proxy_host);
++      goto not_resolved;
++    }
++    port = conn->proxy_port;
++    g_free (conn->proxy_host);
++    conn->proxy_host = ip;
++  } else {
++    if (!(ip = do_resolve (url->host))) {
++      GST_ERROR ("could not resolve %s", url->host);
++      goto not_resolved;
++    }
++    /* get the port from the url */
++    gst_rtsp_url_get_port (url, &port);
++
++    g_free (conn->ip);
++    conn->ip = ip;
++  }
++
++  /* connect to the host/port */
++  res = do_connect (ip, port, &conn->fd0, conn->fdset, timeout);
++  if (res != GST_RTSP_OK)
++    goto connect_failed;
++
++  /* this is our read URL */
++  conn->readfd = &conn->fd0;
++
++  if (conn->tunneled) {
++    res = setup_tunneling (conn, timeout);
++    if (res != GST_RTSP_OK)
++      goto tunneling_failed;
++  } else {
++    conn->writefd = &conn->fd0;
++  }
++
++  return GST_RTSP_OK;
++
++not_resolved:
++  {
++    return GST_RTSP_ENET;
++  }
++connect_failed:
++  {
++    GST_ERROR ("failed to connect");
++    return res;
++  }
++tunneling_failed:
++  {
++    GST_ERROR ("failed to setup tunneling");
++    return res;
++  }
++}
++
++static void
++auth_digest_compute_hex_urp (const gchar * username,
++    const gchar * realm, const gchar * password, gchar hex_urp[33])
++{
++  GChecksum *md5_context = g_checksum_new (G_CHECKSUM_MD5);
++  const gchar *digest_string;
++
++  g_checksum_update (md5_context, (const guchar *) username, strlen (username));
++  g_checksum_update (md5_context, (const guchar *) ":", 1);
++  g_checksum_update (md5_context, (const guchar *) realm, strlen (realm));
++  g_checksum_update (md5_context, (const guchar *) ":", 1);
++  g_checksum_update (md5_context, (const guchar *) password, strlen (password));
++  digest_string = g_checksum_get_string (md5_context);
++
++  memset (hex_urp, 0, 33);
++  memcpy (hex_urp, digest_string, strlen (digest_string));
++
++  g_checksum_free (md5_context);
++}
++
++static void
++auth_digest_compute_response (const gchar * method,
++    const gchar * uri, const gchar * hex_a1, const gchar * nonce,
++    gchar response[33])
++{
++  char hex_a2[33] = { 0, };
++  GChecksum *md5_context = g_checksum_new (G_CHECKSUM_MD5);
++  const gchar *digest_string;
++
++  /* compute A2 */
++  g_checksum_update (md5_context, (const guchar *) method, strlen (method));
++  g_checksum_update (md5_context, (const guchar *) ":", 1);
++  g_checksum_update (md5_context, (const guchar *) uri, strlen (uri));
++  digest_string = g_checksum_get_string (md5_context);
++  memcpy (hex_a2, digest_string, strlen (digest_string));
++
++  /* compute KD */
++  g_checksum_reset (md5_context);
++  g_checksum_update (md5_context, (const guchar *) hex_a1, strlen (hex_a1));
++  g_checksum_update (md5_context, (const guchar *) ":", 1);
++  g_checksum_update (md5_context, (const guchar *) nonce, strlen (nonce));
++  g_checksum_update (md5_context, (const guchar *) ":", 1);
++
++  g_checksum_update (md5_context, (const guchar *) hex_a2, 32);
++  digest_string = g_checksum_get_string (md5_context);
++  memset (response, 0, 33);
++  memcpy (response, digest_string, strlen (digest_string));
++
++  g_checksum_free (md5_context);
++}
++
++static void
++add_auth_header (GstRTSPConnection * conn, GstRTSPMessage * message)
++{
++  switch (conn->auth_method) {
++    case GST_RTSP_AUTH_BASIC:{
++      gchar *user_pass;
++      gchar *user_pass64;
++      gchar *auth_string;
++
++      user_pass = g_strdup_printf ("%s:%s", conn->username, conn->passwd);
++      user_pass64 = g_base64_encode ((guchar *) user_pass, strlen (user_pass));
++      auth_string = g_strdup_printf ("Basic %s", user_pass64);
++
++      gst_rtsp_message_take_header (message, GST_RTSP_HDR_AUTHORIZATION,
++          auth_string);
++
++      g_free (user_pass);
++      g_free (user_pass64);
++      break;
++    }
++    case GST_RTSP_AUTH_DIGEST:{
++      gchar response[33], hex_urp[33];
++      gchar *auth_string, *auth_string2;
++      gchar *realm;
++      gchar *nonce;
++      gchar *opaque;
++      const gchar *uri;
++      const gchar *method;
++
++      /* we need to have some params set */
++      if (conn->auth_params == NULL)
++        break;
++
++      /* we need the realm and nonce */
++      realm = (gchar *) g_hash_table_lookup (conn->auth_params, "realm");
++      nonce = (gchar *) g_hash_table_lookup (conn->auth_params, "nonce");
++      if (realm == NULL || nonce == NULL)
++        break;
++
++      auth_digest_compute_hex_urp (conn->username, realm, conn->passwd,
++          hex_urp);
++
++      method = gst_rtsp_method_as_text (message->type_data.request.method);
++      uri = message->type_data.request.uri;
++
++      /* Assume no qop, algorithm=md5, stale=false */
++      /* For algorithm MD5, a1 = urp. */
++      auth_digest_compute_response (method, uri, hex_urp, nonce, response);
++      auth_string = g_strdup_printf ("Digest username=\"%s\", "
++          "realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
++          conn->username, realm, nonce, uri, response);
++
++      opaque = (gchar *) g_hash_table_lookup (conn->auth_params, "opaque");
++      if (opaque) {
++        auth_string2 = g_strdup_printf ("%s, opaque=\"%s\"", auth_string,
++            opaque);
++        g_free (auth_string);
++        auth_string = auth_string2;
++      }
++      gst_rtsp_message_take_header (message, GST_RTSP_HDR_AUTHORIZATION,
++          auth_string);
++      break;
++    }
++    default:
++      /* Nothing to do */
++      break;
++  }
++}
++
++static void
++gen_date_string (gchar * date_string, guint len)
++{
++  GTimeVal tv;
++  time_t t;
++#ifdef HAVE_GMTIME_R
++  struct tm tm_;
++#endif
++
++  g_get_current_time (&tv);
++  t = (time_t) tv.tv_sec;
++
++#ifdef HAVE_GMTIME_R
++  strftime (date_string, len, "%a, %d %b %Y %H:%M:%S GMT", gmtime_r (&t, &tm_));
++#else
++  strftime (date_string, len, "%a, %d %b %Y %H:%M:%S GMT", gmtime (&t));
++#endif
++}
++
++static GstRTSPResult
++write_bytes (gint fd, const guint8 * buffer, guint * idx, guint size)
++{
++  guint left;
++
++  if (G_UNLIKELY (*idx > size))
++    return GST_RTSP_ERROR;
++
++  left = size - *idx;
++
++  while (left) {
++    gint r;
++
++    r = WRITE_SOCKET (fd, &buffer[*idx], left);
++    if (G_UNLIKELY (r == 0)) {
++      return GST_RTSP_EINTR;
++    } else if (G_UNLIKELY (r < 0)) {
++      if (ERRNO_IS_EAGAIN)
++        return GST_RTSP_EINTR;
++      if (!ERRNO_IS_EINTR)
++        return GST_RTSP_ESYS;
++    } else {
++      left -= r;
++      *idx += r;
++    }
++  }
++  return GST_RTSP_OK;
++}
++
++static gint
++fill_raw_bytes (GstRTSPConnection * conn, guint8 * buffer, guint size)
++{
++  gint out = 0;
++
++  if (G_UNLIKELY (conn->initial_buffer != NULL)) {
++    gsize left = strlen (&conn->initial_buffer[conn->initial_buffer_offset]);
++
++    out = MIN (left, size);
++    memcpy (buffer, &conn->initial_buffer[conn->initial_buffer_offset], out);
++
++    if (left == (gsize) out) {
++      g_free (conn->initial_buffer);
++      conn->initial_buffer = NULL;
++      conn->initial_buffer_offset = 0;
++    } else
++      conn->initial_buffer_offset += out;
++  }
++
++  if (G_LIKELY (size > (guint) out)) {
++    gint r;
++
++    r = READ_SOCKET (conn->readfd->fd, &buffer[out], size - out);
++    if (r <= 0) {
++      if (out == 0)
++        out = r;
++    } else
++      out += r;
++  }
++
++  return out;
++}
++
++static gint
++fill_bytes (GstRTSPConnection * conn, guint8 * buffer, guint size)
++{
++  DecodeCtx *ctx = conn->ctxp;
++  gint out = 0;
++
++  if (ctx) {
++    while (size > 0) {
++      guint8 in[sizeof (ctx->out) * 4 / 3];
++      gint r;
++
++      while (size > 0 && ctx->cout < ctx->coutl) {
++        /* we have some leftover bytes */
++        *buffer++ = ctx->out[ctx->cout++];
++        size--;
++        out++;
++      }
++
++      /* got what we needed? */
++      if (size == 0)
++        break;
++
++      /* try to read more bytes */
++      r = fill_raw_bytes (conn, in, sizeof (in));
++      if (r <= 0) {
++        if (out == 0)
++          out = r;
++        break;
++      }
++
++      ctx->cout = 0;
++      ctx->coutl =
++          g_base64_decode_step ((gchar *) in, r, ctx->out, &ctx->state,
++          &ctx->save);
++    }
++  } else {
++    out = fill_raw_bytes (conn, buffer, size);
++  }
++
++  return out;
++}
++
++static GstRTSPResult
++read_bytes (GstRTSPConnection * conn, guint8 * buffer, guint * idx, guint size)
++{
++  guint left;
++
++  if (G_UNLIKELY (*idx > size))
++    return GST_RTSP_ERROR;
++
++  left = size - *idx;
++
++  while (left) {
++    gint r;
++
++    r = fill_bytes (conn, &buffer[*idx], left);
++    if (G_UNLIKELY (r == 0)) {
++      return GST_RTSP_EEOF;
++    } else if (G_UNLIKELY (r < 0)) {
++      if (ERRNO_IS_EAGAIN)
++        return GST_RTSP_EINTR;
++      if (!ERRNO_IS_EINTR)
++        return GST_RTSP_ESYS;
++    } else {
++      left -= r;
++      *idx += r;
++    }
++  }
++  return GST_RTSP_OK;
++}
++
++/* The code below tries to handle clients using \r, \n or \r\n to indicate the
++ * end of a line. It even does its best to handle clients which mix them (even
++ * though this is a really stupid idea (tm).) It also handles Line White Space
++ * (LWS), where a line end followed by whitespace is considered LWS. This is
++ * the method used in RTSP (and HTTP) to break long lines.
++ */
++static GstRTSPResult
++read_line (GstRTSPConnection * conn, guint8 * buffer, guint * idx, guint size)
++{
++  while (TRUE) {
++    guint8 c;
++    gint r;
++
++    if (conn->read_ahead == READ_AHEAD_EOH) {
++      /* the last call to read_line() already determined that we have reached
++       * the end of the headers, so convey that information now */
++      conn->read_ahead = 0;
++      break;
++    } else if (conn->read_ahead == READ_AHEAD_CRLF) {
++      /* the last call to read_line() left off after having read \r\n */
++      c = '\n';
++    } else if (conn->read_ahead == READ_AHEAD_CRLFCR) {
++      /* the last call to read_line() left off after having read \r\n\r */
++      c = '\r';
++    } else if (conn->read_ahead != 0) {
++      /* the last call to read_line() left us with a character to start with */
++      c = (guint8) conn->read_ahead;
++      conn->read_ahead = 0;
++    } else {
++      /* read the next character */
++      r = fill_bytes (conn, &c, 1);
++      if (G_UNLIKELY (r == 0)) {
++        return GST_RTSP_EEOF;
++      } else if (G_UNLIKELY (r < 0)) {
++        if (ERRNO_IS_EAGAIN)
++          return GST_RTSP_EINTR;
++        if (!ERRNO_IS_EINTR)
++          return GST_RTSP_ESYS;
++        continue;
++      }
++    }
++
++    /* special treatment of line endings */
++    if (c == '\r' || c == '\n') {
++      guint8 read_ahead;
++
++    retry:
++      /* need to read ahead one more character to know what to do... */
++      r = fill_bytes (conn, &read_ahead, 1);
++      if (G_UNLIKELY (r == 0)) {
++        return GST_RTSP_EEOF;
++      } else if (G_UNLIKELY (r < 0)) {
++        if (ERRNO_IS_EAGAIN) {
++          /* remember the original character we read and try again next time */
++          if (conn->read_ahead == 0)
++            conn->read_ahead = c;
++          return GST_RTSP_EINTR;
++        }
++        if (!ERRNO_IS_EINTR)
++          return GST_RTSP_ESYS;
++        goto retry;
++      }
++
++      if (read_ahead == ' ' || read_ahead == '\t') {
++        if (conn->read_ahead == READ_AHEAD_CRLFCR) {
++          /* got \r\n\r followed by whitespace, treat it as a normal line
++           * followed by one starting with LWS */
++          conn->read_ahead = read_ahead;
++          break;
++        } else {
++          /* got LWS, change the line ending to a space and continue */
++          c = ' ';
++          conn->read_ahead = read_ahead;
++        }
++      } else if (conn->read_ahead == READ_AHEAD_CRLFCR) {
++        if (read_ahead == '\r' || read_ahead == '\n') {
++          /* got \r\n\r\r or \r\n\r\n, treat it as the end of the headers */
++          conn->read_ahead = READ_AHEAD_EOH;
++          break;
++        } else {
++          /* got \r\n\r followed by something else, this is not really
++           * supported since we have probably just eaten the first character
++           * of the body or the next message, so just ignore the second \r
++           * and live with it... */
++          conn->read_ahead = read_ahead;
++          break;
++        }
++      } else if (conn->read_ahead == READ_AHEAD_CRLF) {
++        if (read_ahead == '\r') {
++          /* got \r\n\r so far, need one more character... */
++          conn->read_ahead = READ_AHEAD_CRLFCR;
++          goto retry;
++        } else if (read_ahead == '\n') {
++          /* got \r\n\n, treat it as the end of the headers */
++          conn->read_ahead = READ_AHEAD_EOH;
++          break;
++        } else {
++          /* found the end of a line, keep read_ahead for the next line */
++          conn->read_ahead = read_ahead;
++          break;
++        }
++      } else if (c == read_ahead) {
++        /* got double \r or \n, treat it as the end of the headers */
++        conn->read_ahead = READ_AHEAD_EOH;
++        break;
++      } else if (c == '\r' && read_ahead == '\n') {
++        /* got \r\n so far, still need more to know what to do... */
++        conn->read_ahead = READ_AHEAD_CRLF;
++        goto retry;
++      } else {
++        /* found the end of a line, keep read_ahead for the next line */
++        conn->read_ahead = read_ahead;
++        break;
++      }
++    }
++
++    if (G_LIKELY (*idx < size - 1))
++      buffer[(*idx)++] = c;
++  }
++  buffer[*idx] = '\0';
++
++  return GST_RTSP_OK;
++}
++
++/**
++ * gst_rtsp_connection_write:
++ * @conn: a #GstRTSPConnection
++ * @data: the data to write
++ * @size: the size of @data
++ * @timeout: a timeout value or #NULL
++ *
++ * Attempt to write @size bytes of @data to the connected @conn, blocking up to
++ * the specified @timeout. @timeout can be #NULL, in which case this function
++ * might block forever.
++ * 
++ * This function can be cancelled with gst_rtsp_connection_flush().
++ *
++ * Returns: #GST_RTSP_OK on success.
++ */
++GstRTSPResult
++gst_rtsp_connection_write (GstRTSPConnection * conn, const guint8 * data,
++    guint size, GTimeVal * timeout)
++{
++  guint offset;
++  gint retval;
++  GstClockTime to;
++  GstRTSPResult res;
++
++  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (data != NULL || size == 0, GST_RTSP_EINVAL);
++  g_return_val_if_fail (conn->writefd != NULL, GST_RTSP_EINVAL);
++
++  gst_poll_set_controllable (conn->fdset, TRUE);
++  gst_poll_fd_ctl_write (conn->fdset, conn->writefd, TRUE);
++  gst_poll_fd_ctl_read (conn->fdset, conn->readfd, FALSE);
++  /* clear all previous poll results */
++  gst_poll_fd_ignored (conn->fdset, conn->writefd);
++  gst_poll_fd_ignored (conn->fdset, conn->readfd);
++
++  to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
++
++  offset = 0;
++
++  while (TRUE) {
++    /* try to write */
++    res = write_bytes (conn->writefd->fd, data, &offset, size);
++    if (G_LIKELY (res == GST_RTSP_OK))
++      break;
++    if (G_UNLIKELY (res != GST_RTSP_EINTR))
++      goto write_error;
++
++    /* not all is written, wait until we can write more */
++    do {
++      retval = gst_poll_wait (conn->fdset, to);
++    } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
++
++    if (G_UNLIKELY (retval == 0))
++      goto timeout;
++
++    if (G_UNLIKELY (retval == -1)) {
++      if (errno == EBUSY)
++        goto stopped;
++      else
++        goto select_error;
++    }
++  }
++  return GST_RTSP_OK;
++
++  /* ERRORS */
++timeout:
++  {
++    return GST_RTSP_ETIMEOUT;
++  }
++select_error:
++  {
++    return GST_RTSP_ESYS;
++  }
++stopped:
++  {
++    return GST_RTSP_EINTR;
++  }
++write_error:
++  {
++    return res;
++  }
++}
++
++static GString *
++message_to_string (GstRTSPConnection * conn, GstRTSPMessage * message)
++{
++  GString *str = NULL;
++
++  str = g_string_new ("");
++
++  switch (message->type) {
++    case GST_RTSP_MESSAGE_REQUEST:
++      /* create request string, add CSeq */
++      g_string_append_printf (str, "%s %s RTSP/1.0\r\n"
++          "CSeq: %d\r\n",
++          gst_rtsp_method_as_text (message->type_data.request.method),
++          message->type_data.request.uri, conn->cseq++);
++      /* add session id if we have one */
++      if (conn->session_id[0] != '\0') {
++        gst_rtsp_message_remove_header (message, GST_RTSP_HDR_SESSION, -1);
++        gst_rtsp_message_add_header (message, GST_RTSP_HDR_SESSION,
++            conn->session_id);
++      }
++      /* add any authentication headers */
++      add_auth_header (conn, message);
++      break;
++    case GST_RTSP_MESSAGE_RESPONSE:
++      /* create response string */
++      g_string_append_printf (str, "RTSP/1.0 %d %s\r\n",
++          message->type_data.response.code, message->type_data.response.reason);
++      break;
++    case GST_RTSP_MESSAGE_HTTP_REQUEST:
++      /* create request string */
++      g_string_append_printf (str, "%s %s HTTP/%s\r\n",
++          gst_rtsp_method_as_text (message->type_data.request.method),
++          message->type_data.request.uri,
++          gst_rtsp_version_as_text (message->type_data.request.version));
++      /* add any authentication headers */
++      add_auth_header (conn, message);
++      break;
++    case GST_RTSP_MESSAGE_HTTP_RESPONSE:
++      /* create response string */
++      g_string_append_printf (str, "HTTP/%s %d %s\r\n",
++          gst_rtsp_version_as_text (message->type_data.request.version),
++          message->type_data.response.code, message->type_data.response.reason);
++      break;
++    case GST_RTSP_MESSAGE_DATA:
++    {
++      guint8 data_header[4];
++
++      /* prepare data header */
++      data_header[0] = '$';
++      data_header[1] = message->type_data.data.channel;
++      data_header[2] = (message->body_size >> 8) & 0xff;
++      data_header[3] = message->body_size & 0xff;
++
++      /* create string with header and data */
++      str = g_string_append_len (str, (gchar *) data_header, 4);
++      str =
++          g_string_append_len (str, (gchar *) message->body,
++          message->body_size);
++      break;
++    }
++    default:
++      g_string_free (str, TRUE);
++      g_return_val_if_reached (NULL);
++      break;
++  }
++
++  /* append headers and body */
++  if (message->type != GST_RTSP_MESSAGE_DATA) {
++    gchar date_string[100];
++
++    gen_date_string (date_string, sizeof (date_string));
++
++    /* add date header */
++    gst_rtsp_message_remove_header (message, GST_RTSP_HDR_DATE, -1);
++    gst_rtsp_message_add_header (message, GST_RTSP_HDR_DATE, date_string);
++
++    /* append headers */
++    gst_rtsp_message_append_headers (message, str);
++
++    /* append Content-Length and body if needed */
++    if (message->body != NULL && message->body_size > 0) {
++      gchar *len;
++
++      len = g_strdup_printf ("%d", message->body_size);
++      g_string_append_printf (str, "%s: %s\r\n",
++          gst_rtsp_header_as_text (GST_RTSP_HDR_CONTENT_LENGTH), len);
++      g_free (len);
++      /* header ends here */
++      g_string_append (str, "\r\n");
++      str =
++          g_string_append_len (str, (gchar *) message->body,
++          message->body_size);
++    } else {
++      /* just end headers */
++      g_string_append (str, "\r\n");
++    }
++  }
++
++  return str;
++}
++
++/**
++ * gst_rtsp_connection_send:
++ * @conn: a #GstRTSPConnection
++ * @message: the message to send
++ * @timeout: a timeout value or #NULL
++ *
++ * Attempt to send @message to the connected @conn, blocking up to
++ * the specified @timeout. @timeout can be #NULL, in which case this function
++ * might block forever.
++ * 
++ * This function can be cancelled with gst_rtsp_connection_flush().
++ *
++ * Returns: #GST_RTSP_OK on success.
++ */
++GstRTSPResult
++gst_rtsp_connection_send (GstRTSPConnection * conn, GstRTSPMessage * message,
++    GTimeVal * timeout)
++{
++  GString *string = NULL;
++  GstRTSPResult res;
++  gchar *str;
++  gsize len;
++
++  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
++
++  if (G_UNLIKELY (!(string = message_to_string (conn, message))))
++    goto no_message;
++
++  if (conn->tunneled) {
++    str = g_base64_encode ((const guchar *) string->str, string->len);
++    g_string_free (string, TRUE);
++    len = strlen (str);
++  } else {
++    str = string->str;
++    len = string->len;
++    g_string_free (string, FALSE);
++  }
++
++  /* write request */
++  res = gst_rtsp_connection_write (conn, (guint8 *) str, len, timeout);
++
++  g_free (str);
++
++  return res;
++
++no_message:
++  {
++    g_warning ("Wrong message");
++    return GST_RTSP_EINVAL;
++  }
++}
++
++static GstRTSPResult
++parse_string (gchar * dest, gint size, gchar ** src)
++{
++  GstRTSPResult res = GST_RTSP_OK;
++  gint idx;
++
++  idx = 0;
++  /* skip spaces */
++  while (g_ascii_isspace (**src))
++    (*src)++;
++
++  while (!g_ascii_isspace (**src) && **src != '\0') {
++    if (idx < size - 1)
++      dest[idx++] = **src;
++    else
++      res = GST_RTSP_EPARSE;
++    (*src)++;
++  }
++  if (size > 0)
++    dest[idx] = '\0';
++
++  return res;
++}
++
++static GstRTSPResult
++parse_protocol_version (gchar * protocol, GstRTSPMsgType * type,
++    GstRTSPVersion * version)
++{
++  GstRTSPResult res = GST_RTSP_OK;
++  gchar *ver;
++
++  if (G_LIKELY ((ver = strchr (protocol, '/')) != NULL)) {
++    guint major;
++    guint minor;
++    gchar dummychar;
++
++    *ver++ = '\0';
++
++    /* the version number must be formatted as X.Y with nothing following */
++    if (sscanf (ver, "%u.%u%c", &major, &minor, &dummychar) != 2)
++      res = GST_RTSP_EPARSE;
++
++    if (g_ascii_strcasecmp (protocol, "RTSP") == 0) {
++      if (major != 1 || minor != 0) {
++        *version = GST_RTSP_VERSION_INVALID;
++        res = GST_RTSP_ERROR;
++      }
++    } else if (g_ascii_strcasecmp (protocol, "HTTP") == 0) {
++      if (*type == GST_RTSP_MESSAGE_REQUEST)
++        *type = GST_RTSP_MESSAGE_HTTP_REQUEST;
++      else if (*type == GST_RTSP_MESSAGE_RESPONSE)
++        *type = GST_RTSP_MESSAGE_HTTP_RESPONSE;
++
++      if (major == 1 && minor == 1) {
++        *version = GST_RTSP_VERSION_1_1;
++      } else if (major != 1 || minor != 0) {
++        *version = GST_RTSP_VERSION_INVALID;
++        res = GST_RTSP_ERROR;
++      }
++    } else
++      res = GST_RTSP_EPARSE;
++  } else
++    res = GST_RTSP_EPARSE;
++
++  return res;
++}
++
++static GstRTSPResult
++parse_response_status (guint8 * buffer, GstRTSPMessage * msg)
++{
++  GstRTSPResult res = GST_RTSP_OK;
++  GstRTSPResult res2;
++  gchar versionstr[20];
++  gchar codestr[4];
++  gint code;
++  gchar *bptr;
++
++  bptr = (gchar *) buffer;
++
++  if (parse_string (versionstr, sizeof (versionstr), &bptr) != GST_RTSP_OK)
++    res = GST_RTSP_EPARSE;
++
++  if (parse_string (codestr, sizeof (codestr), &bptr) != GST_RTSP_OK)
++    res = GST_RTSP_EPARSE;
++  code = atoi (codestr);
++  if (G_UNLIKELY (*codestr == '\0' || code < 0 || code >= 600))
++    res = GST_RTSP_EPARSE;
++
++  while (g_ascii_isspace (*bptr))
++    bptr++;
++
++  if (G_UNLIKELY (gst_rtsp_message_init_response (msg, code, bptr,
++              NULL) != GST_RTSP_OK))
++    res = GST_RTSP_EPARSE;
++
++  res2 = parse_protocol_version (versionstr, &msg->type,
++      &msg->type_data.response.version);
++  if (G_LIKELY (res == GST_RTSP_OK))
++    res = res2;
++
++  return res;
++}
++
++static GstRTSPResult
++parse_request_line (guint8 * buffer, GstRTSPMessage * msg)
++{
++  GstRTSPResult res = GST_RTSP_OK;
++  GstRTSPResult res2;
++  gchar versionstr[20];
++  gchar methodstr[20];
++  gchar urlstr[4096];
++  gchar *bptr;
++  GstRTSPMethod method;
++
++  bptr = (gchar *) buffer;
++
++  if (parse_string (methodstr, sizeof (methodstr), &bptr) != GST_RTSP_OK)
++    res = GST_RTSP_EPARSE;
++  method = gst_rtsp_find_method (methodstr);
++
++  if (parse_string (urlstr, sizeof (urlstr), &bptr) != GST_RTSP_OK)
++    res = GST_RTSP_EPARSE;
++  if (G_UNLIKELY (*urlstr == '\0'))
++    res = GST_RTSP_EPARSE;
++
++  if (parse_string (versionstr, sizeof (versionstr), &bptr) != GST_RTSP_OK)
++    res = GST_RTSP_EPARSE;
++
++  if (G_UNLIKELY (*bptr != '\0'))
++    res = GST_RTSP_EPARSE;
++
++  if (G_UNLIKELY (gst_rtsp_message_init_request (msg, method,
++              urlstr) != GST_RTSP_OK))
++    res = GST_RTSP_EPARSE;
++
++  res2 = parse_protocol_version (versionstr, &msg->type,
++      &msg->type_data.request.version);
++  if (G_LIKELY (res == GST_RTSP_OK))
++    res = res2;
++
++  if (G_LIKELY (msg->type == GST_RTSP_MESSAGE_REQUEST)) {
++    /* GET and POST are not allowed as RTSP methods */
++    if (msg->type_data.request.method == GST_RTSP_GET ||
++        msg->type_data.request.method == GST_RTSP_POST) {
++      msg->type_data.request.method = GST_RTSP_INVALID;
++      if (res == GST_RTSP_OK)
++        res = GST_RTSP_ERROR;
++    }
++  } else if (msg->type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
++    /* only GET and POST are allowed as HTTP methods */
++    if (msg->type_data.request.method != GST_RTSP_GET &&
++        msg->type_data.request.method != GST_RTSP_POST) {
++      msg->type_data.request.method = GST_RTSP_INVALID;
++      if (res == GST_RTSP_OK)
++        res = GST_RTSP_ERROR;
++    }
++  }
++
++  return res;
++}
++
++/* parsing lines means reading a Key: Value pair */
++static GstRTSPResult
++parse_line (guint8 * buffer, GstRTSPMessage * msg)
++{
++  GstRTSPHeaderField field;
++  gchar *line = (gchar *) buffer;
++  gchar *value;
++
++  if ((value = strchr (line, ':')) == NULL || value == line)
++    goto parse_error;
++
++  /* trim space before the colon */
++  if (value[-1] == ' ')
++    value[-1] = '\0';
++
++  /* replace the colon with a NUL */
++  *value++ = '\0';
++
++  /* find the header */
++  field = gst_rtsp_find_header_field (line);
++  if (field == GST_RTSP_HDR_INVALID)
++    goto done;
++
++  /* split up the value in multiple key:value pairs if it contains comma(s) */
++  while (*value != '\0') {
++    gchar *next_value;
++    gchar *comma = NULL;
++    gboolean quoted = FALSE;
++    guint comment = 0;
++
++    /* trim leading space */
++    if (*value == ' ')
++      value++;
++
++    /* for headers which may not appear multiple times, and thus may not
++     * contain multiple values on the same line, we can short-circuit the loop
++     * below and the entire value results in just one key:value pair*/
++    if (!gst_rtsp_header_allow_multiple (field))
++      next_value = value + strlen (value);
++    else
++      next_value = value;
++
++    /* find the next value, taking special care of quotes and comments */
++    while (*next_value != '\0') {
++      if ((quoted || comment != 0) && *next_value == '\\' &&
++          next_value[1] != '\0')
++        next_value++;
++      else if (comment == 0 && *next_value == '"')
++        quoted = !quoted;
++      else if (!quoted && *next_value == '(')
++        comment++;
++      else if (comment != 0 && *next_value == ')')
++        comment--;
++      else if (!quoted && comment == 0) {
++        /* To quote RFC 2068: "User agents MUST take special care in parsing
++         * the WWW-Authenticate field value if it contains more than one
++         * challenge, or if more than one WWW-Authenticate header field is
++         * provided, since the contents of a challenge may itself contain a
++         * comma-separated list of authentication parameters."
++         *
++         * What this means is that we cannot just look for an unquoted comma
++         * when looking for multiple values in Proxy-Authenticate and
++         * WWW-Authenticate headers. Instead we need to look for the sequence
++         * "comma [space] token space token" before we can split after the
++         * comma...
++         */
++        if (field == GST_RTSP_HDR_PROXY_AUTHENTICATE ||
++            field == GST_RTSP_HDR_WWW_AUTHENTICATE) {
++          if (*next_value == ',') {
++            if (next_value[1] == ' ') {
++              /* skip any space following the comma so we do not mistake it for
++               * separating between two tokens */
++              next_value++;
++            }
++            comma = next_value;
++          } else if (*next_value == ' ' && next_value[1] != ',' &&
++              next_value[1] != '=' && comma != NULL) {
++            next_value = comma;
++            comma = NULL;
++            break;
++          }
++        } else if (*next_value == ',')
++          break;
++      }
++
++      next_value++;
++    }
++
++    /* trim space */
++    if (value != next_value && next_value[-1] == ' ')
++      next_value[-1] = '\0';
++
++    if (*next_value != '\0')
++      *next_value++ = '\0';
++
++    /* add the key:value pair */
++    if (*value != '\0')
++      gst_rtsp_message_add_header (msg, field, value);
++
++    value = next_value;
++  }
++
++done:
++  return GST_RTSP_OK;
++
++  /* ERRORS */
++parse_error:
++  {
++    return GST_RTSP_EPARSE;
++  }
++}
++
++/* convert all consecutive whitespace to a single space */
++static void
++normalize_line (guint8 * buffer)
++{
++  while (*buffer) {
++    if (g_ascii_isspace (*buffer)) {
++      guint8 *tmp;
++
++      *buffer++ = ' ';
++      for (tmp = buffer; g_ascii_isspace (*tmp); tmp++) {
++      }
++      if (buffer != tmp)
++        memmove (buffer, tmp, strlen ((gchar *) tmp) + 1);
++    } else {
++      buffer++;
++    }
++  }
++}
++
++/* returns:
++ *  GST_RTSP_OK when a complete message was read.
++ *  GST_RTSP_EEOF: when the socket is closed
++ *  GST_RTSP_EINTR: when more data is needed.
++ *  GST_RTSP_..: some other error occured.
++ */
++static GstRTSPResult
++build_next (GstRTSPBuilder * builder, GstRTSPMessage * message,
++    GstRTSPConnection * conn)
++{
++  GstRTSPResult res;
++
++  while (TRUE) {
++    switch (builder->state) {
++      case STATE_START:
++        builder->offset = 0;
++        res =
++            read_bytes (conn, (guint8 *) builder->buffer, &builder->offset, 1);
++        if (res != GST_RTSP_OK)
++          goto done;
++
++        /* we have 1 bytes now and we can see if this is a data message or
++         * not */
++        if (builder->buffer[0] == '$') {
++          /* data message, prepare for the header */
++          builder->state = STATE_DATA_HEADER;
++        } else {
++          builder->line = 0;
++          builder->state = STATE_READ_LINES;
++        }
++        break;
++      case STATE_DATA_HEADER:
++      {
++        res =
++            read_bytes (conn, (guint8 *) builder->buffer, &builder->offset, 4);
++        if (res != GST_RTSP_OK)
++          goto done;
++
++        gst_rtsp_message_init_data (message, builder->buffer[1]);
++
++        builder->body_len = (builder->buffer[2] << 8) | builder->buffer[3];
++        builder->body_data = g_malloc (builder->body_len + 1);
++        builder->body_data[builder->body_len] = '\0';
++        builder->offset = 0;
++        builder->state = STATE_DATA_BODY;
++        break;
++      }
++      case STATE_DATA_BODY:
++      {
++        res =
++            read_bytes (conn, builder->body_data, &builder->offset,
++            builder->body_len);
++        if (res != GST_RTSP_OK)
++          goto done;
++
++        /* we have the complete body now, store in the message adjusting the
++         * length to include the traling '\0' */
++        gst_rtsp_message_take_body (message,
++            (guint8 *) builder->body_data, builder->body_len + 1);
++        builder->body_data = NULL;
++        builder->body_len = 0;
++
++        builder->state = STATE_END;
++        break;
++      }
++      case STATE_READ_LINES:
++      {
++        res = read_line (conn, builder->buffer, &builder->offset,
++            sizeof (builder->buffer));
++        if (res != GST_RTSP_OK)
++          goto done;
++
++        /* we have a regular response */
++        if (builder->buffer[0] == '\0') {
++          gchar *hdrval;
++
++          /* empty line, end of message header */
++          /* see if there is a Content-Length header, but ignore it if this
++           * is a POST request with an x-sessioncookie header */
++          if (gst_rtsp_message_get_header (message,
++                  GST_RTSP_HDR_CONTENT_LENGTH, &hdrval, 0) == GST_RTSP_OK &&
++              (message->type != GST_RTSP_MESSAGE_HTTP_REQUEST ||
++                  message->type_data.request.method != GST_RTSP_POST ||
++                  gst_rtsp_message_get_header (message,
++                      GST_RTSP_HDR_X_SESSIONCOOKIE, NULL, 0) != GST_RTSP_OK)) {
++            /* there is, prepare to read the body */
++            builder->body_len = atol (hdrval);
++            builder->body_data = g_malloc (builder->body_len + 1);
++            builder->body_data[builder->body_len] = '\0';
++            builder->offset = 0;
++            builder->state = STATE_DATA_BODY;
++          } else {
++            builder->state = STATE_END;
++          }
++          break;
++        }
++
++        /* we have a line */
++        normalize_line (builder->buffer);
++        if (builder->line == 0) {
++          /* first line, check for response status */
++          if (memcmp (builder->buffer, "RTSP", 4) == 0 ||
++              memcmp (builder->buffer, "HTTP", 4) == 0) {
++            builder->status = parse_response_status (builder->buffer, message);
++          } else {
++            builder->status = parse_request_line (builder->buffer, message);
++          }
++        } else {
++          /* else just parse the line */
++          res = parse_line (builder->buffer, message);
++          if (res != GST_RTSP_OK)
++            builder->status = res;
++        }
++        builder->line++;
++        builder->offset = 0;
++        break;
++      }
++      case STATE_END:
++      {
++        gchar *session_cookie;
++        gchar *session_id;
++
++        if (message->type == GST_RTSP_MESSAGE_DATA) {
++          /* data messages don't have headers */
++          res = GST_RTSP_OK;
++          goto done;
++        }
++
++        /* save the tunnel session in the connection */
++        if (message->type == GST_RTSP_MESSAGE_HTTP_REQUEST &&
++            !conn->manual_http &&
++            conn->tstate == TUNNEL_STATE_NONE &&
++            gst_rtsp_message_get_header (message, GST_RTSP_HDR_X_SESSIONCOOKIE,
++                &session_cookie, 0) == GST_RTSP_OK) {
++          strncpy (conn->tunnelid, session_cookie, TUNNELID_LEN);
++          conn->tunnelid[TUNNELID_LEN - 1] = '\0';
++          conn->tunneled = TRUE;
++        }
++
++        /* save session id in the connection for further use */
++        if (message->type == GST_RTSP_MESSAGE_RESPONSE &&
++            gst_rtsp_message_get_header (message, GST_RTSP_HDR_SESSION,
++                &session_id, 0) == GST_RTSP_OK) {
++          gint maxlen, i;
++
++          maxlen = sizeof (conn->session_id) - 1;
++          /* the sessionid can have attributes marked with ;
++           * Make sure we strip them */
++          for (i = 0; session_id[i] != '\0'; i++) {
++            if (session_id[i] == ';') {
++              maxlen = i;
++              /* parse timeout */
++              do {
++                i++;
++              } while (g_ascii_isspace (session_id[i]));
++              if (g_str_has_prefix (&session_id[i], "timeout=")) {
++                gint to;
++
++                /* if we parsed something valid, configure */
++                if ((to = atoi (&session_id[i + 8])) > 0)
++                  conn->timeout = to;
++              }
++              break;
++            }
++          }
++
++          /* make sure to not overflow */
++          strncpy (conn->session_id, session_id, maxlen);
++          conn->session_id[maxlen] = '\0';
++        }
++        res = builder->status;
++        goto done;
++      }
++      default:
++        res = GST_RTSP_ERROR;
++        break;
++    }
++  }
++done:
++  return res;
++}
++
++/**
++ * gst_rtsp_connection_read:
++ * @conn: a #GstRTSPConnection
++ * @data: the data to read
++ * @size: the size of @data
++ * @timeout: a timeout value or #NULL
++ *
++ * Attempt to read @size bytes into @data from the connected @conn, blocking up to
++ * the specified @timeout. @timeout can be #NULL, in which case this function
++ * might block forever.
++ *
++ * This function can be cancelled with gst_rtsp_connection_flush().
++ *
++ * Returns: #GST_RTSP_OK on success.
++ */
++GstRTSPResult
++gst_rtsp_connection_read (GstRTSPConnection * conn, guint8 * data, guint size,
++    GTimeVal * timeout)
++{
++  guint offset;
++  gint retval;
++  GstClockTime to;
++  GstRTSPResult res;
++
++  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (conn->readfd != NULL, GST_RTSP_EINVAL);
++
++  if (G_UNLIKELY (size == 0))
++    return GST_RTSP_OK;
++
++  offset = 0;
++
++  /* configure timeout if any */
++  to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
++
++  gst_poll_set_controllable (conn->fdset, TRUE);
++  gst_poll_fd_ctl_write (conn->fdset, conn->writefd, FALSE);
++  gst_poll_fd_ctl_read (conn->fdset, conn->readfd, TRUE);
++
++  while (TRUE) {
++    res = read_bytes (conn, data, &offset, size);
++    if (G_UNLIKELY (res == GST_RTSP_EEOF))
++      goto eof;
++    if (G_LIKELY (res == GST_RTSP_OK))
++      break;
++    if (G_UNLIKELY (res != GST_RTSP_EINTR))
++      goto read_error;
++
++    do {
++      retval = gst_poll_wait (conn->fdset, to);
++    } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
++
++    /* check for timeout */
++    if (G_UNLIKELY (retval == 0))
++      goto select_timeout;
++
++    if (G_UNLIKELY (retval == -1)) {
++      if (errno == EBUSY)
++        goto stopped;
++      else
++        goto select_error;
++    }
++    gst_poll_set_controllable (conn->fdset, FALSE);
++  }
++  return GST_RTSP_OK;
++
++  /* ERRORS */
++select_error:
++  {
++    return GST_RTSP_ESYS;
++  }
++select_timeout:
++  {
++    return GST_RTSP_ETIMEOUT;
++  }
++stopped:
++  {
++    return GST_RTSP_EINTR;
++  }
++eof:
++  {
++    return GST_RTSP_EEOF;
++  }
++read_error:
++  {
++    return res;
++  }
++}
++
++static GstRTSPMessage *
++gen_tunnel_reply (GstRTSPConnection * conn, GstRTSPStatusCode code,
++    const GstRTSPMessage * request)
++{
++  GstRTSPMessage *msg;
++  GstRTSPResult res;
++
++  if (gst_rtsp_status_as_text (code) == NULL)
++    code = GST_RTSP_STS_INTERNAL_SERVER_ERROR;
++
++  GST_RTSP_CHECK (gst_rtsp_message_new_response (&msg, code, NULL, request),
++      no_message);
++
++  gst_rtsp_message_add_header (msg, GST_RTSP_HDR_SERVER,
++      "GStreamer RTSP Server");
++  gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CONNECTION, "close");
++  gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CACHE_CONTROL, "no-store");
++  gst_rtsp_message_add_header (msg, GST_RTSP_HDR_PRAGMA, "no-cache");
++
++  if (code == GST_RTSP_STS_OK) {
++    if (conn->ip)
++      gst_rtsp_message_add_header (msg, GST_RTSP_HDR_X_SERVER_IP_ADDRESS,
++          conn->ip);
++    gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CONTENT_TYPE,
++        "application/x-rtsp-tunnelled");
++  }
++
++  return msg;
++
++  /* ERRORS */
++no_message:
++  {
++    return NULL;
++  }
++}
++
++/**
++ * gst_rtsp_connection_receive:
++ * @conn: a #GstRTSPConnection
++ * @message: the message to read
++ * @timeout: a timeout value or #NULL
++ *
++ * Attempt to read into @message from the connected @conn, blocking up to
++ * the specified @timeout. @timeout can be #NULL, in which case this function
++ * might block forever.
++ * 
++ * This function can be cancelled with gst_rtsp_connection_flush().
++ *
++ * Returns: #GST_RTSP_OK on success.
++ */
++GstRTSPResult
++gst_rtsp_connection_receive (GstRTSPConnection * conn, GstRTSPMessage * message,
++    GTimeVal * timeout)
++{
++  GstRTSPResult res;
++  GstRTSPBuilder builder;
++  gint retval;
++  GstClockTime to;
++
++  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (conn->readfd != NULL, GST_RTSP_EINVAL);
++
++  /* configure timeout if any */
++  to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
++
++  gst_poll_set_controllable (conn->fdset, TRUE);
++  gst_poll_fd_ctl_write (conn->fdset, conn->writefd, FALSE);
++  gst_poll_fd_ctl_read (conn->fdset, conn->readfd, TRUE);
++
++  memset (&builder, 0, sizeof (GstRTSPBuilder));
++  while (TRUE) {
++    res = build_next (&builder, message, conn);
++    if (G_UNLIKELY (res == GST_RTSP_EEOF))
++      goto eof;
++    else if (G_LIKELY (res == GST_RTSP_OK)) {
++      if (!conn->manual_http) {
++        if (message->type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
++          if (conn->tstate == TUNNEL_STATE_NONE &&
++              message->type_data.request.method == GST_RTSP_GET) {
++            GstRTSPMessage *response;
++
++            conn->tstate = TUNNEL_STATE_GET;
++
++            /* tunnel GET request, we can reply now */
++            response = gen_tunnel_reply (conn, GST_RTSP_STS_OK, message);
++            res = gst_rtsp_connection_send (conn, response, timeout);
++            gst_rtsp_message_free (response);
++            if (res == GST_RTSP_OK)
++              res = GST_RTSP_ETGET;
++            goto cleanup;
++          } else if (conn->tstate == TUNNEL_STATE_NONE &&
++              message->type_data.request.method == GST_RTSP_POST) {
++            conn->tstate = TUNNEL_STATE_POST;
++
++            /* tunnel POST request, the caller now has to link the two
++             * connections. */
++            res = GST_RTSP_ETPOST;
++            goto cleanup;
++          } else {
++            res = GST_RTSP_EPARSE;
++            goto cleanup;
++          }
++        } else if (message->type == GST_RTSP_MESSAGE_HTTP_RESPONSE) {
++          res = GST_RTSP_EPARSE;
++          goto cleanup;
++        }
++      }
++
++      break;
++    } else if (G_UNLIKELY (res != GST_RTSP_EINTR))
++      goto read_error;
++
++    do {
++      retval = gst_poll_wait (conn->fdset, to);
++    } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
++
++    /* check for timeout */
++    if (G_UNLIKELY (retval == 0))
++      goto select_timeout;
++
++    if (G_UNLIKELY (retval == -1)) {
++      if (errno == EBUSY)
++        goto stopped;
++      else
++        goto select_error;
++    }
++    gst_poll_set_controllable (conn->fdset, FALSE);
++  }
++
++  /* we have a message here */
++  build_reset (&builder);
++
++  return GST_RTSP_OK;
++
++  /* ERRORS */
++select_error:
++  {
++    res = GST_RTSP_ESYS;
++    goto cleanup;
++  }
++select_timeout:
++  {
++    res = GST_RTSP_ETIMEOUT;
++    goto cleanup;
++  }
++stopped:
++  {
++    res = GST_RTSP_EINTR;
++    goto cleanup;
++  }
++eof:
++  {
++    res = GST_RTSP_EEOF;
++    goto cleanup;
++  }
++read_error:
++cleanup:
++  {
++    build_reset (&builder);
++    gst_rtsp_message_unset (message);
++    return res;
++  }
++}
++
++/**
++ * gst_rtsp_connection_close:
++ * @conn: a #GstRTSPConnection
++ *
++ * Close the connected @conn. After this call, the connection is in the same
++ * state as when it was first created.
++ * 
++ * Returns: #GST_RTSP_OK on success.
++ */
++GstRTSPResult
++gst_rtsp_connection_close (GstRTSPConnection * conn)
++{
++  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
++
++  g_free (conn->ip);
++  conn->ip = NULL;
++
++  conn->read_ahead = 0;
++
++  g_free (conn->initial_buffer);
++  conn->initial_buffer = NULL;
++  conn->initial_buffer_offset = 0;
++
++  REMOVE_POLLFD (conn->fdset, &conn->fd0);
++  REMOVE_POLLFD (conn->fdset, &conn->fd1);
++  conn->writefd = NULL;
++  conn->readfd = NULL;
++  conn->tunneled = FALSE;
++  conn->tstate = TUNNEL_STATE_NONE;
++  conn->ctxp = NULL;
++  g_free (conn->username);
++  conn->username = NULL;
++  g_free (conn->passwd);
++  conn->passwd = NULL;
++  gst_rtsp_connection_clear_auth_params (conn);
++  conn->timeout = 60;
++  conn->cseq = 0;
++  conn->session_id[0] = '\0';
++
++  return GST_RTSP_OK;
++}
++
++/**
++ * gst_rtsp_connection_free:
++ * @conn: a #GstRTSPConnection
++ *
++ * Close and free @conn.
++ * 
++ * Returns: #GST_RTSP_OK on success.
++ */
++GstRTSPResult
++gst_rtsp_connection_free (GstRTSPConnection * conn)
++{
++  GstRTSPResult res;
++
++  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
++
++  res = gst_rtsp_connection_close (conn);
++  gst_poll_free (conn->fdset);
++  g_timer_destroy (conn->timer);
++  gst_rtsp_url_free (conn->url);
++  g_free (conn->proxy_host);
++  g_free (conn);
++#ifdef G_OS_WIN32
++  WSACleanup ();
++#endif
++
++  return res;
++}
++
++/**
++ * gst_rtsp_connection_poll:
++ * @conn: a #GstRTSPConnection
++ * @events: a bitmask of #GstRTSPEvent flags to check
++ * @revents: location for result flags 
++ * @timeout: a timeout
++ *
++ * Wait up to the specified @timeout for the connection to become available for
++ * at least one of the operations specified in @events. When the function returns
++ * with #GST_RTSP_OK, @revents will contain a bitmask of available operations on
++ * @conn.
++ *
++ * @timeout can be #NULL, in which case this function might block forever.
++ *
++ * This function can be cancelled with gst_rtsp_connection_flush().
++ * 
++ * Returns: #GST_RTSP_OK on success.
++ *
++ * Since: 0.10.15
++ */
++GstRTSPResult
++gst_rtsp_connection_poll (GstRTSPConnection * conn, GstRTSPEvent events,
++    GstRTSPEvent * revents, GTimeVal * timeout)
++{
++  GstClockTime to;
++  gint retval;
++
++  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (events != 0, GST_RTSP_EINVAL);
++  g_return_val_if_fail (revents != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (conn->readfd != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (conn->writefd != NULL, GST_RTSP_EINVAL);
++
++  gst_poll_set_controllable (conn->fdset, TRUE);
++
++  /* add fd to writer set when asked to */
++  gst_poll_fd_ctl_write (conn->fdset, conn->writefd,
++      events & GST_RTSP_EV_WRITE);
++
++  /* add fd to reader set when asked to */
++  gst_poll_fd_ctl_read (conn->fdset, conn->readfd, events & GST_RTSP_EV_READ);
++
++  /* configure timeout if any */
++  to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
++
++  do {
++    retval = gst_poll_wait (conn->fdset, to);
++  } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
++
++  if (G_UNLIKELY (retval == 0))
++    goto select_timeout;
++
++  if (G_UNLIKELY (retval == -1)) {
++    if (errno == EBUSY)
++      goto stopped;
++    else
++      goto select_error;
++  }
++
++  *revents = 0;
++  if (events & GST_RTSP_EV_READ) {
++    if (gst_poll_fd_can_read (conn->fdset, conn->readfd))
++      *revents |= GST_RTSP_EV_READ;
++  }
++  if (events & GST_RTSP_EV_WRITE) {
++    if (gst_poll_fd_can_write (conn->fdset, conn->writefd))
++      *revents |= GST_RTSP_EV_WRITE;
++  }
++  return GST_RTSP_OK;
++
++  /* ERRORS */
++select_timeout:
++  {
++    return GST_RTSP_ETIMEOUT;
++  }
++select_error:
++  {
++    return GST_RTSP_ESYS;
++  }
++stopped:
++  {
++    return GST_RTSP_EINTR;
++  }
++}
++
++/**
++ * gst_rtsp_connection_next_timeout:
++ * @conn: a #GstRTSPConnection
++ * @timeout: a timeout
++ *
++ * Calculate the next timeout for @conn, storing the result in @timeout.
++ * 
++ * Returns: #GST_RTSP_OK.
++ */
++GstRTSPResult
++gst_rtsp_connection_next_timeout (GstRTSPConnection * conn, GTimeVal * timeout)
++{
++  gdouble elapsed;
++  glong sec;
++  gulong usec;
++
++  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (timeout != NULL, GST_RTSP_EINVAL);
++
++  elapsed = g_timer_elapsed (conn->timer, &usec);
++  if (elapsed >= conn->timeout) {
++    sec = 0;
++    usec = 0;
++  } else {
++    sec = conn->timeout - elapsed;
++  }
++
++  timeout->tv_sec = sec;
++  timeout->tv_usec = usec;
++
++  return GST_RTSP_OK;
++}
++
++/**
++ * gst_rtsp_connection_reset_timeout:
++ * @conn: a #GstRTSPConnection
++ *
++ * Reset the timeout of @conn.
++ * 
++ * Returns: #GST_RTSP_OK.
++ */
++GstRTSPResult
++gst_rtsp_connection_reset_timeout (GstRTSPConnection * conn)
++{
++  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
++
++  g_timer_start (conn->timer);
++
++  return GST_RTSP_OK;
++}
++
++/**
++ * gst_rtsp_connection_flush:
++ * @conn: a #GstRTSPConnection
++ * @flush: start or stop the flush
++ *
++ * Start or stop the flushing action on @conn. When flushing, all current
++ * and future actions on @conn will return #GST_RTSP_EINTR until the connection
++ * is set to non-flushing mode again.
++ * 
++ * Returns: #GST_RTSP_OK.
++ */
++GstRTSPResult
++gst_rtsp_connection_flush (GstRTSPConnection * conn, gboolean flush)
++{
++  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
++
++  gst_poll_set_flushing (conn->fdset, flush);
++
++  return GST_RTSP_OK;
++}
++
++/**
++ * gst_rtsp_connection_set_proxy:
++ * @conn: a #GstRTSPConnection
++ * @host: the proxy host
++ * @port: the proxy port
++ *
++ * Set the proxy host and port.
++ * 
++ * Returns: #GST_RTSP_OK.
++ *
++ * Since: 0.10.23
++ */
++GstRTSPResult
++gst_rtsp_connection_set_proxy (GstRTSPConnection * conn,
++    const gchar * host, guint port)
++{
++  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
++
++  g_free (conn->proxy_host);
++  conn->proxy_host = g_strdup (host);
++  conn->proxy_port = port;
++
++  return GST_RTSP_OK;
++}
++
++/**
++ * gst_rtsp_connection_set_auth:
++ * @conn: a #GstRTSPConnection
++ * @method: authentication method
++ * @user: the user
++ * @pass: the password
++ *
++ * Configure @conn for authentication mode @method with @user and @pass as the
++ * user and password respectively.
++ * 
++ * Returns: #GST_RTSP_OK.
++ */
++GstRTSPResult
++gst_rtsp_connection_set_auth (GstRTSPConnection * conn,
++    GstRTSPAuthMethod method, const gchar * user, const gchar * pass)
++{
++  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
++
++  if (method == GST_RTSP_AUTH_DIGEST && ((user == NULL || pass == NULL)
++          || g_strrstr (user, ":") != NULL))
++    return GST_RTSP_EINVAL;
++
++  /* Make sure the username and passwd are being set for authentication */
++  if (method == GST_RTSP_AUTH_NONE && (user == NULL || pass == NULL))
++    return GST_RTSP_EINVAL;
++
++  /* ":" chars are not allowed in usernames for basic auth */
++  if (method == GST_RTSP_AUTH_BASIC && g_strrstr (user, ":") != NULL)
++    return GST_RTSP_EINVAL;
++
++  g_free (conn->username);
++  g_free (conn->passwd);
++
++  conn->auth_method = method;
++  conn->username = g_strdup (user);
++  conn->passwd = g_strdup (pass);
++
++  return GST_RTSP_OK;
++}
++
++/**
++ * str_case_hash:
++ * @key: ASCII string to hash
++ *
++ * Hashes @key in a case-insensitive manner.
++ *
++ * Returns: the hash code.
++ **/
++static guint
++str_case_hash (gconstpointer key)
++{
++  const char *p = key;
++  guint h = g_ascii_toupper (*p);
++
++  if (h)
++    for (p += 1; *p != '\0'; p++)
++      h = (h << 5) - h + g_ascii_toupper (*p);
++
++  return h;
++}
++
++/**
++ * str_case_equal:
++ * @v1: an ASCII string
++ * @v2: another ASCII string
++ *
++ * Compares @v1 and @v2 in a case-insensitive manner
++ *
++ * Returns: %TRUE if they are equal (modulo case)
++ **/
++static gboolean
++str_case_equal (gconstpointer v1, gconstpointer v2)
++{
++  const char *string1 = v1;
++  const char *string2 = v2;
++
++  return g_ascii_strcasecmp (string1, string2) == 0;
++}
++
++/**
++ * gst_rtsp_connection_set_auth_param:
++ * @conn: a #GstRTSPConnection
++ * @param: authentication directive
++ * @value: value
++ *
++ * Setup @conn with authentication directives. This is not necesary for
++ * methods #GST_RTSP_AUTH_NONE and #GST_RTSP_AUTH_BASIC. For
++ * #GST_RTSP_AUTH_DIGEST, directives should be taken from the digest challenge
++ * in the WWW-Authenticate response header and can include realm, domain,
++ * nonce, opaque, stale, algorithm, qop as per RFC2617.
++ * 
++ * Since: 0.10.20
++ */
++void
++gst_rtsp_connection_set_auth_param (GstRTSPConnection * conn,
++    const gchar * param, const gchar * value)
++{
++  g_return_if_fail (conn != NULL);
++  g_return_if_fail (param != NULL);
++
++  if (conn->auth_params == NULL) {
++    conn->auth_params =
++        g_hash_table_new_full (str_case_hash, str_case_equal, g_free, g_free);
++  }
++  g_hash_table_insert (conn->auth_params, g_strdup (param), g_strdup (value));
++}
++
++/**
++ * gst_rtsp_connection_clear_auth_params:
++ * @conn: a #GstRTSPConnection
++ *
++ * Clear the list of authentication directives stored in @conn.
++ *
++ * Since: 0.10.20
++ */
++void
++gst_rtsp_connection_clear_auth_params (GstRTSPConnection * conn)
++{
++  g_return_if_fail (conn != NULL);
++
++  if (conn->auth_params != NULL) {
++    g_hash_table_destroy (conn->auth_params);
++    conn->auth_params = NULL;
++  }
++}
++
++static GstRTSPResult
++set_qos_dscp (gint fd, guint qos_dscp)
++{
++  union gst_sockaddr sa;
++  socklen_t slen = sizeof (sa);
++  gint af;
++  gint tos;
++
++  if (fd == -1)
++    return GST_RTSP_OK;
++
++  if (getsockname (fd, &sa.sa, &slen) < 0)
++    goto no_getsockname;
++
++  af = sa.sa.sa_family;
++
++  /* if this is an IPv4-mapped address then do IPv4 QoS */
++  if (af == AF_INET6) {
++    if (IN6_IS_ADDR_V4MAPPED (&sa.sa_in6.sin6_addr))
++      af = AF_INET;
++  }
++
++  /* extract and shift 6 bits of the DSCP */
++  tos = (qos_dscp & 0x3f) << 2;
++
++  switch (af) {
++    case AF_INET:
++      if (SETSOCKOPT (fd, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)) < 0)
++        goto no_setsockopt;
++      break;
++    case AF_INET6:
++#ifdef IPV6_TCLASS
++      if (SETSOCKOPT (fd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof (tos)) < 0)
++        goto no_setsockopt;
++      break;
++#endif
++    default:
++      goto wrong_family;
++  }
++
++  return GST_RTSP_OK;
++
++  /* ERRORS */
++no_getsockname:
++no_setsockopt:
++  {
++    return GST_RTSP_ESYS;
++  }
++
++wrong_family:
++  {
++    return GST_RTSP_ERROR;
++  }
++}
++
++/**
++ * gst_rtsp_connection_set_qos_dscp:
++ * @conn: a #GstRTSPConnection
++ * @qos_dscp: DSCP value
++ *
++ * Configure @conn to use the specified DSCP value.
++ *
++ * Returns: #GST_RTSP_OK on success.
++ *
++ * Since: 0.10.20
++ */
++GstRTSPResult
++gst_rtsp_connection_set_qos_dscp (GstRTSPConnection * conn, guint qos_dscp)
++{
++  GstRTSPResult res;
++
++  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (conn->readfd != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (conn->writefd != NULL, GST_RTSP_EINVAL);
++
++  res = set_qos_dscp (conn->fd0.fd, qos_dscp);
++  if (res == GST_RTSP_OK)
++    res = set_qos_dscp (conn->fd1.fd, qos_dscp);
++
++  return res;
++}
++
++
++/**
++ * gst_rtsp_connection_get_url:
++ * @conn: a #GstRTSPConnection
++ *
++ * Retrieve the URL of the other end of @conn.
++ *
++ * Returns: The URL. This value remains valid until the
++ * connection is freed.
++ *
++ * Since: 0.10.23
++ */
++GstRTSPUrl *
++gst_rtsp_connection_get_url (const GstRTSPConnection * conn)
++{
++  g_return_val_if_fail (conn != NULL, NULL);
++
++  return conn->url;
++}
++
++/**
++ * gst_rtsp_connection_get_ip:
++ * @conn: a #GstRTSPConnection
++ *
++ * Retrieve the IP address of the other end of @conn.
++ *
++ * Returns: The IP address as a string. this value remains valid until the
++ * connection is closed.
++ *
++ * Since: 0.10.20
++ */
++const gchar *
++gst_rtsp_connection_get_ip (const GstRTSPConnection * conn)
++{
++  g_return_val_if_fail (conn != NULL, NULL);
++
++  return conn->ip;
++}
++
++/**
++ * gst_rtsp_connection_set_ip:
++ * @conn: a #GstRTSPConnection
++ * @ip: an ip address
++ *
++ * Set the IP address of the server.
++ *
++ * Since: 0.10.23
++ */
++void
++gst_rtsp_connection_set_ip (GstRTSPConnection * conn, const gchar * ip)
++{
++  g_return_if_fail (conn != NULL);
++
++  g_free (conn->ip);
++  conn->ip = g_strdup (ip);
++}
++
++/**
++ * gst_rtsp_connection_get_readfd:
++ * @conn: a #GstRTSPConnection
++ *
++ * Get the file descriptor for reading.
++ *
++ * Returns: the file descriptor used for reading or -1 on error. The file
++ * descriptor remains valid until the connection is closed.
++ *
++ * Since: 0.10.23
++ */
++gint
++gst_rtsp_connection_get_readfd (const GstRTSPConnection * conn)
++{
++  g_return_val_if_fail (conn != NULL, -1);
++  g_return_val_if_fail (conn->readfd != NULL, -1);
++
++  return conn->readfd->fd;
++}
++
++/**
++ * gst_rtsp_connection_get_writefd:
++ * @conn: a #GstRTSPConnection
++ *
++ * Get the file descriptor for writing.
++ *
++ * Returns: the file descriptor used for writing or -1 on error. The file
++ * descriptor remains valid until the connection is closed.
++ *
++ * Since: 0.10.23
++ */
++gint
++gst_rtsp_connection_get_writefd (const GstRTSPConnection * conn)
++{
++  g_return_val_if_fail (conn != NULL, -1);
++  g_return_val_if_fail (conn->writefd != NULL, -1);
++
++  return conn->writefd->fd;
++}
++
++/**
++ * gst_rtsp_connection_set_http_mode:
++ * @conn: a #GstRTSPConnection
++ * @enable: %TRUE to enable manual HTTP mode
++ *
++ * By setting the HTTP mode to %TRUE the message parsing will support HTTP
++ * messages in addition to the RTSP messages. It will also disable the
++ * automatic handling of setting up an HTTP tunnel.
++ *
++ * Since: 0.10.25
++ */
++void
++gst_rtsp_connection_set_http_mode (GstRTSPConnection * conn, gboolean enable)
++{
++  g_return_if_fail (conn != NULL);
++
++  conn->manual_http = enable;
++}
++
++/**
++ * gst_rtsp_connection_set_tunneled:
++ * @conn: a #GstRTSPConnection
++ * @tunneled: the new state
++ *
++ * Set the HTTP tunneling state of the connection. This must be configured before
++ * the @conn is connected.
++ *
++ * Since: 0.10.23
++ */
++void
++gst_rtsp_connection_set_tunneled (GstRTSPConnection * conn, gboolean tunneled)
++{
++  g_return_if_fail (conn != NULL);
++  g_return_if_fail (conn->readfd == NULL);
++  g_return_if_fail (conn->writefd == NULL);
++
++  conn->tunneled = tunneled;
++}
++
++/**
++ * gst_rtsp_connection_is_tunneled:
++ * @conn: a #GstRTSPConnection
++ *
++ * Get the tunneling state of the connection. 
++ *
++ * Returns: if @conn is using HTTP tunneling.
++ *
++ * Since: 0.10.23
++ */
++gboolean
++gst_rtsp_connection_is_tunneled (const GstRTSPConnection * conn)
++{
++  g_return_val_if_fail (conn != NULL, FALSE);
++
++  return conn->tunneled;
++}
++
++/**
++ * gst_rtsp_connection_get_tunnelid:
++ * @conn: a #GstRTSPConnection
++ *
++ * Get the tunnel session id the connection. 
++ *
++ * Returns: returns a non-empty string if @conn is being tunneled over HTTP.
++ *
++ * Since: 0.10.23
++ */
++const gchar *
++gst_rtsp_connection_get_tunnelid (const GstRTSPConnection * conn)
++{
++  g_return_val_if_fail (conn != NULL, NULL);
++
++  if (!conn->tunneled)
++    return NULL;
++
++  return conn->tunnelid;
++}
++
++/**
++ * gst_rtsp_connection_do_tunnel:
++ * @conn: a #GstRTSPConnection
++ * @conn2: a #GstRTSPConnection or %NULL
++ *
++ * If @conn received the first tunnel connection and @conn2 received
++ * the second tunnel connection, link the two connections together so that
++ * @conn manages the tunneled connection.
++ *
++ * After this call, @conn2 cannot be used anymore and must be freed with
++ * gst_rtsp_connection_free().
++ *
++ * If @conn2 is %NULL then only the base64 decoding context will be setup for
++ * @conn.
++ *
++ * Returns: return GST_RTSP_OK on success.
++ *
++ * Since: 0.10.23
++ */
++GstRTSPResult
++gst_rtsp_connection_do_tunnel (GstRTSPConnection * conn,
++    GstRTSPConnection * conn2)
++{
++  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
++
++  if (conn2 != NULL) {
++    g_return_val_if_fail (conn->tstate == TUNNEL_STATE_GET, GST_RTSP_EINVAL);
++    g_return_val_if_fail (conn2->tstate == TUNNEL_STATE_POST, GST_RTSP_EINVAL);
++    g_return_val_if_fail (!memcmp (conn2->tunnelid, conn->tunnelid,
++            TUNNELID_LEN), GST_RTSP_EINVAL);
++
++    /* both connections have fd0 as the read/write socket. start by taking the
++     * socket from conn2 and set it as the socket in conn */
++    conn->fd1 = conn2->fd0;
++
++    /* clean up some of the state of conn2 */
++    gst_poll_remove_fd (conn2->fdset, &conn2->fd0);
++    conn2->fd0.fd = -1;
++    conn2->readfd = conn2->writefd = NULL;
++
++    /* We make fd0 the write socket and fd1 the read socket. */
++    conn->writefd = &conn->fd0;
++    conn->readfd = &conn->fd1;
++
++    conn->tstate = TUNNEL_STATE_COMPLETE;
++  }
++
++  /* we need base64 decoding for the readfd */
++  conn->ctx.state = 0;
++  conn->ctx.save = 0;
++  conn->ctx.cout = 0;
++  conn->ctx.coutl = 0;
++  conn->ctxp = &conn->ctx;
++
++  return GST_RTSP_OK;
++}
++
++#define READ_COND   (G_IO_IN | G_IO_HUP | G_IO_ERR)
++#define WRITE_COND  (G_IO_OUT | G_IO_ERR)
++
++typedef struct
++{
++  guint8 *data;
++  guint size;
++  guint id;
++} GstRTSPRec;
++
++/* async functions */
++struct _GstRTSPWatch
++{
++  GSource source;
++
++  GstRTSPConnection *conn;
++
++  GstRTSPBuilder builder;
++  GstRTSPMessage message;
++
++  GPollFD readfd;
++  GPollFD writefd;
++  gboolean write_added;
++
++  /* queued message for transmission */
++  guint id;
++  GMutex *mutex;
++  GQueue *messages;
++  guint8 *write_data;
++  guint write_off;
++  guint write_size;
++  guint write_id;
++
++  GstRTSPWatchFuncs funcs;
++
++  gpointer user_data;
++  GDestroyNotify notify;
++};
++
++static gboolean
++gst_rtsp_source_prepare (GSource * source, gint * timeout)
++{
++  GstRTSPWatch *watch = (GstRTSPWatch *) source;
++
++  if (watch->conn->initial_buffer != NULL)
++    return TRUE;
++
++  *timeout = (watch->conn->timeout * 1000);
++
++  return FALSE;
++}
++
++static gboolean
++gst_rtsp_source_check (GSource * source)
++{
++  GstRTSPWatch *watch = (GstRTSPWatch *) source;
++
++  if (watch->readfd.revents & READ_COND)
++    return TRUE;
++
++  if (watch->writefd.revents & WRITE_COND)
++    return TRUE;
++
++  return FALSE;
++}
++
++static gboolean
++gst_rtsp_source_dispatch (GSource * source, GSourceFunc callback G_GNUC_UNUSED,
++    gpointer user_data G_GNUC_UNUSED)
++{
++  GstRTSPWatch *watch = (GstRTSPWatch *) source;
++  GstRTSPResult res;
++
++  /* first read as much as we can */
++  if (watch->readfd.revents & READ_COND || watch->conn->initial_buffer != NULL) {
++    do {
++      res = build_next (&watch->builder, &watch->message, watch->conn);
++      if (res == GST_RTSP_EINTR)
++        break;
++      else if (G_UNLIKELY (res == GST_RTSP_EEOF))
++        goto eof;
++      else if (G_LIKELY (res == GST_RTSP_OK)) {
++        if (!watch->conn->manual_http &&
++            watch->message.type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
++          if (watch->conn->tstate == TUNNEL_STATE_NONE &&
++              watch->message.type_data.request.method == GST_RTSP_GET) {
++            GstRTSPMessage *response;
++            GstRTSPStatusCode code;
++
++            watch->conn->tstate = TUNNEL_STATE_GET;
++
++            if (watch->funcs.tunnel_start)
++              code = watch->funcs.tunnel_start (watch, watch->user_data);
++            else
++              code = GST_RTSP_STS_OK;
++
++            /* queue the response */
++            response = gen_tunnel_reply (watch->conn, code, &watch->message);
++            gst_rtsp_watch_send_message (watch, response, NULL);
++            gst_rtsp_message_free (response);
++            goto read_done;
++          } else if (watch->conn->tstate == TUNNEL_STATE_NONE &&
++              watch->message.type_data.request.method == GST_RTSP_POST) {
++            watch->conn->tstate = TUNNEL_STATE_POST;
++
++            /* in the callback the connection should be tunneled with the
++             * GET connection */
++            if (watch->funcs.tunnel_complete)
++              watch->funcs.tunnel_complete (watch, watch->user_data);
++            goto read_done;
++          }
++        }
++      }
++
++      if (!watch->conn->manual_http) {
++        /* if manual HTTP support is not enabled, then restore the message to
++         * what it would have looked like without the support for parsing HTTP
++         * messages being present */
++        if (watch->message.type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
++          watch->message.type = GST_RTSP_MESSAGE_REQUEST;
++          watch->message.type_data.request.method = GST_RTSP_INVALID;
++          if (watch->message.type_data.request.version != GST_RTSP_VERSION_1_0)
++            watch->message.type_data.request.version = GST_RTSP_VERSION_INVALID;
++          res = GST_RTSP_EPARSE;
++        } else if (watch->message.type == GST_RTSP_MESSAGE_HTTP_RESPONSE) {
++          watch->message.type = GST_RTSP_MESSAGE_RESPONSE;
++          if (watch->message.type_data.response.version != GST_RTSP_VERSION_1_0)
++            watch->message.type_data.response.version =
++                GST_RTSP_VERSION_INVALID;
++          res = GST_RTSP_EPARSE;
++        }
++      }
++
++      if (G_LIKELY (res == GST_RTSP_OK)) {
++        if (watch->funcs.message_received)
++          watch->funcs.message_received (watch, &watch->message,
++              watch->user_data);
++      } else {
++        if (watch->funcs.error_full)
++          GST_RTSP_CHECK (watch->funcs.error_full (watch, res, &watch->message,
++                  0, watch->user_data), error);
++        else
++          goto error;
++      }
++
++    read_done:
++      gst_rtsp_message_unset (&watch->message);
++      build_reset (&watch->builder);
++    } while (FALSE);
++  }
++
++  if (watch->writefd.revents & WRITE_COND) {
++    g_mutex_lock (watch->mutex);
++    do {
++      if (watch->write_data == NULL) {
++        GstRTSPRec *rec;
++
++        /* get a new message from the queue */
++        rec = g_queue_pop_tail (watch->messages);
++        if (rec == NULL)
++          goto done;
++
++        watch->write_off = 0;
++        watch->write_data = rec->data;
++        watch->write_size = rec->size;
++        watch->write_id = rec->id;
++
++        g_slice_free (GstRTSPRec, rec);
++      }
++
++      res = write_bytes (watch->writefd.fd, watch->write_data,
++          &watch->write_off, watch->write_size);
++      g_mutex_unlock (watch->mutex);
++      if (res == GST_RTSP_EINTR)
++        goto write_blocked;
++      else if (G_LIKELY (res == GST_RTSP_OK)) {
++        if (watch->funcs.message_sent)
++          watch->funcs.message_sent (watch, watch->write_id, watch->user_data);
++      } else {
++        if (watch->funcs.error_full)
++          GST_RTSP_CHECK (watch->funcs.error_full (watch, res, NULL,
++                  watch->write_id, watch->user_data), error);
++        else
++          goto error;
++      }
++      g_mutex_lock (watch->mutex);
++
++      g_free (watch->write_data);
++      watch->write_data = NULL;
++    } while (TRUE);
++
++  done:
++    if (watch->write_added) {
++      g_source_remove_poll ((GSource *) watch, &watch->writefd);
++      watch->write_added = FALSE;
++      watch->writefd.revents = 0;
++    }
++
++    g_mutex_unlock (watch->mutex);
++  }
++
++write_blocked:
++  return TRUE;
++
++  /* ERRORS */
++eof:
++  {
++    if (watch->funcs.closed)
++      watch->funcs.closed (watch, watch->user_data);
++    return FALSE;
++  }
++error:
++  {
++    if (watch->funcs.error)
++      watch->funcs.error (watch, res, watch->user_data);
++    return FALSE;
++  }
++}
++
++static void
++gst_rtsp_rec_free (gpointer data)
++{
++  GstRTSPRec *rec = data;
++
++  g_free (rec->data);
++  g_slice_free (GstRTSPRec, rec);
++}
++
++static void
++gst_rtsp_source_finalize (GSource * source)
++{
++  GstRTSPWatch *watch = (GstRTSPWatch *) source;
++
++  build_reset (&watch->builder);
++  gst_rtsp_message_unset (&watch->message);
++
++  g_queue_foreach (watch->messages, (GFunc) gst_rtsp_rec_free, NULL);
++  g_queue_free (watch->messages);
++  watch->messages = NULL;
++
++  g_mutex_free (watch->mutex);
++
++  g_free (watch->write_data);
++
++  if (watch->notify)
++    watch->notify (watch->user_data);
++}
++
++static GSourceFuncs gst_rtsp_source_funcs = {
++  gst_rtsp_source_prepare,
++  gst_rtsp_source_check,
++  gst_rtsp_source_dispatch,
++  gst_rtsp_source_finalize,
++  NULL,
++  NULL
++};
++
++/**
++ * gst_rtsp_watch_new:
++ * @conn: a #GstRTSPConnection
++ * @funcs: watch functions
++ * @user_data: user data to pass to @funcs
++ * @notify: notify when @user_data is not referenced anymore
++ *
++ * Create a watch object for @conn. The functions provided in @funcs will be
++ * called with @user_data when activity happened on the watch.
++ *
++ * The new watch is usually created so that it can be attached to a
++ * maincontext with gst_rtsp_watch_attach(). 
++ *
++ * @conn must exist for the entire lifetime of the watch.
++ *
++ * Returns: a #GstRTSPWatch that can be used for asynchronous RTSP
++ * communication. Free with gst_rtsp_watch_unref () after usage.
++ *
++ * Since: 0.10.23
++ */
++GstRTSPWatch *
++gst_rtsp_watch_new (GstRTSPConnection * conn,
++    GstRTSPWatchFuncs * funcs, gpointer user_data, GDestroyNotify notify)
++{
++  GstRTSPWatch *result;
++
++  g_return_val_if_fail (conn != NULL, NULL);
++  g_return_val_if_fail (funcs != NULL, NULL);
++  g_return_val_if_fail (conn->readfd != NULL, NULL);
++  g_return_val_if_fail (conn->writefd != NULL, NULL);
++
++  result = (GstRTSPWatch *) g_source_new (&gst_rtsp_source_funcs,
++      sizeof (GstRTSPWatch));
++
++  result->conn = conn;
++  result->builder.state = STATE_START;
++
++  result->mutex = g_mutex_new ();
++  result->messages = g_queue_new ();
++
++  result->readfd.fd = -1;
++  result->writefd.fd = -1;
++
++  gst_rtsp_watch_reset (result);
++
++  result->funcs = *funcs;
++  result->user_data = user_data;
++  result->notify = notify;
++
++  /* only add the read fd, the write fd is only added when we have data
++   * to send. */
++  g_source_add_poll ((GSource *) result, &result->readfd);
++
++  return result;
++}
++
++/**
++ * gst_rtsp_watch_reset:
++ * @watch: a #GstRTSPWatch
++ *
++ * Reset @watch, this is usually called after gst_rtsp_connection_do_tunnel()
++ * when the file descriptors of the connection might have changed.
++ *
++ * Since: 0.10.23
++ */
++void
++gst_rtsp_watch_reset (GstRTSPWatch * watch)
++{
++  if (watch->readfd.fd != -1)
++    g_source_remove_poll ((GSource *) watch, &watch->readfd);
++  if (watch->writefd.fd != -1)
++    g_source_remove_poll ((GSource *) watch, &watch->writefd);
++
++  watch->readfd.fd = watch->conn->readfd->fd;
++  watch->readfd.events = READ_COND;
++  watch->readfd.revents = 0;
++
++  watch->writefd.fd = watch->conn->writefd->fd;
++  watch->writefd.events = WRITE_COND;
++  watch->writefd.revents = 0;
++  watch->write_added = FALSE;
++
++  g_source_add_poll ((GSource *) watch, &watch->readfd);
++}
++
++/**
++ * gst_rtsp_watch_attach:
++ * @watch: a #GstRTSPWatch
++ * @context: a GMainContext (if NULL, the default context will be used)
++ *
++ * Adds a #GstRTSPWatch to a context so that it will be executed within that context.
++ *
++ * Returns: the ID (greater than 0) for the watch within the GMainContext. 
++ *
++ * Since: 0.10.23
++ */
++guint
++gst_rtsp_watch_attach (GstRTSPWatch * watch, GMainContext * context)
++{
++  g_return_val_if_fail (watch != NULL, 0);
++
++  return g_source_attach ((GSource *) watch, context);
++}
++
++/**
++ * gst_rtsp_watch_unref:
++ * @watch: a #GstRTSPWatch
++ *
++ * Decreases the reference count of @watch by one. If the resulting reference
++ * count is zero the watch and associated memory will be destroyed.
++ *
++ * Since: 0.10.23
++ */
++void
++gst_rtsp_watch_unref (GstRTSPWatch * watch)
++{
++  g_return_if_fail (watch != NULL);
++
++  g_source_unref ((GSource *) watch);
++}
++
++/**
++ * gst_rtsp_watch_write_data:
++ * @watch: a #GstRTSPWatch
++ * @data: the data to queue
++ * @size: the size of @data
++ * @id: location for a message ID or %NULL
++ *
++ * Write @data using the connection of the @watch. If it cannot be sent
++ * immediately, it will be queued for transmission in @watch. The contents of
++ * @message will then be serialized and transmitted when the connection of the
++ * @watch becomes writable. In case the @message is queued, the ID returned in
++ * @id will be non-zero and used as the ID argument in the message_sent
++ * callback.
++ *
++ * This function will take ownership of @data and g_free() it after use.
++ *
++ * Returns: #GST_RTSP_OK on success.
++ *
++ * Since: 0.10.25
++ */
++GstRTSPResult
++gst_rtsp_watch_write_data (GstRTSPWatch * watch, const guint8 * data,
++    guint size, guint * id)
++{
++  GstRTSPResult res;
++  GstRTSPRec *rec;
++  guint off = 0;
++
++  g_return_val_if_fail (watch != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (size != 0, GST_RTSP_EINVAL);
++
++  g_mutex_lock (watch->mutex);
++
++  if (watch->messages->length == 0) {
++    res = write_bytes (watch->writefd.fd, data, &off, size);
++    if (res != GST_RTSP_EINTR) {
++      if (id != NULL)
++        *id = 0;
++      g_free ((gpointer) data);
++      goto done;
++    }
++  }
++
++  /* make a record with the data and id */
++  rec = g_slice_new (GstRTSPRec);
++  if (off == 0) {
++    rec->data = (guint8 *) data;
++    rec->size = size;
++  } else {
++    rec->data = g_memdup (data + off, size - off);
++    rec->size = size - off;
++    g_free ((gpointer) data);
++  }
++
++  do {
++    /* make sure rec->id is never 0 */
++    rec->id = ++watch->id;
++  } while (G_UNLIKELY (rec->id == 0));
++
++  /* add the record to a queue. FIXME we would like to have an upper limit here */
++  g_queue_push_head (watch->messages, rec);
++
++  /* make sure the main context will now also check for writability on the
++   * socket */
++  if (!watch->write_added) {
++    g_source_add_poll ((GSource *) watch, &watch->writefd);
++    watch->write_added = TRUE;
++  }
++
++  if (id != NULL)
++    *id = rec->id;
++  res = GST_RTSP_OK;
++
++done:
++  g_mutex_unlock (watch->mutex);
++  return res;
++}
++
++/**
++ * gst_rtsp_watch_send_message:
++ * @watch: a #GstRTSPWatch
++ * @message: a #GstRTSPMessage
++ * @id: location for a message ID or %NULL
++ *
++ * Send a @message using the connection of the @watch. If it cannot be sent
++ * immediately, it will be queued for transmission in @watch. The contents of
++ * @message will then be serialized and transmitted when the connection of the
++ * @watch becomes writable. In case the @message is queued, the ID returned in
++ * @id will be non-zero and used as the ID argument in the message_sent
++ * callback.
++ *
++ * Returns: #GST_RTSP_OK on success.
++ *
++ * Since: 0.10.25
++ */
++GstRTSPResult
++gst_rtsp_watch_send_message (GstRTSPWatch * watch, GstRTSPMessage * message,
++    guint * id)
++{
++  GString *str;
++  guint size;
++
++  g_return_val_if_fail (watch != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
++
++  /* make a record with the message as a string and id */
++  str = message_to_string (watch->conn, message);
++  size = str->len;
++  return gst_rtsp_watch_write_data (watch,
++      (guint8 *) g_string_free (str, FALSE), size, id);
++}
++
++/**
++ * gst_rtsp_watch_queue_data:
++ * @watch: a #GstRTSPWatch
++ * @data: the data to queue
++ * @size: the size of @data
++ *
++ * Queue @data for transmission in @watch. It will be transmitted when the
++ * connection of the @watch becomes writable.
++ *
++ * This function will take ownership of @data and g_free() it after use.
++ *
++ * The return value of this function will be used as the id argument in the
++ * message_sent callback.
++ *
++ * Deprecated: Use gst_rtsp_watch_write_data()
++ *
++ * Returns: an id.
++ *
++ * Since: 0.10.24
++ */
++#ifndef GST_REMOVE_DEPRECATED
++guint
++gst_rtsp_watch_queue_data (GstRTSPWatch * watch, const guint8 * data,
++    guint size)
++{
++  GstRTSPRec *rec;
++
++  g_return_val_if_fail (watch != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (size != 0, GST_RTSP_EINVAL);
++
++  g_mutex_lock (watch->mutex);
++
++  /* make a record with the data and id */
++  rec = g_slice_new (GstRTSPRec);
++  rec->data = (guint8 *) data;
++  rec->size = size;
++  do {
++    /* make sure rec->id is never 0 */
++    rec->id = ++watch->id;
++  } while (G_UNLIKELY (rec->id == 0));
++
++  /* add the record to a queue. FIXME we would like to have an upper limit here */
++  g_queue_push_head (watch->messages, rec);
++
++  /* make sure the main context will now also check for writability on the
++   * socket */
++  if (!watch->write_added) {
++    g_source_add_poll ((GSource *) watch, &watch->writefd);
++    watch->write_added = TRUE;
++  }
++
++  g_mutex_unlock (watch->mutex);
++  return rec->id;
++}
++#endif /* GST_REMOVE_DEPRECATED */
++
++/**
++ * gst_rtsp_watch_queue_message:
++ * @watch: a #GstRTSPWatch
++ * @message: a #GstRTSPMessage
++ *
++ * Queue a @message for transmission in @watch. The contents of this
++ * message will be serialized and transmitted when the connection of the
++ * @watch becomes writable.
++ *
++ * The return value of this function will be used as the id argument in the
++ * message_sent callback.
++ *
++ * Deprecated: Use gst_rtsp_watch_send_message()
++ *
++ * Returns: an id.
++ *
++ * Since: 0.10.23
++ */
++#ifndef GST_REMOVE_DEPRECATED
++guint
++gst_rtsp_watch_queue_message (GstRTSPWatch * watch, GstRTSPMessage * message)
++{
++  GString *str;
++  guint size;
++
++  g_return_val_if_fail (watch != NULL, GST_RTSP_EINVAL);
++  g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
++
++  /* make a record with the message as a string and id */
++  str = message_to_string (watch->conn, message);
++  size = str->len;
++  return gst_rtsp_watch_queue_data (watch,
++      (guint8 *) g_string_free (str, FALSE), size);
++}
++#endif /* GST_REMOVE_DEPRECATED */
index 0000000,0000000..dfa2c50
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,85 @@@
++#include "rtsp-marshal.h"
++
++#include      <glib-object.h>
++
++
++#ifdef G_ENABLE_DEBUG
++#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
++#define g_marshal_value_peek_char(v)     g_value_get_char (v)
++#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
++#define g_marshal_value_peek_int(v)      g_value_get_int (v)
++#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
++#define g_marshal_value_peek_long(v)     g_value_get_long (v)
++#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
++#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
++#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
++#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
++#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
++#define g_marshal_value_peek_float(v)    g_value_get_float (v)
++#define g_marshal_value_peek_double(v)   g_value_get_double (v)
++#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
++#define g_marshal_value_peek_param(v)    g_value_get_param (v)
++#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
++#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
++#define g_marshal_value_peek_object(v)   g_value_get_object (v)
++#else /* !G_ENABLE_DEBUG */
++/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
++ *          Do not access GValues directly in your code. Instead, use the
++ *          g_value_get_*() functions
++ */
++#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
++#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
++#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
++#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
++#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
++#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
++#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
++#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
++#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
++#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
++#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
++#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
++#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
++#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
++#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
++#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
++#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
++#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
++#endif /* !G_ENABLE_DEBUG */
++
++
++/* ENUM:POINTER,POINTER (rtsp-marshal.list:1) */
++void
++gst_rtsp_marshal_ENUM__POINTER_POINTER (GClosure * closure,
++    GValue * return_value G_GNUC_UNUSED,
++    guint n_param_values,
++    const GValue * param_values,
++    gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
++{
++  typedef gint (*GMarshalFunc_ENUM__POINTER_POINTER) (gpointer data1,
++      gpointer arg_1, gpointer arg_2, gpointer data2);
++  register GMarshalFunc_ENUM__POINTER_POINTER callback;
++  register GCClosure *cc = (GCClosure *) closure;
++  register gpointer data1, data2;
++  gint v_return;
++
++  g_return_if_fail (return_value != NULL);
++  g_return_if_fail (n_param_values == 3);
++
++  if (G_CCLOSURE_SWAP_DATA (closure)) {
++    data1 = closure->data;
++    data2 = g_value_peek_pointer (param_values + 0);
++  } else {
++    data1 = g_value_peek_pointer (param_values + 0);
++    data2 = closure->data;
++  }
++  callback =
++      (GMarshalFunc_ENUM__POINTER_POINTER) (marshal_data ? marshal_data :
++      cc->callback);
++
++  v_return = callback (data1,
++      g_marshal_value_peek_pointer (param_values + 1),
++      g_marshal_value_peek_pointer (param_values + 2), data2);
++
++  g_value_set_enum (return_value, v_return);
++}
index 0000000,0000000..0106232
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++
++#ifndef __gst_rtsp_marshal_MARSHAL_H__
++#define __gst_rtsp_marshal_MARSHAL_H__
++
++#include      <glib-object.h>
++
++G_BEGIN_DECLS
++
++/* ENUM:POINTER,POINTER (rtsp-marshal.list:1) */
++extern void gst_rtsp_marshal_ENUM__POINTER_POINTER (GClosure     *closure,
++                                                    GValue       *return_value,
++                                                    guint         n_param_values,
++                                                    const GValue *param_values,
++                                                    gpointer      invocation_hint,
++                                                    gpointer      marshal_data);
++
++G_END_DECLS
++
++#endif /* __gst_rtsp_marshal_MARSHAL_H__ */
++
index 0000000,0000000..bc16338
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,73 @@@
++commit bf5ef87699b8ef602548eec131312d7a733e278e
++Author: Josep Torra <n770galaxy@gmail.com>
++Date:   Tue Apr 14 18:03:09 2009 +0200
++
++    Added RTSP headers related to Windows Media extension.
++
++diff --git a/gst-libs/gst/rtsp/gstrtspdefs.c b/gst-libs/gst/rtsp/gstrtspdefs.c
++index 0ab2b95..8b086e5 100644
++--- a/gst-libs/gst/rtsp/gstrtspdefs.c
+++++ b/gst-libs/gst/rtsp/gstrtspdefs.c
++@@ -164,6 +164,27 @@ static const gchar *rtsp_headers[] = {
++   "ETag",                       /* ETag */
++   "If-Match",                   /* If-Match */
++ 
+++  /* WM extensions [MS-RTSP] */
+++  "Accept-Charset",             /* Accept-Charset */
+++  "Supported",                  /* Supported */
+++  "Vary",                       /* Vary */
+++  "X-Accelerate-Streaming",     /* X-Accelerate-Streaming */
+++  "X-Accept-Authentication",    /* X-Accept-Authentication */
+++  "X-Accept-Proxy-Authentication",      /* X-Accept-Proxy-Authentication */
+++  "X-Broadcast-Id",             /* X-Broadcast-Id */
+++  "X-Burst-Streaming",          /* X-Burst-Streaming */
+++  "X-Notice",                   /* X-Notice */
+++  "X-Player-Lag-Time",          /* X-Player-Lag-Time */
+++  "X-Playlist",                 /* X-Playlist */
+++  "X-Playlist-Change-Notice",   /* X-Playlist-Change-Notice */
+++  "X-Playlist-Gen-Id",          /* X-Playlist-Gen-Id */
+++  "X-Playlist-Seek-Id",         /* X-Playlist-Seek-Id */
+++  "X-Proxy-Client-Agent",       /* X-Proxy-Client-Agent */
+++  "X-Proxy-Client-Verb",        /* X-Proxy-Client-Verb */
+++  "X-Receding-PlaylistChange",  /* X-Receding-PlaylistChange */
+++  "X-RTP-Info",                 /* X-RTP-Info */
+++  "X-StartupProfile",           /* X-StartupProfile */
+++
++   NULL
++ };
++ 
++diff --git a/gst-libs/gst/rtsp/gstrtspdefs.h b/gst-libs/gst/rtsp/gstrtspdefs.h
++index dd4b531..ae3b105 100644
++--- a/gst-libs/gst/rtsp/gstrtspdefs.h
+++++ b/gst-libs/gst/rtsp/gstrtspdefs.h
++@@ -287,7 +287,29 @@ typedef enum {
++ 
++   /* Since 0.10.23 */
++   GST_RTSP_HDR_ETAG,                /* ETag */
++-  GST_RTSP_HDR_IF_MATCH             /* If-Match */
+++  GST_RTSP_HDR_IF_MATCH,            /* If-Match */
+++
+++  /* WM extensions [MS-RTSP] */
+++  GST_RTSP_HDR_ACCEPT_CHARSET,      /* Accept-Charset */
+++  GST_RTSP_HDR_SUPPORTED,           /* Supported */
+++  GST_RTSP_HDR_VARY,                /* Vary */
+++  GST_RTSP_HDR_X_ACCELERATE_STREAMING,    /* X-Accelerate-Streaming */
+++  GST_RTSP_HDR_X_ACCEPT_AUTHENT,    /* X-Accept-Authentication */
+++  GST_RTSP_HDR_X_ACCEPT_PROXY_AUTHENT,    /* X-Accept-Proxy-Authentication */
+++  GST_RTSP_HDR_X_BROADCAST_ID,      /* X-Broadcast-Id */
+++  GST_RTSP_HDR_X_BURST_STREAMING,   /* X-Burst-Streaming */
+++  GST_RTSP_HDR_X_NOTICE,            /* X-Notice */
+++  GST_RTSP_HDR_X_PLAYER_LAG_TIME,   /* X-Player-Lag-Time */
+++  GST_RTSP_HDR_X_PLAYLIST,          /* X-Playlist */
+++  GST_RTSP_HDR_X_PLAYLIST_CHANGE_NOTICE,  /* X-Playlist-Change-Notice */
+++  GST_RTSP_HDR_X_PLAYLIST_GEN_ID,   /* X-Playlist-Gen-Id */
+++  GST_RTSP_HDR_X_PLAYLIST_SEEK_ID,  /* X-Playlist-Seek-Id */
+++  GST_RTSP_HDR_X_PROXY_CLIENT_AGENT,      /* X-Proxy-Client-Agent */
+++  GST_RTSP_HDR_X_PROXY_CLIENT_VERB, /* X-Proxy-Client-Verb */
+++  GST_RTSP_HDR_X_RECEDING_PLAYLISTCHANGE, /* X-Receding-PlaylistChange */
+++  GST_RTSP_HDR_X_RTP_INFO,          /* X-RTP-Info */
+++  GST_RTSP_HDR_X_STARTUPPROFILE     /* X-StartupProfile */
+++
++ } GstRTSPHeaderField;
++ 
++ typedef enum {
index 0000000,0000000..38ef9a1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,82 @@@
++/* GStreamer
++ * Copyright (C) <2011> Wim Taymans <wim.taymans@gmail.com>
++ *
++ * 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_META_VIDEO_H__
++#define __GST_META_VIDEO_H__
++
++#include <gst/gst.h>
++
++#include <gst/video/video.h>
++
++G_BEGIN_DECLS
++
++#define GST_META_API_VIDEO   "GstMetaVideo"
++#define GST_META_INFO_VIDEO  (gst_meta_video_get_info())
++
++typedef struct _GstMetaVideo GstMetaVideo;
++
++/**
++ * GstMetaVideo:
++ * @meta: parent #GstMeta
++ * @flags: additional video flags
++ * @n_planes: the number of planes in the image
++ * @offset: array of offsets for the planes
++ * @stride: array of strides for the planes
++ * @map: map the memory of a plane
++ * @unmap: unmap the memory of a plane
++ *
++ * Extra buffer metadata describing image properties
++ */
++struct _GstMetaVideo {
++  GstMeta            meta;
++
++  GstBuffer         *buffer;
++
++  GstVideoFlags      flags;
++  GstVideoFormat     format;
++  guint              width;
++  guint              height;
++
++  guint              n_planes;
++  gsize              offset[GST_VIDEO_MAX_PLANES];
++  gint               stride[GST_VIDEO_MAX_PLANES];
++
++  gpointer (*map)    (GstMetaVideo *meta, guint plane, gint *stride,
++                      GstMapFlags flags);
++  gboolean (*unmap)  (GstMetaVideo *meta, guint plane, gpointer data);
++};
++
++const GstMetaInfo * gst_meta_video_get_info (void);
++
++#define gst_buffer_get_meta_video(b) ((GstMetaVideo*)gst_buffer_get_meta((b),GST_META_INFO_VIDEO))
++GstMetaVideo * gst_buffer_add_meta_video       (GstBuffer *buffer, GstVideoFlags flags,
++                                                GstVideoFormat format, guint width, guint height);
++GstMetaVideo * gst_buffer_add_meta_video_full  (GstBuffer *buffer, GstVideoFlags flags,
++                                                GstVideoFormat format, guint width, guint height,
++                                                guint n_planes, gsize offset[GST_VIDEO_MAX_PLANES],
++                                                gint stride[GST_VIDEO_MAX_PLANES]);
++
++gpointer       gst_meta_video_map        (GstMetaVideo *meta, guint plane, gint *stride,
++                                          GstMapFlags flags);
++gboolean       gst_meta_video_unmap      (GstMetaVideo *meta, guint plane, gpointer data);
++
++
++G_END_DECLS
++
++#endif /* __GST_META_VIDEO_H__ */
@@@ -1262,7 -1263,7 +1262,7 @@@ _create_stream_group (GstEncodeBin * eb
    /* FIXME : Once we have properties for specific converters, use those */
    if (GST_IS_ENCODING_VIDEO_PROFILE (sprof)) {
      const gboolean native_video =
--        ! !(ebin->flags & GST_ENC_FLAG_NO_VIDEO_CONVERSION);
++        !!(ebin->flags & GST_ENC_FLAG_NO_VIDEO_CONVERSION);
      GstElement *cspace = NULL, *scale, *vrate, *cspace2 = NULL;
  
      GST_LOG ("Adding conversion elements for video stream");
@@@ -6,25 -6,28 +6,27 @@@ glib_gen_basename = gstpla
  built_sources = gstplay-marshal.c
  built_headers = gstplay-marshal.h
  
 -plugin_LTLIBRARIES = libgstplaybin.la libgstdecodebin.la libgstdecodebin2.la
 +plugin_LTLIBRARIES = libgstplayback.la
  
 -csp_cflags = -DCOLORSPACE=\"ffmpegcolorspace\"
++csp_cflags = -DCOLORSPACE=\"videoconvert\"
 -libgstplaybin_la_SOURCES = \
 +libgstplayback_la_SOURCES = \
 +      gstdecodebin2.c \
 +      gsturidecodebin.c \
        gstplayback.c \
 -      gstplaybin.c \
        gstplaybin2.c \
        gstplaysink.c \
 -      gstplaybasebin.c \
        gstplay-enum.c \
 -      gststreaminfo.c \
        gststreamselector.c \
        gstsubtitleoverlay.c \
        gstplaysinkvideoconvert.c \
        gstplaysinkaudioconvert.c \
        gststreamsynchronizer.c
  
 -nodist_libgstplaybin_la_SOURCES = $(built_sources)
 -libgstplaybin_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(csp_cflags)
 -libgstplaybin_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 -libgstplaybin_la_LIBADD = \
 +nodist_libgstplayback_la_SOURCES = $(built_sources)
- libgstplayback_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
++libgstplayback_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(csp_cflags)
 +libgstplayback_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 +libgstplayback_la_LIBADD = \
        $(top_builddir)/gst-libs/gst/pbutils/libgstpbutils-@GST_MAJORMINOR@.la \
        $(top_builddir)/gst-libs/gst/interfaces/libgstinterfaces-@GST_MAJORMINOR@.la \
        $(top_builddir)/gst-libs/gst/video/libgstvideo-@GST_MAJORMINOR@.la \
@@@ -52,14 -75,38 +54,14 @@@ include $(top_srcdir)/common/gst-glib-g
  
  Android.mk: Makefile.am $(BUILT_SOURCES)
        androgenizer \
 -      -:PROJECT libgstdecodebin -:SHARED libgstdecodebin \
 -       -:TAGS eng debug \
 -         -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
 -       -:SOURCES $(libgstdecodebin_la_SOURCES) \
 -                 $(nodist_libgstdecodebin_la_SOURCES) \
 -       -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstdecodebin_la_CFLAGS) $(csp_cflags) \
 -       -:LDFLAGS $(libgstdecodebin_la_LDFLAGS) \
 -                 $(libgstdecodebin_la_LIBADD) \
 -                 -ldl \
 -       -:PASSTHROUGH LOCAL_ARM_MODE:=arm \
 -                     LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \
 -       \
 -      -:PROJECT libgstdecodebin2 -:SHARED libgstdecodebin2 \
 -       -:TAGS eng debug \
 -         -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
 -       -:SOURCES $(libgstdecodebin2_la_SOURCES) \
 -                 $(nodist_libgstdecodebin2_la_SOURCES) \
 -       -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstdecodebin2_la_CFLAGS) $(csp_cflags) \
 -       -:LDFLAGS $(libgstdecodebin2_la_LDFLAGS) \
 -                 $(libgstdecodebin2_la_LIBADD) \
 -                 -ldl \
 -       -:PASSTHROUGH LOCAL_ARM_MODE:=arm \
 -                     LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \
 -       \
 -      -:PROJECT libgstplaybin -:SHARED libgstplaybin \
 +      -:PROJECT libgstplayback -:SHARED libgstplayback \
         -:TAGS eng debug \
           -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
 -       -:SOURCES $(libgstplaybin_la_SOURCES) \
 -                 $(nodist_libgstplaybin_la_SOURCES) \
 -       -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstplaybin_la_CFLAGS) $(csp_cflags) \
 -       -:LDFLAGS $(libgstplaybin_la_LDFLAGS) \
 -                 $(libgstplaybin_la_LIBADD) \
 +       -:SOURCES $(libgstplayback_la_SOURCES) \
 +                 $(nodist_libgstplayback_la_SOURCES) \
-        -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstplayback_la_CFLAGS) \
++       -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstplayback_la_CFLAGS) $(csp_cflags) \
 +       -:LDFLAGS $(libgstplayback_la_LDFLAGS) \
 +                 $(libgstplayback_la_LIBADD) \
                   -ldl \
         -:PASSTHROUGH LOCAL_ARM_MODE:=arm \
                       LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \
@@@ -1575,11 -1614,11 +1575,11 @@@ setup_caps_delay
      ppad->pad = gst_object_ref (pad);
      ppad->chain = chain;
      ppad->event_probe_id =
 -        gst_pad_add_event_probe (pad, (GCallback) pad_event_cb, ppad);
 +        gst_pad_add_probe (pad, GST_PROBE_TYPE_EVENT, pad_event_cb, ppad, NULL);
      chain->pending_pads = g_list_prepend (chain->pending_pads, ppad);
-     CHAIN_MUTEX_UNLOCK (chain);
      g_signal_connect (G_OBJECT (pad), "notify::caps",
          G_CALLBACK (caps_notify_cb), chain);
+     CHAIN_MUTEX_UNLOCK (chain);
      return;
    }
  }
Simple merge
Simple merge
@@@ -120,9 -122,27 +120,25 @@@ static GstStaticPadTemplate gst_video_r
  
  static void gst_video_rate_swap_prev (GstVideoRate * videorate,
      GstBuffer * buffer, gint64 time);
- static gboolean gst_video_rate_event (GstPad * pad, GstEvent * event);
- static gboolean gst_video_rate_query (GstPad * pad, GstQuery * query);
- static GstFlowReturn gst_video_rate_chain (GstPad * pad, GstBuffer * buffer);
 -static gboolean gst_video_rate_event (GstBaseTransform * trans,
++static gboolean gst_video_rate_sink_event (GstBaseTransform * trans,
+     GstEvent * event);
+ static gboolean gst_video_rate_query (GstBaseTransform * trans,
+     GstPadDirection direction, GstQuery * query);
+ static gboolean gst_video_rate_setcaps (GstBaseTransform * trans,
+     GstCaps * in_caps, GstCaps * out_caps);
+ static GstCaps *gst_video_rate_transform_caps (GstBaseTransform * trans,
 -    GstPadDirection direction, GstCaps * caps);
++    GstPadDirection direction, GstCaps * caps, GstCaps * filter);
+ static void gst_video_rate_fixate_caps (GstBaseTransform * trans,
+     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
 -static GstFlowReturn gst_video_rate_prepare_output_buffer (GstBaseTransform *
 -    trans, GstBuffer * input, gint size, GstCaps * caps, GstBuffer ** buf);
+ static GstFlowReturn gst_video_rate_transform_ip (GstBaseTransform * trans,
+     GstBuffer * buf);
+ static gboolean gst_video_rate_start (GstBaseTransform * trans);
  
  static void gst_video_rate_set_property (GObject * object,
      guint prop_id, const GValue * value, GParamSpec * pspec);
@@@ -132,25 -152,47 +148,28 @@@ static void gst_video_rate_get_propert
  static GParamSpec *pspec_drop = NULL;
  static GParamSpec *pspec_duplicate = NULL;
  
 -GST_BOILERPLATE (GstVideoRate, gst_video_rate,
 -    GstBaseTransform, GST_TYPE_BASE_TRANSFORM);
 -
 -static void
 -gst_video_rate_base_init (gpointer g_class)
 -{
 -  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
 -
 -  gst_element_class_set_details_simple (element_class,
 -      "Video rate adjuster", "Filter/Effect/Video",
 -      "Drops/duplicates/adjusts timestamps on video frames to make a perfect stream",
 -      "Wim Taymans <wim@fluendo.com>");
 -
 -  gst_element_class_add_pad_template (element_class,
 -      gst_static_pad_template_get (&gst_video_rate_sink_template));
 -  gst_element_class_add_pad_template (element_class,
 -      gst_static_pad_template_get (&gst_video_rate_src_template));
 -}
 +#define gst_video_rate_parent_class parent_class
- G_DEFINE_TYPE (GstVideoRate, gst_video_rate, GST_TYPE_ELEMENT);
++G_DEFINE_TYPE (GstVideoRate, gst_video_rate, GST_TYPE_BASE_TRANSFORM);
  
  static void
  gst_video_rate_class_init (GstVideoRateClass * klass)
  {
    GObjectClass *object_class = G_OBJECT_CLASS (klass);
-   parent_class = g_type_class_peek_parent (klass);
 +  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+   GstBaseTransformClass *base_class = GST_BASE_TRANSFORM_CLASS (klass);
  
 -  parent_class = g_type_class_peek_parent (klass);
 -
    object_class->set_property = gst_video_rate_set_property;
    object_class->get_property = gst_video_rate_get_property;
  
 -  base_class->prepare_output_buffer =
 -      GST_DEBUG_FUNCPTR (gst_video_rate_prepare_output_buffer);
 -  base_class->event = GST_DEBUG_FUNCPTR (gst_video_rate_event);
+   base_class->set_caps = GST_DEBUG_FUNCPTR (gst_video_rate_setcaps);
+   base_class->transform_caps =
+       GST_DEBUG_FUNCPTR (gst_video_rate_transform_caps);
+   base_class->transform_ip = GST_DEBUG_FUNCPTR (gst_video_rate_transform_ip);
++  base_class->sink_event = GST_DEBUG_FUNCPTR (gst_video_rate_sink_event);
+   base_class->start = GST_DEBUG_FUNCPTR (gst_video_rate_start);
+   base_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_video_rate_fixate_caps);
+   base_class->query = GST_DEBUG_FUNCPTR (gst_video_rate_query);
    g_object_class_install_property (object_class, ARG_IN,
        g_param_spec_uint64 ("in", "In",
            "Number of input frames", 0, G_MAXUINT64, 0,
            "Period over which to average the framerate (in ns) (0 = disabled)",
            0, G_MAXINT64, DEFAULT_AVERAGE_PERIOD,
            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-   element_class->change_state = GST_DEBUG_FUNCPTR (gst_video_rate_change_state);
- }
- /* return the caps that can be used on out_pad given in_caps on in_pad */
- static gboolean
- gst_video_rate_transformcaps (GstPad * in_pad, GstCaps * in_caps,
-     GstPad * out_pad, GstCaps ** out_caps, GstCaps * filter)
- {
-   GstCaps *intersect, *in_templ;
-   gint i;
-   GSList *extra_structures = NULL;
-   GSList *iter;
-   in_templ = gst_pad_get_pad_template_caps (in_pad);
-   intersect =
-       gst_caps_intersect_full (in_caps, in_templ, GST_CAPS_INTERSECT_FIRST);
-   gst_caps_unref (in_templ);
 +
-   /* all possible framerates are allowed */
-   for (i = 0; i < gst_caps_get_size (intersect); i++) {
-     GstStructure *structure;
++  gst_element_class_set_details_simple (element_class,
++      "Video rate adjuster", "Filter/Effect/Video",
++      "Drops/duplicates/adjusts timestamps on video frames to make a perfect stream",
++      "Wim Taymans <wim@fluendo.com>");
 +
-     structure = gst_caps_get_structure (intersect, i);
++  gst_element_class_add_pad_template (element_class,
++      gst_static_pad_template_get (&gst_video_rate_sink_template));
++  gst_element_class_add_pad_template (element_class,
++      gst_static_pad_template_get (&gst_video_rate_src_template));
 +
+ }
  
-     if (gst_structure_has_field (structure, "framerate")) {
-       GstStructure *copy_structure;
+ static GstCaps *
+ gst_video_rate_transform_caps (GstBaseTransform * trans,
 -    GstPadDirection direction, GstCaps * caps)
++    GstPadDirection direction, GstCaps * caps, GstCaps * filter)
+ {
+   GstCaps *ret;
+   GstStructure *s;
  
-       copy_structure = gst_structure_copy (structure);
-       gst_structure_set (copy_structure,
-           "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
-       extra_structures = g_slist_append (extra_structures, copy_structure);
-     }
-   }
+   /* Should always be called with simple caps */
+   g_return_val_if_fail (GST_CAPS_IS_SIMPLE (caps), NULL);
  
-   /* append the extra structures */
-   for (iter = extra_structures; iter != NULL; iter = g_slist_next (iter)) {
-     gst_caps_append_structure (intersect, (GstStructure *) iter->data);
-   }
-   g_slist_free (extra_structures);
+   ret = gst_caps_copy (caps);
  
-   if (filter) {
-     GstCaps *tmp;
+   s = gst_structure_copy (gst_caps_get_structure (caps, 0));
  
-     tmp = gst_caps_intersect_full (filter, intersect, GST_CAPS_INTERSECT_FIRST);
-     gst_caps_unref (intersect);
-     intersect = tmp;
-   }
+   /* set the framerate as a range */
+   gst_structure_set (s, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1,
+       G_MAXINT, 1, NULL);
  
-   *out_caps = intersect;
+   gst_caps_append_structure (ret, s);
  
-   return TRUE;
+   return ret;
  }
  
- static GstCaps *
- gst_video_rate_getcaps (GstPad * pad, GstCaps * filter)
+ static void
+ gst_video_rate_fixate_caps (GstBaseTransform * trans,
+     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
  {
-   GstVideoRate *videorate;
-   GstPad *otherpad;
-   GstCaps *caps;
-   videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
-   otherpad = (pad == videorate->srcpad) ? videorate->sinkpad :
-       videorate->srcpad;
-   /* we can do what the peer can */
-   caps = gst_pad_peer_get_caps (otherpad, filter);
-   if (caps) {
-     GstCaps *transform, *intersect;
-     gst_video_rate_transformcaps (otherpad, caps, pad, &transform, filter);
+   GstStructure *s;
+   gint num, denom;
  
-     /* Now prefer the downstream caps if possible */
-     intersect =
-         gst_caps_intersect_full (caps, transform, GST_CAPS_INTERSECT_FIRST);
-     if (!gst_caps_is_empty (intersect)) {
-       gst_caps_append (intersect, transform);
-       gst_caps_unref (caps);
-       caps = intersect;
-     } else {
-       gst_caps_unref (intersect);
-       caps = transform;
-     }
-   } else {
-     /* no peer, our padtemplate is enough then */
-     caps = gst_pad_get_pad_template_caps (pad);
-     if (filter) {
-       GstCaps *intersection;
-       intersection =
-           gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
-       gst_caps_unref (caps);
-       caps = intersection;
-     }
-   }
+   s = gst_caps_get_structure (caps, 0);
+   if (G_UNLIKELY (!gst_structure_get_fraction (s, "framerate", &num, &denom)))
+     return;
  
-   return caps;
+   s = gst_caps_get_structure (othercaps, 0);
+   gst_structure_fixate_field_nearest_fraction (s, "framerate", num, denom);
  }
  
  static gboolean
- gst_video_rate_set_src_caps (GstVideoRate * videorate, GstCaps * caps)
+ gst_video_rate_setcaps (GstBaseTransform * trans, GstCaps * in_caps,
+     GstCaps * out_caps)
  {
 -  GstVideoRate *videorate;
++  GstVideoRate *videorate = GST_VIDEO_RATE (trans);
    GstStructure *structure;
+   gboolean ret = TRUE;
    gint rate_numerator, rate_denominator;
  
-   GST_DEBUG_OBJECT (videorate, "src caps %" GST_PTR_FORMAT, caps);
+   videorate = GST_VIDEO_RATE (trans);
+   GST_DEBUG_OBJECT (trans, "setcaps called in: %" GST_PTR_FORMAT
+       " out: %" GST_PTR_FORMAT, in_caps, out_caps);
+   structure = gst_caps_get_structure (in_caps, 0);
+   if (!gst_structure_get_fraction (structure, "framerate",
+           &rate_numerator, &rate_denominator))
+     goto no_framerate;
+   videorate->from_rate_numerator = rate_numerator;
+   videorate->from_rate_denominator = rate_denominator;
  
-   structure = gst_caps_get_structure (caps, 0);
+   structure = gst_caps_get_structure (out_caps, 0);
    if (!gst_structure_get_fraction (structure, "framerate",
            &rate_numerator, &rate_denominator))
      goto no_framerate;
@@@ -474,26 -379,8 +367,8 @@@ gst_video_rate_reset (GstVideoRate * vi
  }
  
  static void
 -gst_video_rate_init (GstVideoRate * videorate, GstVideoRateClass * klass)
 +gst_video_rate_init (GstVideoRate * videorate)
  {
-   videorate->sinkpad =
-       gst_pad_new_from_static_template (&gst_video_rate_sink_template, "sink");
-   gst_pad_set_event_function (videorate->sinkpad,
-       GST_DEBUG_FUNCPTR (gst_video_rate_event));
-   gst_pad_set_chain_function (videorate->sinkpad,
-       GST_DEBUG_FUNCPTR (gst_video_rate_chain));
-   gst_pad_set_getcaps_function (videorate->sinkpad,
-       GST_DEBUG_FUNCPTR (gst_video_rate_getcaps));
-   gst_element_add_pad (GST_ELEMENT (videorate), videorate->sinkpad);
-   videorate->srcpad =
-       gst_pad_new_from_static_template (&gst_video_rate_src_template, "src");
-   gst_pad_set_query_function (videorate->srcpad,
-       GST_DEBUG_FUNCPTR (gst_video_rate_query));
-   gst_pad_set_getcaps_function (videorate->srcpad,
-       GST_DEBUG_FUNCPTR (gst_video_rate_getcaps));
-   gst_element_add_pad (GST_ELEMENT (videorate), videorate->srcpad);
    gst_video_rate_reset (videorate);
    videorate->silent = DEFAULT_SILENT;
    videorate->new_pref = DEFAULT_NEW_PREF;
@@@ -604,32 -495,24 +482,20 @@@ gst_video_rate_notify_duplicate (GstVid
  
  #define MAGIC_LIMIT  25
  static gboolean
- gst_video_rate_event (GstPad * pad, GstEvent * event)
 -gst_video_rate_event (GstBaseTransform * trans, GstEvent * event)
++gst_video_rate_sink_event (GstBaseTransform * trans, GstEvent * event)
  {
    GstVideoRate *videorate;
-   gboolean ret;
  
-   videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad));
+   videorate = GST_VIDEO_RATE (trans);
  
    switch (GST_EVENT_TYPE (event)) {
-     case GST_EVENT_CAPS:
-     {
-       GstCaps *caps;
-       gst_event_parse_caps (event, &caps);
-       ret = gst_video_rate_set_sink_caps (videorate, caps);
-       gst_event_unref (event);
-       /* don't forward */
-       goto done;
-     }
 -    case GST_EVENT_NEWSEGMENT:
 +    case GST_EVENT_SEGMENT:
      {
 -      gint64 start, stop, time;
 -      gdouble rate, arate;
 -      gboolean update;
 -      GstFormat format;
 +      const GstSegment *segment;
  
 -      gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
 -          &start, &stop, &time);
 +      gst_event_parse_segment (event, &segment);
  
 -      if (format != GST_FORMAT_TIME)
 +      if (segment->format != GST_FORMAT_TIME)
          goto format_error;
  
        GST_DEBUG_OBJECT (videorate, "handle NEWSEGMENT");
@@@ -800,14 -686,15 +668,17 @@@ gst_video_rate_query (GstBaseTransform 
            gst_query_set_latency (query, live, min, max);
          }
          gst_object_unref (peer);
+         break;
        }
-       break;
+       /* Simple fallthrough if we don't have a latency or not a peer that we
+        * can't ask about its latency yet.. */
      }
      default:
-       res = gst_pad_query_default (pad, query);
 -      res = parent_class->query (trans, direction, query);
++      res =
++          GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
++          query);
        break;
    }
-   gst_object_unref (videorate);
  
    return res;
  }
@@@ -868,10 -753,25 +737,10 @@@ drop
  }
  
  static GstFlowReturn
- gst_video_rate_chain (GstPad * pad, GstBuffer * buffer)
 -gst_video_rate_prepare_output_buffer (GstBaseTransform * trans,
 -    GstBuffer * input, gint size, GstCaps * caps, GstBuffer ** buf)
 -{
 -  if (gst_buffer_is_metadata_writable (input)) {
 -    gst_buffer_set_caps (input, caps);
 -    *buf = gst_buffer_ref (input);
 -  } else {
 -    *buf = gst_buffer_create_sub (input, 0, GST_BUFFER_SIZE (input));
 -    gst_buffer_set_caps (*buf, caps);
 -  }
 -
 -  return GST_FLOW_OK;
 -}
 -
 -static GstFlowReturn
+ gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer)
  {
    GstVideoRate *videorate;
-   GstFlowReturn res = GST_FLOW_OK;
+   GstFlowReturn res = GST_BASE_TRANSFORM_FLOW_DROPPED;
    GstClockTime intime, in_ts, in_dur;
    GstClockTime avg_period;
    gboolean skip = FALSE;
index 0000000,0000000..baca0d5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,48 @@@
++diff --git a/gst/videorate/gstvideorate.c b/gst/videorate/gstvideorate.c
++index 8d22186..7afcfdd 100644
++--- a/gst/videorate/gstvideorate.c
+++++ b/gst/videorate/gstvideorate.c
++@@ -357,6 +357,7 @@ gst_video_rate_reset (GstVideoRate * videorate)
++   videorate->drop = 0;
++   videorate->dup = 0;
++   videorate->next_ts = GST_CLOCK_TIME_NONE;
+++  videorate->discont = TRUE;
++   gst_video_rate_swap_prev (videorate, NULL, 0);
++ 
++   gst_segment_init (&videorate->segment, GST_FORMAT_TIME);
++@@ -409,6 +410,13 @@ gst_video_rate_flush_prev (GstVideoRate * videorate)
++   GST_BUFFER_OFFSET (outbuf) = videorate->out;
++   GST_BUFFER_OFFSET_END (outbuf) = videorate->out + 1;
++ 
+++  if (videorate->discont) {
+++    GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
+++    videorate->discont = FALSE;
+++  }
+++  else
+++    GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DISCONT);
+++
++   /* this is the timestamp we put on the buffer */
++   push_ts = videorate->next_ts;
++ 
++@@ -810,6 +818,9 @@ gst_video_rate_change_state (GstElement * element, GstStateChange transition)
++   videorate = GST_VIDEO_RATE (element);
++ 
++   switch (transition) {
+++    case GST_STATE_CHANGE_READY_TO_PAUSED:
+++      videorate->discont = TRUE;
+++      break;
++     default:
++       break;
++   }
++diff --git a/gst/videorate/gstvideorate.h b/gst/videorate/gstvideorate.h
++index ea6063b..fe7feb6 100644
++--- a/gst/videorate/gstvideorate.h
+++++ b/gst/videorate/gstvideorate.h
++@@ -56,6 +56,7 @@ struct _GstVideoRate
++   GstBuffer *prevbuf;
++   guint64 prev_ts;              /* Previous buffer timestamp */
++   guint64 segment_out;          /* in-segment counting */
+++  gboolean discont;
++ 
++   /* segment handling */
++   GstSegment segment;
@@@ -217,15 -261,36 +227,45 @@@ gst_video_scale_class_init (GstVideoSca
            DEFAULT_PROP_ADD_BORDERS,
            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  
+   g_object_class_install_property (gobject_class, PROP_SHARPNESS,
+       g_param_spec_double ("sharpness", "Sharpness",
+           "Sharpness of filter", 0.0, 2.0, DEFAULT_PROP_SHARPNESS,
+           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+   g_object_class_install_property (gobject_class, PROP_SHARPEN,
+       g_param_spec_double ("sharpen", "Sharpen",
+           "Sharpening", 0.0, 1.0, DEFAULT_PROP_SHARPEN,
+           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+   g_object_class_install_property (gobject_class, PROP_DITHER,
+       g_param_spec_boolean ("dither", "Dither",
+           "Add dither (only used for Lanczos method)",
+           DEFAULT_PROP_DITHER,
+           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ #if 0
+   /* I am hiding submethod for now, since it's poorly named, poorly
+    * documented, and will probably just get people into trouble. */
+   g_object_class_install_property (gobject_class, PROP_SUBMETHOD,
+       g_param_spec_int ("submethod", "submethod",
+           "submethod", 0, 3, DEFAULT_PROP_SUBMETHOD,
+           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ #endif
+   g_object_class_install_property (gobject_class, PROP_ENVELOPE,
+       g_param_spec_double ("envelope", "Envelope",
+           "Size of filter envelope", 0.0, 5.0, DEFAULT_PROP_ENVELOPE,
+           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 +  gst_element_class_set_details_simple (element_class,
 +      "Video scaler", "Filter/Converter/Video/Scaler",
 +      "Resizes video", "Wim Taymans <wim.taymans@chello.be>");
 +
 +  gst_element_class_add_pad_template (element_class,
 +      gst_video_scale_sink_template_factory ());
 +  gst_element_class_add_pad_template (element_class,
 +      gst_video_scale_src_template_factory ());
 +
    trans_class->transform_caps =
        GST_DEBUG_FUNCPTR (gst_video_scale_transform_caps);
    trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_video_scale_set_caps);
@@@ -1054,17 -1162,22 +1149,22 @@@ gst_video_scale_transform (GstBaseTrans
      case GST_VIDEO_FORMAT_ABGR:
      case GST_VIDEO_FORMAT_AYUV:
        if (add_borders)
 -        vs_fill_borders_RGBA (&dest, black);
 +        vs_fill_borders_RGBA (&dest[0], black);
        switch (method) {
          case GST_VIDEO_SCALE_NEAREST:
 -          vs_image_scale_nearest_RGBA (&dest, &src, videoscale->tmp_buf);
 +          vs_image_scale_nearest_RGBA (&dest[0], &src[0], videoscale->tmp_buf);
            break;
          case GST_VIDEO_SCALE_BILINEAR:
 -          vs_image_scale_linear_RGBA (&dest, &src, videoscale->tmp_buf);
 +          vs_image_scale_linear_RGBA (&dest[0], &src[0], videoscale->tmp_buf);
            break;
          case GST_VIDEO_SCALE_4TAP:
 -          vs_image_scale_4tap_RGBA (&dest, &src, videoscale->tmp_buf);
 +          vs_image_scale_4tap_RGBA (&dest[0], &src[0], videoscale->tmp_buf);
            break;
 -          vs_image_scale_lanczos_AYUV (&dest, &src, videoscale->tmp_buf,
+         case GST_VIDEO_SCALE_LANCZOS:
++          vs_image_scale_lanczos_AYUV (&dest[0], &src[0], videoscale->tmp_buf,
+               videoscale->sharpness, videoscale->dither, videoscale->submethod,
+               videoscale->envelope, videoscale->sharpen);
+           break;
          default:
            goto unknown_mode;
        }
        }
        switch (method) {
          case GST_VIDEO_SCALE_NEAREST:
 -          vs_image_scale_nearest_Y (&dest, &src, videoscale->tmp_buf);
 -          vs_image_scale_nearest_Y (&dest_u, &src_u, videoscale->tmp_buf);
 -          vs_image_scale_nearest_Y (&dest_v, &src_v, videoscale->tmp_buf);
 +          vs_image_scale_nearest_Y (&dest[0], &src[0], videoscale->tmp_buf);
 +          vs_image_scale_nearest_Y (&dest[1], &src[1], videoscale->tmp_buf);
 +          vs_image_scale_nearest_Y (&dest[2], &src[2], videoscale->tmp_buf);
            break;
          case GST_VIDEO_SCALE_BILINEAR:
 -          vs_image_scale_linear_Y (&dest, &src, videoscale->tmp_buf);
 -          vs_image_scale_linear_Y (&dest_u, &src_u, videoscale->tmp_buf);
 -          vs_image_scale_linear_Y (&dest_v, &src_v, videoscale->tmp_buf);
 +          vs_image_scale_linear_Y (&dest[0], &src[0], videoscale->tmp_buf);
 +          vs_image_scale_linear_Y (&dest[1], &src[1], videoscale->tmp_buf);
 +          vs_image_scale_linear_Y (&dest[2], &src[2], videoscale->tmp_buf);
            break;
          case GST_VIDEO_SCALE_4TAP:
 -          vs_image_scale_4tap_Y (&dest, &src, videoscale->tmp_buf);
 -          vs_image_scale_4tap_Y (&dest_u, &src_u, videoscale->tmp_buf);
 -          vs_image_scale_4tap_Y (&dest_v, &src_v, videoscale->tmp_buf);
 +          vs_image_scale_4tap_Y (&dest[0], &src[0], videoscale->tmp_buf);
 +          vs_image_scale_4tap_Y (&dest[1], &src[1], videoscale->tmp_buf);
 +          vs_image_scale_4tap_Y (&dest[2], &src[2], videoscale->tmp_buf);
            break;
 -          vs_image_scale_lanczos_Y (&dest, &src, videoscale->tmp_buf,
+         case GST_VIDEO_SCALE_LANCZOS:
 -          vs_image_scale_lanczos_Y (&dest_u, &src_u, videoscale->tmp_buf,
++          vs_image_scale_lanczos_Y (&dest[0], &src[0], videoscale->tmp_buf,
+               videoscale->sharpness, videoscale->dither, videoscale->submethod,
+               videoscale->envelope, videoscale->sharpen);
 -          vs_image_scale_lanczos_Y (&dest_v, &src_v, videoscale->tmp_buf,
++          vs_image_scale_lanczos_Y (&dest[1], &src[1], videoscale->tmp_buf,
+               videoscale->sharpness, videoscale->dither, videoscale->submethod,
+               videoscale->envelope, videoscale->sharpen);
++          vs_image_scale_lanczos_Y (&dest[2], &src[2], videoscale->tmp_buf,
+               videoscale->sharpness, videoscale->dither, videoscale->submethod,
+               videoscale->envelope, videoscale->sharpen);
+           break;
          default:
            goto unknown_mode;
        }
@@@ -67,12 -69,23 +69,18 @@@ typedef struct _GstVideoScaleClass GstV
  struct _GstVideoScale {
    GstVideoFilter element;
  
+   /* properties */
    GstVideoScaleMethod method;
    gboolean add_borders;
+   double sharpness;
+   double sharpen;
+   gboolean dither;
+   int submethod;
+   double envelope;
  
    /* negotiated stuff */
 -  GstVideoFormat format;
 -  gint to_width;
 -  gint to_height;
 -  gint from_width;
 -  gint from_height;
 -  guint src_size;
 -  guint dest_size;
 +  GstVideoInfo from_info;
 +  GstVideoInfo to_info;
  
    gint borders_h;
    gint borders_w;
@@@ -395,8 -398,10 +397,10 @@@ GST_START_TEST (test_loading_profile
    GstEncodingProfile *profile;
    GstCaps *tmpcaps;
    GValue strvalue = { 0, };
 -  GValue miniobjectvalue = { 0, };
 +  GValue objectvalue = { 0, };
  
+   gst_debug_set_threshold_for_name ("default", GST_LEVEL_NONE);
    /* Test loading using short method and all arguments */
    target = gst_encoding_target_load ("myponytarget", "herding", NULL);
    fail_unless (target != NULL);
Simple merge
@@@ -672,9 -627,11 +672,11 @@@ GST_START_TEST (test_convert_frame
    gint i;
    guint8 *data;
  
+   gst_debug_set_threshold_for_name ("default", GST_LEVEL_NONE);
    from_buffer = gst_buffer_new_and_alloc (640 * 480 * 4);
 -  data = GST_BUFFER_DATA (from_buffer);
  
 +  data = gst_buffer_map (from_buffer, NULL, NULL, GST_MAP_WRITE);
    for (i = 0; i < 640 * 480; i++) {
      data[4 * i + 0] = 0;        /* x */
      data[4 * i + 1] = 255;      /* R */
@@@ -752,9 -698,11 +754,11 @@@ GST_START_TEST (test_convert_frame_asyn
    GMainLoop *loop;
    ConvertFrameContext cf_data = { NULL, NULL, NULL };
  
+   gst_debug_set_threshold_for_name ("default", GST_LEVEL_NONE);
    from_buffer = gst_buffer_new_and_alloc (640 * 480 * 4);
 -  data = GST_BUFFER_DATA (from_buffer);
  
 +  data = gst_buffer_map (from_buffer, NULL, NULL, GST_MAP_WRITE);
    for (i = 0; i < 640 * 480; i++) {
      data[4 * i + 0] = 0;        /* x */
      data[4 * i + 1] = 255;      /* R */
Simple merge
Simple merge