<commit_before>/* GStreamer
 * Copyright (C) <1999> <NAME> <<EMAIL>>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gst/gstutils.h>
#include <gst/riff/riff-ids.h>
#include <string.h>

#include "gstasfdemux.h"
#include "asfheaders.h"

static GstStaticPadTemplate gst_asf_demux_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-ms-asf")
    );

GST_DEBUG_CATEGORY_STATIC (asf_debug);
#define GST_CAT_DEFAULT asf_debug

static void gst_asf_demux_base_init (gpointer g_class);
static void gst_asf_demux_class_init (GstASFDemuxClass * klass);
static void gst_asf_demux_init (GstASFDemux * asf_demux);
static gboolean gst_asf_demux_send_event (GstElement * element,
    GstEvent * event);
static void gst_asf_demux_loop (GstElement * element);
static gboolean gst_asf_demux_process_object (GstASFDemux * asf_demux);
static void gst_asf_demux_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec);
static guint32 gst_asf_demux_identify_guid (GstASFDemux * asf_demux,
    ASFGuidHash * guids, ASFGuid * guid_raw);
static gboolean gst_asf_demux_process_chunk (GstASFDemux * asf_demux,
    asf_packet_info * packet_info, asf_segment_info * segment_info);
static const GstEventMask *gst_asf_demux_get_src_event_mask (GstPad * pad);
static gboolean gst_asf_demux_handle_sink_event (GstASFDemux * asf_demux,
    GstEvent * event, guint32 remaining);
static gboolean gst_asf_demux_handle_src_event (GstPad * pad, GstEvent * event);
static const GstFormat *gst_asf_demux_get_src_formats (GstPad * pad);
static const GstQueryType *gst_asf_demux_get_src_query_types (GstPad * pad);
static gboolean gst_asf_demux_handle_src_query (GstPad * pad,
    GstQueryType type, GstFormat * format, gint64 * value);
static gboolean gst_asf_demux_add_video_stream (GstASFDemux * asf_demux,
    asf_stream_video_format * video_format, guint16 id);
static gboolean gst_asf_demux_add_audio_stream (GstASFDemux * asf_demux,
    asf_stream_audio * audio, guint16 id);
static gboolean gst_asf_demux_setup_pad (GstASFDemux * asf_demux,
    GstPad * src_pad, GstCaps * caps, guint16 id);

static GstElementStateReturn gst_asf_demux_change_state (GstElement * element);
static GstCaps *gst_asf_demux_video_caps (guint32 codec_fcc,
    asf_stream_video_format * video, char **codec_name);
static GstCaps *gst_asf_demux_audio_caps (guint16 codec_id,
    asf_stream_audio * audio, guint8 * extradata, char **codec_name);

static GstPadTemplate *videosrctempl, *audiosrctempl;
static GstElementClass *parent_class = NULL;

GType
gst_asf_demux_get_type (void)
{
  static GType asf_demux_type = 0;

  if (!asf_demux_type) {
    static const GTypeInfo asf_demux_info = {
      sizeof (GstASFDemuxClass),
      gst_asf_demux_base_init,
      NULL,
      (GClassInitFunc) gst_asf_demux_class_init,
      NULL,
      NULL,
      sizeof (GstASFDemux),
      0,
      (GInstanceInitFunc) gst_asf_demux_init,
    };

    asf_demux_type =
        g_type_register_static (GST_TYPE_ELEMENT, "GstASFDemux",
        &asf_demux_info, 0);

    GST_DEBUG_CATEGORY_INIT (asf_debug, "asfdemux", 0, "asf demuxer element");
  }
  return asf_demux_type;
}

static void
gst_asf_demux_base_init (gpointer g_class)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
  static GstElementDetails gst_asf_demux_details = {
    "ASF Demuxer",
    "Codec/Demuxer",
    "Demultiplexes ASF Streams",
    "<NAME> <<EMAIL>>"
  };
  int i;
  GstCaps *audcaps, *vidcaps, *temp;
  guint32 vid_list[] = {
    GST_MAKE_FOURCC ('I', '4', '2', '0'),
    GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'),
    GST_MAKE_FOURCC ('M', 'J', 'P', 'G'),
    GST_MAKE_FOURCC ('D', 'V', 'S', 'D'),
    GST_MAKE_FOURCC ('W', 'M', 'V', '1'),
    GST_MAKE_FOURCC ('W', 'M', 'V', '2'),
    GST_MAKE_FOURCC ('M', 'P', 'G', '4'),
    GST_MAKE_FOURCC ('M', 'P', '4', '2'),
    GST_MAKE_FOURCC ('M', 'P', '4', '3'),
    GST_MAKE_FOURCC ('D', 'I', 'V', '3'),
    GST_MAKE_FOURCC ('D', 'X', '5', '0'),
    0                           /* end */
  };
  gint aud_list[] = {
    GST_RIFF_WAVE_FORMAT_MPEGL3,
    GST_RIFF_WAVE_FORMAT_MPEGL12,
    GST_RIFF_WAVE_FORMAT_PCM,
    GST_RIFF_WAVE_FORMAT_VORBIS1,
    GST_RIFF_WAVE_FORMAT_A52,
    GST_RIFF_WAVE_FORMAT_DIVX_WMAV1,
    GST_RIFF_WAVE_FORMAT_DIVX_WMAV2,
    GST_RIFF_WAVE_FORMAT_WMAV9,
    -1                          /* end */
  };

  audcaps = gst_caps_new_empty ();
  for (i = 0; aud_list[i] != -1; i++) {
    temp = gst_asf_demux_audio_caps (aud_list[i], NULL, NULL, NULL);
    gst_caps_append (audcaps, temp);
  }

  audiosrctempl = gst_pad_template_new ("audio_%02d",
      GST_PAD_SRC, GST_PAD_SOMETIMES, audcaps);

  vidcaps = gst_caps_new_empty ();
  for (i = 0; vid_list[i] != 0; i++) {
    temp = gst_asf_demux_video_caps (vid_list[i], NULL, NULL);
    gst_caps_append (vidcaps, temp);
  }

  videosrctempl = gst_pad_template_new ("video_%02d",
      GST_PAD_SRC, GST_PAD_SOMETIMES, vidcaps);
  gst_element_class_add_pad_template (element_class, audiosrctempl);
  gst_element_class_add_pad_template (element_class, videosrctempl);
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&gst_asf_demux_sink_template));
  gst_element_class_set_details (element_class, &gst_asf_demux_details);
}

