Commit message: matroskademux: Initialize track context out parameter to NULL before parsing

Various error return paths don't set it to NULL and callers are only
checking if the pointer is NULL. As it's allocated on the stack this
usually contains random stack memory, and more often than not the memory
of a previously parsed track.

This then causes all kinds of memory corruptions further down the line.

Thanks to Natalie Silvanovich for reporting.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/issues/858

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/903>
CVE: CVE-2021-3498
CWE: CWE-787
Vulnerability description: GStreamer before 1.18.4 might cause heap corruption when parsing certain malformed Matroska files.

Project: gst-plugins-good
File: matroska-demux.c

gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
    GstEbmlRead * ebml, guint64 cluster_time, guint64 cluster_offset,
    gboolean is_simpleblock)
{
  GstMatroskaTrackContext *stream = NULL;
  GstFlowReturn ret = GST_FLOW_OK;
  gboolean readblock = FALSE;
  guint32 id;
  guint64 block_duration = -1;
  gint64 block_discardpadding = 0;
  GstBuffer *buf = NULL;
  GstMapInfo map;
  gint stream_num = -1, n, laces = 0;
  guint size = 0;
  gint *lace_size = NULL;
  gint64 time = 0;
  gint flags = 0;
  gint64 referenceblock = 0;
  gint64 offset;
  GstClockTime buffer_timestamp;

  offset = gst_ebml_read_get_offset (ebml);

  while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
    if (!is_simpleblock) {
      if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) {
        goto data_error;
      }
    } else {
      id = GST_MATROSKA_ID_SIMPLEBLOCK;
    }

    switch (id) {
        /* one block inside the group. Note, block parsing is one
         * of the harder things, so this code is a bit complicated.
         * See http://www.matroska.org/ for documentation. */
      case GST_MATROSKA_ID_SIMPLEBLOCK:
      case GST_MATROSKA_ID_BLOCK:
      {
        guint64 num;
        guint8 *data;

        if (buf) {
          gst_buffer_unmap (buf, &map);
          gst_buffer_unref (buf);
          buf = NULL;
        }
        if ((ret = gst_ebml_read_buffer (ebml, &id, &buf)) != GST_FLOW_OK)
          break;

        gst_buffer_map (buf, &map, GST_MAP_READ);
        data = map.data;
        size = map.size;

        /* first byte(s): blocknum */
        if ((n = gst_matroska_ebmlnum_uint (data, size, &num)) < 0)
          goto data_error;
        data += n;
        size -= n;

        /* fetch stream from num */
        stream_num = gst_matroska_read_common_stream_from_num (&demux->common,
            num);
        if (G_UNLIKELY (size < 3)) {
          GST_WARNING_OBJECT (demux, "Invalid size %u", size);
          /* non-fatal, try next block(group) */
          ret = GST_FLOW_OK;
          goto done;
        } else if (G_UNLIKELY (stream_num < 0 ||
                stream_num >= demux->common.num_streams)) {
          /* let's not give up on a stray invalid track number */
          GST_WARNING_OBJECT (demux,
              "Invalid stream %d for track number %" G_GUINT64_FORMAT
              "; ignoring block", stream_num, num);
          goto done;
        }

        stream = g_ptr_array_index (demux->common.src, stream_num);

        /* time (relative to cluster time) */
        time = ((gint16) GST_READ_UINT16_BE (data));
        data += 2;
        size -= 2;
        flags = GST_READ_UINT8 (data);
        data += 1;
        size -= 1;

        GST_LOG_OBJECT (demux, "time %" G_GUINT64_FORMAT ", flags %d", time,
            flags);

        switch ((flags & 0x06) >> 1) {
          case 0x0:            /* no lacing */
            laces = 1;
            lace_size = g_new (gint, 1);
            lace_size[0] = size;
            break;

          case 0x1:            /* xiph lacing */
          case 0x2:            /* fixed-size lacing */
          case 0x3:            /* EBML lacing */
            if (size == 0)
              goto invalid_lacing;
            laces = GST_READ_UINT8 (data) + 1;
            data += 1;
            size -= 1;
            lace_size = g_new0 (gint, laces);

            switch ((flags & 0x06) >> 1) {
              case 0x1:        /* xiph lacing */  {
                guint temp, total = 0;

                for (n = 0; ret == GST_FLOW_OK && n < laces - 1; n++) {
                  while (1) {
                    if (size == 0)
                      goto invalid_lacing;
                    temp = GST_READ_UINT8 (data);
                    lace_size[n] += temp;
                    data += 1;
                    size -= 1;
                    if (temp != 0xff)
                      break;
                  }
                  total += lace_size[n];
                }
                lace_size[n] = size - total;
                break;
              }

              case 0x2:        /* fixed-size lacing */
                for (n = 0; n < laces; n++)
                  lace_size[n] = size / laces;
                break;

              case 0x3:        /* EBML lacing */  {
                guint total;

                if ((n = gst_matroska_ebmlnum_uint (data, size, &num)) < 0)
                  goto data_error;
                data += n;
                size -= n;
                total = lace_size[0] = num;
                for (n = 1; ret == GST_FLOW_OK && n < laces - 1; n++) {
                  gint64 snum;
                  gint r;

                  if ((r = gst_matroska_ebmlnum_sint (data, size, &snum)) < 0)
                    goto data_error;
                  data += r;
                  size -= r;
                  lace_size[n] = lace_size[n - 1] + snum;
                  total += lace_size[n];
                }
                if (n < laces)
                  lace_size[n] = size - total;
                break;
              }
            }
            break;
        }

        if (ret != GST_FLOW_OK)
          break;

        readblock = TRUE;
        break;
      }

      case GST_MATROSKA_ID_BLOCKDURATION:{
        ret = gst_ebml_read_uint (ebml, &id, &block_duration);
        GST_DEBUG_OBJECT (demux, "BlockDuration: %" G_GUINT64_FORMAT,
            block_duration);
        break;
      }

      case GST_MATROSKA_ID_DISCARDPADDING:{
        ret = gst_ebml_read_sint (ebml, &id, &block_discardpadding);
        GST_DEBUG_OBJECT (demux, "DiscardPadding: %" GST_STIME_FORMAT,
            GST_STIME_ARGS (block_discardpadding));
        break;
      }

      case GST_MATROSKA_ID_REFERENCEBLOCK:{
        ret = gst_ebml_read_sint (ebml, &id, &referenceblock);
        GST_DEBUG_OBJECT (demux, "ReferenceBlock: %" G_GINT64_FORMAT,
            referenceblock);
        break;
      }

      case GST_MATROSKA_ID_CODECSTATE:{
        guint8 *data;
        guint64 data_len = 0;

        if ((ret =
                gst_ebml_read_binary (ebml, &id, &data,
                    &data_len)) != GST_FLOW_OK)
          break;

        if (G_UNLIKELY (stream == NULL)) {
          GST_WARNING_OBJECT (demux,
              "Unexpected CodecState subelement - ignoring");
          break;
        }

        g_free (stream->codec_state);
        stream->codec_state = data;
        stream->codec_state_size = data_len;

        /* Decode if necessary */
        if (stream->encodings && stream->encodings->len > 0
            && stream->codec_state && stream->codec_state_size > 0) {
          if (!gst_matroska_decode_data (stream->encodings,
                  &stream->codec_state, &stream->codec_state_size,
                  GST_MATROSKA_TRACK_ENCODING_SCOPE_CODEC_DATA, TRUE)) {
            GST_WARNING_OBJECT (demux, "Decoding codec state failed");
          }
        }

        GST_DEBUG_OBJECT (demux, "CodecState of %" G_GSIZE_FORMAT " bytes",
            stream->codec_state_size);
        break;
      }

      default:
        ret = gst_matroska_read_common_parse_skip (&demux->common, ebml,
            "BlockGroup", id);
        break;

      case GST_MATROSKA_ID_BLOCKVIRTUAL:
      case GST_MATROSKA_ID_BLOCKADDITIONS:
      case GST_MATROSKA_ID_REFERENCEPRIORITY:
      case GST_MATROSKA_ID_REFERENCEVIRTUAL:
      case GST_MATROSKA_ID_SLICES:
        GST_DEBUG_OBJECT (demux,
            "Skipping BlockGroup subelement 0x%x - ignoring", id);
        ret = gst_ebml_read_skip (ebml);
        break;
    }

    if (is_simpleblock)
      break;
  }

  /* reading a number or so could have failed */
  if (ret != GST_FLOW_OK)
    goto data_error;

  if (ret == GST_FLOW_OK && readblock) {
    gboolean invisible_frame = FALSE;
    gboolean delta_unit = FALSE;
    guint64 duration = 0;
    gint64 lace_time = 0;
    GstEvent *protect_event;

    stream = g_ptr_array_index (demux->common.src, stream_num);

    if (cluster_time != GST_CLOCK_TIME_NONE) {
      /* FIXME: What to do with negative timestamps? Give timestamp 0 or -1?
       * Drop unless the lace contains timestamp 0? */
      if (time < 0 && (-time) > cluster_time) {
        lace_time = 0;
      } else {
        if (stream->timecodescale == 1.0)
          lace_time = (cluster_time + time) * demux->common.time_scale;
        else
          lace_time =
              gst_util_guint64_to_gdouble ((cluster_time + time) *
              demux->common.time_scale) * stream->timecodescale;
      }
    } else {
      lace_time = GST_CLOCK_TIME_NONE;
    }
    /* Send the GST_PROTECTION event */
    while ((protect_event = g_queue_pop_head (&stream->protection_event_queue))) {
      GST_TRACE_OBJECT (demux, "pushing protection event for stream %d:%s",
          stream->index, GST_STR_NULL (stream->name));
      gst_pad_push_event (stream->pad, protect_event);
    }

    /* need to refresh segment info ASAP */
    if (GST_CLOCK_TIME_IS_VALID (lace_time) && demux->need_segment) {
      GstSegment *segment = &demux->common.segment;
      guint64 clace_time;
      GstEvent *segment_event;

      if (!GST_CLOCK_TIME_IS_VALID (demux->stream_start_time)) {
        demux->stream_start_time = lace_time;
        GST_DEBUG_OBJECT (demux,
            "Setting stream start time to %" GST_TIME_FORMAT,
            GST_TIME_ARGS (lace_time));
      }
      clace_time = MAX (lace_time, demux->stream_start_time);
      if (GST_CLOCK_TIME_IS_VALID (demux->common.segment.position) &&
          demux->common.segment.position != 0) {
        GST_DEBUG_OBJECT (demux,
            "using stored seek position %" GST_TIME_FORMAT,
            GST_TIME_ARGS (demux->common.segment.position));
        clace_time = demux->common.segment.position;
        segment->position = GST_CLOCK_TIME_NONE;
      }
      segment->start = clace_time;
      segment->stop = GST_CLOCK_TIME_NONE;
      segment->time = segment->start - demux->stream_start_time;
      segment->position = segment->start - demux->stream_start_time;
      GST_DEBUG_OBJECT (demux,
          "generated segment starting at %" GST_TIME_FORMAT ": %"
          GST_SEGMENT_FORMAT, GST_TIME_ARGS (lace_time), segment);
      /* now convey our segment notion downstream */
      segment_event = gst_event_new_segment (segment);
      if (demux->segment_seqnum)
        gst_event_set_seqnum (segment_event, demux->segment_seqnum);
      gst_matroska_demux_send_event (demux, segment_event);
      demux->need_segment = FALSE;
      demux->segment_seqnum = 0;
    }

    /* send pending codec data headers for all streams,
     * before we perform sync across all streams */
    gst_matroska_demux_push_codec_data_all (demux);

    if (block_duration != -1) {
      if (stream->timecodescale == 1.0)
        duration = gst_util_uint64_scale (block_duration,
            demux->common.time_scale, 1);
      else
        duration =
            gst_util_gdouble_to_guint64 (gst_util_guint64_to_gdouble
            (gst_util_uint64_scale (block_duration, demux->common.time_scale,
                    1)) * stream->timecodescale);
    } else if (stream->default_duration) {
      duration = stream->default_duration * laces;
    }
    /* else duration is diff between timecode of this and next block */

    if (stream->type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
      /* For SimpleBlock, look at the keyframe bit in flags. Otherwise,
         a ReferenceBlock implies that this is not a keyframe. In either
         case, it only makes sense for video streams. */
      if ((is_simpleblock && !(flags & 0x80)) || referenceblock) {
        delta_unit = TRUE;
        invisible_frame = ((flags & 0x08)) &&
            (!strcmp (stream->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VP8) ||
            !strcmp (stream->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VP9) ||
            !strcmp (stream->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_AV1));
      }

      /* If we're doing a keyframe-only trickmode, only push keyframes on video
       * streams */
      if (delta_unit
          && demux->common.segment.
          flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS) {
        GST_LOG_OBJECT (demux, "Skipping non-keyframe on stream %d",
            stream->index);
        ret = GST_FLOW_OK;
        goto done;
      }
    }

    for (n = 0; n < laces; n++) {
      GstBuffer *sub;

      if (G_UNLIKELY (lace_size[n] > size)) {
        GST_WARNING_OBJECT (demux, "Invalid lace size");
        break;
      }

      /* QoS for video track with an index. the assumption is that
         index entries point to keyframes, but if that is not true we
         will instead skip until the next keyframe. */
      if (GST_CLOCK_TIME_IS_VALID (lace_time) &&
          stream->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
          stream->index_table && demux->common.segment.rate > 0.0) {
        GstMatroskaTrackVideoContext *videocontext =
            (GstMatroskaTrackVideoContext *) stream;
        GstClockTime earliest_time;
        GstClockTime earliest_stream_time;

        GST_OBJECT_LOCK (demux);
        earliest_time = videocontext->earliest_time;
        GST_OBJECT_UNLOCK (demux);
        earliest_stream_time =
            gst_segment_position_from_running_time (&demux->common.segment,
            GST_FORMAT_TIME, earliest_time);

        if (GST_CLOCK_TIME_IS_VALID (lace_time) &&
            GST_CLOCK_TIME_IS_VALID (earliest_stream_time) &&
            lace_time <= earliest_stream_time) {
          /* find index entry (keyframe) <= earliest_stream_time */
          GstMatroskaIndex *entry =
              gst_util_array_binary_search (stream->index_table->data,
              stream->index_table->len, sizeof (GstMatroskaIndex),
              (GCompareDataFunc) gst_matroska_index_seek_find,
              GST_SEARCH_MODE_BEFORE, &earliest_stream_time, NULL);

          /* if that entry (keyframe) is after the current the current
             buffer, we can skip pushing (and thus decoding) all
             buffers until that keyframe. */
          if (entry && GST_CLOCK_TIME_IS_VALID (entry->time) &&
              entry->time > lace_time) {
            GST_LOG_OBJECT (demux, "Skipping lace before late keyframe");
            stream->set_discont = TRUE;
            goto next_lace;
          }
        }
      }

      sub = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL,
          gst_buffer_get_size (buf) - size, lace_size[n]);
      GST_DEBUG_OBJECT (demux, "created subbuffer %p", sub);

      if (delta_unit)
        GST_BUFFER_FLAG_SET (sub, GST_BUFFER_FLAG_DELTA_UNIT);
      else
        GST_BUFFER_FLAG_UNSET (sub, GST_BUFFER_FLAG_DELTA_UNIT);

      if (invisible_frame)
        GST_BUFFER_FLAG_SET (sub, GST_BUFFER_FLAG_DECODE_ONLY);

      if (stream->encodings != NULL && stream->encodings->len > 0)
        sub = gst_matroska_decode_buffer (stream, sub);

      if (sub == NULL) {
        GST_WARNING_OBJECT (demux, "Decoding buffer failed");
        goto next_lace;
      }

      if (!stream->dts_only) {
        GST_BUFFER_PTS (sub) = lace_time;
      } else {
        GST_BUFFER_DTS (sub) = lace_time;
        if (stream->intra_only)
          GST_BUFFER_PTS (sub) = lace_time;
      }

      buffer_timestamp = gst_matroska_track_get_buffer_timestamp (stream, sub);

      if (GST_CLOCK_TIME_IS_VALID (lace_time)) {
        GstClockTime last_stop_end;

        /* Check if this stream is after segment stop */
        if (GST_CLOCK_TIME_IS_VALID (demux->common.segment.stop) &&
            lace_time >= demux->common.segment.stop) {
          GST_DEBUG_OBJECT (demux,
              "Stream %d after segment stop %" GST_TIME_FORMAT, stream->index,
              GST_TIME_ARGS (demux->common.segment.stop));
          gst_buffer_unref (sub);
          goto eos;
        }
        if (offset >= stream->to_offset
            || (GST_CLOCK_TIME_IS_VALID (demux->to_time)
                && lace_time > demux->to_time)) {
          GST_DEBUG_OBJECT (demux, "Stream %d after playback section",
              stream->index);
          gst_buffer_unref (sub);
          goto eos;
        }

        /* handle gaps, e.g. non-zero start-time, or an cue index entry
         * that landed us with timestamps not quite intended */
        GST_OBJECT_LOCK (demux);
        if (demux->max_gap_time &&
            GST_CLOCK_TIME_IS_VALID (demux->last_stop_end) &&
            demux->common.segment.rate > 0.0) {
          GstClockTimeDiff diff;

          /* only send segments with increasing start times,
           * otherwise if these go back and forth downstream (sinks) increase
           * accumulated time and running_time */
          diff = GST_CLOCK_DIFF (demux->last_stop_end, lace_time);
          if (diff > 0 && diff > demux->max_gap_time
              && lace_time > demux->common.segment.start
              && (!GST_CLOCK_TIME_IS_VALID (demux->common.segment.stop)
                  || lace_time < demux->common.segment.stop)) {
            GstEvent *event;
            GST_DEBUG_OBJECT (demux,
                "Gap of %" G_GINT64_FORMAT " ns detected in"
                "stream %d (%" GST_TIME_FORMAT " -> %" GST_TIME_FORMAT "). "
                "Sending updated SEGMENT events", diff,
                stream->index, GST_TIME_ARGS (stream->pos),
                GST_TIME_ARGS (lace_time));

            event = gst_event_new_gap (demux->last_stop_end, diff);
            GST_OBJECT_UNLOCK (demux);
            gst_pad_push_event (stream->pad, event);
            GST_OBJECT_LOCK (demux);
          }
        }

        if (!GST_CLOCK_TIME_IS_VALID (demux->common.segment.position)
            || demux->common.segment.position < lace_time) {
          demux->common.segment.position = lace_time;
        }
        GST_OBJECT_UNLOCK (demux);

        last_stop_end = lace_time;
        if (duration) {
          GST_BUFFER_DURATION (sub) = duration / laces;
          last_stop_end += GST_BUFFER_DURATION (sub);
        }

        if (!GST_CLOCK_TIME_IS_VALID (demux->last_stop_end) ||
            demux->last_stop_end < last_stop_end)
          demux->last_stop_end = last_stop_end;

        GST_OBJECT_LOCK (demux);
        if (demux->common.segment.duration == -1 ||
            demux->stream_start_time + demux->common.segment.duration <
            last_stop_end) {
          demux->common.segment.duration =
              last_stop_end - demux->stream_start_time;
          GST_OBJECT_UNLOCK (demux);
          if (!demux->invalid_duration) {
            gst_element_post_message (GST_ELEMENT_CAST (demux),
                gst_message_new_duration_changed (GST_OBJECT_CAST (demux)));
            demux->invalid_duration = TRUE;
          }
        } else {
          GST_OBJECT_UNLOCK (demux);
        }
      }

      stream->pos = lace_time;

      gst_matroska_demux_sync_streams (demux);

      if (stream->set_discont) {
        GST_DEBUG_OBJECT (demux, "marking DISCONT");
        GST_BUFFER_FLAG_SET (sub, GST_BUFFER_FLAG_DISCONT);
        stream->set_discont = FALSE;
      } else {
        GST_BUFFER_FLAG_UNSET (sub, GST_BUFFER_FLAG_DISCONT);
      }

      /* reverse playback book-keeping */
      if (!GST_CLOCK_TIME_IS_VALID (stream->from_time))
        stream->from_time = lace_time;
      if (stream->from_offset == -1)
        stream->from_offset = offset;

      GST_DEBUG_OBJECT (demux,
          "Pushing lace %d, data of size %" G_GSIZE_FORMAT
          " for stream %d, time=%" GST_TIME_FORMAT " and duration=%"
          GST_TIME_FORMAT, n, gst_buffer_get_size (sub), stream_num,
          GST_TIME_ARGS (buffer_timestamp),
          GST_TIME_ARGS (GST_BUFFER_DURATION (sub)));

#if 0
      if (demux->common.element_index) {
        if (stream->index_writer_id == -1)
          gst_index_get_writer_id (demux->common.element_index,
              GST_OBJECT (stream->pad), &stream->index_writer_id);

        GST_LOG_OBJECT (demux, "adding association %" GST_TIME_FORMAT "-> %"
            G_GUINT64_FORMAT " for writer id %d",
            GST_TIME_ARGS (buffer_timestamp), cluster_offset,
            stream->index_writer_id);
        gst_index_add_association (demux->common.element_index,
            stream->index_writer_id, GST_BUFFER_FLAG_IS_SET (sub,
                GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : GST_ASSOCIATION_FLAG_KEY_UNIT,
            GST_FORMAT_TIME, buffer_timestamp, GST_FORMAT_BYTES, cluster_offset,
            NULL);
      }
#endif

      /* Postprocess the buffers depending on the codec used */
      if (stream->postprocess_frame) {
        GST_LOG_OBJECT (demux, "running post process");
        ret = stream->postprocess_frame (GST_ELEMENT (demux), stream, &sub);
      }

      /* At this point, we have a sub-buffer pointing at data within a larger
         buffer. This data might not be aligned with anything. If the data is
         raw samples though, we want it aligned to the raw type (eg, 4 bytes
         for 32 bit samples, etc), or bad things will happen downstream as
         elements typically assume minimal alignment.
         Therefore, create an aligned copy if necessary. */
      sub = gst_matroska_demux_align_buffer (demux, sub, stream->alignment);

      if (!strcmp (stream->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)) {
        guint64 start_clip = 0, end_clip = 0;

        /* Codec delay is part of the timestamps */
        if (GST_BUFFER_PTS_IS_VALID (sub) && stream->codec_delay) {
          if (GST_BUFFER_PTS (sub) > stream->codec_delay) {
            GST_BUFFER_PTS (sub) -= stream->codec_delay;
          } else {
            GST_BUFFER_PTS (sub) = 0;

            /* Opus GstAudioClippingMeta units are scaled by 48000/sample_rate.
               That is, if a Opus track has audio encoded at 24000 Hz and 132
               samples need to be clipped, GstAudioClippingMeta.start will be
               set to 264. (This is also the case for buffer offsets.)
               Opus sample rates are always divisors of 48000 Hz, which is the
               maximum allowed sample rate. */
            start_clip =
                gst_util_uint64_scale_round (stream->codec_delay, 48000,
                GST_SECOND);

            if (GST_BUFFER_DURATION_IS_VALID (sub)) {
              if (GST_BUFFER_DURATION (sub) > stream->codec_delay)
                GST_BUFFER_DURATION (sub) -= stream->codec_delay;
              else
                GST_BUFFER_DURATION (sub) = 0;
            }
          }
        }

        if (block_discardpadding) {
          end_clip =
              gst_util_uint64_scale_round (block_discardpadding, 48000,
              GST_SECOND);
        }

        if (start_clip || end_clip) {
          gst_buffer_add_audio_clipping_meta (sub, GST_FORMAT_DEFAULT,
              start_clip, end_clip);
        }
      }

      if (GST_BUFFER_PTS_IS_VALID (sub)) {
        stream->pos = GST_BUFFER_PTS (sub);
        if (GST_BUFFER_DURATION_IS_VALID (sub))
          stream->pos += GST_BUFFER_DURATION (sub);
      } else if (GST_BUFFER_DTS_IS_VALID (sub)) {
        stream->pos = GST_BUFFER_DTS (sub);
        if (GST_BUFFER_DURATION_IS_VALID (sub))
          stream->pos += GST_BUFFER_DURATION (sub);
      }

      ret = gst_pad_push (stream->pad, sub);

      if (demux->common.segment.rate < 0) {
        if (lace_time > demux->common.segment.stop && ret == GST_FLOW_EOS) {
          /* In reverse playback we can get a GST_FLOW_EOS when
           * we are at the end of the segment, so we just need to jump
           * back to the previous section. */
          GST_DEBUG_OBJECT (demux, "downstream has reached end of segment");
          ret = GST_FLOW_OK;
        }
      }
      /* combine flows */
      ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner,
          stream->pad, ret);

    next_lace:
      size -= lace_size[n];
      if (lace_time != GST_CLOCK_TIME_NONE && duration)
        lace_time += duration / laces;
      else
        lace_time = GST_CLOCK_TIME_NONE;
    }
  }

