Merge branch 'master' into 0.11
authorWim Taymans <wim.taymans@collabora.co.uk>
Wed, 23 Nov 2011 10:08:39 +0000 (11:08 +0100)
committerWim Taymans <wim.taymans@collabora.co.uk>
Wed, 23 Nov 2011 10:08:39 +0000 (11:08 +0100)
Conflicts:
ext/opus/gstopusdec.c
ext/opus/gstopusenc.c
ext/opus/gstopusparse.c
gst/audiovisualizers/gstwavescope.c
gst/filter/Makefile.am
gst/filter/gstfilter.c
gst/filter/gstiir.c
gst/playondemand/gstplayondemand.c

1  2 
configure.ac
ext/opus/gstopusdec.c
ext/opus/gstopusenc.c
ext/opus/gstopusparse.c
gst-libs/gst/video/gstbasevideodecoder.c
gst-libs/gst/video/gstbasevideoencoder.c
gst/audiovisualizers/Makefile.am
gst/audiovisualizers/gstwavescope.c
tests/check/Makefile.am

diff --cc configure.ac
@@@ -291,24 -291,6 +291,24 @@@ dnl used in example
  AG_GST_DEFAULT_ELEMENTS
  
  dnl *** plug-ins to include ***
-  fieldanalysis freeze frei0r gaudieffects geometrictransform h264parse \
 +dnl Non ported plugins (non-dependant, then dependant)
 +dnl Make sure you have a space before and after all plugins
 +GST_PLUGINS_NONPORTED=" adpcmdec adpcmenc aiff asfmux \
 + autoconvert camerabin cdxaparse coloreffects \
 + dccp debugutils dtmf faceoverlay festival \
-  musepack musicbrainz nas neon ofa openal rsvg schro sdl sndfile soundtouch spandsp timidity \
++ fieldanalysis freeverb freeze frei0r gaudieffects geometrictransform h264parse \
 + hdvparse hls id3tag inter interlace ivfparse jpegformat jp2kdecimator \
 + kate liveadder legacyresample librfb mpegdemux mpegtsmux \
 + mpegpsmux mpegvideoparse mve mxf mythtv nsf nuvdemux \
 + patchdetect pcapparse pnm rawparse real removesilence rtpmux rtpvp8 scaletempo \
 + sdi segmentclip siren speed subenc stereo tta videofilters \
 + videomaxrate videomeasure videoparsers videosignal vmnc \
 + decklink fbdev linsys shm vcd \
 + voaacenc apexsink bz2 camerabin2 cdaudio celt cog curl dc1394 dirac directfb dts resindvd \
 + gsettings gsm jp2k ladspa modplug mpeg2enc mplex mimic \
++ musepack musicbrainz nas neon ofa openal rsvg schro sdl smooth sndfile soundtouch spandsp timidity \
 + wildmidi xvid "
 +AC_SUBST(GST_PLUGINS_NONPORTED)
  
  dnl these are all the gst plug-ins, compilable without additional libs
  AG_GST_CHECK_PLUGIN(adpcmdec)
@@@ -235,25 -255,32 +246,30 @@@ opus_dec_chain_parse_data (GstOpusDec 
      size = 0;
    }
  