static void
gst_asf_demux_class_init (GstASFDemuxClass * klass)
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;

  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);

  gobject_class->get_property = gst_asf_demux_get_property;

  gstelement_class->change_state = gst_asf_demux_change_state;
  gstelement_class->send_event = gst_asf_demux_send_event;
}

static void
gst_asf_demux_init (GstASFDemux * asf_demux)
{
  guint i;

  asf_demux->sinkpad =
      gst_pad_new_from_template (gst_static_pad_template_get
      (&gst_asf_demux_sink_template), "sink");
  gst_element_add_pad (GST_ELEMENT (asf_demux), asf_demux->sinkpad);

  gst_element_set_loop_function (GST_ELEMENT (asf_demux), gst_asf_demux_loop);

  /* We should zero everything to be on the safe side */
  for (i = 0; i < GST_ASF_DEMUX_NUM_VIDEO_PADS; i++) {
    asf_demux->video_pad[i] = NULL;
    asf_demux->video_PTS[i] = 0;
  }

  for (i = 0; i < GST_ASF_DEMUX_NUM_AUDIO_PADS; i++) {
    asf_demux->audio_pad[i] = NULL;
    asf_demux->audio_PTS[i] = 0;
  }

  asf_demux->num_audio_streams = 0;
  asf_demux->num_video_streams = 0;
  asf_demux->num_streams = 0;

  GST_FLAG_SET (asf_demux, GST_ELEMENT_EVENT_AWARE);
}

static gboolean
gst_asf_demux_send_event (GstElement * element, GstEvent * event)
{
  const GList *pads;

  pads = gst_element_get_pad_list (element);

  while (pads) {
    GstPad *pad = GST_PAD (pads->data);

    if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
      /* we ref the event here as we might have to try again if the event
       * failed on this pad */
      gst_event_ref (event);
      if (gst_asf_demux_handle_src_event (pad, event)) {
        gst_event_unref (event);
        return TRUE;
      }
    }

    pads = g_list_next (pads);
  }

  gst_event_unref (event);
  return FALSE;
}

static void
gst_asf_demux_loop (GstElement * element)
{
  GstASFDemux *asf_demux;

  g_return_if_fail (element != NULL);
  g_return_if_fail (GST_IS_ASF_DEMUX (element));

  asf_demux = GST_ASF_DEMUX (element);

  /* this is basically an infinite loop */
  gst_asf_demux_process_object (asf_demux);
}

static guint32
_read_var_length (GstASFDemux * asf_demux, guint8 type, guint32 * rsize)
{
  guint32 got_bytes;
  guint8 *var;
  guint32 ret = 0;
  GstByteStream *bs = asf_demux->bs;

  if (type == 0) {
    return 0;
  }

  got_bytes = gst_bytestream_peek_bytes (bs, &var, 4);

  while (got_bytes < 4) {
    guint32 remaining;
    GstEvent *event;

    gst_bytestream_get_status (bs, &remaining, &event);
    gst_event_unref (event);

    got_bytes = gst_bytestream_peek_bytes (bs, &var, 4);
  }

  switch (type) {
    case 1:
      ret = (GST_READ_UINT32_LE (var)) & 0xff;
      gst_bytestream_flush (bs, 1);
      *rsize += 1;
      break;
    case 2:
      ret = (GST_READ_UINT32_LE (var)) & 0xffff;
      gst_bytestream_flush (bs, 2);
      *rsize += 2;
      break;
    case 3:
      ret = GST_READ_UINT32_LE (var);
      gst_bytestream_flush (bs, 4);
      *rsize += 4;
      break;
  }

  return ret;
}

#define READ_UINT_BITS_FUNCTION(bits)						\
static gboolean									\
_read_uint ## bits (GstASFDemux *asf_demux, guint ## bits *ret)	        	\
{										\
  GstEvent *event;								\
  guint32 remaining;								\
  guint8* data;									\
										\
  g_return_val_if_fail (ret != NULL, FALSE);					\
										\
  do {										\
    if (gst_bytestream_peek_bytes (asf_demux->bs, &data, bits / 8) == bits / 8) { \
      *ret = GST_READ_UINT ## bits ## _LE (data);				\
      gst_bytestream_flush (asf_demux->bs, bits / 8);				\
      return TRUE;								\
    }										\
    gst_bytestream_get_status (asf_demux->bs, &remaining, &event);		\
  } while (gst_asf_demux_handle_sink_event (asf_demux, event, remaining));	\
										\
  return FALSE;									\
}