done:
  if (buf) {
    gst_buffer_unmap (buf, &map);
    gst_buffer_unref (buf);
  }
  g_free (lace_size);

  return ret;

  /* EXITS */
eos:
  {
    stream->eos = TRUE;
    ret = GST_FLOW_OK;
    /* combine flows */
    ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner, stream->pad,
        ret);
    goto done;
  }
invalid_lacing:
  {
    GST_ELEMENT_WARNING (demux, STREAM, DEMUX, (NULL), ("Invalid lacing size"));
    /* non-fatal, try next block(group) */
    ret = GST_FLOW_OK;
    goto done;
  }
data_error:
  {
    GST_ELEMENT_WARNING (demux, STREAM, DEMUX, (NULL), ("Data error"));
    /* non-fatal, try next block(group) */
    ret = GST_FLOW_OK;
    goto done;
  }
}

Project: gst-plugins-good
File: matroska-demux.c

gst_matroska_demux_check_seekability (GstMatroskaDemux * demux)
{
  GstQuery *query;
  gboolean seekable = FALSE;
  gint64 start = -1, stop = -1;

  query = gst_query_new_seeking (GST_FORMAT_BYTES);
  if (!gst_pad_peer_query (demux->common.sinkpad, query)) {
    GST_DEBUG_OBJECT (demux, "seeking query failed");
    goto done;
  }

  gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);

  /* try harder to query upstream size if we didn't get it the first time */
  if (seekable && stop == -1) {
    GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
    gst_pad_peer_query_duration (demux->common.sinkpad, GST_FORMAT_BYTES,
        &stop);
  }

  /* if upstream doesn't know the size, it's likely that it's not seekable in
   * practice even if it technically may be seekable */
  if (seekable && (start != 0 || stop <= start)) {
    GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
    seekable = FALSE;
  }