-   samples =
-       opus_packet_get_samples_per_frame (data,
-       dec->sample_rate) * opus_packet_get_nb_frames (data, size);
-   GST_DEBUG ("bandwidth %d", opus_packet_get_bandwidth (data));
-   GST_DEBUG ("samples %d", samples);
+   if (data) {
+     samples =
+         opus_packet_get_samples_per_frame (data,
+         dec->sample_rate) * opus_packet_get_nb_frames (data, size);
+     packet_size = samples * dec->n_channels * 2;
+     GST_DEBUG_OBJECT (dec, "bandwidth %d", opus_packet_get_bandwidth (data));
+     GST_DEBUG_OBJECT (dec, "samples %d", samples);
+   } else {
+     /* use maximum size (120 ms) as we do now know in advance how many samples
+        will be returned */
+     samples = 120 * dec->sample_rate / 1000;
+   }
 -  packet_size = samples * dec->n_channels * 2;
 -
 -  res = gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec),
 -      GST_BUFFER_OFFSET_NONE, packet_size,
 -      GST_PAD_CAPS (GST_AUDIO_DECODER_SRC_PAD (dec)), &outbuf);
  
 -  if (res != GST_FLOW_OK) {
 -    GST_DEBUG_OBJECT (dec, "buf alloc flow: %s", gst_flow_get_name (res));
 -    return res;
 +  packet_size = samples * dec->n_channels * 2;
 +  outbuf = gst_buffer_new_and_alloc (packet_size);
 +  if (!outbuf) {
 +    goto buffer_failed;
    }
  
 -  out_data = (gint16 *) GST_BUFFER_DATA (outbuf);
 +  out_data = (gint16 *) gst_buffer_map (outbuf, &out_size, NULL, GST_MAP_WRITE);
  
-   GST_LOG_OBJECT (dec, "decoding %d samples, in size %u", samples, size);
    n = opus_decode (dec->state, data, size, out_data, samples, 0);
 +  gst_buffer_unmap (buf, data, size);
 +  gst_buffer_unmap (outbuf, out_data, out_size);
    if (n < 0) {
      GST_ELEMENT_ERROR (dec, STREAM, DECODE, ("Decoding error: %d", n), (NULL));
      return GST_FLOW_ERROR;
@@@ -330,28 -370,9 +365,13 @@@ memcmp_buffers (GstBuffer * buf1, GstBu
    if (size1 != size2)
      return FALSE;
  
 -  return !memcmp (GST_BUFFER_DATA (buf1), GST_BUFFER_DATA (buf2), size1);
 +  data1 = gst_buffer_map (buf1, NULL, NULL, GST_MAP_READ);
 +  res = gst_buffer_memcmp (buf2, 0, data1, size1) == 0;
 +  gst_buffer_unmap (buf1, data1, size1);
 +
 +  return res;
  }
  
- static gboolean
- gst_opus_dec_is_header (GstBuffer * buf, const char *magic, guint magic_size)
- {
-   guint8 *data;
-   gsize size;
-   gboolean ret;
-   data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
-   if (!data)
-     return FALSE;
-   ret = (size >= magic_size && !memcmp (magic, data, magic_size));
-   gst_buffer_unmap (buf, data, size);
-   return ret;
- }
  static GstFlowReturn
  gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf)
  {
@@@ -80,8 -86,32 +86,33 @@@ gst_opus_enc_bandwidth_get_type (void
    return id;
  }
  
- #define FORMAT_STR GST_AUDIO_NE(S16)
+ #define GST_OPUS_ENC_TYPE_FRAME_SIZE (gst_opus_enc_frame_size_get_type())
+ static GType
+ gst_opus_enc_frame_size_get_type (void)
+ {
+   static const GEnumValue values[] = {
+     {2, "2.5", "2.5"},
+     {5, "5", "5"},
+     {10, "10", "10"},
+     {20, "20", "20"},
+     {40, "40", "40"},
+     {60, "60", "60"},
+     {0, NULL, NULL}
+   };
+   static volatile GType id = 0;
  
+   if (g_once_init_enter ((gsize *) & id)) {
+     GType _id;
+     _id = g_enum_register_static ("GstOpusEncFrameSize", values);
+     g_once_init_leave ((gsize *) & id, _id);
+   }
+   return id;
+ }
++#define FORMAT_STR GST_AUDIO_NE(S16)
  static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
      GST_PAD_SINK,
      GST_PAD_ALWAYS,
@@@ -224,11 -282,16 +263,18 @@@ gst_opus_enc_class_init (GstOpusEncClas
        PROP_PACKET_LOSS_PERCENT, g_param_spec_int ("packet-loss-percentage",
            "Loss percentage", "Packet loss percentage", 0, 100,
            DEFAULT_PACKET_LOSS_PERCENT,
-           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+           GST_PARAM_MUTABLE_PLAYING));
+   g_object_class_install_property (G_OBJECT_CLASS (klass),
+       PROP_MAX_PAYLOAD_SIZE, g_param_spec_uint ("max-payload-size",
+           "Max payload size", "Maximum payload size in bytes", 2, 1275,
+           DEFAULT_MAX_PAYLOAD_SIZE,
+           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+           GST_PARAM_MUTABLE_PLAYING));
  
    gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_opus_enc_finalize);
 +
 +  GST_DEBUG_CATEGORY_INIT (opusenc_debug, "opusenc", 0, "Opus encoder");
  }
  
  static void