#define GST_READ_UINT8_LE(x) GST_READ_UINT8(x)
READ_UINT_BITS_FUNCTION (8)
    READ_UINT_BITS_FUNCTION (16)
    READ_UINT_BITS_FUNCTION (32)
    READ_UINT_BITS_FUNCTION (64)
#define GET_UINT(a,b)
     static gboolean _read_guid (GstASFDemux * asf_demux, ASFGuid * guid)
{
  return (_read_uint32 (asf_demux, &guid->v1) &&
      _read_uint32 (asf_demux, &guid->v2) &&
      _read_uint32 (asf_demux, &guid->v3) &&
      _read_uint32 (asf_demux, &guid->v4));
}

static gboolean
_read_obj_file (GstASFDemux * asf_demux, asf_obj_file * object)
{
  return (_read_guid (asf_demux, &object->file_id) &&
      _read_uint64 (asf_demux, &object->file_size) &&
      _read_uint64 (asf_demux, &object->creation_time) &&
      _read_uint64 (asf_demux, &object->packets_count) &&
      _read_uint64 (asf_demux, &object->play_time) &&
      _read_uint64 (asf_demux, &object->send_time) &&
      _read_uint64 (asf_demux, &object->preroll) &&
      _read_uint32 (asf_demux, &object->flags) &&
      _read_uint32 (asf_demux, &object->min_pktsize) &&
      _read_uint32 (asf_demux, &object->max_pktsize) &&
      _read_uint32 (asf_demux, &object->min_bitrate));
}

static gboolean
_read_bitrate_record (GstASFDemux * asf_demux, asf_bitrate_record * record)
{
  return (_read_uint16 (asf_demux, &record->stream_id) &&
      _read_uint32 (asf_demux, &record->bitrate));
}

static gboolean
_read_obj_comment (GstASFDemux * asf_demux, asf_obj_comment * comment)
{
  return (_read_uint16 (asf_demux, &comment->title_length) &&
      _read_uint16 (asf_demux, &comment->author_length) &&
      _read_uint16 (asf_demux, &comment->copyright_length) &&
      _read_uint16 (asf_demux, &comment->description_length) &&
      _read_uint16 (asf_demux, &comment->rating_length));
}

static gboolean
_read_obj_header (GstASFDemux * asf_demux, asf_obj_header * header)
{
  return (_read_uint32 (asf_demux, &header->num_objects) &&
      _read_uint8 (asf_demux, &header->unknown1) &&
      _read_uint8 (asf_demux, &header->unknown2));
}

static gboolean
_read_obj_stream (GstASFDemux * asf_demux, asf_obj_stream * stream)
{
  return (_read_guid (asf_demux, &stream->type) &&
      _read_guid (asf_demux, &stream->correction) &&
      _read_uint64 (asf_demux, &stream->unknown1) &&
      _read_uint32 (asf_demux, &stream->type_specific_size) &&
      _read_uint32 (asf_demux, &stream->stream_specific_size) &&
      _read_uint16 (asf_demux, &stream->id) &&
      _read_uint32 (asf_demux, &stream->unknown2));
}

static gboolean
_read_replicated_data (GstASFDemux * asf_demux, asf_replicated_data * rep)
{
  return (_read_uint32 (asf_demux, &rep->object_size) &&
      _read_uint32 (asf_demux, &rep->frag_timestamp));
}

static gboolean
_read_obj_data (GstASFDemux * asf_demux, asf_obj_data * object)
{
  return (_read_guid (asf_demux, &object->file_id) &&
      _read_uint64 (asf_demux, &object->packets) &&
      _read_uint8 (asf_demux, &object->unknown1) &&
          /*_read_uint8 (asf_demux, &object->unknown2) && */
      _read_uint8 (asf_demux, &object->correction));
}

static gboolean
_read_obj_data_correction (GstASFDemux * asf_demux,
    asf_obj_data_correction * object)
{
  return (_read_uint8 (asf_demux, &object->type) &&
      _read_uint8 (asf_demux, &object->cycle));
}

static gboolean
_read_obj_data_packet (GstASFDemux * asf_demux, asf_obj_data_packet * object)
{
  return (_read_uint8 (asf_demux, &object->flags) &&
      _read_uint8 (asf_demux, &object->property));
}

static gboolean
_read_stream_audio (GstASFDemux * asf_demux, asf_stream_audio * audio)
{
  return (_read_uint16 (asf_demux, &audio->codec_tag) &&
      _read_uint16 (asf_demux, &audio->channels) &&
      _read_uint32 (asf_demux, &audio->sample_rate) &&
      _read_uint32 (asf_demux, &audio->byte_rate) &&
      _read_uint16 (asf_demux, &audio->block_align) &&
      _read_uint16 (asf_demux, &audio->word_size) &&
      _read_uint16 (asf_demux, &audio->size));
}

static gboolean
_read_stream_correction (GstASFDemux * asf_demux,
    asf_stream_correction * object)
{
  return (_read_uint8 (asf_demux, &object->span) &&
      _read_uint16 (asf_demux, &object->packet_size) &&
      _read_uint16 (asf_demux, &object->chunk_size) &&
      _read_uint16 (asf_demux, &object->data_size) &&
      _read_uint8 (asf_demux, &object->silence_data));
}