done:
  GST_INFO_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
      G_GUINT64_FORMAT ")", seekable, start, stop);
  demux->seekable = seekable;

  gst_query_unref (query);
}

Project: gst-plugins-good
File: matroska-demux.c

gst_matroska_demux_check_subtitle_buffer (GstElement * element,
    GstMatroskaTrackContext * stream, GstBuffer ** buf)
{
  GstMatroskaTrackSubtitleContext *sub_stream;
  const gchar *encoding;
  GError *err = NULL;
  GstBuffer *newbuf;
  gchar *utf8;
  GstMapInfo map;
  gboolean needs_unmap = TRUE;

  sub_stream = (GstMatroskaTrackSubtitleContext *) stream;

  if (!gst_buffer_get_size (*buf) || !gst_buffer_map (*buf, &map, GST_MAP_READ))
    return GST_FLOW_OK;

  /* The subtitle buffer we push out should not include a NUL terminator as
   * part of the data. */
  if (map.data[map.size - 1] == '\0') {
    gst_buffer_set_size (*buf, map.size - 1);
    gst_buffer_unmap (*buf, &map);
    gst_buffer_map (*buf, &map, GST_MAP_READ);
  }

  if (!sub_stream->invalid_utf8) {
    if (g_utf8_validate ((gchar *) map.data, map.size, NULL)) {
      goto next;
    }
    GST_WARNING_OBJECT (element, "subtitle stream %" G_GUINT64_FORMAT
        " is not valid UTF-8, this is broken according to the matroska"
        " specification", stream->num);
    sub_stream->invalid_utf8 = TRUE;
  }

  /* file with broken non-UTF8 subtitle, do the best we can do to fix it */
  encoding = g_getenv ("GST_SUBTITLE_ENCODING");
  if (encoding == NULL || *encoding == '\0') {
    /* if local encoding is UTF-8 and no encoding specified
     * via the environment variable, assume ISO-8859-15 */
    if (g_get_charset (&encoding)) {
      encoding = "ISO-8859-15";
    }
  }

  utf8 =
      g_convert_with_fallback ((gchar *) map.data, map.size, "UTF-8", encoding,
      (char *) "*", NULL, NULL, &err);

  if (err) {
    GST_LOG_OBJECT (element, "could not convert string from '%s' to UTF-8: %s",
        encoding, err->message);
    g_error_free (err);
    g_free (utf8);

    /* invalid input encoding, fall back to ISO-8859-15 (always succeeds) */
    encoding = "ISO-8859-15";
    utf8 =
        g_convert_with_fallback ((gchar *) map.data, map.size, "UTF-8",
        encoding, (char *) "*", NULL, NULL, NULL);
  }

  GST_LOG_OBJECT (element, "converted subtitle text from %s to UTF-8 %s",
      encoding, (err) ? "(using ISO-8859-15 as fallback)" : "");

  if (utf8 == NULL)
    utf8 = g_strdup ("invalid subtitle");

  newbuf = gst_buffer_new_wrapped (utf8, strlen (utf8));
  gst_buffer_unmap (*buf, &map);
  gst_buffer_copy_into (newbuf, *buf,
      GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_META,
      0, -1);
  gst_buffer_unref (*buf);

  *buf = newbuf;
  gst_buffer_map (*buf, &map, GST_MAP_READ);

next:

  if (sub_stream->check_markup) {
    /* caps claim markup text, so we need to escape text,
     * except if text is already markup and then needs no further escaping */
    sub_stream->seen_markup_tag = sub_stream->seen_markup_tag ||
        gst_matroska_demux_subtitle_chunk_has_tag (element, (gchar *) map.data);

    if (!sub_stream->seen_markup_tag) {
      utf8 = g_markup_escape_text ((gchar *) map.data, map.size);

      newbuf = gst_buffer_new_wrapped (utf8, strlen (utf8));
      gst_buffer_unmap (*buf, &map);
      gst_buffer_copy_into (newbuf, *buf,
          GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_FLAGS |
          GST_BUFFER_COPY_META, 0, -1);
      gst_buffer_unref (*buf);

      *buf = newbuf;
      needs_unmap = FALSE;
    }
  }

  if (needs_unmap)
    gst_buffer_unmap (*buf, &map);

  return GST_FLOW_OK;
}