@@@ -525,47 -519,15 +503,14 @@@ gst_opus_enc_encode (GstOpusEnc * enc, 
  {
    guint8 *bdata, *data, *mdata = NULL;
    gsize bsize, size;
 -  gsize bytes;
 +  gsize bytes = enc->frame_samples * enc->n_channels * 2;
-   gsize bytes_per_packet =
-       (enc->bitrate * enc->frame_samples / enc->sample_rate + 4) / 8;
    gint ret = GST_FLOW_OK;
  
-   if (G_LIKELY (buf)) {
-     bdata = GST_BUFFER_DATA (buf);
-     bsize = GST_BUFFER_SIZE (buf);
-     if (G_UNLIKELY (bsize % bytes)) {
-       GST_DEBUG_OBJECT (enc, "draining; adding silence samples");
-       size = ((bsize / bytes) + 1) * bytes;
-       mdata = g_malloc0 (size);
-       memcpy (mdata, bdata, bsize);
-       bdata = NULL;
-       data = mdata;
-     } else {
-       data = bdata;
-       size = bsize;
-     }
-   } else {
-     GST_DEBUG_OBJECT (enc, "nothing to drain");
-     goto done;
-   }
-   return ret;
- }
- static GstFlowReturn
- gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf)
- {
-   guint8 *bdata, *data, *mdata = NULL;
-   gsize bsize, size;
-   gsize bytes = enc->frame_samples * enc->n_channels * 2;
-   gsize bytes_per_packet =
-       (enc->bitrate * enc->frame_samples / enc->sample_rate + 4) / 8;
-   gint ret = GST_FLOW_OK;
+   g_mutex_lock (enc->property_lock);
  
 -  bytes = enc->frame_samples * enc->n_channels * 2;
    if (G_LIKELY (buf)) {
 -    bdata = GST_BUFFER_DATA (buf);
 -    bsize = GST_BUFFER_SIZE (buf);
 +    bdata = gst_buffer_map (buf, &bsize, NULL, GST_MAP_READ);
 +
      if (G_UNLIKELY (bsize % bytes)) {
        GST_DEBUG_OBJECT (enc, "draining; adding silence samples");
  
      goto done;
    }
  
 -
    while (size) {
 -    gint outsize;
 +    gint encoded_size;
 +    unsigned char *out_data;
 +    gsize out_size;
      GstBuffer *outbuf;
  
-     outbuf = gst_buffer_new_and_alloc (bytes_per_packet);
 -    ret = gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc),
 -        GST_BUFFER_OFFSET_NONE, enc->max_payload_size,
 -        GST_PAD_CAPS (GST_AUDIO_ENCODER_SRC_PAD (enc)), &outbuf);
 -
 -    if (GST_FLOW_OK != ret)
++    outbuf = gst_buffer_new_and_alloc (enc->max_payload_size);
 +    if (!outbuf)
        goto done;
  
-     GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes) to %d bytes",
-         enc->frame_samples, bytes, bytes_per_packet);
+     GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)",
+         enc->frame_samples);
  
 -    outsize =
 +    out_data = gst_buffer_map (outbuf, &out_size, NULL, GST_MAP_WRITE);
 +    encoded_size =
          opus_encode (enc->state, (const gint16 *) data, enc->frame_samples,
-         out_data, bytes_per_packet);
 -        GST_BUFFER_DATA (outbuf), enc->max_payload_size);
++        out_data, enc->max_payload_size);
 +    gst_buffer_unmap (outbuf, out_data, out_size);
  
 -    if (outsize < 0) {
 -      GST_ERROR_OBJECT (enc, "Encoding failed: %d", outsize);
 +    if (encoded_size < 0) {
 +      GST_ERROR_OBJECT (enc, "Encoding failed: %d", encoded_size);
        ret = GST_FLOW_ERROR;
        goto done;
-     } else if (encoded_size != bytes_per_packet) {
 -    } else if (outsize > enc->max_payload_size) {
++    } else if (encoded_size > enc->max_payload_size) {
        GST_WARNING_OBJECT (enc,
-           "Encoded size %d is different from %d bytes per packet", encoded_size,
-           bytes_per_packet);
+           "Encoded size %d is higher than max payload size (%d bytes)",
+           outsize, enc->max_payload_size);
        ret = GST_FLOW_ERROR;
        goto done;
      }
  
  done:
  
 +  if (bdata)
 +    gst_buffer_unmap (buf, bdata, bsize);
+   g_mutex_unlock (enc->property_lock);
    if (mdata)
      g_free (mdata);
  