static gboolean
_read_stream_video (GstASFDemux * asf_demux, asf_stream_video * video)
{
  return (_read_uint32 (asf_demux, &video->width) &&
      _read_uint32 (asf_demux, &video->height) &&
      _read_uint8 (asf_demux, &video->unknown) &&
      _read_uint16 (asf_demux, &video->size));
}

static gboolean
_read_stream_video_format (GstASFDemux * asf_demux,
    asf_stream_video_format * fmt)
{
  return (_read_uint32 (asf_demux, &fmt->size) &&
      _read_uint32 (asf_demux, &fmt->width) &&
      _read_uint32 (asf_demux, &fmt->height) &&
      _read_uint16 (asf_demux, &fmt->planes) &&
      _read_uint16 (asf_demux, &fmt->depth) &&
      _read_uint32 (asf_demux, &fmt->tag) &&
      _read_uint32 (asf_demux, &fmt->image_size) &&
      _read_uint32 (asf_demux, &fmt->xpels_meter) &&
      _read_uint32 (asf_demux, &fmt->ypels_meter) &&
      _read_uint32 (asf_demux, &fmt->num_colors) &&
      _read_uint32 (asf_demux, &fmt->imp_colors));
}







static gboolean
gst_asf_demux_process_file (GstASFDemux * asf_demux, guint64 * obj_size)
{
  asf_obj_file object;

  /* Get the rest of the header's header */
  _read_obj_file (asf_demux, &object);
  asf_demux->packet_size = object.max_pktsize;
  asf_demux->play_time = (guint32) object.play_time / 10;
  asf_demux->preroll = object.preroll;

  GST_INFO ("Object is a file with %" G_GUINT64_FORMAT " data packets",
      object.packets_count);

  return TRUE;
}

static gboolean
gst_asf_demux_process_bitrate_props_object (GstASFDemux * asf_demux,
    guint64 * obj_size)
{
  guint16 num_streams;
  guint8 stream_id;
  guint16 i;
  asf_bitrate_record bitrate_record;

  if (!_read_uint16 (asf_demux, &num_streams))
    return FALSE;

  GST_INFO ("Object is a bitrate properties object with %u streams.",
      num_streams);

  for (i = 0; i < num_streams; i++) {
    _read_bitrate_record (asf_demux, &bitrate_record);
    stream_id = bitrate_record.stream_id & 0x7f;
    asf_demux->bitrate[stream_id] = bitrate_record.bitrate;
  }

  return TRUE;
}

static gboolean
gst_asf_demux_process_comment (GstASFDemux * asf_demux, guint64 * obj_size)
{
  asf_obj_comment object;
  GstByteStream *bs = asf_demux->bs;
  gchar *utf8_comments[5] = { NULL, NULL, NULL, NULL, NULL };
  guchar *data;
  const gchar *tags[5] = { GST_TAG_TITLE, GST_TAG_ARTIST, GST_TAG_COPYRIGHT,
    GST_TAG_COMMENT, NULL       /* ? */
  };
  guint16 *lengths = (guint16 *) & object;
  gint i;
  gsize in, out;
  GstTagList *taglist;
  const GList *padlist;
  GValue value = { 0 };

  GST_INFO ("Object is a comment.");

  /* Get the rest of the comment's header */
  _read_obj_comment (asf_demux, &object);
  GST_DEBUG
      ("Comment lengths: title=%d author=%d copyright=%d description=%d rating=%d",
      object.title_length, object.author_length, object.copyright_length,
      object.description_length, object.rating_length);
  for (i = 0; i < 5; i++) {
    /* might be just '/0', '/0'... */
    if (lengths[i] > 2 && lengths[i] % 2 == 0) {
      if (gst_bytestream_peek_bytes (bs, &data, lengths[i]) != lengths[i])
        goto fail;

      /* convert to UTF-8 */
      utf8_comments[i] = g_convert (data, lengths[i],
          "UTF-8", "UTF-16LE", &in, &out, NULL);

      gst_bytestream_flush_fast (bs, lengths[i]);
    } else {
      if (lengths[i] > 0 && !gst_bytestream_flush (bs, lengths[i]))
        goto fail;
    }
  }

  /* parse metadata into taglist */
  taglist = gst_tag_list_new ();
  g_value_init (&value, G_TYPE_STRING);
  for (i = 0; i < 5; i++) {
    if (utf8_comments[i] && tags[i]) {
      g_value_set_string (&value, utf8_comments[i]);
      gst_tag_list_add_values (taglist, GST_TAG_MERGE_APPEND,
          tags[i], &value, NULL);
      g_free (utf8_comments[i]);
    }
  }
  g_value_unset (&value);

  for (padlist = gst_element_get_pad_list (GST_ELEMENT (asf_demux));
      padlist != NULL; padlist = padlist->next) {
    if (GST_PAD_IS_SRC (padlist->data) && GST_PAD_IS_USABLE (padlist->data)) {
      gst_pad_push (GST_PAD (padlist->data),
          GST_DATA (gst_event_new_tag (taglist)));
    }
  }
  gst_element_found_tags (GST_ELEMENT (asf_demux), taglist);

  return TRUE;
<commit_msg>gst/asfdemux/gstasfdemux.c: - freeing the event also frees its associated taglist, that's unfortunate as we need tagl...<commit_after>/* GStreamer
 * Copyright (C) <1999> <NAME> <<EMAIL>>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gst/gstutils.h>
#include <gst/riff/riff-ids.h>
#include <string.h>

#include "gstasfdemux.h"
#include "asfheaders.h"

static GstStaticPadTemplate gst_asf_demux_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-ms-asf")
    );