Project: gst-plugins-good
File: matroska-demux.c

gst_matroska_demux_query (GstMatroskaDemux * demux, GstPad * pad,
    GstQuery * query)
{
  gboolean res = FALSE;
  GstMatroskaTrackContext *context = NULL;

  if (pad) {
    context = gst_pad_get_element_private (pad);
  }

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:
    {
      GstFormat format;

      gst_query_parse_position (query, &format, NULL);

      res = TRUE;
      if (format == GST_FORMAT_TIME) {
        GST_OBJECT_LOCK (demux);
        if (context)
          gst_query_set_position (query, GST_FORMAT_TIME,
              MAX (context->pos, demux->stream_start_time) -
              demux->stream_start_time);
        else
          gst_query_set_position (query, GST_FORMAT_TIME,
              MAX (demux->common.segment.position, demux->stream_start_time) -
              demux->stream_start_time);
        GST_OBJECT_UNLOCK (demux);
      } else if (format == GST_FORMAT_DEFAULT && context
          && context->default_duration) {
        GST_OBJECT_LOCK (demux);
        gst_query_set_position (query, GST_FORMAT_DEFAULT,
            context->pos / context->default_duration);
        GST_OBJECT_UNLOCK (demux);
      } else {
        GST_DEBUG_OBJECT (demux,
            "only position query in TIME and DEFAULT format is supported");
        res = FALSE;
      }

      break;
    }
    case GST_QUERY_DURATION:
    {
      GstFormat format;

      gst_query_parse_duration (query, &format, NULL);

      res = TRUE;
      if (format == GST_FORMAT_TIME) {
        GST_OBJECT_LOCK (demux);
        gst_query_set_duration (query, GST_FORMAT_TIME,
            demux->common.segment.duration);
        GST_OBJECT_UNLOCK (demux);
      } else if (format == GST_FORMAT_DEFAULT && context
          && context->default_duration) {
        GST_OBJECT_LOCK (demux);
        gst_query_set_duration (query, GST_FORMAT_DEFAULT,
            demux->common.segment.duration / context->default_duration);
        GST_OBJECT_UNLOCK (demux);
      } else {
        GST_DEBUG_OBJECT (demux,
            "only duration query in TIME and DEFAULT format is supported");
        res = FALSE;
      }
      break;
    }

    case GST_QUERY_SEEKING:
    {
      GstFormat fmt;

      gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
      GST_OBJECT_LOCK (demux);
      if (fmt == GST_FORMAT_TIME) {
        gboolean seekable;

        if (demux->streaming) {
          /* assuming we'll be able to get an index ... */
          seekable = demux->seekable;
        } else {
          seekable = TRUE;
        }

        gst_query_set_seeking (query, GST_FORMAT_TIME, seekable,
            0, demux->common.segment.duration);
        res = TRUE;
      }
      GST_OBJECT_UNLOCK (demux);
      break;
    }
    case GST_QUERY_SEGMENT:
    {
      GstFormat format;
      gint64 start, stop;

      format = demux->common.segment.format;

      start =
          gst_segment_to_stream_time (&demux->common.segment, format,
          demux->common.segment.start);
      if ((stop = demux->common.segment.stop) == -1)
        stop = demux->common.segment.duration;
      else
        stop =
            gst_segment_to_stream_time (&demux->common.segment, format, stop);

      gst_query_set_segment (query, demux->common.segment.rate, format, start,
          stop);
      res = TRUE;
      break;
    }
    default:
      if (pad)
        res = gst_pad_query_default (pad, (GstObject *) demux, query);
      else
        res =
            GST_ELEMENT_CLASS (parent_class)->query (GST_ELEMENT_CAST (demux),
            query);
      break;
  }

  return res;
}