@@@ -100,12 -107,23 +107,27 @@@ static gboolea
  gst_opus_parse_start (GstBaseParse * base)
  {
    GstOpusParse *parse = GST_OPUS_PARSE (base);
-   GstCaps *caps;
  
 +  caps = gst_caps_new_empty_simple ("audio/x-opus");
 +  gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (GST_BASE_PARSE (parse)), caps);
 +  gst_caps_unref (caps);
 +
+   parse->header_sent = FALSE;
+   parse->next_ts = 0;
+   return TRUE;
+ }
+ static gboolean
+ gst_opus_parse_stop (GstBaseParse * base)
+ {
+   GstOpusParse *parse = GST_OPUS_PARSE (base);
+   g_slist_foreach (parse->headers, (GFunc) gst_buffer_unref, NULL);
+   parse->headers = NULL;
+   parse->header_sent = FALSE;
    return TRUE;
  }
  
@@@ -122,37 -147,173 +151,173 @@@ gst_opus_parse_check_valid_frame (GstBa
  
    parse = GST_OPUS_PARSE (base);
  
 -  data = GST_BUFFER_DATA (frame->buffer);
 -  size = GST_BUFFER_SIZE (frame->buffer);
 +  data = gst_buffer_map (frame->buffer, &size, NULL, GST_MAP_READ);
    GST_DEBUG_OBJECT (parse, "Checking for frame, %u bytes in buffer", size);
-   if (size < 4) {
-     GST_DEBUG_OBJECT (parse, "Too small");
-     goto beach;
-   }
-   packet_size = GST_READ_UINT32_BE (data);
-   GST_DEBUG_OBJECT (parse, "Packet size: %u bytes", packet_size);
-   if (packet_size > MAX_PAYLOAD_BYTES) {
-     GST_DEBUG_OBJECT (parse, "Too large");
-     goto beach;
+   /* check for headers */
+   is_idheader = gst_opus_header_is_header (frame->buffer, "OpusHead", 8);
+   is_commentheader = gst_opus_header_is_header (frame->buffer, "OpusTags", 8);
+   is_header = is_idheader || is_commentheader;
+   if (!is_header) {
+     /* Next, check if there's an Opus packet there */
+     nframes =
+         opus_packet_parse (data, size, &toc, frames, frame_sizes,
+         &payload_offset);
    }
-   if (packet_size > size - 4) {
-     GST_DEBUG_OBJECT (parse, "Truncated");
-     goto beach;
+   if (!is_header && nframes < 0) {
+     /* Then, check for the test vector framing */
+     GST_DEBUG_OBJECT (parse,
+         "No Opus packet found, trying test vector framing");
+     if (size < 4) {
+       GST_DEBUG_OBJECT (parse, "Too small");
+       goto beach;
+     }
+     packet_size = GST_READ_UINT32_BE (data);
+     GST_DEBUG_OBJECT (parse, "Packet size: %u bytes", packet_size);
+     if (packet_size > MAX_PAYLOAD_BYTES) {
+       GST_DEBUG_OBJECT (parse, "Too large");
+       goto beach;
+     }
+     if (packet_size > size - 4) {
+       GST_DEBUG_OBJECT (parse, "Truncated");
+       goto beach;
+     }
+     nframes =
+         opus_packet_parse (data + 8, packet_size, &toc, frames, frame_sizes,
+         &payload_offset);
+     if (nframes < 0) {
+       GST_DEBUG_OBJECT (parse, "No test vector framing either");
+       goto beach;
+     }
+     packet_offset = 8;
+     data += packet_offset;
    }
  
-   channels = opus_packet_get_nb_channels (data + 8);
-   bandwidth = opus_packet_get_bandwidth (data + 8);
-   if (channels < 0 || bandwidth < 0) {
-     GST_DEBUG_OBJECT (parse, "It looked like a packet, but it is not");
-     goto beach;
+   if (!parse->header_sent) {
+     GstCaps *caps;
+     /* Opus streams can decode to 1 or 2 channels, so use the header
+        value if we have one, or 2 otherwise */
+     if (is_idheader) {
+       channels = data[9];
+     } else {
+       channels = 2;
+     }
+     g_slist_foreach (parse->headers, (GFunc) gst_buffer_unref, NULL);
+     parse->headers = NULL;
+     gst_opus_header_create_caps (&caps, &parse->headers, channels, 0, NULL);
+     gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps);
+     parse->header_sent = TRUE;
    }
  
-   GST_DEBUG_OBJECT (parse, "Got Opus packet, %d bytes");
+   if (is_header) {
+     *skip = 0;
+     *frame_size = size;
+   } else {
+     *skip = packet_offset;
+     *frame_size = payload_offset;
+   }
  
-   *skip = 8;
-   *frame_size = packet_size;
+   GST_DEBUG_OBJECT (parse, "Got Opus packet at offset %d, %d bytes", *skip,
+       *frame_size);
    ret = TRUE;
  
  beach:
 +  gst_buffer_unmap (frame->buffer, data, size);
    return ret;
  }
+ /* Adapted copy of the one in gstoggstream.c... */
+ static guint64
+ packet_duration_opus (const guint8 * data, size_t len)
+ {
+   static const guint64 durations[32] = {
+     10000, 20000, 40000, 60000, /* Silk NB */
+     10000, 20000, 40000, 60000, /* Silk MB */
+     10000, 20000, 40000, 60000, /* Silk WB */
+     10000, 20000,               /* Hybrid SWB */
+     10000, 20000,               /* Hybrid FB */
+     2500, 5000, 10000, 20000,   /* CELT NB */
+     2500, 5000, 10000, 20000,   /* CELT NB */
+     2500, 5000, 10000, 20000,   /* CELT NB */
+     2500, 5000, 10000, 20000,   /* CELT NB */
+   };
+   gint64 duration;
+   gint64 frame_duration;
+   gint nframes;
+   guint8 toc;
+   if (len < 1)
+     return 0;
+   toc = data[0];
+   frame_duration = durations[toc >> 3] * 1000;
+   switch (toc & 3) {
+     case 0:
+       nframes = 1;
+       break;
+     case 1:
+       nframes = 2;
+       break;
+     case 2:
+       nframes = 2;
+       break;
+     case 3:
+       if (len < 2) {
+         GST_WARNING ("Code 3 Opus packet has less than 2 bytes");
+         return 0;
+       }
+       nframes = data[1] & 63;
+       break;
+   }
+   duration = nframes * frame_duration;
+   if (duration > 120 * GST_MSECOND) {
+     GST_WARNING ("Opus packet duration > 120 ms, invalid");
+     return 0;
+   }
+   GST_LOG ("Opus packet: frame size %.1f ms, %d frames, duration %.1f ms",
+       frame_duration / 1000000.f, nframes, duration / 1000000.f);
+   return duration;
+ }
+ static GstFlowReturn
+ gst_opus_parse_parse_frame (GstBaseParse * base, GstBaseParseFrame * frame)
+ {
+   guint64 duration;
+   GstOpusParse *parse;
+   parse = GST_OPUS_PARSE (base);
+   if (gst_opus_header_is_header (frame->buffer, "OpusHead", 8)
+       || gst_opus_header_is_header (frame->buffer, "OpusTags", 8)) {
+     GST_BUFFER_TIMESTAMP (frame->buffer) = 0;
+     GST_BUFFER_DURATION (frame->buffer) = GST_CLOCK_TIME_NONE;
+     GST_BUFFER_OFFSET_END (frame->buffer) = GST_CLOCK_TIME_NONE;
+     GST_BUFFER_OFFSET (frame->buffer) = GST_CLOCK_TIME_NONE;
+     return GST_FLOW_OK;
+   }
+   GST_BUFFER_TIMESTAMP (frame->buffer) = parse->next_ts;
+   duration =
+       packet_duration_opus (GST_BUFFER_DATA (frame->buffer),
+       GST_BUFFER_SIZE (frame->buffer));
+   parse->next_ts += duration;
+   GST_BUFFER_DURATION (frame->buffer) = duration;
+   GST_BUFFER_OFFSET_END (frame->buffer) =
+       gst_util_uint64_scale (parse->next_ts, 48000, GST_SECOND);
+   GST_BUFFER_OFFSET (frame->buffer) = parse->next_ts;
+   return GST_FLOW_OK;
+ }
@@@ -1386,14 -1402,10 +1386,10 @@@ gst_base_video_decoder_finish_frame (Gs
  
    /* Push all pending events that arrived before this frame */
    for (l = base_video_decoder->base_video_codec.frames; l; l = l->next) {
 -    GstVideoFrame *tmp = l->data;
 +    GstVideoFrameState *tmp = l->data;
  
      if (tmp->events) {
-       GList *k;
-       for (k = g_list_last (tmp->events); k; k = k->prev)
-         events = g_list_prepend (events, k->data);
-       g_list_free (tmp->events);
+       events = tmp->events;
        tmp->events = NULL;
      }
  
@@@ -2052,10 -2025,10 +2048,10 @@@ gst_base_video_decoder_alloc_src_frame 
  /**
   * gst_base_video_decoder_get_max_decode_time:
   * @base_video_decoder: a #GstBaseVideoDecoder
 - * @frame: a #GstVideoFrame
 + * @frame: a #GstVideoFrameState
   *
   * Determines maximum possible decoding time for @frame that will
-  * allow it to decode and arrive in time (as determined by QoS messages).
+  * allow it to decode and arrive in time (as determined by QoS events).
   * In particular, a negative result means decoding in time is no longer possible
   * and should therefore occur as soon/skippy as possible.
   *
Simple merge
@@@ -61,29 -56,86 +62,79 @@@ GST_STATIC_PAD_TEMPLATE ("sink"
  GST_DEBUG_CATEGORY_STATIC (wave_scope_debug);
  #define GST_CAT_DEFAULT wave_scope_debug
  
- static gboolean gst_wave_scope_render (GstBaseAudioVisualizer * scope,
-     GstBuffer * audio, GstBuffer * video);
+ enum
+ {
+   PROP_0,
+   PROP_STYLE
+ };
+ enum
+ {
+   STYLE_DOTS = 0,
+   STYLE_LINES,
+   NUM_STYLES
+ };
+ #define GST_TYPE_WAVE_SCOPE_STYLE (gst_wave_scope_style_get_type ())
+ static GType
+ gst_wave_scope_style_get_type (void)
+ {
+   static GType gtype = 0;
+   if (gtype == 0) {
+     static const GEnumValue values[] = {
+       {STYLE_DOTS, "draw dots (default)", "dots"},
+       {STYLE_LINES, "draw lines", "lines"},
+       {0, NULL, NULL}
+     };
  
+     gtype = g_enum_register_static ("GstWaveScopeStyle", values);
+   }
+   return gtype;
+ }
+ static void gst_wave_scope_set_property (GObject * object, guint prop_id,
+     const GValue * value, GParamSpec * pspec);
+ static void gst_wave_scope_get_property (GObject * object, guint prop_id,
+     GValue * value, GParamSpec * pspec);
+ static void render_dots (GstBaseAudioVisualizer * scope, guint32 * vdata,
+     gint16 * adata, guint num_samples);
+ static void render_lines (GstBaseAudioVisualizer * scope, guint32 * vdata,
+     gint16 * adata, guint num_samples);
+ static gboolean gst_wave_scope_render (GstBaseAudioVisualizer * base,
+     GstBuffer * audio, GstBuffer * video);
  
 -
 -GST_BOILERPLATE (GstWaveScope, gst_wave_scope, GstBaseAudioVisualizer,
 -    GST_TYPE_BASE_AUDIO_VISUALIZER);
 -
 -static void
 -gst_wave_scope_base_init (gpointer g_class)
 -{
 -  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
 -
 -  gst_element_class_set_details_simple (element_class, "Waveform oscilloscope",
 -      "Visualization",
 -      "Simple waveform oscilloscope", "Stefan Kost <ensonic@users.sf.net>");
 -
 -  gst_element_class_add_pad_template (element_class,
 -      gst_static_pad_template_get (&gst_wave_scope_src_template));
 -  gst_element_class_add_pad_template (element_class,
 -      gst_static_pad_template_get (&gst_wave_scope_sink_template));
 -}
 +G_DEFINE_TYPE (GstWaveScope, gst_wave_scope, GST_TYPE_BASE_AUDIO_VISUALIZER);
  
  static void
  gst_wave_scope_class_init (GstWaveScopeClass * g_class)
  {
-   GstElementClass *element_class = (GstElementClass *) g_class;
+   GObjectClass *gobject_class = (GObjectClass *) g_class;
++  GstElementClass *gstelement_class = (GstElementClass *) g_class;
    GstBaseAudioVisualizerClass *scope_class =
        (GstBaseAudioVisualizerClass *) g_class;
  
-   gst_element_class_set_details_simple (element_class, "Waveform oscilloscope",
-       "Visualization",
-       "Simple waveform oscilloscope", "Stefan Kost <ensonic@users.sf.net>");
+   gobject_class->set_property = gst_wave_scope_set_property;
+   gobject_class->get_property = gst_wave_scope_get_property;
 -  scope_class->render = GST_DEBUG_FUNCPTR (gst_wave_scope_render);
 -
+   g_object_class_install_property (gobject_class, PROP_STYLE,
+       g_param_spec_enum ("style", "drawing style",
+           "Drawing styles for the wave form display.",
+           GST_TYPE_WAVE_SCOPE_STYLE, STYLE_DOTS,
+           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 +
-   gst_element_class_add_pad_template (element_class,
++  gst_element_class_set_details_simple (gstelement_class,
++      "Waveform oscilloscope", "Visualization", "Simple waveform oscilloscope",
++      "Stefan Kost <ensonic@users.sf.net>");
++
++  gst_element_class_add_pad_template (gstelement_class,
 +      gst_static_pad_template_get (&gst_wave_scope_src_template));
-   gst_element_class_add_pad_template (element_class,
++  gst_element_class_add_pad_template (gstelement_class,
 +      gst_static_pad_template_get (&gst_wave_scope_sink_template));
 +
 +  scope_class->render = GST_DEBUG_FUNCPTR (gst_wave_scope_render);
  }
  
  static void
@@@ -106,21 -196,60 +195,68 @@@ render_dots (GstBaseAudioVisualizer * s
    guint w = scope->width;
  
    /* draw dots */
-   num_samples = asize / (scope->channels * sizeof (gint16));
-   dx = (gfloat) scope->width / (gfloat) num_samples;
+   dx = (gfloat) w / (gfloat) num_samples;
    dy = scope->height / 65536.0;
    oy = scope->height / 2;
-   s = 0;
-   for (i = 0; i < num_samples; i++) {
-     x = (guint) ((gfloat) i * dx);
-     for (c = 0; c < scope->channels; c++) {
-       y = (guint) (oy + (gfloat) adata[s++] * dy);
-       off = (y * w) + x;
-       vdata[off] = 0x00FFFFFF;
+   for (c = 0; c < channels; c++) {
+     s = c;
+     for (i = 0; i < num_samples; i++) {
+       x = (guint) ((gfloat) i * dx);
+       y = (guint) (oy + (gfloat) adata[s] * dy);
+       s += channels;
+       draw_dot (vdata, x, y, w, 0x00FFFFFF);
      }
    }
 -  guint32 *vdata = (guint32 *) GST_BUFFER_DATA (video);
 -  gint16 *adata = (gint16 *) GST_BUFFER_DATA (audio);
+ }
+ static void
+ render_lines (GstBaseAudioVisualizer * scope, guint32 * vdata, gint16 * adata,
+     guint num_samples)
+ {
+   gint channels = scope->channels;
+   guint i, c, s, x, y, oy;
+   gfloat dx, dy;
+   guint w = scope->width;
+   gint x2, y2;
+   /* draw lines */
+   dx = (gfloat) w / (gfloat) num_samples;
+   dy = scope->height / 65536.0;
+   oy = scope->height / 2;
+   for (c = 0; c < channels; c++) {
+     s = c;
+     x2 = 0;
+     y2 = (guint) (oy + (gfloat) adata[s] * dy);
+     for (i = 1; i < num_samples; i++) {
+       x = (guint) ((gfloat) i * dx);
+       y = (guint) (oy + (gfloat) adata[s] * dy);
+       s += channels;
+       draw_line (vdata, x2, x, y2, y, w, 0x00FFFFFF);
+       x2 = x;
+       y2 = y;
+     }
+   }
+ }
+ static gboolean
+ gst_wave_scope_render (GstBaseAudioVisualizer * base, GstBuffer * audio,
+     GstBuffer * video)
+ {
+   GstWaveScope *scope = GST_WAVE_SCOPE (base);
 -  num_samples = GST_BUFFER_SIZE (audio) / (base->channels * sizeof (gint16));
++  guint32 *vdata;
++  gsize asize;
++  gint16 *adata;
+   guint num_samples;
++  adata = gst_buffer_map (audio, &asize, NULL, GST_MAP_READ);
++  vdata = gst_buffer_map (video, NULL, NULL, GST_MAP_WRITE);
++
++  num_samples = asize / (base->channels * sizeof (gint16));
+   scope->process (base, vdata, adata, num_samples);
++
 +  gst_buffer_unmap (video, vdata, -1);
 +  gst_buffer_unmap (audio, adata, -1);
++
    return TRUE;
  }
  
Simple merge