GST_DEBUG_CATEGORY_STATIC (asf_debug);
#define GST_CAT_DEFAULT asf_debug

static void gst_asf_demux_base_init (gpointer g_class);
static void gst_asf_demux_class_init (GstASFDemuxClass * klass);
static void gst_asf_demux_init (GstASFDemux * asf_demux);
static gboolean gst_asf_demux_send_event (GstElement * element,
    GstEvent * event);
static void gst_asf_demux_loop (GstElement * element);
static gboolean gst_asf_demux_process_object (GstASFDemux * asf_demux);
static void gst_asf_demux_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec);
static guint32 gst_asf_demux_identify_guid (GstASFDemux * asf_demux,
    ASFGuidHash * guids, ASFGuid * guid_raw);
static gboolean gst_asf_demux_process_chunk (GstASFDemux * asf_demux,
    asf_packet_info * packet_info, asf_segment_info * segment_info);
static const GstEventMask *gst_asf_demux_get_src_event_mask (GstPad * pad);
static gboolean gst_asf_demux_handle_sink_event (GstASFDemux * asf_demux,
    GstEvent * event, guint32 remaining);
static gboolean gst_asf_demux_handle_src_event (GstPad * pad, GstEvent * event);
static const GstFormat *gst_asf_demux_get_src_formats (GstPad * pad);
static const GstQueryType *gst_asf_demux_get_src_query_types (GstPad * pad);
static gboolean gst_asf_demux_handle_src_query (GstPad * pad,
    GstQueryType type, GstFormat * format, gint64 * value);
static gboolean gst_asf_demux_add_video_stream (GstASFDemux * asf_demux,
    asf_stream_video_format * video_format, guint16 id);
static gboolean gst_asf_demux_add_audio_stream (GstASFDemux * asf_demux,
    asf_stream_audio * audio, guint16 id);
static gboolean gst_asf_demux_setup_pad (GstASFDemux * asf_demux,
    GstPad * src_pad, GstCaps * caps, guint16 id);

static GstElementStateReturn gst_asf_demux_change_state (GstElement * element);
static GstCaps *gst_asf_demux_video_caps (guint32 codec_fcc,
    asf_stream_video_format * video, char **codec_name);
static GstCaps *gst_asf_demux_audio_caps (guint16 codec_id,
    asf_stream_audio * audio, guint8 * extradata, char **codec_name);

static GstPadTemplate *videosrctempl, *audiosrctempl;
static GstElementClass *parent_class = NULL;

GType
gst_asf_demux_get_type (void)
{
  static GType asf_demux_type = 0;

  if (!asf_demux_type) {
    static const GTypeInfo asf_demux_info = {
      sizeof (GstASFDemuxClass),
      gst_asf_demux_base_init,
      NULL,
      (GClassInitFunc) gst_asf_demux_class_init,
      NULL,
      NULL,
      sizeof (GstASFDemux),
      0,
      (GInstanceInitFunc) gst_asf_demux_init,
    };

    asf_demux_type =
        g_type_register_static (GST_TYPE_ELEMENT, "GstASFDemux",
        &asf_demux_info, 0);

    GST_DEBUG_CATEGORY_INIT (asf_debug, "asfdemux", 0, "asf demuxer element");
  }
  return asf_demux_type;
}

static void
gst_asf_demux_base_init (gpointer g_class)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
  static GstElementDetails gst_asf_demux_details = {
    "ASF Demuxer",
    "Codec/Demuxer",
    "Demultiplexes ASF Streams",
    "<NAME> <<EMAIL>>"
  };
  int i;
  GstCaps *audcaps, *vidcaps, *temp;
  guint32 vid_list[] = {
    GST_MAKE_FOURCC ('I', '4', '2', '0'),
    GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'),
    GST_MAKE_FOURCC ('M', 'J', 'P', 'G'),
    GST_MAKE_FOURCC ('D', 'V', 'S', 'D'),
    GST_MAKE_FOURCC ('W', 'M', 'V', '1'),
    GST_MAKE_FOURCC ('W', 'M', 'V', '2'),
    GST_MAKE_FOURCC ('M', 'P', 'G', '4'),
    GST_MAKE_FOURCC ('M', 'P', '4', '2'),
    GST_MAKE_FOURCC ('M', 'P', '4', '3'),
    GST_MAKE_FOURCC ('D', 'I', 'V', '3'),
    GST_MAKE_FOURCC ('D', 'X', '5', '0'),
    0                           /* end */
  };
  gint aud_list[] = {
    GST_RIFF_WAVE_FORMAT_MPEGL3,
    GST_RIFF_WAVE_FORMAT_MPEGL12,
    GST_RIFF_WAVE_FORMAT_PCM,
    GST_RIFF_WAVE_FORMAT_VORBIS1,
    GST_RIFF_WAVE_FORMAT_A52,
    GST_RIFF_WAVE_FORMAT_DIVX_WMAV1,
    GST_RIFF_WAVE_FORMAT_DIVX_WMAV2,
    GST_RIFF_WAVE_FORMAT_WMAV9,
    -1                          /* end */
  };

  audcaps = gst_caps_new_empty ();
  for (i = 0; aud_list[i] != -1; i++) {
    temp = gst_asf_demux_audio_caps (aud_list[i], NULL, NULL, NULL);
    gst_caps_append (audcaps, temp);
  }

  audiosrctempl = gst_pad_template_new ("audio_%02d",
      GST_PAD_SRC, GST_PAD_SOMETIMES, audcaps);

  vidcaps = gst_caps_new_empty ();
  for (i = 0; vid_list[i] != 0; i++) {
    temp = gst_asf_demux_video_caps (vid_list[i], NULL, NULL);
    gst_caps_append (vidcaps, temp);
  }

  videosrctempl = gst_pad_template_new ("video_%02d",
      GST_PAD_SRC, GST_PAD_SOMETIMES, vidcaps);
  gst_element_class_add_pad_template (element_class, audiosrctempl);
  gst_element_class_add_pad_template (element_class, videosrctempl);
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&gst_asf_demux_sink_template));
  gst_element_class_set_details (element_class, &gst_asf_demux_details);
}