Project: gst-plugins-good
File: qtdemux.c

round_up_pow2 (guint n)
{
  n = n - 1;
  n = n | (n >> 1);
  n = n | (n >> 2);
  n = n | (n >> 4);
  n = n | (n >> 8);
  n = n | (n >> 16);
  return n + 1;
}

Project: gst-plugins-good
File: matroska-demux.c

gst_matroska_demux_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec)
{
  GstMatroskaDemux *demux;

  g_return_if_fail (GST_IS_MATROSKA_DEMUX (object));
  demux = GST_MATROSKA_DEMUX (object);

  switch (prop_id) {
    case PROP_MAX_GAP_TIME:
      GST_OBJECT_LOCK (demux);
      g_value_set_uint64 (value, demux->max_gap_time);
      GST_OBJECT_UNLOCK (demux);
      break;
    case PROP_MAX_BACKTRACK_DISTANCE:
      GST_OBJECT_LOCK (demux);
      g_value_set_uint (value, demux->max_backtrack_distance);
      GST_OBJECT_UNLOCK (demux);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

Project: gst-plugins-good
File: matroska-demux.c

gst_matroska_track_encoding_scope_name (gint val)
{
  GEnumValue *en;
  GEnumClass *enum_class =
      g_type_class_ref (MATROSKA_TRACK_ENCODING_SCOPE_TYPE);

  en = g_enum_get_value (G_ENUM_CLASS (enum_class), val);
  return en ? en->value_nick : NULL;
}

Project: gst-plugins-good
File: matroska-demux.c

gst_matroska_demux_parse_colour (GstMatroskaDemux * demux, GstEbmlRead * ebml,
    GstMatroskaTrackVideoContext * video_context)
{
  GstFlowReturn ret;
  GstVideoColorimetry colorimetry;
  guint32 id;
  guint64 num;

  colorimetry.range = GST_VIDEO_COLOR_RANGE_UNKNOWN;
  colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_UNKNOWN;
  colorimetry.transfer = GST_VIDEO_TRANSFER_UNKNOWN;
  colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_UNKNOWN;

  DEBUG_ELEMENT_START (demux, ebml, "TrackVideoColour");

  if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK)
    goto beach;

  while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
    if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
      goto beach;

    switch (id) {
      case GST_MATROSKA_ID_VIDEOMATRIXCOEFFICIENTS:{
        if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
          goto beach;

        colorimetry.matrix = gst_video_color_matrix_from_iso ((guint) num);
        break;
      }

      case GST_MATROSKA_ID_VIDEORANGE:{
        if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
          goto beach;

        switch (num) {
          case 0:
            colorimetry.range = GST_VIDEO_COLOR_RANGE_UNKNOWN;
            break;
          case 1:
            colorimetry.range = GST_VIDEO_COLOR_RANGE_16_235;
            break;
          case 2:
            colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
            break;
          default:
            GST_FIXME_OBJECT (demux, "Unsupported color range  %"
                G_GUINT64_FORMAT, num);
            break;
        }
        break;
      }

      case GST_MATROSKA_ID_VIDEOTRANSFERCHARACTERISTICS:{
        if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
          goto beach;

        colorimetry.transfer =
            gst_video_transfer_function_from_iso ((guint) num);
        break;
      }

      case GST_MATROSKA_ID_VIDEOPRIMARIES:{
        if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
          goto beach;

        colorimetry.primaries =
            gst_video_color_primaries_from_iso ((guint) num);
        break;
      }

      case GST_MATROSKA_ID_MASTERINGMETADATA:{
        if ((ret =
                gst_matroska_demux_parse_mastering_metadata (demux, ebml,
                    video_context)) != GST_FLOW_OK)
          goto beach;
        break;
      }

      case GST_MATROSKA_ID_MAXCLL:{
        if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
          goto beach;
        if (num > G_MAXUINT16) {
          GST_WARNING_OBJECT (demux,
              "Too large maxCLL value %" G_GUINT64_FORMAT, num);
        } else {
          video_context->content_light_level.max_content_light_level = num;
        }
        break;
      }

      case GST_MATROSKA_ID_MAXFALL:{
        if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
          goto beach;
        if (num >= G_MAXUINT16) {
          GST_WARNING_OBJECT (demux,
              "Too large maxFALL value %" G_GUINT64_FORMAT, num);
        } else {
          video_context->content_light_level.max_frame_average_light_level =
              num;
        }
        break;
      }

      default:
        GST_FIXME_OBJECT (demux, "Unsupported subelement 0x%x in Colour", id);
        ret = gst_ebml_read_skip (ebml);
        break;
    }
  }

  memcpy (&video_context->colorimetry, &colorimetry,
      sizeof (GstVideoColorimetry));

beach:
  DEBUG_ELEMENT_STOP (demux, ebml, "TrackVideoColour", ret);
  return ret;
}