static void
gst_asf_demux_class_init (GstASFDemuxClass * klass)
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;

  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);

  gobject_class->get_property = gst_asf_demux_get_property;

  gstelement_class->change_state = gst_asf_demux_change_state;
  gstelement_class->send_event = gst_asf_demux_send_event;
}

static void
gst_asf_demux_init (GstASFDemux * asf_demux)
{
  guint i;

  asf_demux->sinkpad =
      gst_pad_new_from_template (gst_static_pad_template_get
      (&gst_asf_demux_sink_template), "sink");
  gst_element_add_pad (GST_ELEMENT (asf_demux), asf_demux->sinkpad);

  gst_element_set_loop_function (GST_ELEMENT (asf_demux), gst_asf_demux_loop);

  /* We should zero everything to be on the safe side */
  for (i = 0; i < GST_ASF_DEMUX_NUM_VIDEO_PADS; i++) {
    asf_demux->video_pad[i] = NULL;
    asf_demux->video_PTS[i] = 0;
  }

  for (i = 0; i < GST_ASF_DEMUX_NUM_AUDIO_PADS; i++) {
    asf_demux->audio_pad[i] = NULL;
    asf_demux->audio_PTS[i] = 0;
  }

  asf_demux->num_audio_streams = 0;
  asf_demux->num_video_streams = 0;
  asf_demux->num_streams = 0;

  GST_FLAG_SET (asf_demux, GST_ELEMENT_EVENT_AWARE);
}

static gboolean
gst_asf_demux_send_event (GstElement * element, GstEvent * event)
{
  const GList *pads;

  pads = gst_element_get_pad_list (element);

  while (pads) {
    GstPad *pad = GST_PAD (pads->data);

    if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
      /* we ref the event here as we might have to try again if the event
       * failed on this pad */
      gst_event_ref (event);
      if (gst_asf_demux_handle_src_event (pad, event)) {
        gst_event_unref (event);
        return TRUE;
      }
    }

    pads = g_list_next (pads);
  }

  gst_event_unref (event);
  return FALSE;
}

static void
gst_asf_demux_loop (GstElement * element)
{
  GstASFDemux *asf_demux;

  g_return_if_fail (element != NULL);
  g_return_if_fail (GST_IS_ASF_DEMUX (element));

  asf_demux = GST_ASF_DEMUX (element);

  /* this is basically an infinite loop */
  gst_asf_demux_process_object (asf_demux);
}

static guint32
_read_var_length (GstASFDemux * asf_demux, guint8 type, guint32 * rsize)
{
  guint32 got_bytes;
  guint8 *var;
  guint32 ret = 0;
  GstByteStream *bs = asf_demux->bs;

  if (type == 0) {
    return 0;
  }

  got_bytes = gst_bytestream_peek_bytes (bs, &var, 4);

  while (got_bytes < 4) {
    guint32 remaining;
    GstEvent *event;

    gst_bytestream_get_status (bs, &remaining, &event);
    gst_event_unref (event);

    got_bytes = gst_bytestream_peek_bytes (bs, &var, 4);
  }

  switch (type) {
    case 1:
      ret = (GST_READ_UINT32_LE (var)) & 0xff;
      gst_bytestream_flush (bs, 1);
      *rsize += 1;
      break;
    case 2:
      ret = (GST_READ_UINT32_LE (var)) & 0xffff;
      gst_bytestream_flush (bs, 2);
      *rsize += 2;
      break;
    case 3:
      ret = GST_READ_UINT32_LE (var);
      gst_bytestream_flush (bs, 4);
      *rsize += 4;
      break;
  }

  return ret;
}

#define READ_UINT_BITS_FUNCTION(bits)						\
static gboolean									\
_read_uint ## bits (GstASFDemux *asf_demux, guint ## bits *ret)	        	\
{										\
  GstEvent *event;								\
  guint32 remaining;								\
  guint8* data;									\
										\
  g_return_val_if_fail (ret != NULL, FALSE);					\
										\
  do {										\
    if (gst_bytestream_peek_bytes (asf_demux->bs, &data, bits / 8) == bits / 8) { \
      *ret = GST_READ_UINT ## bits ## _LE (data);				\
      gst_bytestream_flush (asf_demux->bs, bits / 8);				\
      return TRUE;								\
    }										\
    gst_bytestream_get_status (asf_demux->bs, &remaining, &event);		\
  } while (gst_asf_demux_handle_sink_event (asf_demux, event, remaining));	\
										\
  return FALSE;									\
}

#define GST_READ_UINT8_LE(x) GST_READ_UINT8(x)
READ_UINT_BITS_FUNCTION (8)
    READ_UINT_BITS_FUNCTION (16)
    READ_UINT_BITS_FUNCTION (32)
    READ_UINT_BITS_FUNCTION (64)
#define GET_UINT(a,b)
     static gboolean _read_guid (GstASFDemux * asf_demux, ASFGuid * guid)
{
  return (_read_uint32 (asf_demux, &guid->v1) &&
      _read_uint32 (asf_demux, &guid->v2) &&
      _read_uint32 (asf_demux, &guid->v3) &&
      _read_uint32 (asf_demux, &guid->v4));
}

static gboolean
_read_obj_file (GstASFDemux * asf_demux, asf_obj_file * object)
{
  return (_read_guid (asf_demux, &object->file_id) &&
      _read_uint64 (asf_demux, &object->file_size) &&
      _read_uint64 (asf_demux, &object->creation_time) &&
      _read_uint64 (asf_demux, &object->packets_count) &&
      _read_uint64 (asf_demux, &object->play_time) &&
      _read_uint64 (asf_demux, &object->send_time) &&
      _read_uint64 (asf_demux, &object->preroll) &&
      _read_uint32 (asf_demux, &object->flags) &&
      _read_uint32 (asf_demux, &object->min_pktsize) &&
      _read_uint32 (asf_demux, &object->max_pktsize) &&
      _read_uint32 (asf_demux, &object->min_bitrate));
}

static gboolean
_read_bitrate_record (GstASFDemux * asf_demux, asf_bitrate_record * record)
{
  return (_read_uint16 (asf_demux, &record->stream_id) &&
      _read_uint32 (asf_demux, &record->bitrate));
}

static gboolean
_read_obj_comment (GstASFDemux * asf_demux, asf_obj_comment * comment)
{
  return (_read_uint16 (asf_demux, &comment->title_length) &&
      _read_uint16 (asf_demux, &comment->author_length) &&
      _read_uint16 (asf_demux, &comment->copyright_length) &&
      _read_uint16 (asf_demux, &comment->description_length) &&
      _read_uint16 (asf_demux, &comment->rating_length));
}

static gboolean
_read_obj_header (GstASFDemux * asf_demux, asf_obj_header * header)
{
  return (_read_uint32 (asf_demux, &header->num_objects) &&
      _read_uint8 (asf_demux, &header->unknown1) &&
      _read_uint8 (asf_demux, &header->unknown2));
}

static gboolean
_read_obj_stream (GstASFDemux * asf_demux, asf_obj_stream * stream)
{
  return (_read_guid (asf_demux, &stream->type) &&
      _read_guid (asf_demux, &stream->correction) &&
      _read_uint64 (asf_demux, &stream->unknown1) &&
      _read_uint32 (asf_demux, &stream->type_specific_size) &&
      _read_uint32 (asf_demux, &stream->stream_specific_size) &&
      _read_uint16 (asf_demux, &stream->id) &&
      _read_uint32 (asf_demux, &stream->unknown2));
}

static gboolean
_read_replicated_data (GstASFDemux * asf_demux, asf_replicated_data * rep)
{
  return (_read_uint32 (asf_demux, &rep->object_size) &&
      _read_uint32 (asf_demux, &rep->frag_timestamp));
}

static gboolean
_read_obj_data (GstASFDemux * asf_demux, asf_obj_data * object)
{
  return (_read_guid (asf_demux, &object->file_id) &&
      _read_uint64 (asf_demux, &object->packets) &&
      _read_uint8 (asf_demux, &object->unknown1) &&
          /*_read_uint8 (asf_demux, &object->unknown2) && */
      _read_uint8 (asf_demux, &object->correction));
}

static gboolean
_read_obj_data_correction (GstASFDemux * asf_demux,
    asf_obj_data_correction * object)
{
  return (_read_uint8 (asf_demux, &object->type) &&
      _read_uint8 (asf_demux, &object->cycle));
}

static gboolean
_read_obj_data_packet (GstASFDemux * asf_demux, asf_obj_data_packet * object)
{
  return (_read_uint8 (asf_demux, &object->flags) &&
      _read_uint8 (asf_demux, &object->property));
}

static gboolean
_read_stream_audio (GstASFDemux * asf_demux, asf_stream_audio * audio)
{
  return (_read_uint16 (asf_demux, &audio->codec_tag) &&
      _read_uint16 (asf_demux, &audio->channels) &&
      _read_uint32 (asf_demux, &audio->sample_rate) &&
      _read_uint32 (asf_demux, &audio->byte_rate) &&
      _read_uint16 (asf_demux, &audio->block_align) &&
      _read_uint16 (asf_demux, &audio->word_size) &&
      _read_uint16 (asf_demux, &audio->size));
}

static gboolean
_read_stream_correction (GstASFDemux * asf_demux,
    asf_stream_correction * object)
{
  return (_read_uint8 (asf_demux, &object->span) &&
      _read_uint16 (asf_demux, &object->packet_size) &&
      _read_uint16 (asf_demux, &object->chunk_size) &&
      _read_uint16 (asf_demux, &object->data_size) &&
      _read_uint8 (asf_demux, &object->silence_data));
}

static gboolean
_read_stream_video (GstASFDemux * asf_demux, asf_stream_video * video)
{
  return (_read_uint32 (asf_demux, &video->width) &&
      _read_uint32 (asf_demux, &video->height) &&
      _read_uint8 (asf_demux, &video->unknown) &&
      _read_uint16 (asf_demux, &video->size));
}