Project: gst-plugins-good
File: matroska-demux.c

gst_matroska_demux_check_read_size (GstMatroskaDemux * demux, guint64 bytes)
{
  if (G_UNLIKELY (bytes > MAX_BLOCK_SIZE)) {
    /* only a few blocks are expected/allowed to be large,
     * and will be recursed into, whereas others will be read and must fit */
    if (demux->streaming) {
      /* fatal in streaming case, as we can't step over easily */
      GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL),
          ("reading large block of size %" G_GUINT64_FORMAT " not supported; "
              "file might be corrupt.", bytes));
      return GST_FLOW_ERROR;
    } else {
      /* indicate higher level to quietly give up */
      GST_DEBUG_OBJECT (demux,
          "too large block of size %" G_GUINT64_FORMAT, bytes);
      return GST_FLOW_ERROR;
    }
  } else {
    return GST_FLOW_OK;
  }
}

Project: gst-plugins-good
File: matroska-demux.c

gst_matroska_demux_element_send_event (GstElement * element, GstEvent * event)
{
  GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (element);
  gboolean res;

  g_return_val_if_fail (event != NULL, FALSE);

  if (GST_EVENT_TYPE (event) == GST_EVENT_SEEK) {
    /* no seeking until we are (safely) ready */
    if (demux->common.state != GST_MATROSKA_READ_STATE_DATA) {
      GST_DEBUG_OBJECT (demux,
          "not ready for seeking yet, deferring seek: %" GST_PTR_FORMAT, event);
      if (demux->deferred_seek_event)
        gst_event_unref (demux->deferred_seek_event);
      demux->deferred_seek_event = event;
      demux->deferred_seek_pad = NULL;
      return TRUE;
    }
    res = gst_matroska_demux_handle_seek_event (demux, NULL, event);
  } else {
    GST_WARNING_OBJECT (demux, "Unhandled event of type %s",
        GST_EVENT_TYPE_NAME (event));
    res = FALSE;
  }
  gst_event_unref (event);
  return res;
}

Project: gst-plugins-good
File: matroska-demux.c

bit_reader_skip_ebml_num (GstBitReader * br)
{
  guint8 i, v = 0;

  if (!gst_bit_reader_peek_bits_uint8 (br, &v, 8))
    return FALSE;

  for (i = 0; i < 8; i++) {
    if ((v & (0x80 >> i)) != 0)
      break;
  }
  return gst_bit_reader_skip (br, (i + 1) * 8);
}

Project: gst-plugins-good
File: matroska-demux.c

gst_matroska_demux_add_prores_header (GstElement * element,
    GstMatroskaTrackContext * stream, GstBuffer ** buf)
{
  GstBuffer *newbuf = gst_buffer_new_allocate (NULL, 8, NULL);
  GstMapInfo map;
  guint32 frame_size;

  if (!gst_buffer_map (newbuf, &map, GST_MAP_WRITE)) {
    GST_ERROR ("Failed to map newly allocated buffer");
    return GST_FLOW_ERROR;
  }

  frame_size = gst_buffer_get_size (*buf);

  GST_WRITE_UINT32_BE (map.data, frame_size);
  map.data[4] = 'i';
  map.data[5] = 'c';
  map.data[6] = 'p';
  map.data[7] = 'f';

  gst_buffer_unmap (newbuf, &map);
  *buf = gst_buffer_append (newbuf, *buf);

  return GST_FLOW_OK;
}

Project: gst-plugins-good
File: matroska-demux.c

gst_matroska_demux_parse_contents (GstMatroskaDemux * demux, GstEbmlRead * ebml)
{
  GstFlowReturn ret = GST_FLOW_OK;
  guint32 id;

  DEBUG_ELEMENT_START (demux, ebml, "SeekHead");

  if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) {
    DEBUG_ELEMENT_STOP (demux, ebml, "SeekHead", ret);
    return ret;
  }

  while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
    if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
      break;

    switch (id) {
      case GST_MATROSKA_ID_SEEKENTRY:
      {
        ret = gst_matroska_demux_parse_contents_seekentry (demux, ebml);
        /* Ignore EOS and errors here */
        if (ret != GST_FLOW_OK) {
          GST_DEBUG_OBJECT (demux, "Ignoring %s", gst_flow_get_name (ret));
          ret = GST_FLOW_OK;
        }
        break;
      }

      default:
        ret = gst_matroska_read_common_parse_skip (&demux->common,
            ebml, "SeekHead", id);
        break;
    }
  }

  DEBUG_ELEMENT_STOP (demux, ebml, "SeekHead", ret);

  /* Sort clusters by position for easier searching */
  if (demux->clusters)
    g_array_sort (demux->clusters, (GCompareFunc) gst_matroska_cluster_compare);

  return ret;
}

Project: gst-plugins-good
File: matroska-demux.c