static gboolean
_read_stream_video_format (GstASFDemux * asf_demux,
    asf_stream_video_format * fmt)
{
  return (_read_uint32 (asf_demux, &fmt->size) &&
      _read_uint32 (asf_demux, &fmt->width) &&
      _read_uint32 (asf_demux, &fmt->height) &&
      _read_uint16 (asf_demux, &fmt->planes) &&
      _read_uint16 (asf_demux, &fmt->depth) &&
      _read_uint32 (asf_demux, &fmt->tag) &&
      _read_uint32 (asf_demux, &fmt->image_size) &&
      _read_uint32 (asf_demux, &fmt->xpels_meter) &&
      _read_uint32 (asf_demux, &fmt->ypels_meter) &&
      _read_uint32 (asf_demux, &fmt->num_colors) &&
      _read_uint32 (asf_demux, &fmt->imp_colors));
}







static gboolean
gst_asf_demux_process_file (GstASFDemux * asf_demux, guint64 * obj_size)
{
  asf_obj_file object;

  /* Get the rest of the header's header */
  _read_obj_file (asf_demux, &object);
  asf_demux->packet_size = object.max_pktsize;
  asf_demux->play_time = (guint32) object.play_time / 10;
  asf_demux->preroll = object.preroll;

  GST_INFO ("Object is a file with %" G_GUINT64_FORMAT " data packets",
      object.packets_count);

  return TRUE;
}

static gboolean
gst_asf_demux_process_bitrate_props_object (GstASFDemux * asf_demux,
    guint64 * obj_size)
{
  guint16 num_streams;
  guint8 stream_id;
  guint16 i;
  asf_bitrate_record bitrate_record;

  if (!_read_uint16 (asf_demux, &num_streams))
    return FALSE;

  GST_INFO ("Object is a bitrate properties object with %u streams.",
      num_streams);

  for (i = 0; i < num_streams; i++) {
    _read_bitrate_record (asf_demux, &bitrate_record);
    stream_id = bitrate_record.stream_id & 0x7f;
    asf_demux->bitrate[stream_id] = bitrate_record.bitrate;
  }

  return TRUE;
}

static gboolean
gst_asf_demux_process_comment (GstASFDemux * asf_demux, guint64 * obj_size)
{
  asf_obj_comment object;
  GstByteStream *bs = asf_demux->bs;
  gchar *utf8_comments[5] = { NULL, NULL, NULL, NULL, NULL };
  guchar *data;
  const gchar *tags[5] = { GST_TAG_TITLE, GST_TAG_ARTIST, GST_TAG_COPYRIGHT,
    GST_TAG_COMMENT, NULL       /* ? */
  };
  guint16 *lengths = (guint16 *) & object;
  gint i;
  gsize in, out;
  GstTagList *taglist;
  GValue value = { 0 };
  gboolean have_tags = FALSE;

  GST_INFO ("Object is a comment.");

  /* Get the rest of the comment's header */
  _read_obj_comment (asf_demux, &object);
  GST_DEBUG
      ("Comment lengths: title=%d author=%d copyright=%d description=%d rating=%d",
      object.title_length, object.author_length, object.copyright_length,
      object.description_length, object.rating_length);
  for (i = 0; i < 5; i++) {
    /* might be just '/0', '/0'... */
    if (lengths[i] > 2 && lengths[i] % 2 == 0) {
      if (gst_bytestream_peek_bytes (bs, &data, lengths[i]) != lengths[i])
        goto fail;

      /* convert to UTF-8 */
      utf8_comments[i] = g_convert (data, lengths[i],
          "UTF-8", "UTF-16LE", &in, &out, NULL);

      gst_bytestream_flush_fast (bs, lengths[i]);
    } else {
      if (lengths[i] > 0 && !gst_bytestream_flush (bs, lengths[i]))
        goto fail;
    }
  }

  /* parse metadata into taglist */
  taglist = gst_tag_list_new ();
  g_value_init (&value, G_TYPE_STRING);
  for (i = 0; i < 5; i++) {
    if (utf8_comments[i] && tags[i]) {
      have_tags = TRUE;
      g_value_set_string (&value, utf8_comments[i]);
      gst_tag_list_add_values (taglist, GST_TAG_MERGE_APPEND,
          tags[i], &value, NULL);
      g_free (utf8_comments[i]);
    }
  }
  g_value_unset (&value);

  if (have_tags) {
    GstElement *element = GST_ELEMENT (asf_demux);
    GstEvent *event = gst_event_new_tag (taglist);
    const GList *padlist;

    for (padlist = gst_element_get_pad_list (element);
        padlist != NULL; padlist = padlist->next) {
      if (GST_PAD_IS_SRC (padlist->data) && GST_PAD_IS_USABLE (padlist->data)) {
        gst_event_ref (event);
        gst_pad_push (GST_PAD (padlist->data), GST_DATA (event));
      }
    }

    gst_element_found_tags (element, taglist);

    gst_event_unref (event);
  } else {
    gst_tag_list_free (taglist);
  }

  return TRUE;

{'fraction_non_alphanumeric': 0.1167885180888661, 'fraction_numerical': 0.01672646828715569, 'mean_word_length': 4.007159259759557, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 219, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2943040', 'n_tokens_mistral': 15717, 'n_tokens_neox': 14994, 'n_words': 2877}