gst_matroska_demux_reset (GstElement * element)
{
  GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (element);

  GST_DEBUG_OBJECT (demux, "Resetting state");

  gst_matroska_read_common_reset (GST_ELEMENT (demux), &demux->common);

  demux->num_a_streams = 0;
  demux->num_t_streams = 0;
  demux->num_v_streams = 0;
  demux->have_nonintraonly_v_streams = FALSE;

  demux->have_group_id = FALSE;
  demux->group_id = G_MAXUINT;

  demux->clock = NULL;
  demux->tracks_parsed = FALSE;

  if (demux->clusters) {
    g_array_free (demux->clusters, TRUE);
    demux->clusters = NULL;
  }

  g_list_foreach (demux->seek_parsed,
      (GFunc) gst_matroska_read_common_free_parsed_el, NULL);
  g_list_free (demux->seek_parsed);
  demux->seek_parsed = NULL;

  demux->last_stop_end = GST_CLOCK_TIME_NONE;
  demux->seek_block = 0;
  demux->stream_start_time = GST_CLOCK_TIME_NONE;
  demux->to_time = GST_CLOCK_TIME_NONE;
  demux->cluster_time = GST_CLOCK_TIME_NONE;
  demux->cluster_offset = 0;
  demux->cluster_prevsize = 0;
  demux->seen_cluster_prevsize = FALSE;
  demux->next_cluster_offset = 0;
  demux->stream_last_time = GST_CLOCK_TIME_NONE;
  demux->last_cluster_offset = 0;
  demux->index_offset = 0;
  demux->seekable = FALSE;
  demux->need_segment = FALSE;
  demux->segment_seqnum = 0;
  demux->requested_seek_time = GST_CLOCK_TIME_NONE;
  demux->seek_offset = -1;
  demux->audio_lead_in_ts = 0;
  demux->building_index = FALSE;
  if (demux->seek_event) {
    gst_event_unref (demux->seek_event);
    demux->seek_event = NULL;
  }

  demux->seek_index = NULL;
  demux->seek_entry = 0;

  if (demux->new_segment) {
    gst_event_unref (demux->new_segment);
    demux->new_segment = NULL;
  }

  demux->invalid_duration = FALSE;

  demux->cached_length = G_MAXUINT64;

  if (demux->deferred_seek_event)
    gst_event_unref (demux->deferred_seek_event);
  demux->deferred_seek_event = NULL;
  demux->deferred_seek_pad = NULL;

  gst_flow_combiner_clear (demux->flowcombiner);
}

Project: gst-plugins-good
File: matroska-demux.c

gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux,
    GstPad * pad, GstEvent * event)
{
  GstMatroskaIndex *entry = NULL;
  GstMatroskaIndex scan_entry;
  GstSeekFlags flags;
  GstSeekType cur_type, stop_type;
  GstFormat format;
  gboolean flush, keyunit, instant_rate_change, before, after, accurate,
      snap_next;
  gdouble rate;
  gint64 cur, stop;
  GstMatroskaTrackContext *track = NULL;
  GstSegment seeksegment = { 0, };
  guint64 seekpos;
  gboolean update = TRUE;
  gboolean pad_locked = FALSE;
  guint32 seqnum;
  GstSearchMode snap_dir;

  g_return_val_if_fail (event != NULL, FALSE);

  if (pad)
    track = gst_pad_get_element_private (pad);

  GST_DEBUG_OBJECT (demux, "Have seek %" GST_PTR_FORMAT, event);

  gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur,
      &stop_type, &stop);
  seqnum = gst_event_get_seqnum (event);

  /* we can only seek on time */
  if (format != GST_FORMAT_TIME) {
    GST_DEBUG_OBJECT (demux, "Can only seek on TIME");
    return FALSE;
  }

  GST_DEBUG_OBJECT (demux, "configuring seek");

  flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
  keyunit = ! !(flags & GST_SEEK_FLAG_KEY_UNIT);
  after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);
  before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
  accurate = ! !(flags & GST_SEEK_FLAG_ACCURATE);
  instant_rate_change = ! !(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);

  /* Directly send the instant-rate-change event here before taking the
   * stream-lock so that it can be applied as soon as possible */
  if (instant_rate_change) {
    GstEvent *ev;

    /* instant rate change only supported if direction does not change. All
     * other requirements are already checked before creating the seek event
     * but let's double-check here to be sure */
    if ((rate > 0 && demux->common.segment.rate < 0) ||
        (rate < 0 && demux->common.segment.rate > 0) ||
        cur_type != GST_SEEK_TYPE_NONE ||
        stop_type != GST_SEEK_TYPE_NONE || flush) {
      GST_ERROR_OBJECT (demux,
          "Instant rate change seeks only supported in the "
          "same direction, without flushing and position change");
      return FALSE;
    }

    ev = gst_event_new_instant_rate_change (rate /
        demux->common.segment.rate, (GstSegmentFlags) flags);
    gst_event_set_seqnum (ev, seqnum);
    gst_matroska_demux_send_event (demux, ev);
    return TRUE;
  }

  /* copy segment, we need this because we still need the old
   * segment when we close the current segment. */
  memcpy (&seeksegment, &demux->common.segment, sizeof (GstSegment));

  /* pull mode without index means that the actual duration is not known,
   * we might be playing a file that's still being recorded
   * so, invalidate our current duration, which is only a moving target,
   * and should not be used to clamp anything */
  if (!demux->streaming && !demux->common.index && demux->invalid_duration) {
    seeksegment.duration = GST_CLOCK_TIME_NONE;
  }

  /* Subtract stream_start_time so we always seek on a segment
   * in stream time */
  if (GST_CLOCK_TIME_IS_VALID (demux->stream_start_time)) {
    seeksegment.start -= demux->stream_start_time;
    seeksegment.position -= demux->stream_start_time;
    if (GST_CLOCK_TIME_IS_VALID (seeksegment.stop))
      seeksegment.stop -= demux->stream_start_time;
    else
      seeksegment.stop = seeksegment.duration;
  }

  if (!gst_segment_do_seek (&seeksegment, rate, format, flags,
          cur_type, cur, stop_type, stop, &update)) {
    GST_WARNING_OBJECT (demux, "gst_segment_do_seek() failed.");
    return FALSE;
  }

  /* Restore the clip timestamp offset */
  if (GST_CLOCK_TIME_IS_VALID (demux->stream_start_time)) {
    seeksegment.position += demux->stream_start_time;
    seeksegment.start += demux->stream_start_time;
    if (!GST_CLOCK_TIME_IS_VALID (seeksegment.stop))
      seeksegment.stop = seeksegment.duration;
    if (GST_CLOCK_TIME_IS_VALID (seeksegment.stop))
      seeksegment.stop += demux->stream_start_time;
  }

  /* restore segment duration (if any effect),
   * would be determined again when parsing, but anyway ... */
  seeksegment.duration = demux->common.segment.duration;

  /* always do full update if flushing,
   * otherwise problems might arise downstream with missing keyframes etc */
  update = update || flush;

  GST_DEBUG_OBJECT (demux, "New segment %" GST_SEGMENT_FORMAT, &seeksegment);

  /* check sanity before we start flushing and all that */
  snap_next = after && !before;
  if (seeksegment.rate < 0)
    snap_dir = snap_next ? GST_SEARCH_MODE_BEFORE : GST_SEARCH_MODE_AFTER;
  else
    snap_dir = snap_next ? GST_SEARCH_MODE_AFTER : GST_SEARCH_MODE_BEFORE;

  GST_OBJECT_LOCK (demux);

  seekpos = seeksegment.position;
  if (accurate) {
    seekpos -= MIN (seeksegment.position, demux->audio_lead_in_ts);
  }

  track = gst_matroska_read_common_get_seek_track (&demux->common, track);
  if ((entry = gst_matroska_read_common_do_index_seek (&demux->common, track,
              seekpos, &demux->seek_index, &demux->seek_entry,
              snap_dir)) == NULL) {
    /* pull mode without index can scan later on */
    if (demux->streaming) {
      GST_DEBUG_OBJECT (demux, "No matching seek entry in index");
      GST_OBJECT_UNLOCK (demux);
      return FALSE;
    } else if (rate < 0.0) {
      /* FIXME: We should build an index during playback or when scanning
       * that can be used here. The reverse playback code requires seek_index
       * and seek_entry to be set!
       */
      GST_DEBUG_OBJECT (demux,
          "No matching seek entry in index, needed for reverse playback");
      GST_OBJECT_UNLOCK (demux);
      return FALSE;
    }
  }
  GST_DEBUG_OBJECT (demux, "Seek position looks sane");
  GST_OBJECT_UNLOCK (demux);

  if (!update) {
    /* only have to update some segment,
     * but also still have to honour flush and so on */
    GST_DEBUG_OBJECT (demux, "... no update");
    /* bad goto, bad ... */
    goto next;
  }

  if (demux->streaming)
    goto finish;

next:
  if (flush) {
    GstEvent *flush_event = gst_event_new_flush_start ();
    gst_event_set_seqnum (flush_event, seqnum);
    GST_DEBUG_OBJECT (demux, "Starting flush");
    gst_pad_push_event (demux->common.sinkpad, gst_event_ref (flush_event));
    gst_matroska_demux_send_event (demux, flush_event);
  } else {
    GST_DEBUG_OBJECT (demux, "Non-flushing seek, pausing task");
    gst_pad_pause_task (demux->common.sinkpad);
  }
  /* ouch */
  if (!update) {
    GST_PAD_STREAM_LOCK (demux->common.sinkpad);
    pad_locked = TRUE;
    goto exit;
  }

  /* now grab the stream lock so that streaming cannot continue, for
   * non flushing seeks when the element is in PAUSED this could block
   * forever. */
  GST_DEBUG_OBJECT (demux, "Waiting for streaming to stop");
  GST_PAD_STREAM_LOCK (demux->common.sinkpad);
  pad_locked = TRUE;

  /* pull mode without index can do some scanning */
  if (!demux->streaming && !entry) {
    GstEvent *flush_event;

    /* need to stop flushing upstream as we need it next */
    if (flush) {
      flush_event = gst_event_new_flush_stop (TRUE);
      gst_event_set_seqnum (flush_event, seqnum);
      gst_pad_push_event (demux->common.sinkpad, flush_event);
    }
    entry = gst_matroska_demux_search_pos (demux, seekpos);
    /* keep local copy */
    if (entry) {
      scan_entry = *entry;
      g_free (entry);
      entry = &scan_entry;
    } else {
      GST_DEBUG_OBJECT (demux, "Scan failed to find matching position");
      if (flush) {
        flush_event = gst_event_new_flush_stop (TRUE);
        gst_event_set_seqnum (flush_event, seqnum);
        gst_matroska_demux_send_event (demux, flush_event);
      }
      goto seek_error;
    }
  }

finish:
  if (keyunit && seeksegment.rate > 0) {
    GST_DEBUG_OBJECT (demux, "seek to key unit, adjusting segment start from %"
        GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
        GST_TIME_ARGS (seeksegment.start), GST_TIME_ARGS (entry->time));
    seeksegment.start = MAX (entry->time, demux->stream_start_time);
    seeksegment.position = seeksegment.start;
    seeksegment.time = seeksegment.start - demux->stream_start_time;
  } else if (keyunit) {
    GST_DEBUG_OBJECT (demux, "seek to key unit, adjusting segment stop from %"
        GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
        GST_TIME_ARGS (seeksegment.stop), GST_TIME_ARGS (entry->time));
    seeksegment.stop = MAX (entry->time, demux->stream_start_time);
    seeksegment.position = seeksegment.stop;
  }

  if (demux->streaming) {
    GST_OBJECT_LOCK (demux);
    /* track real position we should start at */
    GST_DEBUG_OBJECT (demux, "storing segment start");
    demux->requested_seek_time = seeksegment.position;
    demux->seek_offset = entry->pos + demux->common.ebml_segment_start;
    GST_OBJECT_UNLOCK (demux);
    /* need to seek to cluster start to pick up cluster time */
    /* upstream takes care of flushing and all that
     * ... and newsegment event handling takes care of the rest */
    return perform_seek_to_offset (demux, rate,
        entry->pos + demux->common.ebml_segment_start, seqnum, flags);
  }

exit:
  if (flush) {
    GstEvent *flush_event = gst_event_new_flush_stop (TRUE);
    gst_event_set_seqnum (flush_event, seqnum);
    GST_DEBUG_OBJECT (demux, "Stopping flush");
    gst_pad_push_event (demux->common.sinkpad, gst_event_ref (flush_event));
    gst_matroska_demux_send_event (demux, flush_event);
  }

  GST_OBJECT_LOCK (demux);
  /* now update the real segment info */
  GST_DEBUG_OBJECT (demux, "Committing new seek segment");
  memcpy (&demux->common.segment, &seeksegment, sizeof (GstSegment));
  GST_OBJECT_UNLOCK (demux);

  /* update some (segment) state */
  if (!gst_matroska_demux_move_to_entry (demux, entry, TRUE, update))
    goto seek_error;

  /* notify start of new segment */
  if (demux->common.segment.flags & GST_SEEK_FLAG_SEGMENT) {
    GstMessage *msg;

    msg = gst_message_new_segment_start (GST_OBJECT (demux),
        GST_FORMAT_TIME, demux->common.segment.start);
    gst_message_set_seqnum (msg, seqnum);
    gst_element_post_message (GST_ELEMENT (demux), msg);
  }

  GST_OBJECT_LOCK (demux);
  if (demux->new_segment)
    gst_event_unref (demux->new_segment);

  /* On port from 0.10, discarded !update (for segment.update) here, FIXME? */
  demux->new_segment = gst_event_new_segment (&demux->common.segment);
  gst_event_set_seqnum (demux->new_segment, seqnum);
  if (demux->common.segment.rate < 0 && demux->common.segment.stop == -1)
    demux->to_time = demux->common.segment.position;
  else
    demux->to_time = GST_CLOCK_TIME_NONE;
  demux->segment_seqnum = seqnum;
  GST_OBJECT_UNLOCK (demux);

  /* restart our task since it might have been stopped when we did the
   * flush. */
  gst_pad_start_task (demux->common.sinkpad,
      (GstTaskFunction) gst_matroska_demux_loop, demux->common.sinkpad, NULL);

  /* streaming can continue now */
  if (pad_locked) {
    GST_PAD_STREAM_UNLOCK (demux->common.sinkpad);
  }

  return TRUE;

seek_error:
  {
    if (pad_locked) {
      GST_PAD_STREAM_UNLOCK (demux->common.sinkpad);
    }
    GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), ("Got a seek error"));
    return FALSE;
  }
}

Project: gst-plugins-good
File: matroska-demux.c

gst_matroska_demux_add_mpeg_seq_header (GstElement * element,
    GstMatroskaTrackContext * stream, GstBuffer ** buf)
{
  guint8 *seq_header;
  guint seq_header_len;
  guint32 header, tmp;

  if (stream->codec_state) {
    seq_header = stream->codec_state;
    seq_header_len = stream->codec_state_size;
  } else if (stream->codec_priv) {
    seq_header = stream->codec_priv;
    seq_header_len = stream->codec_priv_size;
  } else {
    return GST_FLOW_OK;
  }

  /* Sequence header only needed for keyframes */
  if (GST_BUFFER_FLAG_IS_SET (*buf, GST_BUFFER_FLAG_DELTA_UNIT))
    return GST_FLOW_OK;

  if (gst_buffer_get_size (*buf) < 4)
    return GST_FLOW_OK;

  gst_buffer_extract (*buf, 0, &tmp, sizeof (guint32));
  header = GUINT32_FROM_BE (tmp);

  /* Sequence start code, if not found prepend */
  if (header != 0x000001b3) {
    GstBuffer *newbuf;

    GST_DEBUG_OBJECT (element, "Prepending MPEG sequence header");

    newbuf = gst_buffer_new_wrapped (g_memdup (seq_header, seq_header_len),
        seq_header_len);

    gst_buffer_copy_into (newbuf, *buf, GST_BUFFER_COPY_TIMESTAMPS |
        GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_MEMORY, 0,
        gst_buffer_get_size (*buf));

    gst_buffer_unref (*buf);
    *buf = newbuf;
  }

  return GST_FLOW_OK;
}