Commit message: qtdemux: Fix out of bounds read in tag parsing code

We can't simply assume that the length of the tag value as given
inside the stream is correct but should also check against the amount of
data we have actually available.

https://bugzilla.gnome.org/show_bug.cgi?id=775451
CVE: CVE-2016-10199
CWE: CWE-125
Vulnerability description: The qtdemux_tag_add_str_full function in gst/isomp4/qtdemux.c in gst-plugins-good in GStreamer before 1.10.3 allows remote attackers to cause a denial of service (out-of-bounds read and crash) via a crafted tag value.

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

qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
{
  GstByteReader tkhd;
  int offset;
  GNode *mdia;
  GNode *mdhd;
  GNode *hdlr;
  GNode *minf;
  GNode *stbl;
  GNode *stsd;
  GNode *mp4a;
  GNode *mp4v;
  GNode *wave;
  GNode *esds;
  GNode *pasp;
  GNode *colr;
  GNode *tref;
  GNode *udta;
  GNode *svmi;
  GNode *fiel;

  QtDemuxStream *stream = NULL;
  gboolean new_stream = FALSE;
  gchar *codec = NULL;
  const guint8 *stsd_data;
  guint16 lang_code;            /* quicktime lang code or packed iso code */
  guint32 version;
  guint32 tkhd_flags = 0;
  guint8 tkhd_version = 0;
  guint32 fourcc;
  guint value_size, stsd_len, len;
  guint32 track_id;
  guint32 dummy;

  GST_DEBUG_OBJECT (qtdemux, "parse_trak");

  if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
      || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
      || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
    goto corrupt_file;

  /* pick between 64 or 32 bits */
  value_size = tkhd_version == 1 ? 8 : 4;
  if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
      !gst_byte_reader_get_uint32_be (&tkhd, &track_id))
    goto corrupt_file;

  if (!qtdemux->got_moov) {
    if (qtdemux_find_stream (qtdemux, track_id))
      goto existing_stream;
    stream = _create_stream ();
    stream->track_id = track_id;
    new_stream = TRUE;
  } else {
    stream = qtdemux_find_stream (qtdemux, track_id);
    if (!stream) {
      GST_WARNING_OBJECT (qtdemux, "Stream not found, going to ignore it");
      goto skip_track;
    }

    /* flush samples data from this track from previous moov */
    gst_qtdemux_stream_flush_segments_data (qtdemux, stream);
    gst_qtdemux_stream_flush_samples_data (qtdemux, stream);
  }
  /* need defaults for fragments */
  qtdemux_parse_trex (qtdemux, stream, &dummy, &dummy, &dummy);

  if (stream->pending_tags == NULL)
    stream->pending_tags = gst_tag_list_new_empty ();

  if ((tkhd_flags & 1) == 0)
    stream->disabled = TRUE;

  GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
      tkhd_version, tkhd_flags, stream->track_id);

  if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia)))
    goto corrupt_file;

  if (!(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd))) {
    /* be nice for some crooked mjp2 files that use mhdr for mdhd */
    if (qtdemux->major_brand != FOURCC_mjp2 ||
        !(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mhdr)))
      goto corrupt_file;
  }

  len = QT_UINT32 ((guint8 *) mdhd->data);
  version = QT_UINT32 ((guint8 *) mdhd->data + 8);
  GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version);
  if (version == 0x01000000) {
    if (len < 38)
      goto corrupt_file;
    stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28);
    stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32);
    lang_code = QT_UINT16 ((guint8 *) mdhd->data + 36);
  } else {
    if (len < 30)
      goto corrupt_file;
    stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 20);
    stream->duration = QT_UINT32 ((guint8 *) mdhd->data + 24);
    lang_code = QT_UINT16 ((guint8 *) mdhd->data + 28);
  }

  if (lang_code < 0x400) {
    qtdemux_lang_map_qt_code_to_iso (stream->lang_id, lang_code);
  } else if (lang_code == 0x7fff) {
    stream->lang_id[0] = 0;     /* unspecified */
  } else {
    stream->lang_id[0] = 0x60 + ((lang_code >> 10) & 0x1F);
    stream->lang_id[1] = 0x60 + ((lang_code >> 5) & 0x1F);
    stream->lang_id[2] = 0x60 + (lang_code & 0x1F);
    stream->lang_id[3] = 0;
  }

  GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT,
      stream->timescale);
  GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT,
      stream->duration);
  GST_LOG_OBJECT (qtdemux, "track language code/id: 0x%04x/%s",
      lang_code, stream->lang_id);

  if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0))
    goto corrupt_file;

  if ((tref = qtdemux_tree_get_child_by_type (trak, FOURCC_tref))) {
    /* chapters track reference */
    GNode *chap = qtdemux_tree_get_child_by_type (tref, FOURCC_chap);
    if (chap) {
      gsize length = GST_READ_UINT32_BE (chap->data);
      if (qtdemux->chapters_track_id)
        GST_FIXME_OBJECT (qtdemux, "Multiple CHAP tracks");

      if (length >= 12) {
        qtdemux->chapters_track_id =
            GST_READ_UINT32_BE ((gint8 *) chap->data + 8);
      }
    }
  }

  /* fragmented files may have bogus duration in moov */
  if (!qtdemux->fragmented &&
      qtdemux->duration != G_MAXINT64 && stream->duration != G_MAXINT32) {
    guint64 tdur1, tdur2;

    /* don't overflow */
    tdur1 = stream->timescale * (guint64) qtdemux->duration;
    tdur2 = qtdemux->timescale * (guint64) stream->duration;

    /* HACK:
     * some of those trailers, nowadays, have prologue images that are
     * themselves video tracks as well. I haven't really found a way to
     * identify those yet, except for just looking at their duration. */
    if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
      GST_WARNING_OBJECT (qtdemux,
          "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
          " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
          "found, assuming preview image or something; skipping track",
          stream->duration, stream->timescale, qtdemux->duration,
          qtdemux->timescale);
      if (new_stream)
        gst_qtdemux_stream_free (qtdemux, stream);
      return TRUE;
    }
  }

  if (!(hdlr = qtdemux_tree_get_child_by_type (mdia, FOURCC_hdlr)))
    goto corrupt_file;

  GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT,
      GST_FOURCC_ARGS (QT_FOURCC ((guint8 *) hdlr->data + 12)));

  len = QT_UINT32 ((guint8 *) hdlr->data);
  if (len >= 20)
    stream->subtype = QT_FOURCC ((guint8 *) hdlr->data + 16);
  GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT,
      GST_FOURCC_ARGS (stream->subtype));

  if (!(minf = qtdemux_tree_get_child_by_type (mdia, FOURCC_minf)))
    goto corrupt_file;

  if (!(stbl = qtdemux_tree_get_child_by_type (minf, FOURCC_stbl)))
    goto corrupt_file;

  /*parse svmi header if existing */
  svmi = qtdemux_tree_get_child_by_type (stbl, FOURCC_svmi);
  if (svmi) {
    len = QT_UINT32 ((guint8 *) svmi->data);
    version = QT_UINT32 ((guint8 *) svmi->data + 8);
    if (!version) {
      GstVideoMultiviewMode mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
      GstVideoMultiviewFlags flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
      guint8 frame_type, frame_layout;

      /* MPEG-A stereo video */
      if (qtdemux->major_brand == FOURCC_ss02)
        flags |= GST_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO;

      frame_type = QT_UINT8 ((guint8 *) svmi->data + 12);
      frame_layout = QT_UINT8 ((guint8 *) svmi->data + 13) & 0x01;
      switch (frame_type) {
        case 0:
          mode = GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
          break;
        case 1:
          mode = GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED;
          break;
        case 2:
          mode = GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
          break;
        case 3:
          /* mode 3 is primary/secondary view sequence, ie
           * left/right views in separate tracks. See section 7.2
           * of ISO/IEC 23000-11:2009 */
          GST_FIXME_OBJECT (qtdemux,
              "Implement stereo video in separate streams");
      }

      if ((frame_layout & 0x1) == 0)
        flags |= GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;

      GST_LOG_OBJECT (qtdemux,
          "StereoVideo: composition type: %u, is_left_first: %u",
          frame_type, frame_layout);
      stream->multiview_mode = mode;
      stream->multiview_flags = flags;
    }
  }

  /* parse stsd */
  if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd)))
    goto corrupt_file;
  stsd_data = (const guint8 *) stsd->data;

  /* stsd should at least have one entry */
  stsd_len = QT_UINT32 (stsd_data);
  if (stsd_len < 24) {
    /* .. but skip stream with empty stsd produced by some Vivotek cameras */
    if (stream->subtype == FOURCC_vivo) {
      if (new_stream)
        gst_qtdemux_stream_free (qtdemux, stream);
      return TRUE;
    } else {
      goto corrupt_file;
    }
  }

  GST_LOG_OBJECT (qtdemux, "stsd len:           %d", stsd_len);

  /* and that entry should fit within stsd */
  len = QT_UINT32 (stsd_data + 16);
  if (len > stsd_len + 16)
    goto corrupt_file;

  stream->fourcc = fourcc = QT_FOURCC (stsd_data + 16 + 4);
  GST_LOG_OBJECT (qtdemux, "stsd type:          %" GST_FOURCC_FORMAT,
      GST_FOURCC_ARGS (stream->fourcc));
  GST_LOG_OBJECT (qtdemux, "stsd type len:      %d", len);

  if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi))
    goto error_encrypted;

  if (fourcc == FOURCC_encv || fourcc == FOURCC_enca) {
    GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
    stream->protected = TRUE;
    if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
      GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
  }

  if (stream->subtype == FOURCC_vide) {
    guint32 w = 0, h = 0;
    gboolean gray;
    gint depth, palette_size, palette_count;
    guint32 matrix[9];
    guint32 *palette_data = NULL;

    stream->sampled = TRUE;

    /* version 1 uses some 64-bit ints */
    if (!gst_byte_reader_skip (&tkhd, 20 + value_size))
      goto corrupt_file;

    if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd"))
      goto corrupt_file;

    if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
        || !gst_byte_reader_get_uint32_be (&tkhd, &h))
      goto corrupt_file;

    stream->display_width = w >> 16;
    stream->display_height = h >> 16;

    qtdemux_inspect_transformation_matrix (qtdemux, stream, matrix,
        &stream->pending_tags);

    offset = 16;
    if (len < 86)
      goto corrupt_file;

    stream->width = QT_UINT16 (stsd_data + offset + 32);
    stream->height = QT_UINT16 (stsd_data + offset + 34);
    stream->fps_n = 0;          /* this is filled in later */
    stream->fps_d = 0;          /* this is filled in later */
    stream->bits_per_sample = QT_UINT16 (stsd_data + offset + 82);
    stream->color_table_id = QT_UINT16 (stsd_data + offset + 84);

    /* if color_table_id is 0, ctab atom must follow; however some files
     * produced by TMPEGEnc have color_table_id = 0 and no ctab atom, so
     * if color table is not present we'll correct the value */
    if (stream->color_table_id == 0 &&
        (len < 90 || QT_FOURCC (stsd_data + offset + 86) != FOURCC_ctab)) {
      stream->color_table_id = -1;
    }

    GST_LOG_OBJECT (qtdemux, "width %d, height %d, bps %d, color table id %d",
        stream->width, stream->height, stream->bits_per_sample,
        stream->color_table_id);

    depth = stream->bits_per_sample;

    /* more than 32 bits means grayscale */
    gray = (depth > 32);
    /* low 32 bits specify the depth  */
    depth &= 0x1F;

    /* different number of palette entries is determined by depth. */
    palette_count = 0;
    if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
      palette_count = (1 << depth);
    palette_size = palette_count * 4;

    if (stream->color_table_id) {
      switch (palette_count) {
        case 0:
          break;
        case 2:
          palette_data = g_memdup (ff_qt_default_palette_2, palette_size);
          break;
        case 4:
          palette_data = g_memdup (ff_qt_default_palette_4, palette_size);
          break;
        case 16:
          if (gray)
            palette_data = g_memdup (ff_qt_grayscale_palette_16, palette_size);
          else
            palette_data = g_memdup (ff_qt_default_palette_16, palette_size);
          break;
        case 256:
          if (gray)
            palette_data = g_memdup (ff_qt_grayscale_palette_256, palette_size);
          else
            palette_data = g_memdup (ff_qt_default_palette_256, palette_size);
          break;
        default:
          GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
              (_("The video in this file might not play correctly.")),
              ("unsupported palette depth %d", depth));
          break;
      }
    } else {
      gint i, j, start, end;

      if (len < 94)
        goto corrupt_file;

      /* read table */
      start = QT_UINT32 (stsd_data + offset + 86);
      palette_count = QT_UINT16 (stsd_data + offset + 90);
      end = QT_UINT16 (stsd_data + offset + 92);

      GST_LOG_OBJECT (qtdemux, "start %d, end %d, palette_count %d",
          start, end, palette_count);

      if (end > 255)
        end = 255;
      if (start > end)
        start = end;

      if (len < 94 + (end - start) * 8)
        goto corrupt_file;

      /* palette is always the same size */
      palette_data = g_malloc0 (256 * 4);
      palette_size = 256 * 4;

      for (j = 0, i = start; i <= end; j++, i++) {
        guint32 a, r, g, b;

        a = QT_UINT16 (stsd_data + offset + 94 + (j * 8));
        r = QT_UINT16 (stsd_data + offset + 96 + (j * 8));
        g = QT_UINT16 (stsd_data + offset + 98 + (j * 8));
        b = QT_UINT16 (stsd_data + offset + 100 + (j * 8));

        palette_data[i] = ((a & 0xff00) << 16) | ((r & 0xff00) << 8) |
            (g & 0xff00) | (b >> 8);
      }
    }

    if (stream->caps)
      gst_caps_unref (stream->caps);

    stream->caps =
        qtdemux_video_caps (qtdemux, stream, fourcc, stsd_data, &codec);
    if (G_UNLIKELY (!stream->caps)) {
      g_free (palette_data);
      goto unknown_stream;
    }

    if (codec) {
      gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
          GST_TAG_VIDEO_CODEC, codec, NULL);
      g_free (codec);
      codec = NULL;
    }


    if (palette_data) {
      GstStructure *s;

      if (stream->rgb8_palette)
        gst_memory_unref (stream->rgb8_palette);
      stream->rgb8_palette = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
          palette_data, palette_size, 0, palette_size, palette_data, g_free);

      s = gst_caps_get_structure (stream->caps, 0);

      /* non-raw video has a palette_data property. raw video has the palette as
       * an extra plane that we append to the output buffers before we push
       * them*/
      if (!gst_structure_has_name (s, "video/x-raw")) {
        GstBuffer *palette;

        palette = gst_buffer_new ();
        gst_buffer_append_memory (palette, stream->rgb8_palette);
        stream->rgb8_palette = NULL;

        gst_caps_set_simple (stream->caps, "palette_data",
            GST_TYPE_BUFFER, palette, NULL);
        gst_buffer_unref (palette);
      }
    } else if (palette_count != 0) {
      GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED,
          (NULL), ("Unsupported palette depth %d", depth));
    }

    GST_LOG_OBJECT (qtdemux, "frame count:   %u",
        QT_UINT16 (stsd_data + offset + 48));

    esds = NULL;
    pasp = NULL;
    colr = NULL;
    fiel = NULL;
    /* pick 'the' stsd child */
    if (!stream->protected)
      mp4v = qtdemux_tree_get_child_by_type (stsd, fourcc);
    else
      mp4v = qtdemux_tree_get_child_by_type (stsd, FOURCC_encv);

    if (mp4v) {
      esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds);
      pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp);
      colr = qtdemux_tree_get_child_by_type (mp4v, FOURCC_colr);
      fiel = qtdemux_tree_get_child_by_type (mp4v, FOURCC_fiel);
    }

    if (pasp) {
      const guint8 *pasp_data = (const guint8 *) pasp->data;

      stream->par_w = QT_UINT32 (pasp_data + 8);
      stream->par_h = QT_UINT32 (pasp_data + 12);
    } else {
      stream->par_w = 0;
      stream->par_h = 0;
    }

    if (fiel) {
      const guint8 *fiel_data = (const guint8 *) fiel->data;
      gint len = QT_UINT32 (fiel_data);

      if (len == 10) {
        stream->interlace_mode = GST_READ_UINT8 (fiel_data + 8);
        stream->field_order = GST_READ_UINT8 (fiel_data + 9);
      }
    }

    if (colr) {
      const guint8 *colr_data = (const guint8 *) colr->data;
      gint len = QT_UINT32 (colr_data);

      if (len == 19 || len == 18) {
        guint32 color_type = GST_READ_UINT32_LE (colr_data + 8);

        if (color_type == FOURCC_nclx || color_type == FOURCC_nclc) {
          guint16 primaries = GST_READ_UINT16_BE (colr_data + 12);
          guint16 transfer_function = GST_READ_UINT16_BE (colr_data + 14);
          guint16 matrix = GST_READ_UINT16_BE (colr_data + 16);
          gboolean full_range = len == 19 ? colr_data[17] >> 7 : FALSE;

          switch (primaries) {
            case 1:
              stream->colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
              break;
            case 5:
              stream->colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT470BG;
              break;
            case 6:
              stream->colorimetry.primaries =
                  GST_VIDEO_COLOR_PRIMARIES_SMPTE170M;
              break;
            case 9:
              stream->colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020;
              break;
            default:
              break;
          }

          switch (transfer_function) {
            case 1:
              stream->colorimetry.transfer = GST_VIDEO_TRANSFER_BT709;
              break;
            case 7:
              stream->colorimetry.transfer = GST_VIDEO_TRANSFER_SMPTE240M;
              break;
            default:
              break;
          }

          switch (matrix) {
            case 1:
              stream->colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT709;
              break;
            case 6:
              stream->colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT601;
              break;
            case 7:
              stream->colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_SMPTE240M;
              break;
            case 9:
              stream->colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT2020;
              break;
            default:
              break;
          }

          stream->colorimetry.range =
              full_range ? GST_VIDEO_COLOR_RANGE_0_255 :
              GST_VIDEO_COLOR_RANGE_16_235;
        } else {
          GST_DEBUG_OBJECT (qtdemux, "Unsupported color type");
        }
      } else {
        GST_WARNING_OBJECT (qtdemux, "Invalid colr atom size");
      }
    }

    if (esds) {
      gst_qtdemux_handle_esds (qtdemux, stream, esds, stream->pending_tags);
    } else {
      switch (fourcc) {
        case FOURCC_H264:
        case FOURCC_avc1:
        case FOURCC_avc3:
        {
          gint len = QT_UINT32 (stsd_data) - 0x66;
          const guint8 *avc_data = stsd_data + 0x66;

          /* find avcC */
          while (len >= 0x8) {
            gint size;

            if (QT_UINT32 (avc_data) <= len)
              size = QT_UINT32 (avc_data) - 0x8;
            else
              size = len - 0x8;

            if (size < 1)
              /* No real data, so break out */
              break;

            switch (QT_FOURCC (avc_data + 0x4)) {
              case FOURCC_avcC:
              {
                /* parse, if found */
                GstBuffer *buf;

                GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");

                /* First 4 bytes are the length of the atom, the next 4 bytes
                 * are the fourcc, the next 1 byte is the version, and the
                 * subsequent bytes are profile_tier_level structure like data. */
                gst_codec_utils_h264_caps_set_level_and_profile (stream->caps,
                    avc_data + 8 + 1, size - 1);
                buf = gst_buffer_new_and_alloc (size);
                gst_buffer_fill (buf, 0, avc_data + 0x8, size);
                gst_caps_set_simple (stream->caps,
                    "codec_data", GST_TYPE_BUFFER, buf, NULL);
                gst_buffer_unref (buf);

                break;
              }
              case FOURCC_strf:
              {
                GstBuffer *buf;

                GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");

                /* First 4 bytes are the length of the atom, the next 4 bytes
                 * are the fourcc, next 40 bytes are BITMAPINFOHEADER,
                 * next 1 byte is the version, and the
                 * subsequent bytes are sequence parameter set like data. */

                size -= 40;     /* we'll be skipping BITMAPINFOHEADER */
                if (size > 1) {
                  gst_codec_utils_h264_caps_set_level_and_profile (stream->caps,
                      avc_data + 8 + 40 + 1, size - 1);

                  buf = gst_buffer_new_and_alloc (size);
                  gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
                  gst_caps_set_simple (stream->caps,
                      "codec_data", GST_TYPE_BUFFER, buf, NULL);
                  gst_buffer_unref (buf);
                }
                break;
              }
              case FOURCC_btrt:
              {
                guint avg_bitrate, max_bitrate;

                /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
                if (size < 12)
                  break;

                max_bitrate = QT_UINT32 (avc_data + 0xc);
                avg_bitrate = QT_UINT32 (avc_data + 0x10);

                if (!max_bitrate && !avg_bitrate)
                  break;

                /* Some muxers seem to swap the average and maximum bitrates
                 * (I'm looking at you, YouTube), so we swap for sanity. */
                if (max_bitrate > 0 && max_bitrate < avg_bitrate) {
                  guint temp = avg_bitrate;

                  avg_bitrate = max_bitrate;
                  max_bitrate = temp;
                }

                if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
                  gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
                      GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
                }
                if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
                  gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
                      GST_TAG_BITRATE, avg_bitrate, NULL);
                }

                break;
              }

              default:
                break;
            }

            len -= size + 8;
            avc_data += size + 8;
          }

          break;
        }
        case FOURCC_H265:
        case FOURCC_hvc1:
        case FOURCC_hev1:
        {
          gint len = QT_UINT32 (stsd_data) - 0x66;
          const guint8 *hevc_data = stsd_data + 0x66;

          /* find hevc */
          while (len >= 0x8) {
            gint size;

            if (QT_UINT32 (hevc_data) <= len)
              size = QT_UINT32 (hevc_data) - 0x8;
            else
              size = len - 0x8;

            if (size < 1)
              /* No real data, so break out */
              break;

            switch (QT_FOURCC (hevc_data + 0x4)) {
              case FOURCC_hvcC:
              {
                /* parse, if found */
                GstBuffer *buf;

                GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");

                /* First 4 bytes are the length of the atom, the next 4 bytes
                 * are the fourcc, the next 1 byte is the version, and the
                 * subsequent bytes are sequence parameter set like data. */
                gst_codec_utils_h265_caps_set_level_tier_and_profile
                    (stream->caps, hevc_data + 8 + 1, size - 1);

                buf = gst_buffer_new_and_alloc (size);
                gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
                gst_caps_set_simple (stream->caps,
                    "codec_data", GST_TYPE_BUFFER, buf, NULL);
                gst_buffer_unref (buf);
                break;
              }
              default:
                break;
            }
            len -= size + 8;
            hevc_data += size + 8;
          }
          break;
        }
        case FOURCC_mp4v:
        case FOURCC_MP4V:
        case FOURCC_fmp4:
        case FOURCC_FMP4:
        {
          GNode *glbl;

          GST_DEBUG_OBJECT (qtdemux, "found %" GST_FOURCC_FORMAT,
              GST_FOURCC_ARGS (fourcc));

          /* codec data might be in glbl extension atom */
          glbl = mp4v ?
              qtdemux_tree_get_child_by_type (mp4v, FOURCC_glbl) : NULL;
          if (glbl) {
            guint8 *data;
            GstBuffer *buf;
            gint len;

            GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
            data = glbl->data;
            len = QT_UINT32 (data);
            if (len > 0x8) {
              len -= 0x8;
              buf = gst_buffer_new_and_alloc (len);
              gst_buffer_fill (buf, 0, data + 8, len);
              gst_caps_set_simple (stream->caps,
                  "codec_data", GST_TYPE_BUFFER, buf, NULL);
              gst_buffer_unref (buf);
            }
          }
          break;
        }
        case FOURCC_mjp2:
        {
          /* see annex I of the jpeg2000 spec */
          GNode *jp2h, *ihdr, *colr, *mjp2, *field, *prefix, *cmap, *cdef;
          const guint8 *data;
          const gchar *colorspace = NULL;
          gint ncomp = 0;
          guint32 ncomp_map = 0;
          gint32 *comp_map = NULL;
          guint32 nchan_def = 0;
          gint32 *chan_def = NULL;

          GST_DEBUG_OBJECT (qtdemux, "found mjp2");
          /* some required atoms */
          mjp2 = qtdemux_tree_get_child_by_type (stsd, FOURCC_mjp2);
          if (!mjp2)
            break;
          jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h);
          if (!jp2h)
            break;

          /* number of components; redundant with info in codestream, but useful
             to a muxer */
          ihdr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_ihdr);
          if (!ihdr || QT_UINT32 (ihdr->data) != 22)
            break;
          ncomp = QT_UINT16 (((guint8 *) ihdr->data) + 16);

          colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr);
          if (!colr)
            break;
          GST_DEBUG_OBJECT (qtdemux, "found colr");
          /* extract colour space info */
          if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) {
            switch (QT_UINT32 ((guint8 *) colr->data + 11)) {
              case 16:
                colorspace = "sRGB";
                break;
              case 17:
                colorspace = "GRAY";
                break;
              case 18:
                colorspace = "sYUV";
                break;
              default:
                colorspace = NULL;
                break;
            }
          }
          if (!colorspace)
            /* colr is required, and only values 16, 17, and 18 are specified,
               so error if we have no colorspace */
            break;

          /* extract component mapping */
          cmap = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cmap);
          if (cmap) {
            guint32 cmap_len = 0;
            int i;
            cmap_len = QT_UINT32 (cmap->data);
            if (cmap_len >= 8) {
              /* normal box, subtract off header */
              cmap_len -= 8;
              /* cmap: { u16 cmp; u8 mtyp; u8 pcol; }* */
              if (cmap_len % 4 == 0) {
                ncomp_map = (cmap_len / 4);
                comp_map = g_new0 (gint32, ncomp_map);
                for (i = 0; i < ncomp_map; i++) {
                  guint16 cmp;
                  guint8 mtyp, pcol;
                  cmp = QT_UINT16 (((guint8 *) cmap->data) + 8 + i * 4);
                  mtyp = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 2);
                  pcol = QT_UINT8 (((guint8 *) cmap->data) + 8 + i * 4 + 3);
                  comp_map[i] = (mtyp << 24) | (pcol << 16) | cmp;
                }
              }
            }
          }
          /* extract channel definitions */
          cdef = qtdemux_tree_get_child_by_type (jp2h, FOURCC_cdef);
          if (cdef) {
            guint32 cdef_len = 0;
            int i;
            cdef_len = QT_UINT32 (cdef->data);
            if (cdef_len >= 10) {
              /* normal box, subtract off header and len */
              cdef_len -= 10;
              /* cdef: u16 n; { u16 cn; u16 typ; u16 asoc; }* */
              if (cdef_len % 6 == 0) {
                nchan_def = (cdef_len / 6);
                chan_def = g_new0 (gint32, nchan_def);
                for (i = 0; i < nchan_def; i++)
                  chan_def[i] = -1;
                for (i = 0; i < nchan_def; i++) {
                  guint16 cn, typ, asoc;
                  cn = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6);
                  typ = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 2);
                  asoc = QT_UINT16 (((guint8 *) cdef->data) + 10 + i * 6 + 4);
                  if (cn < nchan_def) {
                    switch (typ) {
                      case 0:
                        chan_def[cn] = asoc;
                        break;
                      case 1:
                        chan_def[cn] = 0;       /* alpha */
                        break;
                      default:
                        chan_def[cn] = -typ;
                    }
                  }
                }
              }
            }
          }

          gst_caps_set_simple (stream->caps,
              "num-components", G_TYPE_INT, ncomp, NULL);
          gst_caps_set_simple (stream->caps,
              "colorspace", G_TYPE_STRING, colorspace, NULL);

          if (comp_map) {
            GValue arr = { 0, };
            GValue elt = { 0, };
            int i;
            g_value_init (&arr, GST_TYPE_ARRAY);
            g_value_init (&elt, G_TYPE_INT);
            for (i = 0; i < ncomp_map; i++) {
              g_value_set_int (&elt, comp_map[i]);
              gst_value_array_append_value (&arr, &elt);
            }
            gst_structure_set_value (gst_caps_get_structure (stream->caps, 0),
                "component-map", &arr);
            g_value_unset (&elt);
            g_value_unset (&arr);
            g_free (comp_map);
          }

          if (chan_def) {
            GValue arr = { 0, };
            GValue elt = { 0, };
            int i;
            g_value_init (&arr, GST_TYPE_ARRAY);
            g_value_init (&elt, G_TYPE_INT);
            for (i = 0; i < nchan_def; i++) {
              g_value_set_int (&elt, chan_def[i]);
              gst_value_array_append_value (&arr, &elt);
            }
            gst_structure_set_value (gst_caps_get_structure (stream->caps, 0),
                "channel-definitions", &arr);
            g_value_unset (&elt);
            g_value_unset (&arr);
            g_free (chan_def);
          }

          /* some optional atoms */
          field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel);
          prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x);

          /* indicate possible fields in caps */
          if (field) {
            data = (guint8 *) field->data + 8;
            if (*data != 1)
              gst_caps_set_simple (stream->caps, "fields", G_TYPE_INT,
                  (gint) * data, NULL);
          }
          /* add codec_data if provided */
          if (prefix) {
            GstBuffer *buf;
            gint len;

            GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd");
            data = prefix->data;
            len = QT_UINT32 (data);
            if (len > 0x8) {
              len -= 0x8;
              buf = gst_buffer_new_and_alloc (len);
              gst_buffer_fill (buf, 0, data + 8, len);
              gst_caps_set_simple (stream->caps,
                  "codec_data", GST_TYPE_BUFFER, buf, NULL);
              gst_buffer_unref (buf);
            }
          }
          break;
        }
        case FOURCC_jpeg:
        {
          /* https://developer.apple.com/standards/qtff-2001.pdf,
           * page 92, "Video Sample Description", under table 3.1 */
          GstByteReader br;

          const gint compressor_offset = 16 + 4 + 4 * 3 + 2 * 2 + 2 * 4 + 4 + 2;
          const gint min_size = compressor_offset + 32 + 2 + 2;
          GNode *jpeg;
          guint32 len;
          guint16 color_table_id = 0;
          gboolean ok;

          GST_DEBUG_OBJECT (qtdemux, "found jpeg");

          /* recover information on interlaced/progressive */
          jpeg = qtdemux_tree_get_child_by_type (stsd, FOURCC_jpeg);
          if (!jpeg)
            break;

          len = QT_UINT32 (jpeg->data);
          GST_DEBUG_OBJECT (qtdemux, "Found jpeg: len %u, need %d", len,
              min_size);
          if (len >= min_size) {
            gst_byte_reader_init (&br, jpeg->data, len);

            gst_byte_reader_skip (&br, compressor_offset + 32 + 2);
            gst_byte_reader_get_uint16_le (&br, &color_table_id);
            if (color_table_id != 0) {
              /* the spec says there can be concatenated chunks in the data, and we want
               * to find one called field. Walk through them. */
              gint offset = min_size;
              while (offset + 8 < len) {
                guint32 size = 0, tag;
                ok = gst_byte_reader_get_uint32_le (&br, &size);
                ok &= gst_byte_reader_get_uint32_le (&br, &tag);
                if (!ok || size < 8) {
                  GST_WARNING_OBJECT (qtdemux,
                      "Failed to walk optional chunk list");
                  break;
                }
                GST_DEBUG_OBJECT (qtdemux,
                    "Found optional %4.4s chunk, size %u", (const char *) &tag,
                    size);
                if (tag == FOURCC_fiel) {
                  guint8 n_fields, ordering;
                  gst_byte_reader_get_uint8 (&br, &n_fields);
                  gst_byte_reader_get_uint8 (&br, &ordering);
                  if (n_fields == 1 || n_fields == 2) {
                    GST_DEBUG_OBJECT (qtdemux,
                        "Found fiel tag with %u fields, ordering %u", n_fields,
                        ordering);
                    if (n_fields == 2)
                      gst_caps_set_simple (stream->caps, "interlace-mode",
                          G_TYPE_STRING, "interleaved", NULL);
                  } else {
                    GST_WARNING_OBJECT (qtdemux,
                        "Found fiel tag with invalid fields (%u)", n_fields);
                  }
                }
                offset += size;
              }
            } else {
              GST_DEBUG_OBJECT (qtdemux,
                  "Color table ID is 0, not trying to get interlacedness");
            }
          } else {
            GST_WARNING_OBJECT (qtdemux,
                "Length of jpeg chunk is too small, not trying to get interlacedness");
          }

          break;
        }
        case FOURCC_SVQ3:
        case FOURCC_VP31:
        {
          GstBuffer *buf;
          GstBuffer *seqh = NULL;
          guint8 *gamma_data = NULL;
          gint len = QT_UINT32 (stsd_data);

          qtdemux_parse_svq3_stsd_data (qtdemux, stsd, &gamma_data, &seqh);
          if (gamma_data) {
            gst_caps_set_simple (stream->caps, "applied-gamma", G_TYPE_DOUBLE,
                QT_FP32 (gamma_data), NULL);
          }
          if (seqh) {
            /* sorry for the bad name, but we don't know what this is, other
             * than its own fourcc */
            gst_caps_set_simple (stream->caps, "seqh", GST_TYPE_BUFFER, seqh,
                NULL);
          }

          GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd");
          buf = gst_buffer_new_and_alloc (len);
          gst_buffer_fill (buf, 0, stsd_data, len);
          gst_caps_set_simple (stream->caps,
              "codec_data", GST_TYPE_BUFFER, buf, NULL);
          gst_buffer_unref (buf);
          break;
        }
        case FOURCC_rle_:
        case FOURCC_WRLE:
        {
          gst_caps_set_simple (stream->caps,
              "depth", G_TYPE_INT, QT_UINT16 (stsd_data + offset + 82), NULL);
          break;
        }
        case FOURCC_XiTh:
        {
          GNode *xith, *xdxt;

          GST_DEBUG_OBJECT (qtdemux, "found XiTh");
          xith = qtdemux_tree_get_child_by_type (stsd, FOURCC_XiTh);
          if (!xith)
            break;

          xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT);
          if (!xdxt)
            break;

          GST_DEBUG_OBJECT (qtdemux, "found XdxT node");
          /* collect the headers and store them in a stream list so that we can
           * send them out first */
          qtdemux_parse_theora_extension (qtdemux, stream, xdxt);
          break;
        }
        case FOURCC_ovc1:
        {
          GNode *ovc1;
          guint8 *ovc1_data;
          guint ovc1_len;
          GstBuffer *buf;

          GST_DEBUG_OBJECT (qtdemux, "parse ovc1 header");
          ovc1 = qtdemux_tree_get_child_by_type (stsd, FOURCC_ovc1);
          if (!ovc1)
            break;
          ovc1_data = ovc1->data;
          ovc1_len = QT_UINT32 (ovc1_data);
          if (ovc1_len <= 198) {
            GST_WARNING_OBJECT (qtdemux, "Too small ovc1 header, skipping");
            break;
          }
          buf = gst_buffer_new_and_alloc (ovc1_len - 198);
          gst_buffer_fill (buf, 0, ovc1_data + 198, ovc1_len - 198);
          gst_caps_set_simple (stream->caps,
              "codec_data", GST_TYPE_BUFFER, buf, NULL);
          gst_buffer_unref (buf);
          break;
        }
        case FOURCC_vc_1:
        {
          gint len = QT_UINT32 (stsd_data) - 0x66;
          const guint8 *vc1_data = stsd_data + 0x66;

          /* find dvc1 */
          while (len >= 8) {
            gint size;

            if (QT_UINT32 (vc1_data) <= len)
              size = QT_UINT32 (vc1_data) - 8;
            else
              size = len - 8;

            if (size < 1)
              /* No real data, so break out */
              break;

            switch (QT_FOURCC (vc1_data + 0x4)) {
              case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
              {
                GstBuffer *buf;

                GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
                buf = gst_buffer_new_and_alloc (size);
                gst_buffer_fill (buf, 0, vc1_data + 8, size);
                gst_caps_set_simple (stream->caps,
                    "codec_data", GST_TYPE_BUFFER, buf, NULL);
                gst_buffer_unref (buf);
                break;
              }
              default:
                break;
            }
            len -= size + 8;
            vc1_data += size + 8;
          }
          break;
        }
        default:
          break;
      }
    }

    GST_INFO_OBJECT (qtdemux,
        "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
        GST_FOURCC_ARGS (fourcc), stream->caps);

  } else if (stream->subtype == FOURCC_soun) {
    int version, samplesize;
    guint16 compression_id;
    gboolean amrwb = FALSE;

    offset = 32;
    /* sample description entry (16) + sound sample description v0 (20) */
    if (len < 36)
      goto corrupt_file;

    version = QT_UINT32 (stsd_data + offset);
    stream->n_channels = QT_UINT16 (stsd_data + offset + 8);
    samplesize = QT_UINT16 (stsd_data + offset + 10);
    compression_id = QT_UINT16 (stsd_data + offset + 12);
    stream->rate = QT_FP32 (stsd_data + offset + 16);

    GST_LOG_OBJECT (qtdemux, "version/rev:      %08x", version);
    GST_LOG_OBJECT (qtdemux, "vendor:           %08x",
        QT_UINT32 (stsd_data + offset + 4));
    GST_LOG_OBJECT (qtdemux, "n_channels:       %d", stream->n_channels);
    GST_LOG_OBJECT (qtdemux, "sample_size:      %d", samplesize);
    GST_LOG_OBJECT (qtdemux, "compression_id:   %d", compression_id);
    GST_LOG_OBJECT (qtdemux, "packet size:      %d",
        QT_UINT16 (stsd_data + offset + 14));
    GST_LOG_OBJECT (qtdemux, "sample rate:      %g", stream->rate);

    if (compression_id == 0xfffe)
      stream->sampled = TRUE;

    /* first assume uncompressed audio */
    stream->bytes_per_sample = samplesize / 8;
    stream->samples_per_frame = stream->n_channels;
    stream->bytes_per_frame = stream->n_channels * stream->bytes_per_sample;
    stream->samples_per_packet = stream->samples_per_frame;
    stream->bytes_per_packet = stream->bytes_per_sample;

    offset = 52;
    switch (fourcc) {
        /* Yes, these have to be hard-coded */
      case FOURCC_MAC6:
      {
        stream->samples_per_packet = 6;
        stream->bytes_per_packet = 1;
        stream->bytes_per_frame = 1 * stream->n_channels;
        stream->bytes_per_sample = 1;
        stream->samples_per_frame = 6 * stream->n_channels;
        break;
      }
      case FOURCC_MAC3:
      {
        stream->samples_per_packet = 3;
        stream->bytes_per_packet = 1;
        stream->bytes_per_frame = 1 * stream->n_channels;
        stream->bytes_per_sample = 1;
        stream->samples_per_frame = 3 * stream->n_channels;
        break;
      }
      case FOURCC_ima4:
      {
        stream->samples_per_packet = 64;
        stream->bytes_per_packet = 34;
        stream->bytes_per_frame = 34 * stream->n_channels;
        stream->bytes_per_sample = 2;
        stream->samples_per_frame = 64 * stream->n_channels;
        break;
      }
      case FOURCC_ulaw:
      case FOURCC_alaw:
      {
        stream->samples_per_packet = 1;
        stream->bytes_per_packet = 1;
        stream->bytes_per_frame = 1 * stream->n_channels;
        stream->bytes_per_sample = 1;
        stream->samples_per_frame = 1 * stream->n_channels;
        break;
      }
      case FOURCC_agsm:
      {
        stream->samples_per_packet = 160;
        stream->bytes_per_packet = 33;
        stream->bytes_per_frame = 33 * stream->n_channels;
        stream->bytes_per_sample = 2;
        stream->samples_per_frame = 160 * stream->n_channels;
        break;
      }
      default:
        break;
    }

    if (version == 0x00010000) {
      /* sample description entry (16) + sound sample description v1 (20+16) */
      if (len < 52)
        goto corrupt_file;

      switch (fourcc) {
        case FOURCC_twos:
        case FOURCC_sowt:
        case FOURCC_raw_:
          break;
        default:
        {
          /* only parse extra decoding config for non-pcm audio */
          stream->samples_per_packet = QT_UINT32 (stsd_data + offset);
          stream->bytes_per_packet = QT_UINT32 (stsd_data + offset + 4);
          stream->bytes_per_frame = QT_UINT32 (stsd_data + offset + 8);
          stream->bytes_per_sample = QT_UINT32 (stsd_data + offset + 12);

          GST_LOG_OBJECT (qtdemux, "samples/packet:   %d",
              stream->samples_per_packet);
          GST_LOG_OBJECT (qtdemux, "bytes/packet:     %d",
              stream->bytes_per_packet);
          GST_LOG_OBJECT (qtdemux, "bytes/frame:      %d",
              stream->bytes_per_frame);
          GST_LOG_OBJECT (qtdemux, "bytes/sample:     %d",
              stream->bytes_per_sample);

          if (!stream->sampled && stream->bytes_per_packet) {
            stream->samples_per_frame = (stream->bytes_per_frame /
                stream->bytes_per_packet) * stream->samples_per_packet;
            GST_LOG_OBJECT (qtdemux, "samples/frame:    %d",
                stream->samples_per_frame);
          }
          break;
        }
      }
    } else if (version == 0x00020000) {
      union
      {
        gdouble fp;
        guint64 val;
      } qtfp;

      /* sample description entry (16) + sound sample description v2 (56) */
      if (len < 72)
        goto corrupt_file;

      qtfp.val = QT_UINT64 (stsd_data + offset + 4);
      stream->rate = qtfp.fp;
      stream->n_channels = QT_UINT32 (stsd_data + offset + 12);

      GST_LOG_OBJECT (qtdemux, "Sound sample description Version 2");
      GST_LOG_OBJECT (qtdemux, "sample rate:        %g", stream->rate);
      GST_LOG_OBJECT (qtdemux, "n_channels:         %d", stream->n_channels);
      GST_LOG_OBJECT (qtdemux, "bits/channel:       %d",
          QT_UINT32 (stsd_data + offset + 20));
      GST_LOG_OBJECT (qtdemux, "format flags:       %X",
          QT_UINT32 (stsd_data + offset + 24));
      GST_LOG_OBJECT (qtdemux, "bytes/packet:       %d",
          QT_UINT32 (stsd_data + offset + 28));
      GST_LOG_OBJECT (qtdemux, "LPCM frames/packet: %d",
          QT_UINT32 (stsd_data + offset + 32));
    } else if (version != 0x00000) {
      GST_WARNING_OBJECT (qtdemux, "unknown audio STSD version %08x", version);
    }

    if (stream->caps)
      gst_caps_unref (stream->caps);

    stream->caps = qtdemux_audio_caps (qtdemux, stream, fourcc,
        stsd_data + 32, len - 16, &codec);

    switch (fourcc) {
      case FOURCC_in24:
      {
        GNode *enda;
        GNode *in24;

        in24 = qtdemux_tree_get_child_by_type (stsd, FOURCC_in24);

        enda = qtdemux_tree_get_child_by_type (in24, FOURCC_enda);
        if (!enda) {
          wave = qtdemux_tree_get_child_by_type (in24, FOURCC_wave);
          if (wave)
            enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda);
        }
        if (enda) {
          int enda_value = QT_UINT16 ((guint8 *) enda->data + 8);
          gst_caps_set_simple (stream->caps,
              "format", G_TYPE_STRING, (enda_value) ? "S24LE" : "S24BE", NULL);
        }
        break;
      }
      case FOURCC_owma:
      {
        GNode *owma;
        const guint8 *owma_data;
        const gchar *codec_name = NULL;
        guint owma_len;
        GstBuffer *buf;
        gint version = 1;
        /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
        /* FIXME this should also be gst_riff_strf_auds,
         * but the latter one is actually missing bits-per-sample :( */
        typedef struct
        {
          gint16 wFormatTag;
          gint16 nChannels;
          gint32 nSamplesPerSec;
          gint32 nAvgBytesPerSec;
          gint16 nBlockAlign;
          gint16 wBitsPerSample;
          gint16 cbSize;
        } WAVEFORMATEX;
        WAVEFORMATEX *wfex;

        GST_DEBUG_OBJECT (qtdemux, "parse owma");
        owma = qtdemux_tree_get_child_by_type (stsd, FOURCC_owma);
        if (!owma)
          break;
        owma_data = owma->data;
        owma_len = QT_UINT32 (owma_data);
        if (owma_len <= 54) {
          GST_WARNING_OBJECT (qtdemux, "Too small owma header, skipping");
          break;
        }
        wfex = (WAVEFORMATEX *) (owma_data + 36);
        buf = gst_buffer_new_and_alloc (owma_len - 54);
        gst_buffer_fill (buf, 0, owma_data + 54, owma_len - 54);
        if (wfex->wFormatTag == 0x0161) {
          codec_name = "Windows Media Audio";
          version = 2;
        } else if (wfex->wFormatTag == 0x0162) {
          codec_name = "Windows Media Audio 9 Pro";
          version = 3;
        } else if (wfex->wFormatTag == 0x0163) {
          codec_name = "Windows Media Audio 9 Lossless";
          /* is that correct? gstffmpegcodecmap.c is missing it, but
           * fluendo codec seems to support it */
          version = 4;
        }

        gst_caps_set_simple (stream->caps,
            "codec_data", GST_TYPE_BUFFER, buf,
            "wmaversion", G_TYPE_INT, version,
            "block_align", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->nBlockAlign),
            "bitrate", G_TYPE_INT, GST_READ_UINT32_LE (&wfex->nAvgBytesPerSec),
            "width", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->wBitsPerSample),
            "depth", G_TYPE_INT, GST_READ_UINT16_LE (&wfex->wBitsPerSample),
            NULL);
        gst_buffer_unref (buf);

        if (codec_name) {
          g_free (codec);
          codec = g_strdup (codec_name);
        }
        break;
      }
      case FOURCC_wma_:
      {
        gint len = QT_UINT32 (stsd_data) - offset;
        const guint8 *wfex_data = stsd_data + offset;
        const gchar *codec_name = NULL;
        gint version = 1;
        /* from http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx */
        /* FIXME this should also be gst_riff_strf_auds,
         * but the latter one is actually missing bits-per-sample :( */
        typedef struct
        {
          gint16 wFormatTag;
          gint16 nChannels;
          gint32 nSamplesPerSec;
          gint32 nAvgBytesPerSec;
          gint16 nBlockAlign;
          gint16 wBitsPerSample;
          gint16 cbSize;
        } WAVEFORMATEX;
        WAVEFORMATEX wfex;

        /* FIXME: unify with similar wavformatex parsing code above */
        GST_DEBUG_OBJECT (qtdemux, "parse wma, looking for wfex");

        /* find wfex */
        while (len >= 8) {
          gint size;

          if (QT_UINT32 (wfex_data) <= len)
            size = QT_UINT32 (wfex_data) - 8;
          else
            size = len - 8;

          if (size < 1)
            /* No real data, so break out */
            break;

          switch (QT_FOURCC (wfex_data + 4)) {
            case GST_MAKE_FOURCC ('w', 'f', 'e', 'x'):
            {
              GST_DEBUG_OBJECT (qtdemux, "found wfex in stsd");

              if (size < 8 + 18)
                break;

              wfex.wFormatTag = GST_READ_UINT16_LE (wfex_data + 8 + 0);
              wfex.nChannels = GST_READ_UINT16_LE (wfex_data + 8 + 2);
              wfex.nSamplesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 4);
              wfex.nAvgBytesPerSec = GST_READ_UINT32_LE (wfex_data + 8 + 8);
              wfex.nBlockAlign = GST_READ_UINT16_LE (wfex_data + 8 + 12);
              wfex.wBitsPerSample = GST_READ_UINT16_LE (wfex_data + 8 + 14);
              wfex.cbSize = GST_READ_UINT16_LE (wfex_data + 8 + 16);

              GST_LOG_OBJECT (qtdemux, "Found wfex box in stsd:");
              GST_LOG_OBJECT (qtdemux, "FormatTag = 0x%04x, Channels = %u, "
                  "SamplesPerSec = %u, AvgBytesPerSec = %u, BlockAlign = %u, "
                  "BitsPerSample = %u, Size = %u", wfex.wFormatTag,
                  wfex.nChannels, wfex.nSamplesPerSec, wfex.nAvgBytesPerSec,
                  wfex.nBlockAlign, wfex.wBitsPerSample, wfex.cbSize);

              if (wfex.wFormatTag == 0x0161) {
                codec_name = "Windows Media Audio";
                version = 2;
              } else if (wfex.wFormatTag == 0x0162) {
                codec_name = "Windows Media Audio 9 Pro";
                version = 3;
              } else if (wfex.wFormatTag == 0x0163) {
                codec_name = "Windows Media Audio 9 Lossless";
                /* is that correct? gstffmpegcodecmap.c is missing it, but
                 * fluendo codec seems to support it */
                version = 4;
              }

              gst_caps_set_simple (stream->caps,
                  "wmaversion", G_TYPE_INT, version,
                  "block_align", G_TYPE_INT, wfex.nBlockAlign,
                  "bitrate", G_TYPE_INT, wfex.nAvgBytesPerSec,
                  "width", G_TYPE_INT, wfex.wBitsPerSample,
                  "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);

              if (size > wfex.cbSize) {
                GstBuffer *buf;

                buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
                gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
                    size - wfex.cbSize);
                gst_caps_set_simple (stream->caps,
                    "codec_data", GST_TYPE_BUFFER, buf, NULL);
                gst_buffer_unref (buf);
              } else {
                GST_WARNING_OBJECT (qtdemux, "no codec data");
              }

              if (codec_name) {
                g_free (codec);
                codec = g_strdup (codec_name);
              }
              break;
            }
            default:
              break;
          }
          len -= size + 8;
          wfex_data += size + 8;
        }
        break;
      }
      case FOURCC_opus:
      {
        GNode *opus;
        const guint8 *opus_data;
        guint8 *channel_mapping = NULL;
        guint32 rate;
        guint8 channels;
        guint8 channel_mapping_family;
        guint8 stream_count;
        guint8 coupled_count;
        guint8 i;

        opus = qtdemux_tree_get_child_by_type (stsd, FOURCC_opus);
        opus_data = opus->data;

        channels = GST_READ_UINT8 (opus_data + 45);
        rate = GST_READ_UINT32_LE (opus_data + 48);
        channel_mapping_family = GST_READ_UINT8 (opus_data + 54);
        stream_count = GST_READ_UINT8 (opus_data + 55);
        coupled_count = GST_READ_UINT8 (opus_data + 56);

        if (channels > 0) {
          channel_mapping = g_malloc (channels * sizeof (guint8));
          for (i = 0; i < channels; i++)
            channel_mapping[i] = GST_READ_UINT8 (opus_data + i + 57);
        }

        stream->caps = gst_codec_utils_opus_create_caps (rate, channels,
            channel_mapping_family, stream_count, coupled_count,
            channel_mapping);
        break;
      }
      default:
        break;
    }

    if (codec) {
      GstStructure *s;
      gint bitrate = 0;

      gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
          GST_TAG_AUDIO_CODEC, codec, NULL);
      g_free (codec);
      codec = NULL;

      /* some bitrate info may have ended up in caps */
      s = gst_caps_get_structure (stream->caps, 0);
      gst_structure_get_int (s, "bitrate", &bitrate);
      if (bitrate > 0)
        gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
            GST_TAG_BITRATE, bitrate, NULL);
    }

    if (stream->protected && fourcc == FOURCC_mp4a)
      mp4a = qtdemux_tree_get_child_by_type (stsd, FOURCC_enca);
    else
      mp4a = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4a);

    wave = NULL;
    esds = NULL;
    if (mp4a) {
      wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
      if (wave)
        esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
      if (!esds)
        esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds);
    }


    /* If the fourcc's bottom 16 bits gives 'sm', then the top
       16 bits is a byte-swapped wave-style codec identifier,
       and we can find a WAVE header internally to a 'wave' atom here.
       This can more clearly be thought of as 'ms' as the top 16 bits, and a
       codec id as the bottom 16 bits - but byte-swapped to store in QT (which
       is big-endian).
     */
    if ((fourcc & 0xffff) == (('s' << 8) | 'm')) {
      if (len < offset + 20) {
        GST_WARNING_OBJECT (qtdemux, "No wave atom in MS-style audio");
      } else {
        guint32 datalen = QT_UINT32 (stsd_data + offset + 16);
        const guint8 *data = stsd_data + offset + 16;
        GNode *wavenode;
        GNode *waveheadernode;

        wavenode = g_node_new ((guint8 *) data);
        if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
          const guint8 *waveheader;
          guint32 headerlen;

          waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
          if (waveheadernode) {
            waveheader = (const guint8 *) waveheadernode->data;
            headerlen = QT_UINT32 (waveheader);

            if (headerlen > 8) {
              gst_riff_strf_auds *header = NULL;
              GstBuffer *headerbuf;
              GstBuffer *extra;

              waveheader += 8;
              headerlen -= 8;

              headerbuf = gst_buffer_new_and_alloc (headerlen);
              gst_buffer_fill (headerbuf, 0, waveheader, headerlen);

              if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
                      headerbuf, &header, &extra)) {
                gst_caps_unref (stream->caps);
                /* FIXME: Need to do something with the channel reorder map */
                stream->caps = gst_riff_create_audio_caps (header->format, NULL,
                    header, extra, NULL, NULL, NULL);

                if (extra)
                  gst_buffer_unref (extra);
                g_free (header);
              }
            }
          } else
            GST_DEBUG ("Didn't find waveheadernode for this codec");
        }
        g_node_destroy (wavenode);
      }
    } else if (esds) {
      gst_qtdemux_handle_esds (qtdemux, stream, esds, stream->pending_tags);
    } else {
      switch (fourcc) {
#if 0
          /* FIXME: what is in the chunk? */
        case FOURCC_QDMC:
        {
          gint len = QT_UINT32 (stsd_data);

          /* seems to be always = 116 = 0x74 */
          break;
        }
#endif
        case FOURCC_QDM2:
        {
          gint len = QT_UINT32 (stsd_data);

          if (len > 0x4C) {
            GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x4C);

            gst_buffer_fill (buf, 0, stsd_data + 0x4C, len - 0x4C);
            gst_caps_set_simple (stream->caps,
                "codec_data", GST_TYPE_BUFFER, buf, NULL);
            gst_buffer_unref (buf);
          }
          gst_caps_set_simple (stream->caps,
              "samplesize", G_TYPE_INT, samplesize, NULL);
          break;
        }
        case FOURCC_alac:
        {
          GNode *alac, *wave = NULL;

          /* apparently, m4a has this atom appended directly in the stsd entry,
           * while mov has it in a wave atom */
          alac = qtdemux_tree_get_child_by_type (stsd, FOURCC_alac);
          if (alac) {
            /* alac now refers to stsd entry atom */
            wave = qtdemux_tree_get_child_by_type (alac, FOURCC_wave);
            if (wave)
              alac = qtdemux_tree_get_child_by_type (wave, FOURCC_alac);
            else
              alac = qtdemux_tree_get_child_by_type (alac, FOURCC_alac);
          }
          if (alac) {
            const guint8 *alac_data = alac->data;
            gint len = QT_UINT32 (alac->data);
            GstBuffer *buf;

            if (len < 36) {
              GST_DEBUG_OBJECT (qtdemux,
                  "discarding alac atom with unexpected len %d", len);
            } else {
              /* codec-data contains alac atom size and prefix,
               * ffmpeg likes it that way, not quite gst-ish though ...*/
              buf = gst_buffer_new_and_alloc (len);
              gst_buffer_fill (buf, 0, alac->data, len);
              gst_caps_set_simple (stream->caps,
                  "codec_data", GST_TYPE_BUFFER, buf, NULL);
              gst_buffer_unref (buf);

              stream->bytes_per_frame = QT_UINT32 (alac_data + 12);
              stream->n_channels = QT_UINT8 (alac_data + 21);
              stream->rate = QT_UINT32 (alac_data + 32);
            }
          }
          gst_caps_set_simple (stream->caps,
              "samplesize", G_TYPE_INT, samplesize, NULL);
          break;
        }
        case FOURCC_fLaC:
        {
          /* The codingname of the sample entry is 'fLaC' */
          GNode *flac = qtdemux_tree_get_child_by_type (stsd, FOURCC_fLaC);

          if (flac) {
            /* The 'dfLa' box is added to the sample entry to convey
               initializing information for the decoder. */
            const GNode *dfla =
                qtdemux_tree_get_child_by_type (flac, FOURCC_dfLa);

            if (dfla) {
              const guint32 len = QT_UINT32 (dfla->data);

              /* Must contain at least dfLa box header (12),
               * METADATA_BLOCK_HEADER (4), METADATA_BLOCK_STREAMINFO (34) */
              if (len < 50) {
                GST_DEBUG_OBJECT (qtdemux,
                    "discarding dfla atom with unexpected len %d", len);
              } else {
                /* skip dfLa header to get the METADATA_BLOCKs */
                const guint8 *metadata_blocks = (guint8 *) dfla->data + 12;
                const guint32 metadata_blocks_len = len - 12;

                gchar *stream_marker = g_strdup ("fLaC");
                GstBuffer *block = gst_buffer_new_wrapped (stream_marker,
                    strlen (stream_marker));

                guint32 index = 0;
                guint32 remainder = 0;
                guint32 block_size = 0;
                gboolean is_last = FALSE;

                GValue array = G_VALUE_INIT;
                GValue value = G_VALUE_INIT;

                g_value_init (&array, GST_TYPE_ARRAY);
                g_value_init (&value, GST_TYPE_BUFFER);

                gst_value_set_buffer (&value, block);
                gst_value_array_append_value (&array, &value);
                g_value_reset (&value);

                gst_buffer_unref (block);

                /* check there's at least one METADATA_BLOCK_HEADER's worth
                 * of data, and we haven't already finished parsing */
                while (!is_last && ((index + 3) < metadata_blocks_len)) {
                  remainder = metadata_blocks_len - index;

                  /* add the METADATA_BLOCK_HEADER size to the signalled size */
                  block_size = 4 +
                      (metadata_blocks[index + 1] << 16) +
                      (metadata_blocks[index + 2] << 8) +
                      metadata_blocks[index + 3];

                  /* be careful not to read off end of box */
                  if (block_size > remainder) {
                    break;
                  }

                  is_last = metadata_blocks[index] >> 7;

                  block = gst_buffer_new_and_alloc (block_size);

                  gst_buffer_fill (block, 0, &metadata_blocks[index],
                      block_size);

                  gst_value_set_buffer (&value, block);
                  gst_value_array_append_value (&array, &value);
                  g_value_reset (&value);

                  gst_buffer_unref (block);

                  index += block_size;
                }

                /* only append the metadata if we successfully read all of it */
                if (is_last) {
                  gst_structure_set_value (gst_caps_get_structure (stream->caps,
                          0), "streamheader", &array);
                } else {
                  GST_WARNING_OBJECT (qtdemux,
                      "discarding all METADATA_BLOCKs due to invalid "
                      "block_size %d at idx %d, rem %d", block_size, index,
                      remainder);
                }

                g_value_unset (&value);
                g_value_unset (&array);

                /* The sample rate obtained from the stsd may not be accurate
                 * since it cannot represent rates greater than 65535Hz, so
                 * override that value with the sample rate from the
                 * METADATA_BLOCK_STREAMINFO block */
                stream->rate =
                    (QT_UINT32 (metadata_blocks + 14) >> 12) & 0xFFFFF;
              }
            }
          }
          break;
        }
        case FOURCC_sawb:
          /* Fallthrough! */
          amrwb = TRUE;
        case FOURCC_samr:
        {
          gint len = QT_UINT32 (stsd_data);

          if (len > 0x34) {
            GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x34);
            guint bitrate;

            gst_buffer_fill (buf, 0, stsd_data + 0x34, len - 0x34);

            /* If we have enough data, let's try to get the 'damr' atom. See
             * the 3GPP container spec (26.244) for more details. */
            if ((len - 0x34) > 8 &&
                (bitrate = qtdemux_parse_amr_bitrate (buf, amrwb))) {
              gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
                  GST_TAG_MAXIMUM_BITRATE, bitrate, NULL);
            }

            gst_caps_set_simple (stream->caps,
                "codec_data", GST_TYPE_BUFFER, buf, NULL);
            gst_buffer_unref (buf);
          }
          break;
        }
        case FOURCC_mp4a:
        {
          /* mp4a atom withtout ESDS; Attempt to build codec data from atom */
          gint len = QT_UINT32 (stsd_data);

          if (len >= 50) {
            guint16 sound_version = QT_UINT16 (stsd_data + 32);

            if (sound_version == 1) {
              guint16 channels = QT_UINT16 (stsd_data + 40);
              guint32 time_scale = QT_UINT32 (stsd_data + 46);
              guint8 codec_data[2];
              GstBuffer *buf;
              gint profile = 2; /* FIXME: Can this be determined somehow? There doesn't seem to be anything in mp4a atom that specifis compression */

              gint sample_rate_index =
                  gst_codec_utils_aac_get_index_from_sample_rate (time_scale);

              /* build AAC codec data */
              codec_data[0] = profile << 3;
              codec_data[0] |= ((sample_rate_index >> 1) & 0x7);
              codec_data[1] = (sample_rate_index & 0x01) << 7;
              codec_data[1] |= (channels & 0xF) << 3;

              buf = gst_buffer_new_and_alloc (2);
              gst_buffer_fill (buf, 0, codec_data, 2);
              gst_caps_set_simple (stream->caps,
                  "codec_data", GST_TYPE_BUFFER, buf, NULL);
              gst_buffer_unref (buf);
            }
          }
          break;
        }
        default:
          GST_INFO_OBJECT (qtdemux,
              "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
          break;
      }
    }
    GST_INFO_OBJECT (qtdemux,
        "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
        GST_FOURCC_ARGS (fourcc), stream->caps);

  } else if (stream->subtype == FOURCC_strm) {
    if (fourcc == FOURCC_rtsp) {
      stream->redirect_uri = qtdemux_get_rtsp_uri_from_hndl (qtdemux, minf);
    } else {
      GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT,
          GST_FOURCC_ARGS (fourcc));
      goto unknown_stream;
    }
    stream->sampled = TRUE;
  } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
      || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt) {

    stream->sampled = TRUE;
    stream->sparse = TRUE;

    stream->caps =
        qtdemux_sub_caps (qtdemux, stream, fourcc, stsd_data, &codec);
    if (codec) {
      gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
          GST_TAG_SUBTITLE_CODEC, codec, NULL);
      g_free (codec);
      codec = NULL;
    }

    /* hunt for sort-of codec data */
    switch (fourcc) {
      case FOURCC_mp4s:
      {
        GNode *mp4s = NULL;
        GNode *esds = NULL;

        /* look for palette in a stsd->mp4s->esds sub-atom */
        mp4s = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4s);
        if (mp4s)
          esds = qtdemux_tree_get_child_by_type (mp4s, FOURCC_esds);
        if (esds == NULL) {
          /* Invalid STSD */
          GST_LOG_OBJECT (qtdemux, "Skipping invalid stsd: no esds child");
          break;
        }

        gst_qtdemux_handle_esds (qtdemux, stream, esds, stream->pending_tags);
        break;
      }
      default:
        GST_INFO_OBJECT (qtdemux,
            "unhandled type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
        break;
    }
    GST_INFO_OBJECT (qtdemux,
        "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
        GST_FOURCC_ARGS (fourcc), stream->caps);
  } else {
    /* everything in 1 sample */
    stream->sampled = TRUE;

    stream->caps =
        qtdemux_generic_caps (qtdemux, stream, fourcc, stsd_data, &codec);

    if (stream->caps == NULL)
      goto unknown_stream;

    if (codec) {
      gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
          GST_TAG_SUBTITLE_CODEC, codec, NULL);
      g_free (codec);
      codec = NULL;
    }
  }

  /* promote to sampled format */
  if (stream->fourcc == FOURCC_samr) {
    /* force mono 8000 Hz for AMR */
    stream->sampled = TRUE;
    stream->n_channels = 1;
    stream->rate = 8000;
  } else if (stream->fourcc == FOURCC_sawb) {
    /* force mono 16000 Hz for AMR-WB */
    stream->sampled = TRUE;
    stream->n_channels = 1;
    stream->rate = 16000;
  } else if (stream->fourcc == FOURCC_mp4a) {
    stream->sampled = TRUE;
  }

  /* collect sample information */
  if (!qtdemux_stbl_init (qtdemux, stream, stbl))
    goto samples_failed;

  if (qtdemux->fragmented) {
    guint64 offset;

    /* need all moov samples as basis; probably not many if any at all */
    /* prevent moof parsing taking of at this time */
    offset = qtdemux->moof_offset;
    qtdemux->moof_offset = 0;
    if (stream->n_samples &&
        !qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1)) {
      qtdemux->moof_offset = offset;
      goto samples_failed;
    }
    qtdemux->moof_offset = 0;
    /* movie duration more reliable in this case (e.g. mehd) */
    if (qtdemux->segment.duration &&
        GST_CLOCK_TIME_IS_VALID (qtdemux->segment.duration))
      stream->duration =
          GSTTIME_TO_QTSTREAMTIME (stream, qtdemux->segment.duration);
  }

  /* configure segments */
  if (!qtdemux_parse_segments (qtdemux, stream, trak))
    goto segments_failed;

  /* add some language tag, if useful */
  if (stream->lang_id[0] != '\0' && strcmp (stream->lang_id, "unk") &&
      strcmp (stream->lang_id, "und")) {
    const gchar *lang_code;

    /* convert ISO 639-2 code to ISO 639-1 */
    lang_code = gst_tag_get_language_code (stream->lang_id);
    gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
        GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
  }

  /* Check for UDTA tags */
  if ((udta = qtdemux_tree_get_child_by_type (trak, FOURCC_udta))) {
    qtdemux_parse_udta (qtdemux, stream->pending_tags, udta);
  }

  /* now we are ready to add the stream */
  if (qtdemux->n_streams >= GST_QTDEMUX_MAX_STREAMS)
    goto too_many_streams;

  if (!qtdemux->got_moov) {
    qtdemux->streams[qtdemux->n_streams] = stream;
    qtdemux->n_streams++;
    GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", qtdemux->n_streams);
  }

  return TRUE;

/* ERRORS */
skip_track:
  {
    GST_INFO_OBJECT (qtdemux, "skip disabled track");
    if (new_stream)
      gst_qtdemux_stream_free (qtdemux, stream);
    return TRUE;
  }
corrupt_file:
  {
    GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
        (_("This file is corrupt and cannot be played.")), (NULL));
    if (new_stream)
      gst_qtdemux_stream_free (qtdemux, stream);
    return FALSE;
  }
error_encrypted:
  {
    GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL));
    if (new_stream)
      gst_qtdemux_stream_free (qtdemux, stream);
    return FALSE;
  }
samples_failed:
segments_failed:
  {
    /* we posted an error already */
    /* free stbl sub-atoms */
    gst_qtdemux_stbl_free (stream);
    if (new_stream)
      gst_qtdemux_stream_free (qtdemux, stream);
    return FALSE;
  }
existing_stream:
  {
    GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
        track_id);
    if (new_stream)
      gst_qtdemux_stream_free (qtdemux, stream);
    return TRUE;
  }
unknown_stream:
  {
    GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
        GST_FOURCC_ARGS (stream->subtype));
    if (new_stream)
      gst_qtdemux_stream_free (qtdemux, stream);
    return TRUE;
  }
too_many_streams:
  {
    GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
        (_("This file contains too many streams. Only playing first %d"),
            GST_QTDEMUX_MAX_STREAMS), (NULL));
    return TRUE;
  }
}

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

gst_qtdemux_convert_seek (GstPad * pad, GstFormat * format,
    GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop)
{
  gboolean res;

  g_return_val_if_fail (format != NULL, FALSE);
  g_return_val_if_fail (cur != NULL, FALSE);
  g_return_val_if_fail (stop != NULL, FALSE);

  if (*format == GST_FORMAT_TIME)
    return TRUE;

  res = TRUE;
  if (cur_type != GST_SEEK_TYPE_NONE)
    res = gst_pad_query_convert (pad, *format, *cur, GST_FORMAT_TIME, cur);
  if (res && stop_type != GST_SEEK_TYPE_NONE)
    res = gst_pad_query_convert (pad, *format, *stop, GST_FORMAT_TIME, stop);

  if (res)
    *format = GST_FORMAT_TIME;

  return res;
}

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

qtdemux_is_string_tag_3gp (GstQTDemux * qtdemux, guint32 fourcc)
{
  return fourcc == FOURCC_cprt || fourcc == FOURCC_gnre || fourcc == FOURCC_titl
      || fourcc == FOURCC_dscp || fourcc == FOURCC_perf || fourcc == FOURCC_auth
      || fourcc == FOURCC_albm;
}

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

gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
    GstBuffer * buf)
{
  GstMapInfo map;
  guint nsize = 0;
  gchar *str;

  /* not many cases for now */
  if (G_UNLIKELY (stream->fourcc == FOURCC_mp4s)) {
    /* send a one time dvd clut event */
    if (stream->pending_event && stream->pad)
      gst_pad_push_event (stream->pad, stream->pending_event);
    stream->pending_event = NULL;
  }

  if (G_UNLIKELY (stream->subtype != FOURCC_text
          && stream->subtype != FOURCC_sbtl &&
          stream->subtype != FOURCC_subp)) {
    return buf;
  }

  gst_buffer_map (buf, &map, GST_MAP_READ);

  /* empty buffer is sent to terminate previous subtitle */
  if (map.size <= 2) {
    gst_buffer_unmap (buf, &map);
    gst_buffer_unref (buf);
    return NULL;
  }
  if (stream->subtype == FOURCC_subp) {
    /* That's all the processing needed for subpictures */
    gst_buffer_unmap (buf, &map);
    return buf;
  }

  nsize = GST_READ_UINT16_BE (map.data);
  nsize = MIN (nsize, map.size - 2);

  GST_LOG_OBJECT (qtdemux, "3GPP timed text subtitle: %d/%" G_GSIZE_FORMAT "",
      nsize, map.size);

  /* takes care of UTF-8 validation or UTF-16 recognition,
   * no other encoding expected */
  str = gst_tag_freeform_string_to_utf8 ((gchar *) map.data + 2, nsize, NULL);
  gst_buffer_unmap (buf, &map);
  if (str) {
    gst_buffer_unref (buf);
    buf = _gst_buffer_new_wrapped (str, strlen (str), g_free);
  } else {
    /* this should not really happen unless the subtitle is corrupted */
    gst_buffer_unref (buf);
    buf = NULL;
  }

  /* FIXME ? convert optional subsequent style info to markup */

  return buf;
}

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

gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
    QtDemuxStream * str, gint64 media_offset)
{
  QtDemuxSample *result = str->samples;
  guint32 index = 0;

  if (result == NULL || str->n_samples == 0)
    return -1;

  if (media_offset == result->offset)
    return index;

  result++;
  while (index < str->n_samples - 1) {
    if (!qtdemux_parse_samples (qtdemux, str, index + 1))
      goto parse_failed;

    if (media_offset < result->offset)
      break;

    index++;
    result++;
  }
  return index;

  /* ERRORS */
parse_failed:
  {
    GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
    return -1;
  }
}

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

qtdemux_add_fragmented_samples (GstQTDemux * qtdemux)
{
  guint64 length, offset;
  GstBuffer *buf = NULL;
  GstFlowReturn ret = GST_FLOW_OK;
  GstFlowReturn res = GST_FLOW_OK;
  GstMapInfo map;

  offset = qtdemux->moof_offset;
  GST_DEBUG_OBJECT (qtdemux, "next moof at offset %" G_GUINT64_FORMAT, offset);

  if (!offset) {
    GST_DEBUG_OBJECT (qtdemux, "no next moof");
    return GST_FLOW_EOS;
  }

  /* best not do pull etc with lock held */
  GST_OBJECT_UNLOCK (qtdemux);

  ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
  if (ret != GST_FLOW_OK)
    goto flow_failed;

  ret = gst_qtdemux_pull_atom (qtdemux, offset, length, &buf);
  if (G_UNLIKELY (ret != GST_FLOW_OK))
    goto flow_failed;
  gst_buffer_map (buf, &map, GST_MAP_READ);
  if (!qtdemux_parse_moof (qtdemux, map.data, map.size, offset, NULL)) {
    gst_buffer_unmap (buf, &map);
    gst_buffer_unref (buf);
    buf = NULL;
    goto parse_failed;
  }

  gst_buffer_unmap (buf, &map);
  gst_buffer_unref (buf);
  buf = NULL;

  offset += length;
  /* look for next moof */
  ret = qtdemux_find_atom (qtdemux, &offset, &length, FOURCC_moof);
  if (G_UNLIKELY (ret != GST_FLOW_OK))
    goto flow_failed;

exit:
  GST_OBJECT_LOCK (qtdemux);

  qtdemux->moof_offset = offset;

  return res;

parse_failed:
  {
    GST_DEBUG_OBJECT (qtdemux, "failed to parse moof");
    offset = 0;
    res = GST_FLOW_ERROR;
    goto exit;
  }
flow_failed:
  {
    /* maybe upstream temporarily flushing */
    if (ret != GST_FLOW_FLUSHING) {
      GST_DEBUG_OBJECT (qtdemux, "no next moof");
      offset = 0;
    } else {
      GST_DEBUG_OBJECT (qtdemux, "upstream WRONG_STATE");
      /* resume at current position next time */
    }
    res = ret;
    goto exit;
  }
}

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

gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
    gboolean use_sparse, gint64 * key_time, gint64 * key_offset)
{
  guint64 min_offset;
  gint64 min_byte_offset = -1;
  gint n;

  min_offset = desired_time;

  /* for each stream, find the index of the sample in the segment
   * and move back to the previous keyframe. */
  for (n = 0; n < qtdemux->n_streams; n++) {
    QtDemuxStream *str;
    guint32 index, kindex;
    guint32 seg_idx;
    GstClockTime media_start;
    GstClockTime media_time;
    GstClockTime seg_time;
    QtDemuxSegment *seg;
    gboolean empty_segment = FALSE;

    str = qtdemux->streams[n];

    if (str->sparse && !use_sparse)
      continue;

    seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time);
    GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);

    /* get segment and time in the segment */
    seg = &str->segments[seg_idx];
    seg_time = (desired_time - seg->time) * seg->rate;

    while (QTSEGMENT_IS_EMPTY (seg)) {
      seg_time = 0;
      empty_segment = TRUE;
      GST_DEBUG_OBJECT (str->pad, "Segment %d is empty, moving to next one",
          seg_idx);
      seg_idx++;
      if (seg_idx == str->n_segments)
        break;
      seg = &str->segments[seg_idx];
    }

    if (seg_idx == str->n_segments) {
      /* FIXME track shouldn't have the last segment as empty, but if it
       * happens we better handle it */
      continue;
    }

    /* get the media time in the segment */
    media_start = seg->media_start + seg_time;

    /* get the index of the sample with media time */
    index = gst_qtdemux_find_index_linear (qtdemux, str, media_start);
    GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u"
        " at offset %" G_GUINT64_FORMAT " (empty segment: %d)",
        GST_TIME_ARGS (media_start), index, str->samples[index].offset,
        empty_segment);

    if (!empty_segment) {
      /* find previous keyframe */
      kindex = gst_qtdemux_find_keyframe (qtdemux, str, index);

      /* if the keyframe is at a different position, we need to update the
       * requested seek time */
      if (index != kindex) {
        index = kindex;

        /* get timestamp of keyframe */
        media_time = QTSAMPLE_DTS (str, &str->samples[kindex]);
        GST_DEBUG_OBJECT (qtdemux,
            "keyframe at %u with time %" GST_TIME_FORMAT " at offset %"
            G_GUINT64_FORMAT, kindex, GST_TIME_ARGS (media_time),
            str->samples[kindex].offset);

        /* keyframes in the segment get a chance to change the
         * desired_offset. keyframes out of the segment are
         * ignored. */
        if (media_time >= seg->media_start) {
          GstClockTime seg_time;

          /* this keyframe is inside the segment, convert back to
           * segment time */
          seg_time = (media_time - seg->media_start) + seg->time;
          if (seg_time < min_offset)
            min_offset = seg_time;
        }
      }
    }

    if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset)
      min_byte_offset = str->samples[index].offset;
  }

  if (key_time)
    *key_time = min_offset;
  if (key_offset)
    *key_offset = min_byte_offset;
}

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

qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
{
  const gchar *fmt;

  if (tags == NULL) {
    tags = gst_tag_list_new_empty ();
    gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
  }

  if (qtdemux->major_brand == FOURCC_mjp2)
    fmt = "Motion JPEG 2000";
  else if ((qtdemux->major_brand & 0xffff) == FOURCC_3g__)
    fmt = "3GP";
  else if (qtdemux->major_brand == FOURCC_qt__)
    fmt = "Quicktime";
  else if (qtdemux->fragmented)
    fmt = "ISO fMP4";
  else
    fmt = "ISO MP4/M4A";

  GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'",
      GST_FOURCC_ARGS (qtdemux->major_brand), fmt);

  gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT,
      fmt, NULL);

  return tags;
}

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

qtdemux_tree_get_child_by_type_full (GNode * node, guint32 fourcc,
    GstByteReader * parser)
{
  GNode *child;
  guint8 *buffer;
  guint32 child_fourcc, child_len;

  for (child = g_node_first_child (node); child;
      child = g_node_next_sibling (child)) {
    buffer = (guint8 *) child->data;

    child_len = QT_UINT32 (buffer);
    child_fourcc = QT_FOURCC (buffer + 4);

    if (G_UNLIKELY (child_fourcc == fourcc)) {
      if (G_UNLIKELY (child_len < (4 + 4)))
        return NULL;
      /* FIXME: must verify if atom length < parent atom length */
      gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
      return child;
    }
  }
  return NULL;
}

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

qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes)
{
  const guint8 *uuid = (const guint8 *) uuid_bytes;

  return g_strdup_printf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
      "%02x%02x-%02x%02x%02x%02x%02x%02x",
      uuid[0], uuid[1], uuid[2], uuid[3],
      uuid[4], uuid[5], uuid[6], uuid[7],
      uuid[8], uuid[9], uuid[10], uuid[11],
      uuid[12], uuid[13], uuid[14], uuid[15]);
}

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

gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux)
{
  const QtDemuxRandomAccessEntry *best_entry = NULL;
  guint i;

  GST_OBJECT_LOCK (qtdemux);

  g_assert (qtdemux->n_streams > 0);

  for (i = 0; i < qtdemux->n_streams; i++) {
    const QtDemuxRandomAccessEntry *entry;
    QtDemuxStream *stream;
    gboolean is_audio_or_video;

    stream = qtdemux->streams[i];

    g_free (stream->samples);
    stream->samples = NULL;
    stream->n_samples = 0;
    stream->stbl_index = -1;    /* no samples have yet been parsed */
    stream->sample_index = -1;

    if (stream->ra_entries == NULL)
      continue;

    if (stream->subtype == FOURCC_vide || stream->subtype == FOURCC_soun)
      is_audio_or_video = TRUE;
    else
      is_audio_or_video = FALSE;

    entry =
        gst_qtdemux_stream_seek_fragment (qtdemux, stream,
        stream->time_position, !is_audio_or_video);

    GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
        "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);

    stream->pending_seek = entry;

    /* decide position to jump to just based on audio/video tracks, not subs */
    if (!is_audio_or_video)
      continue;

    if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
      best_entry = entry;
  }

  if (best_entry == NULL) {
    GST_OBJECT_UNLOCK (qtdemux);
    return FALSE;
  }

  GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
      "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
      GST_TIME_ARGS (qtdemux->streams[0]->time_position),
      best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));

  qtdemux->moof_offset = best_entry->moof_offset;

  qtdemux_add_fragmented_samples (qtdemux);

  GST_OBJECT_UNLOCK (qtdemux);
  return TRUE;
}

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

qtdemux_tag_add_covr (GstQTDemux * qtdemux, GstTagList * taglist,
    const char *tag1, const char *dummy, GNode * node)
{
  GNode *data;
  int len;
  int type;
  GstSample *sample;

  data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
  if (data) {
    len = QT_UINT32 (data->data);
    type = QT_UINT32 ((guint8 *) data->data + 8);
    GST_DEBUG_OBJECT (qtdemux, "have covr tag, type=%d,len=%d", type, len);
    if ((type == 0x0000000d || type == 0x0000000e) && len > 16) {
      if ((sample =
              gst_tag_image_data_to_image_sample ((guint8 *) data->data + 16,
                  len - 16, GST_TAG_IMAGE_TYPE_NONE))) {
        GST_DEBUG_OBJECT (qtdemux, "adding tag size %d", len - 16);
        gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, sample, NULL);
        gst_sample_unref (sample);
      }
    }
  }
}

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

qtdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
    GstPadMode mode, gboolean active)
{
  gboolean res;
  GstQTDemux *demux = GST_QTDEMUX (parent);

  switch (mode) {
    case GST_PAD_MODE_PUSH:
      demux->pullbased = FALSE;
      res = TRUE;
      break;
    case GST_PAD_MODE_PULL:
      if (active) {
        demux->pullbased = TRUE;
        res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop,
            sinkpad, NULL);
      } else {
        res = gst_pad_stop_task (sinkpad);
      }
      break;
    default:
      res = FALSE;
      break;
  }
  return res;
}

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

gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
{
  if (qtdemux->posted_redirect) {
    GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
        (_("This file contains no playable streams.")),
        ("no known streams found, a redirect message has been posted"));
  } else {
    GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
        (_("This file contains no playable streams.")),
        ("no known streams found"));
  }
}

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

gst_qtdemux_stbl_free (QtDemuxStream * stream)
{
  g_free ((gpointer) stream->stco.data);
  stream->stco.data = NULL;
  g_free ((gpointer) stream->stsz.data);
  stream->stsz.data = NULL;
  g_free ((gpointer) stream->stsc.data);
  stream->stsc.data = NULL;
  g_free ((gpointer) stream->stts.data);
  stream->stts.data = NULL;
  g_free ((gpointer) stream->stss.data);
  stream->stss.data = NULL;
  g_free ((gpointer) stream->stps.data);
  stream->stps.data = NULL;
  g_free ((gpointer) stream->ctts.data);
  stream->ctts.data = NULL;
}

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

qtdemux_tag_add_location (GstQTDemux * qtdemux, GstTagList * taglist,
    const char *tag, const char *dummy, GNode * node)
{
  const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
  int offset;
  char *name;
  gchar *data;
  gdouble longitude, latitude, altitude;
  gint len;

  len = QT_UINT32 (node->data);
  if (len <= 14)
    goto short_read;

  data = node->data;
  offset = 14;

  /* TODO: language code skipped */

  name = gst_tag_freeform_string_to_utf8 (data + offset, -1, env_vars);

  if (!name) {
    /* do not alarm in trivial case, but bail out otherwise */
    if (*(data + offset) != 0) {
      GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8, "
          "giving up", tag);
    }
  } else {
    gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
        GST_TAG_GEO_LOCATION_NAME, name, NULL);
    offset += strlen (name);
    g_free (name);
  }

  if (len < offset + 2 + 4 + 4 + 4)
    goto short_read;

  /* +1 +1 = skip null-terminator and location role byte */
  offset += 1 + 1;
  /* table in spec says unsigned, semantics say negative has meaning ... */
  longitude = QT_SFP32 (data + offset);

  offset += 4;
  latitude = QT_SFP32 (data + offset);

  offset += 4;
  altitude = QT_SFP32 (data + offset);

  /* one invalid means all are invalid */
  if (longitude >= -180.0 && longitude <= 180.0 &&
      latitude >= -90.0 && latitude <= 90.0) {
    gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
        GST_TAG_GEO_LOCATION_LATITUDE, latitude,
        GST_TAG_GEO_LOCATION_LONGITUDE, longitude,
        GST_TAG_GEO_LOCATION_ELEVATION, altitude, NULL);
  }

  /* TODO: no GST_TAG_, so astronomical body and additional notes skipped */

  return;

  /* ERRORS */
short_read:
  {
    GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP location");
    return;
  }
}

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

gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps)
{
  GstStructure *structure;
  const gchar *variant;
  const GstCaps *mediacaps = NULL;

  GST_DEBUG_OBJECT (demux, "Sink set caps: %" GST_PTR_FORMAT, caps);

  structure = gst_caps_get_structure (caps, 0);
  variant = gst_structure_get_string (structure, "variant");

  if (variant && strcmp (variant, "mss-fragmented") == 0) {
    QtDemuxStream *stream;
    const GValue *value;

    demux->fragmented = TRUE;
    demux->mss_mode = TRUE;

    if (demux->n_streams > 1) {
      /* can't do this, we can only renegotiate for another mss format */
      return FALSE;
    }

    value = gst_structure_get_value (structure, "media-caps");
    /* create stream */
    if (value) {
      const GValue *timescale_v;

      /* TODO update when stream changes during playback */

      if (demux->n_streams == 0) {
        stream = _create_stream ();
        demux->streams[demux->n_streams] = stream;
        demux->n_streams = 1;
      } else {
        stream = demux->streams[0];
      }

      timescale_v = gst_structure_get_value (structure, "timescale");
      if (timescale_v) {
        stream->timescale = g_value_get_uint64 (timescale_v);
      } else {
        /* default mss timescale */
        stream->timescale = 10000000;
      }
      demux->timescale = stream->timescale;

      mediacaps = gst_value_get_caps (value);
      if (!stream->caps || !gst_caps_is_equal_fixed (mediacaps, stream->caps)) {
        GST_DEBUG_OBJECT (demux, "We have a new caps %" GST_PTR_FORMAT,
            mediacaps);
        stream->new_caps = TRUE;
      }
      gst_caps_replace (&stream->caps, (GstCaps *) mediacaps);
      structure = gst_caps_get_structure (mediacaps, 0);
      if (g_str_has_prefix (gst_structure_get_name (structure), "video")) {
        stream->subtype = FOURCC_vide;

        gst_structure_get_int (structure, "width", &stream->width);
        gst_structure_get_int (structure, "height", &stream->height);
        gst_structure_get_fraction (structure, "framerate", &stream->fps_n,
            &stream->fps_d);
      } else if (g_str_has_prefix (gst_structure_get_name (structure), "audio")) {
        gint rate = 0;
        stream->subtype = FOURCC_soun;
        gst_structure_get_int (structure, "channels", &stream->n_channels);
        gst_structure_get_int (structure, "rate", &rate);
        stream->rate = rate;
      }
    }
    gst_caps_replace (&demux->media_caps, (GstCaps *) mediacaps);
  } else {
    demux->mss_mode = FALSE;
  }

  return TRUE;
}

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

gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
{
  guint64 length = 0;
  guint32 fourcc = 0;
  GstBuffer *buf = NULL;
  GstFlowReturn ret = GST_FLOW_OK;
  guint64 cur_offset = qtdemux->offset;
  GstMapInfo map;

  ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
  if (G_UNLIKELY (ret != GST_FLOW_OK))
    goto beach;
  gst_buffer_map (buf, &map, GST_MAP_READ);
  if (G_LIKELY (map.size >= 8))
    extract_initial_length_and_fourcc (map.data, map.size, &length, &fourcc);
  gst_buffer_unmap (buf, &map);
  gst_buffer_unref (buf);

  /* maybe we already got most we needed, so only consider this eof */
  if (G_UNLIKELY (length == 0)) {
    GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
        (_("Invalid atom size.")),
        ("Header atom '%" GST_FOURCC_FORMAT "' has empty length",
            GST_FOURCC_ARGS (fourcc)));
    ret = GST_FLOW_EOS;
    goto beach;
  }

  switch (fourcc) {
    case FOURCC_moof:
      /* record for later parsing when needed */
      if (!qtdemux->moof_offset) {
        qtdemux->moof_offset = qtdemux->offset;
      }
      if (qtdemux_pull_mfro_mfra (qtdemux)) {
        /* FIXME */
      } else {
        qtdemux->offset += length;      /* skip moof and keep going */
      }
      if (qtdemux->got_moov) {
        GST_INFO_OBJECT (qtdemux, "moof header, got moov, done with headers");
        ret = GST_FLOW_EOS;
        goto beach;
      }
      break;
    case FOURCC_mdat:
    case FOURCC_free:
    case FOURCC_wide:
    case FOURCC_PICT:
    case FOURCC_pnot:
    {
      GST_LOG_OBJECT (qtdemux,
          "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
          GST_FOURCC_ARGS (fourcc), cur_offset);
      qtdemux->offset = add_offset (qtdemux->offset, length);
      break;
    }
    case FOURCC_moov:
    {
      GstBuffer *moov = NULL;

      if (qtdemux->got_moov) {
        GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
        qtdemux->offset = add_offset (qtdemux->offset, length);
        goto beach;
      }

      ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
      if (ret != GST_FLOW_OK)
        goto beach;
      gst_buffer_map (moov, &map, GST_MAP_READ);

      if (length != map.size) {
        /* Some files have a 'moov' atom at the end of the file which contains
         * a terminal 'free' atom where the body of the atom is missing.
         * Check for, and permit, this special case.
         */
        if (map.size >= 8) {
          guint8 *final_data = map.data + (map.size - 8);
          guint32 final_length = QT_UINT32 (final_data);
          guint32 final_fourcc = QT_FOURCC (final_data + 4);

          if (final_fourcc == FOURCC_free
              && map.size + final_length - 8 == length) {
            /* Ok, we've found that special case. Allocate a new buffer with
             * that free atom actually present. */
            GstBuffer *newmoov = gst_buffer_new_and_alloc (length);
            gst_buffer_fill (newmoov, 0, map.data, map.size);
            gst_buffer_memset (newmoov, map.size, 0, final_length - 8);
            gst_buffer_unmap (moov, &map);
            gst_buffer_unref (moov);
            moov = newmoov;
            gst_buffer_map (moov, &map, GST_MAP_READ);
          }
        }
      }

      if (length != map.size) {
        GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
            (_("This file is incomplete and cannot be played.")),
            ("We got less than expected (received %" G_GSIZE_FORMAT
                ", wanted %u, offset %" G_GUINT64_FORMAT ")", map.size,
                (guint) length, cur_offset));
        gst_buffer_unmap (moov, &map);
        gst_buffer_unref (moov);
        ret = GST_FLOW_ERROR;
        goto beach;
      }
      qtdemux->offset += length;

      qtdemux_parse_moov (qtdemux, map.data, length);
      qtdemux_node_dump (qtdemux, qtdemux->moov_node);

      qtdemux_parse_tree (qtdemux);
      g_node_destroy (qtdemux->moov_node);
      gst_buffer_unmap (moov, &map);
      gst_buffer_unref (moov);
      qtdemux->moov_node = NULL;
      qtdemux->got_moov = TRUE;

      break;
    }
    case FOURCC_ftyp:
    {
      GstBuffer *ftyp = NULL;

      /* extract major brand; might come in handy for ISO vs QT issues */
      ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp);
      if (ret != GST_FLOW_OK)
        goto beach;
      qtdemux->offset += length;
      gst_buffer_map (ftyp, &map, GST_MAP_READ);
      qtdemux_parse_ftyp (qtdemux, map.data, map.size);
      gst_buffer_unmap (ftyp, &map);
      gst_buffer_unref (ftyp);
      break;
    }
    case FOURCC_uuid:
    {
      GstBuffer *uuid = NULL;

      /* uuid are extension atoms */
      ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &uuid);
      if (ret != GST_FLOW_OK)
        goto beach;
      qtdemux->offset += length;
      gst_buffer_map (uuid, &map, GST_MAP_READ);
      qtdemux_parse_uuid (qtdemux, map.data, map.size);
      gst_buffer_unmap (uuid, &map);
      gst_buffer_unref (uuid);
      break;
    }
    case FOURCC_sidx:
    {
      GstBuffer *sidx = NULL;
      ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &sidx);
      if (ret != GST_FLOW_OK)
        goto beach;
      qtdemux->offset += length;
      gst_buffer_map (sidx, &map, GST_MAP_READ);
      qtdemux_parse_sidx (qtdemux, map.data, map.size);
      gst_buffer_unmap (sidx, &map);
      gst_buffer_unref (sidx);
      break;
    }
    default:
    {
      GstBuffer *unknown = NULL;

      GST_LOG_OBJECT (qtdemux,
          "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT
          " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length,
          cur_offset);
      ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown);
      if (ret != GST_FLOW_OK)
        goto beach;
      gst_buffer_map (unknown, &map, GST_MAP_READ);
      GST_MEMDUMP ("Unknown tag", map.data, map.size);
      gst_buffer_unmap (unknown, &map);
      gst_buffer_unref (unknown);
      qtdemux->offset += length;
      break;
    }
  }

beach:
  if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
    /* digested all data, show what we have */
    qtdemux_prepare_streams (qtdemux);
    ret = qtdemux_expose_streams (qtdemux);

    qtdemux->state = QTDEMUX_STATE_MOVIE;
    GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
        qtdemux->state);
    return ret;
  }
  return ret;
}

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

gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
    GstBuffer * buf)
{
  guint64 start, stop, cstart, cstop, diff;
  GstClockTime pts, duration;
  gsize size, osize;
  gint num_rate, denom_rate;
  gint frame_size;
  gboolean clip_data;
  guint offset;

  osize = size = gst_buffer_get_size (buf);
  offset = 0;

  /* depending on the type, setup the clip parameters */
  if (stream->subtype == FOURCC_soun) {
    frame_size = stream->bytes_per_frame;
    num_rate = GST_SECOND;
    denom_rate = (gint) stream->rate;
    clip_data = TRUE;
  } else if (stream->subtype == FOURCC_vide) {
    frame_size = size;
    num_rate = stream->fps_n;
    denom_rate = stream->fps_d;
    clip_data = FALSE;
  } else
    goto wrong_type;

  if (frame_size <= 0)
    goto bad_frame_size;

  /* we can only clip if we have a valid pts */
  pts = GST_BUFFER_PTS (buf);
  if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts)))
    goto no_pts;

  duration = GST_BUFFER_DURATION (buf);

  if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (duration))) {
    duration =
        gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate);
  }

  start = pts;
  stop = start + duration;

  if (G_UNLIKELY (!gst_segment_clip (&stream->segment,
              GST_FORMAT_TIME, start, stop, &cstart, &cstop)))
    goto clipped;

  /* see if some clipping happened */
  diff = cstart - start;
  if (diff > 0) {
    pts += diff;
    duration -= diff;

    if (clip_data) {
      /* bring clipped time to samples and to bytes */
      diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
      diff *= frame_size;

      GST_DEBUG_OBJECT (qtdemux,
          "clipping start to %" GST_TIME_FORMAT " %"
          G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);

      offset = diff;
      size -= diff;
    }
  }
  diff = stop - cstop;
  if (diff > 0) {
    duration -= diff;

    if (clip_data) {
      /* bring clipped time to samples and then to bytes */
      diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate);
      diff *= frame_size;
      GST_DEBUG_OBJECT (qtdemux,
          "clipping stop to %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
          " bytes", GST_TIME_ARGS (cstop), diff);
      size -= diff;
    }
  }

  if (offset != 0 || size != osize)
    gst_buffer_resize (buf, offset, size);

  GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
  GST_BUFFER_PTS (buf) = pts;
  GST_BUFFER_DURATION (buf) = duration;

  return buf;

  /* dropped buffer */
wrong_type:
  {
    GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
    return buf;
  }
bad_frame_size:
  {
    GST_DEBUG_OBJECT (qtdemux, "bad frame size");
    return buf;
  }
no_pts:
  {
    GST_DEBUG_OBJECT (qtdemux, "no pts on buffer");
    return buf;
  }
clipped:
  {
    GST_DEBUG_OBJECT (qtdemux, "clipped buffer");
    gst_buffer_unref (buf);
    return NULL;
  }
}

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

gst_qtdemux_add_stream (GstQTDemux * qtdemux,
    QtDemuxStream * stream, GstTagList * list)
{
  gboolean ret = TRUE;
  /* consistent default for push based mode */
  gst_segment_init (&stream->segment, GST_FORMAT_TIME);

  if (stream->subtype == FOURCC_vide) {
    gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);

    stream->pad =
        gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
    g_free (name);

    if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
      gst_object_unref (stream->pad);
      stream->pad = NULL;
      ret = FALSE;
      goto done;
    }

    qtdemux->n_video_streams++;
  } else if (stream->subtype == FOURCC_soun) {
    gchar *name = g_strdup_printf ("audio_%u", qtdemux->n_audio_streams);

    stream->pad =
        gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
    g_free (name);
    if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
      gst_object_unref (stream->pad);
      stream->pad = NULL;
      ret = FALSE;
      goto done;
    }
    qtdemux->n_audio_streams++;
  } else if (stream->subtype == FOURCC_strm) {
    GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
  } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
      || stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt) {
    gchar *name = g_strdup_printf ("subtitle_%u", qtdemux->n_sub_streams);

    stream->pad =
        gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
    g_free (name);
    if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
      gst_object_unref (stream->pad);
      stream->pad = NULL;
      ret = FALSE;
      goto done;
    }
    qtdemux->n_sub_streams++;
  } else if (stream->caps) {
    gchar *name = g_strdup_printf ("video_%u", qtdemux->n_video_streams);

    stream->pad =
        gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
    g_free (name);
    if (!gst_qtdemux_configure_stream (qtdemux, stream)) {
      gst_object_unref (stream->pad);
      stream->pad = NULL;
      ret = FALSE;
      goto done;
    }
    qtdemux->n_video_streams++;
  } else {
    GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
    goto done;
  }

  if (stream->pad) {
    GList *l;

    GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
        GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
    gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
    gst_flow_combiner_add_pad (qtdemux->flowcombiner, stream->pad);

    if (stream->pending_tags)
      gst_tag_list_unref (stream->pending_tags);
    stream->pending_tags = list;
    list = NULL;
    /* global tags go on each pad anyway */
    stream->send_global_tags = TRUE;
    /* send upstream GST_EVENT_PROTECTION events that were received before
       this source pad was created */
    for (l = qtdemux->protection_event_queue.head; l != NULL; l = l->next)
      gst_pad_push_event (stream->pad, gst_event_ref (l->data));
  }
done:
  if (list)
    gst_tag_list_unref (list);
  return ret;
}

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

qtdemux_parse_transformation_matrix (GstQTDemux * qtdemux,
    GstByteReader * reader, guint32 * matrix, const gchar * atom)
{
  /*
   * 9 values of 32 bits (fixed point 16.16, except 2 5 and 8 that are 2.30)
   * [0 1 2]
   * [3 4 5]
   * [6 7 8]
   */

  if (gst_byte_reader_get_remaining (reader) < 36)
    return FALSE;

  matrix[0] = gst_byte_reader_get_uint32_be_unchecked (reader);
  matrix[1] = gst_byte_reader_get_uint32_be_unchecked (reader);
  matrix[2] = gst_byte_reader_get_uint32_be_unchecked (reader);
  matrix[3] = gst_byte_reader_get_uint32_be_unchecked (reader);
  matrix[4] = gst_byte_reader_get_uint32_be_unchecked (reader);
  matrix[5] = gst_byte_reader_get_uint32_be_unchecked (reader);
  matrix[6] = gst_byte_reader_get_uint32_be_unchecked (reader);
  matrix[7] = gst_byte_reader_get_uint32_be_unchecked (reader);
  matrix[8] = gst_byte_reader_get_uint32_be_unchecked (reader);

  GST_DEBUG_OBJECT (qtdemux, "Transformation matrix from atom %s", atom);
  GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[0] >> 16,
      matrix[0] & 0xFFFF, matrix[1] >> 16, matrix[1] & 0xFF, matrix[2] >> 16,
      matrix[2] & 0xFF);
  GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[3] >> 16,
      matrix[3] & 0xFFFF, matrix[4] >> 16, matrix[4] & 0xFF, matrix[5] >> 16,
      matrix[5] & 0xFF);
  GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[6] >> 16,
      matrix[6] & 0xFFFF, matrix[7] >> 16, matrix[7] & 0xFF, matrix[8] >> 16,
      matrix[8] & 0xFF);

  return TRUE;
}

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

gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
    guint64 media_time)
{
  QtDemuxSample *result;
  guint32 index;

  /* convert media_time to mov format */
  media_time =
      gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);

  result = gst_util_array_binary_search (str->samples, str->stbl_index + 1,
      sizeof (QtDemuxSample), (GCompareDataFunc) find_func,
      GST_SEARCH_MODE_BEFORE, &media_time, NULL);

  if (G_LIKELY (result))
    index = result - str->samples;
  else
    index = 0;

  return index;
}

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

gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
{
  gdouble rate;
  GstFormat format;
  GstSeekFlags flags;
  GstSeekType cur_type, stop_type;
  gint64 cur, stop, key_cur;
  gboolean res;
  gint64 byte_cur;
  gint64 original_stop;
  guint32 seqnum;

  GST_DEBUG_OBJECT (qtdemux, "doing push-based seek");

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

  /* only forward streaming and seeking is possible */
  if (rate <= 0)
    goto unsupported_seek;

  /* convert to TIME if needed and possible */
  if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
          stop_type, &stop))
    goto no_format;

  /* Upstream seek in bytes will have undefined stop, but qtdemux stores
   * the original stop position to use when upstream pushes the new segment
   * for this seek */
  original_stop = stop;
  stop = -1;

  /* find reasonable corresponding BYTE position,
   * also try to mind about keyframes, since we can not go back a bit for them
   * later on */
  gst_qtdemux_adjust_seek (qtdemux, cur, FALSE, &key_cur, &byte_cur);

  if (byte_cur == -1)
    goto abort_seek;

  GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, "
      "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur,
      stop);

  GST_OBJECT_LOCK (qtdemux);
  qtdemux->seek_offset = byte_cur;
  if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
    qtdemux->push_seek_start = cur;
  } else {
    qtdemux->push_seek_start = key_cur;
  }

  if (stop_type == GST_SEEK_TYPE_NONE) {
    qtdemux->push_seek_stop = qtdemux->segment.stop;
  } else {
    qtdemux->push_seek_stop = original_stop;
  }
  GST_OBJECT_UNLOCK (qtdemux);

  /* BYTE seek event */
  event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur,
      stop_type, stop);
  gst_event_set_seqnum (event, seqnum);
  res = gst_pad_push_event (qtdemux->sinkpad, event);

  return res;

  /* ERRORS */
abort_seek:
  {
    GST_DEBUG_OBJECT (qtdemux, "could not determine byte position to seek to, "
        "seek aborted.");
    return FALSE;
  }
unsupported_seek:
  {
    GST_DEBUG_OBJECT (qtdemux, "unsupported seek, seek aborted.");
    return FALSE;
  }
no_format:
  {
    GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
    return FALSE;
  }
}

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

_gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
{
  return gst_buffer_new_wrapped_full (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
      mem, size, 0, size, mem, free_func);
}

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

gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment,
    guint32 seqnum, GstSeekFlags flags)
{
  gint64 desired_offset;
  gint n;

  desired_offset = segment->position;

  GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
      GST_TIME_ARGS (desired_offset));

  /* may not have enough fragmented info to do this adjustment,
   * and we can't scan (and probably should not) at this time with
   * possibly flushing upstream */
  if ((flags & GST_SEEK_FLAG_KEY_UNIT) && !qtdemux->fragmented) {
    gint64 min_offset;

    gst_qtdemux_adjust_seek (qtdemux, desired_offset, TRUE, &min_offset, NULL);
    GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
        GST_TIME_FORMAT, GST_TIME_ARGS (min_offset));
    desired_offset = min_offset;
  }

  /* and set all streams to the final position */
  gst_flow_combiner_reset (qtdemux->flowcombiner);
  qtdemux->segment_seqnum = seqnum;
  for (n = 0; n < qtdemux->n_streams; n++) {
    QtDemuxStream *stream = qtdemux->streams[n];

    stream->time_position = desired_offset;
    stream->accumulated_base = 0;
    stream->sample_index = -1;
    stream->offset_in_sample = 0;
    stream->segment_index = -1;
    stream->sent_eos = FALSE;

    if (segment->flags & GST_SEEK_FLAG_FLUSH)
      gst_segment_init (&stream->segment, GST_FORMAT_TIME);
  }
  segment->position = desired_offset;
  segment->time = desired_offset;
  if (segment->rate >= 0) {
    segment->start = desired_offset;

    /* we stop at the end */
    if (segment->stop == -1)
      segment->stop = segment->duration;
  } else {
    segment->stop = desired_offset;
  }

  if (qtdemux->fragmented)
    qtdemux->fragmented_seek_pending = TRUE;

  return TRUE;
}

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

_create_stream (void)
{
  QtDemuxStream *stream;

  stream = g_new0 (QtDemuxStream, 1);
  /* new streams always need a discont */
  stream->discont = TRUE;
  /* we enable clipping for raw audio/video streams */
  stream->need_clip = FALSE;
  stream->need_process = FALSE;
  stream->segment_index = -1;
  stream->time_position = 0;
  stream->sample_index = -1;
  stream->offset_in_sample = 0;
  stream->new_stream = TRUE;
  stream->multiview_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
  stream->multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
  stream->protected = FALSE;
  stream->protection_scheme_type = 0;
  stream->protection_scheme_version = 0;
  stream->protection_scheme_info = NULL;
  stream->n_samples_moof = 0;
  stream->duration_moof = 0;
  stream->duration_last_moof = 0;
  stream->alignment = 1;
  g_queue_init (&stream->protection_scheme_event_queue);
  return stream;
}

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

qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
    const guint8 * end)
{
  while (G_UNLIKELY (buf < end)) {
    GNode *child;
    guint32 len;

    if (G_UNLIKELY (buf + 4 > end)) {
      GST_LOG_OBJECT (qtdemux, "buffer overrun");
      break;
    }
    len = QT_UINT32 (buf);
    if (G_UNLIKELY (len == 0)) {
      GST_LOG_OBJECT (qtdemux, "empty container");
      break;
    }
    if (G_UNLIKELY (len < 8)) {
      GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len);
      break;
    }
    if (G_UNLIKELY (len > (end - buf))) {
      GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len,
          (gint) (end - buf));
      break;
    }

    child = g_node_new ((guint8 *) buf);
    g_node_append (node, child);
    GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
    qtdemux_parse_node (qtdemux, child, buf, len);

    buf += len;
  }
  return TRUE;
}

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

gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
    QtDemuxStream * stream, GstBuffer * buf,
    GstClockTime dts, GstClockTime pts, GstClockTime duration,
    gboolean keyframe, GstClockTime position, guint64 byte_position)
{
  GstFlowReturn ret = GST_FLOW_OK;

  /* offset the timestamps according to the edit list */

  if (G_UNLIKELY (stream->fourcc == FOURCC_rtsp)) {
    gchar *url;
    GstMapInfo map;

    gst_buffer_map (buf, &map, GST_MAP_READ);
    url = g_strndup ((gchar *) map.data, map.size);
    gst_buffer_unmap (buf, &map);
    if (url != NULL && strlen (url) != 0) {
      /* we have RTSP redirect now */
      gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
          gst_message_new_element (GST_OBJECT_CAST (qtdemux),
              gst_structure_new ("redirect",
                  "new-location", G_TYPE_STRING, url, NULL)));
      qtdemux->posted_redirect = TRUE;
    } else {
      GST_WARNING_OBJECT (qtdemux, "Redirect URI of stream is empty, not "
          "posting");
    }
    g_free (url);
  }

  /* position reporting */
  if (qtdemux->segment.rate >= 0) {
    qtdemux->segment.position = position;
    gst_qtdemux_sync_streams (qtdemux);
  }

  if (G_UNLIKELY (!stream->pad)) {
    GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring");
    gst_buffer_unref (buf);
    goto exit;
  }

  /* send out pending buffers */
  while (stream->buffers) {
    GstBuffer *buffer = (GstBuffer *) stream->buffers->data;

    if (G_UNLIKELY (stream->discont)) {
      GST_LOG_OBJECT (qtdemux, "marking discont buffer");
      GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
      stream->discont = FALSE;
    } else {
      GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
    }

    if (stream->alignment > 1)
      buffer = gst_qtdemux_align_buffer (qtdemux, buffer, stream->alignment);
    gst_pad_push (stream->pad, buffer);

    stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
  }

  /* we're going to modify the metadata */
  buf = gst_buffer_make_writable (buf);

  if (G_UNLIKELY (stream->need_process))
    buf = gst_qtdemux_process_buffer (qtdemux, stream, buf);

  if (!buf) {
    goto exit;
  }

  GST_BUFFER_DTS (buf) = dts;
  GST_BUFFER_PTS (buf) = pts;
  GST_BUFFER_DURATION (buf) = duration;
  GST_BUFFER_OFFSET (buf) = -1;
  GST_BUFFER_OFFSET_END (buf) = -1;

  if (G_UNLIKELY (stream->rgb8_palette))
    gst_buffer_append_memory (buf, gst_memory_ref (stream->rgb8_palette));

  if (G_UNLIKELY (stream->padding)) {
    gst_buffer_resize (buf, stream->padding, -1);
  }
#if 0
  if (G_UNLIKELY (qtdemux->element_index)) {
    GstClockTime stream_time;

    stream_time =
        gst_segment_to_stream_time (&stream->segment, GST_FORMAT_TIME,
        timestamp);
    if (GST_CLOCK_TIME_IS_VALID (stream_time)) {
      GST_LOG_OBJECT (qtdemux,
          "adding association %" GST_TIME_FORMAT "-> %"
          G_GUINT64_FORMAT, GST_TIME_ARGS (stream_time), byte_position);
      gst_index_add_association (qtdemux->element_index,
          qtdemux->index_id,
          keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
          GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, stream_time,
          GST_FORMAT_BYTES, byte_position, NULL);
    }
  }
#endif

  if (stream->need_clip)
    buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf);

  if (G_UNLIKELY (buf == NULL))
    goto exit;

  if (G_UNLIKELY (stream->discont)) {
    GST_LOG_OBJECT (qtdemux, "marking discont buffer");
    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
    stream->discont = FALSE;
  } else {
    GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
  }

  if (!keyframe) {
    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
    stream->on_keyframe = FALSE;
  } else {
    stream->on_keyframe = TRUE;
  }


  GST_LOG_OBJECT (qtdemux,
      "Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
      ", duration %" GST_TIME_FORMAT " on pad %s", GST_TIME_ARGS (dts),
      GST_TIME_ARGS (pts), GST_TIME_ARGS (duration),
      GST_PAD_NAME (stream->pad));

  if (stream->protected && stream->protection_scheme_type == FOURCC_cenc) {
    GstStructure *crypto_info;
    QtDemuxCencSampleSetInfo *info =
        (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
    gint index;
    GstEvent *event;

    while ((event = g_queue_pop_head (&stream->protection_scheme_event_queue))) {
      gst_pad_push_event (stream->pad, event);
    }

    if (info->crypto_info == NULL) {
      GST_DEBUG_OBJECT (qtdemux, "cenc metadata hasn't been parsed yet");
      gst_buffer_unref (buf);
      goto exit;
    }

    index = stream->sample_index - (stream->n_samples - info->crypto_info->len);
    if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) {
      /* steal structure from array */
      crypto_info = g_ptr_array_index (info->crypto_info, index);
      g_ptr_array_index (info->crypto_info, index) = NULL;
      GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u]", index);
      if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
        GST_ERROR_OBJECT (qtdemux, "failed to attach cenc metadata to buffer");
    }
  }

  if (stream->alignment > 1)
    buf = gst_qtdemux_align_buffer (qtdemux, buf, stream->alignment);

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

  if (GST_CLOCK_TIME_IS_VALID (pts) && GST_CLOCK_TIME_IS_VALID (duration)) {
    /* mark position in stream, we'll need this to know when to send GAP event */
    stream->segment.position = pts + duration;
  }

exit:
  return ret;
}

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

gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
    GNode * esds, GstTagList * list)
{
  int len = QT_UINT32 (esds->data);
  guint8 *ptr = esds->data;
  guint8 *end = ptr + len;
  int tag;
  guint8 *data_ptr = NULL;
  int data_len = 0;
  guint8 object_type_id = 0;
  const char *codec_name = NULL;
  GstCaps *caps = NULL;

  GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len);
  ptr += 8;
  GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr));
  ptr += 4;
  while (ptr + 1 < end) {
    tag = QT_UINT8 (ptr);
    GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag);
    ptr++;
    len = read_descr_size (ptr, end, &ptr);
    GST_DEBUG_OBJECT (qtdemux, "len = %d", len);

    /* Check the stated amount of data is available for reading */
    if (len < 0 || ptr + len > end)
      break;

    switch (tag) {
      case ES_DESCRIPTOR_TAG:
        GST_DEBUG_OBJECT (qtdemux, "ID %04x", QT_UINT16 (ptr));
        GST_DEBUG_OBJECT (qtdemux, "priority %04x", QT_UINT8 (ptr + 2));
        ptr += 3;
        break;
      case DECODER_CONFIG_DESC_TAG:{
        guint max_bitrate, avg_bitrate;

        object_type_id = QT_UINT8 (ptr);
        max_bitrate = QT_UINT32 (ptr + 5);
        avg_bitrate = QT_UINT32 (ptr + 9);
        GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id);
        GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", QT_UINT8 (ptr + 1));
        GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2));
        GST_DEBUG_OBJECT (qtdemux, "max bitrate %u", max_bitrate);
        GST_DEBUG_OBJECT (qtdemux, "avg bitrate %u", avg_bitrate);
        if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) {
          gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
              GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL);
        }
        if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) {
          gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
              avg_bitrate, NULL);
        }
        ptr += 13;
        break;
      }
      case DECODER_SPECIFIC_INFO_TAG:
        GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len);
        if (object_type_id == 0xe0 && len == 0x40) {
          guint8 *data;
          GstStructure *s;
          guint32 clut[16];
          gint i;

          GST_DEBUG_OBJECT (qtdemux,
              "Have VOBSUB palette. Creating palette event");
          /* move to decConfigDescr data and read palette */
          data = ptr;
          for (i = 0; i < 16; i++) {
            clut[i] = QT_UINT32 (data);
            data += 4;
          }

          s = gst_structure_new ("application/x-gst-dvd", "event",
              G_TYPE_STRING, "dvd-spu-clut-change",
              "clut00", G_TYPE_INT, clut[0], "clut01", G_TYPE_INT, clut[1],
              "clut02", G_TYPE_INT, clut[2], "clut03", G_TYPE_INT, clut[3],
              "clut04", G_TYPE_INT, clut[4], "clut05", G_TYPE_INT, clut[5],
              "clut06", G_TYPE_INT, clut[6], "clut07", G_TYPE_INT, clut[7],
              "clut08", G_TYPE_INT, clut[8], "clut09", G_TYPE_INT, clut[9],
              "clut10", G_TYPE_INT, clut[10], "clut11", G_TYPE_INT, clut[11],
              "clut12", G_TYPE_INT, clut[12], "clut13", G_TYPE_INT, clut[13],
              "clut14", G_TYPE_INT, clut[14], "clut15", G_TYPE_INT, clut[15],
              NULL);

          /* store event and trigger custom processing */
          stream->pending_event =
              gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
        } else {
          /* Generic codec_data handler puts it on the caps */
          data_ptr = ptr;
          data_len = len;
        }

        ptr += len;
        break;
      case SL_CONFIG_DESC_TAG:
        GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr));
        ptr += 1;
        break;
      default:
        GST_DEBUG_OBJECT (qtdemux, "Unknown/unhandled descriptor tag %02x",
            tag);
        GST_MEMDUMP_OBJECT (qtdemux, "descriptor data", ptr, len);
        ptr += len;
        break;
    }
  }

  /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is
   * in use, and should also be used to override some other parameters for some
   * codecs. */
  switch (object_type_id) {
    case 0x20:                 /* MPEG-4 */
      /* 4 bytes for the visual_object_sequence_start_code and 1 byte for the
       * profile_and_level_indication */
      if (data_ptr != NULL && data_len >= 5 &&
          GST_READ_UINT32_BE (data_ptr) == 0x000001b0) {
        gst_codec_utils_mpeg4video_caps_set_level_and_profile (stream->caps,
            data_ptr + 4, data_len - 4);
      }
      break;                    /* Nothing special needed here */
    case 0x21:                 /* H.264 */
      codec_name = "H.264 / AVC";
      caps = gst_caps_new_simple ("video/x-h264",
          "stream-format", G_TYPE_STRING, "avc",
          "alignment", G_TYPE_STRING, "au", NULL);
      break;
    case 0x40:                 /* AAC (any) */
    case 0x66:                 /* AAC Main */
    case 0x67:                 /* AAC LC */
    case 0x68:                 /* AAC SSR */
      /* Override channels and rate based on the codec_data, as it's often
       * wrong. */
      /* Only do so for basic setup without HE-AAC extension */
      if (data_ptr && data_len == 2) {
        guint channels, rate;

        channels = gst_codec_utils_aac_get_channels (data_ptr, data_len);
        if (channels > 0)
          stream->n_channels = channels;

        rate = gst_codec_utils_aac_get_sample_rate (data_ptr, data_len);
        if (rate > 0)
          stream->rate = rate;
      }

      /* Set level and profile if possible */
      if (data_ptr != NULL && data_len >= 2) {
        gst_codec_utils_aac_caps_set_level_and_profile (stream->caps,
            data_ptr, data_len);
      } else {
        const gchar *profile_str = NULL;
        GstBuffer *buffer;
        GstMapInfo map;
        guint8 *codec_data;
        gint rate_idx, profile;

        /* No codec_data, let's invent something.
         * FIXME: This is wrong for SBR! */

        GST_WARNING_OBJECT (qtdemux, "No codec_data for AAC available");

        buffer = gst_buffer_new_and_alloc (2);
        gst_buffer_map (buffer, &map, GST_MAP_WRITE);
        codec_data = map.data;

        rate_idx =
            gst_codec_utils_aac_get_index_from_sample_rate (stream->rate);

        switch (object_type_id) {
          case 0x66:
            profile_str = "main";
            profile = 0;
            break;
          case 0x67:
            profile_str = "lc";
            profile = 1;
            break;
          case 0x68:
            profile_str = "ssr";
            profile = 2;
            break;
          default:
            profile = 3;
            break;
        }

        codec_data[0] = ((profile + 1) << 3) | ((rate_idx & 0xE) >> 1);
        codec_data[1] = ((rate_idx & 0x1) << 7) | (stream->n_channels << 3);

        gst_buffer_unmap (buffer, &map);
        gst_caps_set_simple (stream->caps, "codec_data", GST_TYPE_BUFFER,
            buffer, NULL);
        gst_buffer_unref (buffer);

        if (profile_str) {
          gst_caps_set_simple (stream->caps, "profile", G_TYPE_STRING,
              profile_str, NULL);
        }
      }
      break;
    case 0x60:                 /* MPEG-2, various profiles */
    case 0x61:
    case 0x62:
    case 0x63:
    case 0x64:
    case 0x65:
      codec_name = "MPEG-2 video";
      caps = gst_caps_new_simple ("video/mpeg",
          "mpegversion", G_TYPE_INT, 2,
          "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
      break;
    case 0x69:                 /* MPEG-2 BC audio */
    case 0x6B:                 /* MPEG-1 audio */
      caps = gst_caps_new_simple ("audio/mpeg",
          "mpegversion", G_TYPE_INT, 1, "parsed", G_TYPE_BOOLEAN, TRUE, NULL);
      codec_name = "MPEG-1 audio";
      break;
    case 0x6A:                 /* MPEG-1 */
      codec_name = "MPEG-1 video";
      caps = gst_caps_new_simple ("video/mpeg",
          "mpegversion", G_TYPE_INT, 1,
          "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
      break;
    case 0x6C:                 /* MJPEG */
      caps =
          gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
          NULL);
      codec_name = "Motion-JPEG";
      break;
    case 0x6D:                 /* PNG */
      caps =
          gst_caps_new_simple ("image/png", "parsed", G_TYPE_BOOLEAN, TRUE,
          NULL);
      codec_name = "PNG still images";
      break;
    case 0x6E:                 /* JPEG2000 */
      codec_name = "JPEG-2000";
      caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL);
      break;
    case 0xA4:                 /* Dirac */
      codec_name = "Dirac";
      caps = gst_caps_new_empty_simple ("video/x-dirac");
      break;
    case 0xA5:                 /* AC3 */
      codec_name = "AC-3 audio";
      caps = gst_caps_new_simple ("audio/x-ac3",
          "framed", G_TYPE_BOOLEAN, TRUE, NULL);
      break;
    case 0xA9:                 /* AC3 */
      codec_name = "DTS audio";
      caps = gst_caps_new_simple ("audio/x-dts",
          "framed", G_TYPE_BOOLEAN, TRUE, NULL);
      break;
    case 0xE1:                 /* QCELP */
      /* QCELP, the codec_data is a riff tag (little endian) with
       * more info (http://ftp.3gpp2.org/TSGC/Working/2003/2003-05-SanDiego/TSG-C-2003-05-San%20Diego/WG1/SWG12/C12-20030512-006%20=%20C12-20030217-015_Draft_Baseline%20Text%20of%20FFMS_R2.doc). */
      caps = gst_caps_new_empty_simple ("audio/qcelp");
      codec_name = "QCELP";
      break;
    default:
      break;
  }

  /* If we have a replacement caps, then change our caps for this stream */
  if (caps) {
    gst_caps_unref (stream->caps);
    stream->caps = caps;
  }

  if (codec_name && list)
    gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
        GST_TAG_AUDIO_CODEC, codec_name, NULL);

  /* Add the codec_data attribute to caps, if we have it */
  if (data_ptr) {
    GstBuffer *buffer;

    buffer = gst_buffer_new_and_alloc (data_len);
    gst_buffer_fill (buffer, 0, data_ptr, data_len);

    GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
    GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);

    gst_caps_set_simple (stream->caps, "codec_data", GST_TYPE_BUFFER,
        buffer, NULL);
    gst_buffer_unref (buffer);
  }

}

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

gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
    GstClockTime media_time)
{
  guint32 index = 0;
  guint64 mov_time;
  QtDemuxSample *sample;

  /* convert media_time to mov format */
  mov_time =
      gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);

  sample = str->samples;
  if (mov_time == sample->timestamp + sample->pts_offset)
    return index;

  /* use faster search if requested time in already parsed range */
  sample = str->samples + str->stbl_index;
  if (str->stbl_index >= 0 &&
      mov_time <= (sample->timestamp + sample->pts_offset))
    return gst_qtdemux_find_index (qtdemux, str, media_time);

  while (index < str->n_samples - 1) {
    if (!qtdemux_parse_samples (qtdemux, str, index + 1))
      goto parse_failed;

    sample = str->samples + index + 1;
    if (mov_time < (sample->timestamp + sample->pts_offset))
      break;

    index++;
  }
  return index;

  /* ERRORS */
parse_failed:
  {
    GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
    return -1;
  }
}

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

add_offset (guint64 offset, guint64 advance)
{
  /* Avoid 64-bit overflow by clamping */
  if (offset > G_MAXUINT64 - advance)
    return G_MAXUINT64;
  return offset + advance;
}

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

qtdemux_is_brand_3gp (GstQTDemux * qtdemux, gboolean major)
{
  if (major) {
    return ((qtdemux->major_brand & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
        FOURCC_3g__);
  } else if (qtdemux->comp_brands != NULL) {
    GstMapInfo map;
    guint8 *data;
    gsize size;
    gboolean res = FALSE;

    gst_buffer_map (qtdemux->comp_brands, &map, GST_MAP_READ);
    data = map.data;
    size = map.size;
    while (size >= 4) {
      res = res || ((QT_FOURCC (data) & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
          FOURCC_3g__);
      data += 4;
      size -= 4;
    }
    gst_buffer_unmap (qtdemux->comp_brands, &map);
    return res;
  } else {
    return FALSE;
  }
}

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

gst_qtdemux_get_index (GstElement * element)
{
  GstIndex *result = NULL;
  GstQTDemux *demux = GST_QTDEMUX (element);

  GST_OBJECT_LOCK (demux);
  if (demux->element_index)
    result = gst_object_ref (demux->element_index);
  GST_OBJECT_UNLOCK (demux);

  GST_DEBUG_OBJECT (demux, "Returning index %" GST_PTR_FORMAT, result);

  return result;
}

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

qtdemux_parse_saiz (GstQTDemux * qtdemux, QtDemuxStream * stream,
    GstByteReader * br, guint32 * sample_count)
{
  guint32 flags = 0;
  guint8 *info_sizes;
  guint8 default_info_size;

  g_return_val_if_fail (qtdemux != NULL, NULL);
  g_return_val_if_fail (stream != NULL, NULL);
  g_return_val_if_fail (br != NULL, NULL);
  g_return_val_if_fail (sample_count != NULL, NULL);

  if (!gst_byte_reader_get_uint32_be (br, &flags))
    return NULL;

  if (flags & 0x1) {
    /* aux_info_type and aux_info_type_parameter are ignored */
    if (!gst_byte_reader_skip (br, 8))
      return NULL;
  }

  if (!gst_byte_reader_get_uint8 (br, &default_info_size))
    return NULL;
  GST_DEBUG_OBJECT (qtdemux, "default_info_size: %u", default_info_size);

  if (!gst_byte_reader_get_uint32_be (br, sample_count))
    return NULL;
  GST_DEBUG_OBJECT (qtdemux, "sample_count: %u", *sample_count);


  if (default_info_size == 0) {
    if (!gst_byte_reader_dup_data (br, *sample_count, &info_sizes)) {
      return NULL;
    }
  } else {
    info_sizes = g_new (guint8, *sample_count);
    memset (info_sizes, default_info_size, *sample_count);
  }

  return info_sizes;
}

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

qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
{
  GNode *dinf;
  GstByteReader dref;
  gchar *uri = NULL;

  /*
   * Get 'dinf', to get its child 'dref', that might contain a 'hndl'
   * atom that might contain a 'data' atom with the rtsp uri.
   * This case was reported in bug #597497, some info about
   * the hndl atom can be found in TN1195
   */
  dinf = qtdemux_tree_get_child_by_type (minf, FOURCC_dinf);
  GST_DEBUG_OBJECT (qtdemux, "Trying to obtain rtsp URI for stream trak");

  if (dinf) {
    guint32 dref_num_entries = 0;
    if (qtdemux_tree_get_child_by_type_full (dinf, FOURCC_dref, &dref) &&
        gst_byte_reader_skip (&dref, 4) &&
        gst_byte_reader_get_uint32_be (&dref, &dref_num_entries)) {
      gint i;

      /* search dref entries for hndl atom */
      for (i = 0; i < dref_num_entries; i++) {
        guint32 size = 0, type;
        guint8 string_len = 0;
        if (gst_byte_reader_get_uint32_be (&dref, &size) &&
            qt_atom_parser_get_fourcc (&dref, &type)) {
          if (type == FOURCC_hndl) {
            GST_DEBUG_OBJECT (qtdemux, "Found hndl atom");

            /* skip data reference handle bytes and the
             * following pascal string and some extra 4
             * bytes I have no idea what are */
            if (!gst_byte_reader_skip (&dref, 4) ||
                !gst_byte_reader_get_uint8 (&dref, &string_len) ||
                !gst_byte_reader_skip (&dref, string_len + 4)) {
              GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl atom");
              break;
            }

            /* iterate over the atoms to find the data atom */
            while (gst_byte_reader_get_remaining (&dref) >= 8) {
              guint32 atom_size;
              guint32 atom_type;

              if (gst_byte_reader_get_uint32_be (&dref, &atom_size) &&
                  qt_atom_parser_get_fourcc (&dref, &atom_type)) {
                if (atom_type == FOURCC_data) {
                  const guint8 *uri_aux = NULL;

                  /* found the data atom that might contain the rtsp uri */
                  GST_DEBUG_OBJECT (qtdemux, "Found data atom inside "
                      "hndl atom, interpreting it as an URI");
                  if (gst_byte_reader_peek_data (&dref, atom_size - 8,
                          &uri_aux)) {
                    if (g_strstr_len ((gchar *) uri_aux, 7, "rtsp://") != NULL)
                      uri = g_strndup ((gchar *) uri_aux, atom_size - 8);
                    else
                      GST_WARNING_OBJECT (qtdemux, "Data atom in hndl atom "
                          "didn't contain a rtsp address");
                  } else {
                    GST_WARNING_OBJECT (qtdemux, "Failed to get the data "
                        "atom contents");
                  }
                  break;
                }
                /* skipping to the next entry */
                if (!gst_byte_reader_skip (&dref, atom_size - 8))
                  break;
              } else {
                GST_WARNING_OBJECT (qtdemux, "Failed to parse hndl child "
                    "atom header");
                break;
              }
            }
            break;
          }
          /* skip to the next entry */
          if (!gst_byte_reader_skip (&dref, size - 8))
            break;
        } else {
          GST_WARNING_OBJECT (qtdemux, "Error parsing dref atom");
        }
      }
      GST_DEBUG_OBJECT (qtdemux, "Finished parsing dref atom");
    }
  }
  return uri;
}

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

gst_qtdemux_drop_data (GstQTDemux * demux, gint bytes)
{
  g_return_if_fail (bytes <= demux->todrop);

  GST_LOG_OBJECT (demux, "Dropping %d bytes", bytes);
  gst_adapter_flush (demux->adapter, bytes);
  demux->neededbytes -= bytes;
  demux->offset += bytes;
  demux->todrop -= bytes;
}

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

qtdemux_parse_sidx (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
{
  GstSidxParser sidx_parser;
  GstIsoffParserResult res;
  guint consumed;

  gst_isoff_qt_sidx_parser_init (&sidx_parser);

  res =
      gst_isoff_qt_sidx_parser_add_data (&sidx_parser, buffer, length,
      &consumed);
  GST_DEBUG_OBJECT (qtdemux, "sidx parse result: %d", res);
  if (res == GST_ISOFF_QT_PARSER_DONE) {
    check_update_duration (qtdemux, sidx_parser.cumulative_pts);
  }
  gst_isoff_qt_sidx_parser_clear (&sidx_parser);
}

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

qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
    GNode * trak)
{
  GNode *edts;
  /* accept edts if they contain gaps at start and there is only
   * one media segment */
  gboolean allow_pushbased_edts = TRUE;
  gint media_segments_count = 0;

  /* parse and prepare segment info from the edit list */
  GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
  stream->n_segments = 0;
  stream->segments = NULL;
  if ((edts = qtdemux_tree_get_child_by_type (trak, FOURCC_edts))) {
    GNode *elst;
    gint n_segments;
    gint i, count, entry_size;
    guint64 time;
    GstClockTime stime;
    guint8 *buffer;
    guint8 version;

    GST_DEBUG_OBJECT (qtdemux, "looking for edit list");
    if (!(elst = qtdemux_tree_get_child_by_type (edts, FOURCC_elst)))
      goto done;

    buffer = elst->data;

    version = QT_UINT8 (buffer + 8);
    entry_size = (version == 1) ? 20 : 12;

    n_segments = QT_UINT32 (buffer + 12);

    /* we might allocate a bit too much, at least allocate 1 segment */
    stream->segments = g_new (QtDemuxSegment, MAX (n_segments, 1));

    /* segments always start from 0 */
    time = 0;
    stime = 0;
    count = 0;
    for (i = 0; i < n_segments; i++) {
      guint64 duration;
      guint64 media_time;
      gboolean time_valid = TRUE;
      QtDemuxSegment *segment;
      guint32 rate_int;
      GstClockTime media_start = GST_CLOCK_TIME_NONE;

      if (version == 1) {
        media_time = QT_UINT64 (buffer + 24 + i * entry_size);
        duration = QT_UINT64 (buffer + 16 + i * entry_size);
        if (media_time == G_MAXUINT64)
          time_valid = FALSE;
      } else {
        media_time = QT_UINT32 (buffer + 20 + i * entry_size);
        duration = QT_UINT32 (buffer + 16 + i * entry_size);
        if (media_time == G_MAXUINT32)
          time_valid = FALSE;
      }

      if (time_valid)
        media_start = QTSTREAMTIME_TO_GSTTIME (stream, media_time);

      segment = &stream->segments[count++];

      /* time and duration expressed in global timescale */
      segment->time = stime;
      /* add non scaled values so we don't cause roundoff errors */
      if (duration || media_start == GST_CLOCK_TIME_NONE) {
        time += duration;
        stime = QTTIME_TO_GSTTIME (qtdemux, time);
        segment->duration = stime - segment->time;
      } else {
        /* zero duration does not imply media_start == media_stop
         * but, only specify media_start.*/
        stime = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
        if (GST_CLOCK_TIME_IS_VALID (stime) && time_valid
            && stime >= media_start) {
          segment->duration = stime - media_start;
        } else {
          segment->duration = GST_CLOCK_TIME_NONE;
        }
      }
      segment->stop_time = stime;

      segment->trak_media_start = media_time;
      /* media_time expressed in stream timescale */
      if (time_valid) {
        segment->media_start = media_start;
        segment->media_stop = segment->media_start + segment->duration;
        media_segments_count++;
      } else {
        segment->media_start = GST_CLOCK_TIME_NONE;
        segment->media_stop = GST_CLOCK_TIME_NONE;
      }
      rate_int =
          QT_UINT32 (buffer + ((version == 1) ? 32 : 24) + i * entry_size);

      if (rate_int <= 1) {
        /* 0 is not allowed, some programs write 1 instead of the floating point
         * value */
        GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT,
            rate_int);
        segment->rate = 1;
      } else {
        segment->rate = rate_int / 65536.0;
      }

      GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT
          ", duration %" GST_TIME_FORMAT ", media_start %" GST_TIME_FORMAT
          " (%" G_GUINT64_FORMAT ") , media_stop %" GST_TIME_FORMAT
          " stop_time %" GST_TIME_FORMAT " rate %g, (%d) timescale %u",
          i, GST_TIME_ARGS (segment->time),
          GST_TIME_ARGS (segment->duration),
          GST_TIME_ARGS (segment->media_start), media_time,
          GST_TIME_ARGS (segment->media_stop),
          GST_TIME_ARGS (segment->stop_time), segment->rate, rate_int,
          stream->timescale);
      if (segment->stop_time > qtdemux->segment.stop) {
        GST_WARNING_OBJECT (qtdemux, "Segment %d "
            " extends to %" GST_TIME_FORMAT
            " past the end of the file duration %" GST_TIME_FORMAT
            " it will be truncated", i, GST_TIME_ARGS (segment->stop_time),
            GST_TIME_ARGS (qtdemux->segment.stop));
        qtdemux->segment.stop = segment->stop_time;
      }
    }
    GST_DEBUG_OBJECT (qtdemux, "found %d segments", count);
    stream->n_segments = count;
    if (media_segments_count != 1)
      allow_pushbased_edts = FALSE;
  }
done:

  /* push based does not handle segments, so act accordingly here,
   * and warn if applicable */
  if (!qtdemux->pullbased && !allow_pushbased_edts) {
    GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
    /* remove and use default one below, we stream like it anyway */
    g_free (stream->segments);
    stream->segments = NULL;
    stream->n_segments = 0;
  }

  /* no segments, create one to play the complete trak */
  if (stream->n_segments == 0) {
    GstClockTime stream_duration =
        QTSTREAMTIME_TO_GSTTIME (stream, stream->duration);

    if (stream->segments == NULL)
      stream->segments = g_new (QtDemuxSegment, 1);

    /* represent unknown our way */
    if (stream_duration == 0)
      stream_duration = GST_CLOCK_TIME_NONE;

    stream->segments[0].time = 0;
    stream->segments[0].stop_time = stream_duration;
    stream->segments[0].duration = stream_duration;
    stream->segments[0].media_start = 0;
    stream->segments[0].media_stop = stream_duration;
    stream->segments[0].rate = 1.0;
    stream->segments[0].trak_media_start = 0;

    GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT,
        GST_TIME_ARGS (stream_duration));
    stream->n_segments = 1;
    stream->dummy_segment = TRUE;
  }
  GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments);

  return TRUE;
}

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

gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard)
{
  gint n;

  GST_DEBUG_OBJECT (qtdemux, "Resetting demux");
  gst_pad_stop_task (qtdemux->sinkpad);

  if (hard || qtdemux->upstream_format_is_time) {
    qtdemux->state = QTDEMUX_STATE_INITIAL;
    qtdemux->neededbytes = 16;
    qtdemux->todrop = 0;
    qtdemux->pullbased = FALSE;
    qtdemux->posted_redirect = FALSE;
    qtdemux->first_mdat = -1;
    qtdemux->header_size = 0;
    qtdemux->mdatoffset = -1;
    qtdemux->restoredata_offset = -1;
    if (qtdemux->mdatbuffer)
      gst_buffer_unref (qtdemux->mdatbuffer);
    if (qtdemux->restoredata_buffer)
      gst_buffer_unref (qtdemux->restoredata_buffer);
    qtdemux->mdatbuffer = NULL;
    qtdemux->restoredata_buffer = NULL;
    qtdemux->mdatleft = 0;
    if (qtdemux->comp_brands)
      gst_buffer_unref (qtdemux->comp_brands);
    qtdemux->comp_brands = NULL;
    qtdemux->last_moov_offset = -1;
    if (qtdemux->moov_node)
      g_node_destroy (qtdemux->moov_node);
    qtdemux->moov_node = NULL;
    qtdemux->moov_node_compressed = NULL;
    if (qtdemux->tag_list)
      gst_mini_object_unref (GST_MINI_OBJECT_CAST (qtdemux->tag_list));
    qtdemux->tag_list = NULL;
#if 0
    if (qtdemux->element_index)
      gst_object_unref (qtdemux->element_index);
    qtdemux->element_index = NULL;
#endif
    qtdemux->major_brand = 0;
    if (qtdemux->pending_newsegment)
      gst_event_unref (qtdemux->pending_newsegment);
    qtdemux->pending_newsegment = NULL;
    qtdemux->upstream_format_is_time = FALSE;
    qtdemux->upstream_seekable = FALSE;
    qtdemux->upstream_size = 0;

    qtdemux->fragment_start = -1;
    qtdemux->fragment_start_offset = -1;
    qtdemux->duration = 0;
    qtdemux->moof_offset = 0;
    qtdemux->chapters_track_id = 0;
    qtdemux->have_group_id = FALSE;
    qtdemux->group_id = G_MAXUINT;

    g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
        NULL);
    g_queue_clear (&qtdemux->protection_event_queue);
  }
  qtdemux->offset = 0;
  gst_adapter_clear (qtdemux->adapter);
  gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
  qtdemux->segment_seqnum = 0;

  if (hard) {
    for (n = 0; n < qtdemux->n_streams; n++) {
      gst_qtdemux_stream_free (qtdemux, qtdemux->streams[n]);
      qtdemux->streams[n] = NULL;
    }
    qtdemux->n_streams = 0;
    qtdemux->n_video_streams = 0;
    qtdemux->n_audio_streams = 0;
    qtdemux->n_sub_streams = 0;
    qtdemux->exposed = FALSE;
    qtdemux->fragmented = FALSE;
    qtdemux->mss_mode = FALSE;
    gst_caps_replace (&qtdemux->media_caps, NULL);
    qtdemux->timescale = 0;
    qtdemux->got_moov = FALSE;
    if (qtdemux->protection_system_ids) {
      g_ptr_array_free (qtdemux->protection_system_ids, TRUE);
      qtdemux->protection_system_ids = NULL;
    }
  } else if (qtdemux->mss_mode) {
    gst_flow_combiner_reset (qtdemux->flowcombiner);
    for (n = 0; n < qtdemux->n_streams; n++)
      gst_qtdemux_stream_clear (qtdemux, qtdemux->streams[n]);
  } else {
    gst_flow_combiner_reset (qtdemux->flowcombiner);
    for (n = 0; n < qtdemux->n_streams; n++) {
      qtdemux->streams[n]->sent_eos = FALSE;
      qtdemux->streams[n]->time_position = 0;
      qtdemux->streams[n]->accumulated_base = 0;
    }
    if (!qtdemux->pending_newsegment) {
      qtdemux->pending_newsegment = gst_event_new_segment (&qtdemux->segment);
      if (qtdemux->segment_seqnum)
        gst_event_set_seqnum (qtdemux->pending_newsegment,
            qtdemux->segment_seqnum);
    }
  }
}

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

gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream)
{
  gst_qtdemux_stream_clear (qtdemux, stream);
  if (stream->caps)
    gst_caps_unref (stream->caps);
  stream->caps = NULL;
  if (stream->pad) {
    gst_element_remove_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
    gst_flow_combiner_remove_pad (qtdemux->flowcombiner, stream->pad);
  }
  g_free (stream);
}

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

qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
    QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist)
{

/* [a b c]
 * [d e f]
 * [g h i]
 *
 * This macro will only compare value abdegh, it expects cfi to have already
 * been checked
 */
#define QTCHECK_MATRIX(m,a,b,d,e) ((m)[0] == (a << 16) && (m)[1] == (b << 16) && \
                                   (m)[3] == (d << 16) && (m)[4] == (e << 16))

  /* only handle the cases where the last column has standard values */
  if (matrix[2] == 0 && matrix[5] == 0 && matrix[8] == 1 << 30) {
    const gchar *rotation_tag = NULL;

    /* no rotation needed */
    if (QTCHECK_MATRIX (matrix, 1, 0, 0, 1)) {
      /* NOP */
    } else if (QTCHECK_MATRIX (matrix, 0, 1, G_MAXUINT16, 0)) {
      rotation_tag = "rotate-90";
    } else if (QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, G_MAXUINT16)) {
      rotation_tag = "rotate-180";
    } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) {
      rotation_tag = "rotate-270";
    } else {
      GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
    }

    GST_DEBUG_OBJECT (qtdemux, "Transformation matrix rotation %s",
        rotation_tag);
    if (rotation_tag != NULL) {
      if (*taglist == NULL)
        *taglist = gst_tag_list_new_empty ();
      gst_tag_list_add (*taglist, GST_TAG_MERGE_REPLACE,
          GST_TAG_IMAGE_ORIENTATION, rotation_tag, NULL);
    }
  } else {
    GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
  }
}

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

gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
{
  gdouble rate;
  GstFormat format;
  GstSeekFlags flags;
  GstSeekType cur_type, stop_type;
  gint64 cur, stop;
  gboolean flush;
  gboolean update;
  GstSegment seeksegment;
  guint32 seqnum = 0;
  GstEvent *flush_event;

  if (event) {
    GST_DEBUG_OBJECT (qtdemux, "doing seek with event");

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

    /* we have to have a format as the segment format. Try to convert
     * if not. */
    if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
            stop_type, &stop))
      goto no_format;

    GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format));
  } else {
    GST_DEBUG_OBJECT (qtdemux, "doing seek without event");
    flags = 0;
  }

  flush = flags & GST_SEEK_FLAG_FLUSH;

  /* stop streaming, either by flushing or by pausing the task */
  if (flush) {
    flush_event = gst_event_new_flush_start ();
    if (seqnum)
      gst_event_set_seqnum (flush_event, seqnum);
    /* unlock upstream pull_range */
    gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
    /* make sure out loop function exits */
    gst_qtdemux_push_event (qtdemux, flush_event);
  } else {
    /* non flushing seek, pause the task */
    gst_pad_pause_task (qtdemux->sinkpad);
  }

  /* wait for streaming to finish */
  GST_PAD_STREAM_LOCK (qtdemux->sinkpad);

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

  if (event) {
    /* configure the segment with the seek variables */
    GST_DEBUG_OBJECT (qtdemux, "configuring seek");
    gst_segment_do_seek (&seeksegment, rate, format, flags,
        cur_type, cur, stop_type, stop, &update);
  }

  /* now do the seek, this actually never returns FALSE */
  gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);

  /* prepare for streaming again */
  if (flush) {
    flush_event = gst_event_new_flush_stop (TRUE);
    if (seqnum)
      gst_event_set_seqnum (flush_event, seqnum);

    gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (flush_event));
    gst_qtdemux_push_event (qtdemux, flush_event);
  }

  /* commit the new segment */
  memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));

  if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
    GstMessage *msg = gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
        qtdemux->segment.format, qtdemux->segment.position);
    if (seqnum)
      gst_message_set_seqnum (msg, seqnum);
    gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg);
  }

  /* restart streaming, NEWSEGMENT will be sent from the streaming thread. */
  gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
      qtdemux->sinkpad, NULL);

  GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);

  return TRUE;

  /* ERRORS */
no_format:
  {
    GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
    return FALSE;
  }
}

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

gst_qtdemux_remove_stream (GstQTDemux * qtdemux, int i)
{
  g_assert (i >= 0 && i < qtdemux->n_streams && qtdemux->streams[i] != NULL);

  gst_qtdemux_stream_free (qtdemux, qtdemux->streams[i]);
  qtdemux->streams[i] = qtdemux->streams[qtdemux->n_streams - 1];
  qtdemux->streams[qtdemux->n_streams - 1] = NULL;
  qtdemux->n_streams--;
}

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

qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc)
{
  return qtdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);
}

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

qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
{
  stream->stbl_index = -1;      /* no samples have yet been parsed */
  stream->sample_index = -1;

  /* time-to-sample atom */
  if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stts, &stream->stts))
    goto corrupt_file;

  /* copy atom data into a new buffer for later use */
  stream->stts.data = g_memdup (stream->stts.data, stream->stts.size);

  /* skip version + flags */
  if (!gst_byte_reader_skip (&stream->stts, 1 + 3) ||
      !gst_byte_reader_get_uint32_be (&stream->stts, &stream->n_sample_times))
    goto corrupt_file;
  GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times);

  /* make sure there's enough data */
  if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) {
    stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8;
    GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks",
        stream->n_sample_times);
    if (!stream->n_sample_times)
      goto corrupt_file;
  }

  /* sync sample atom */
  stream->stps_present = FALSE;
  if ((stream->stss_present =
          ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
              &stream->stss) ? TRUE : FALSE) == TRUE) {
    /* copy atom data into a new buffer for later use */
    stream->stss.data = g_memdup (stream->stss.data, stream->stss.size);

    /* skip version + flags */
    if (!gst_byte_reader_skip (&stream->stss, 1 + 3) ||
        !gst_byte_reader_get_uint32_be (&stream->stss, &stream->n_sample_syncs))
      goto corrupt_file;

    if (stream->n_sample_syncs) {
      /* make sure there's enough data */
      if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
        goto corrupt_file;
    }

    /* partial sync sample atom */
    if ((stream->stps_present =
            ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
                &stream->stps) ? TRUE : FALSE) == TRUE) {
      /* copy atom data into a new buffer for later use */
      stream->stps.data = g_memdup (stream->stps.data, stream->stps.size);

      /* skip version + flags */
      if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
          !gst_byte_reader_get_uint32_be (&stream->stps,
              &stream->n_sample_partial_syncs))
        goto corrupt_file;

      /* if there are no entries, the stss table contains the real
       * sync samples */
      if (stream->n_sample_partial_syncs) {
        /* make sure there's enough data */
        if (!qt_atom_parser_has_chunks (&stream->stps,
                stream->n_sample_partial_syncs, 4))
          goto corrupt_file;
      }
    }
  }

  /* sample size */
  if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsz, &stream->stsz))
    goto no_samples;

  /* copy atom data into a new buffer for later use */
  stream->stsz.data = g_memdup (stream->stsz.data, stream->stsz.size);

  /* skip version + flags */
  if (!gst_byte_reader_skip (&stream->stsz, 1 + 3) ||
      !gst_byte_reader_get_uint32_be (&stream->stsz, &stream->sample_size))
    goto corrupt_file;

  if (!gst_byte_reader_get_uint32_be (&stream->stsz, &stream->n_samples))
    goto corrupt_file;

  if (!stream->n_samples)
    goto no_samples;

  /* sample-to-chunk atom */
  if (!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stsc, &stream->stsc))
    goto corrupt_file;

  /* copy atom data into a new buffer for later use */
  stream->stsc.data = g_memdup (stream->stsc.data, stream->stsc.size);

  /* skip version + flags */
  if (!gst_byte_reader_skip (&stream->stsc, 1 + 3) ||
      !gst_byte_reader_get_uint32_be (&stream->stsc,
          &stream->n_samples_per_chunk))
    goto corrupt_file;

  GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %u",
      stream->n_samples_per_chunk);

  /* make sure there's enough data */
  if (!qt_atom_parser_has_chunks (&stream->stsc, stream->n_samples_per_chunk,
          12))
    goto corrupt_file;


  /* chunk offset */
  if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stco, &stream->stco))
    stream->co_size = sizeof (guint32);
  else if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_co64,
          &stream->stco))
    stream->co_size = sizeof (guint64);
  else
    goto corrupt_file;

  /* copy atom data into a new buffer for later use */
  stream->stco.data = g_memdup (stream->stco.data, stream->stco.size);

  /* skip version + flags */
  if (!gst_byte_reader_skip (&stream->stco, 1 + 3))
    goto corrupt_file;

  /* chunks_are_samples == TRUE means treat chunks as samples */
  stream->chunks_are_samples = stream->sample_size && !stream->sampled;
  if (stream->chunks_are_samples) {
    /* treat chunks as samples */
    if (!gst_byte_reader_get_uint32_be (&stream->stco, &stream->n_samples))
      goto corrupt_file;
  } else {
    /* skip number of entries */
    if (!gst_byte_reader_skip (&stream->stco, 4))
      goto corrupt_file;

    /* make sure there are enough data in the stsz atom */
    if (!stream->sample_size) {
      /* different sizes for each sample */
      if (!qt_atom_parser_has_chunks (&stream->stsz, stream->n_samples, 4))
        goto corrupt_file;
    }
  }

  GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
      stream->n_samples, (guint) sizeof (QtDemuxSample),
      stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));

  if (stream->n_samples >=
      QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample)) {
    GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
        "be larger than %uMB (broken file?)", stream->n_samples,
        QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
    return FALSE;
  }

  g_assert (stream->samples == NULL);
  stream->samples = g_try_new0 (QtDemuxSample, stream->n_samples);
  if (!stream->samples) {
    GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
        stream->n_samples);
    return FALSE;
  }

  /* composition time-to-sample */
  if ((stream->ctts_present =
          ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
              &stream->ctts) ? TRUE : FALSE) == TRUE) {
    GstByteReader cslg = GST_BYTE_READER_INIT (NULL, 0);

    /* copy atom data into a new buffer for later use */
    stream->ctts.data = g_memdup (stream->ctts.data, stream->ctts.size);

    /* skip version + flags */
    if (!gst_byte_reader_skip (&stream->ctts, 1 + 3)
        || !gst_byte_reader_get_uint32_be (&stream->ctts,
            &stream->n_composition_times))
      goto corrupt_file;

    /* make sure there's enough data */
    if (!qt_atom_parser_has_chunks (&stream->ctts, stream->n_composition_times,
            4 + 4))
      goto corrupt_file;

    /* This is optional, if missing we iterate the ctts */
    if (qtdemux_tree_get_child_by_type_full (stbl, FOURCC_cslg, &cslg)) {
      if (!gst_byte_reader_skip (&cslg, 1 + 3)
          || !gst_byte_reader_get_uint32_be (&cslg, &stream->cslg_shift)) {
        g_free ((gpointer) cslg.data);
        goto corrupt_file;
      }
    } else {
      gint32 cslg_least = 0;
      guint num_entries, pos;
      gint i;

      pos = gst_byte_reader_get_pos (&stream->ctts);
      num_entries = stream->n_composition_times;

      stream->cslg_shift = 0;

      for (i = 0; i < num_entries; i++) {
        gint32 offset;

        gst_byte_reader_skip_unchecked (&stream->ctts, 4);
        offset = gst_byte_reader_get_int32_be_unchecked (&stream->ctts);

        if (offset < cslg_least)
          cslg_least = offset;
      }

      if (cslg_least < 0)
        stream->cslg_shift = ABS (cslg_least);
      else
        stream->cslg_shift = 0;

      /* reset the reader so we can generate sample table */
      gst_byte_reader_set_pos (&stream->ctts, pos);
    }
  } else {
    /* Ensure the cslg_shift value is consistent so we can use it
     * unconditionnally to produce TS and Segment */
    stream->cslg_shift = 0;
  }

  return TRUE;

corrupt_file:
  {
    GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
        (_("This file is corrupt and cannot be played.")), (NULL));
    return FALSE;
  }
no_samples:
  {
    gst_qtdemux_stbl_free (stream);
    if (!qtdemux->fragmented) {
      /* not quite good */
      GST_WARNING_OBJECT (qtdemux, "stream has no samples");
      return FALSE;
    } else {
      /* may pick up samples elsewhere */
      return TRUE;
    }
  }
}

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

qtdemux_tag_add_uint32 (GstQTDemux * qtdemux, GstTagList * taglist,
    const char *tag1, const char *dummy, GNode * node)
{
  GNode *data;
  int len;
  int type;
  guint32 num;

  data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
  if (data) {
    len = QT_UINT32 (data->data);
    type = QT_UINT32 ((guint8 *) data->data + 8);
    GST_DEBUG_OBJECT (qtdemux, "have %s tag, type=%d,len=%d", tag1, type, len);
    /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
    if ((type == 0x00000015 || type == 0x0000000f) && len >= 20) {
      num = QT_UINT32 ((guint8 *) data->data + 16);
      if (num) {
        /* do not add num=0 */
        GST_DEBUG_OBJECT (qtdemux, "adding tag %d", num);
        gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, num, NULL);
      }
    }
  }
}

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

gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
    guint32 seg_idx, GstClockTime offset)
{
  QtDemuxSegment *segment;
  guint32 index, kf_index;
  GstClockTime start = 0, stop = GST_CLOCK_TIME_NONE;

  GST_LOG_OBJECT (stream->pad, "activate segment %d, offset %" GST_TIME_FORMAT,
      seg_idx, GST_TIME_ARGS (offset));

  if (!gst_qtdemux_stream_update_segment (qtdemux, stream, seg_idx, offset,
          &start, &stop))
    return FALSE;

  segment = &stream->segments[stream->segment_index];

  /* in the fragmented case, we pick a fragment that starts before our
   * desired position and rely on downstream to wait for a keyframe
   * (FIXME: doesn't seem to work so well with ismv and wmv, as no parser; the
   * tfra entries tells us which trun/sample the key unit is in, but we don't
   * make use of this additional information at the moment) */
  if (qtdemux->fragmented) {
    stream->to_sample = G_MAXUINT32;
    return TRUE;
  }

  /* We don't need to look for a sample in push-based */
  if (!qtdemux->pullbased)
    return TRUE;

  /* and move to the keyframe before the indicated media time of the
   * segment */
  if (G_LIKELY (!QTSEGMENT_IS_EMPTY (segment))) {
    if (qtdemux->segment.rate >= 0) {
      index = gst_qtdemux_find_index_linear (qtdemux, stream, start);
      stream->to_sample = G_MAXUINT32;
      GST_DEBUG_OBJECT (stream->pad,
          "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
          GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
          GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
    } else {
      index = gst_qtdemux_find_index_linear (qtdemux, stream, stop);
      stream->to_sample = index;
      GST_DEBUG_OBJECT (stream->pad,
          "moving data pointer to %" GST_TIME_FORMAT ", index: %u, pts %"
          GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
          GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[index])));
    }
  } else {
    GST_DEBUG_OBJECT (stream->pad, "No need to look for keyframe, "
        "this is an empty segment");
    return TRUE;
  }

  /* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
   * encountered an error and printed a message so we return appropriately */
  if (index == -1)
    return FALSE;

  /* we're at the right spot */
  if (index == stream->sample_index) {
    GST_DEBUG_OBJECT (stream->pad, "we are at the right index");
    return TRUE;
  }

  /* find keyframe of the target index */
  kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index);

/* *INDENT-OFF* */
/* indent does stupid stuff with stream->samples[].timestamp */

  /* if we move forwards, we don't have to go back to the previous
   * keyframe since we already sent that. We can also just jump to
   * the keyframe right before the target index if there is one. */
  if (index > stream->sample_index) {
    /* moving forwards check if we move past a keyframe */
    if (kf_index > stream->sample_index) {
      GST_DEBUG_OBJECT (stream->pad,
           "moving forwards to keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" )", kf_index,
           GST_TIME_ARGS (QTSAMPLE_PTS(stream, &stream->samples[kf_index])),
           GST_TIME_ARGS (QTSAMPLE_DTS(stream, &stream->samples[kf_index])));
      gst_qtdemux_move_stream (qtdemux, stream, kf_index);
    } else {
      GST_DEBUG_OBJECT (stream->pad,
          "moving forwards, keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" ) already sent", kf_index,
          GST_TIME_ARGS (QTSAMPLE_PTS (stream, &stream->samples[kf_index])),
          GST_TIME_ARGS (QTSAMPLE_DTS (stream, &stream->samples[kf_index])));
    }
  } else {
    GST_DEBUG_OBJECT (stream->pad,
        "moving backwards to keyframe at %u (pts %" GST_TIME_FORMAT " dts %"GST_TIME_FORMAT" )", kf_index,
        GST_TIME_ARGS (QTSAMPLE_PTS(stream, &stream->samples[kf_index])),
        GST_TIME_ARGS (QTSAMPLE_DTS(stream, &stream->samples[kf_index])));
    gst_qtdemux_move_stream (qtdemux, stream, kf_index);
  }

/* *INDENT-ON* */

  return TRUE;
}

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

gst_qtdemux_get_duration (GstQTDemux * qtdemux, GstClockTime * duration)
{
  gboolean res = FALSE;

  *duration = GST_CLOCK_TIME_NONE;

  if (qtdemux->duration != 0 &&
      qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
    *duration = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
    res = TRUE;
  } else {
    *duration = GST_CLOCK_TIME_NONE;
  }

  return res;
}

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

gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
    GstEvent * event)
{
  GstQTDemux *demux = GST_QTDEMUX (parent);
  gboolean res = TRUE;

  GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEGMENT:
    {
      gint64 offset = 0;
      QtDemuxStream *stream;
      gint idx;
      GstSegment segment;

      /* some debug output */
      gst_event_copy_segment (event, &segment);
      GST_DEBUG_OBJECT (demux, "received newsegment %" GST_SEGMENT_FORMAT,
          &segment);

      /* erase any previously set segment */
      gst_event_replace (&demux->pending_newsegment, NULL);

      if (segment.format == GST_FORMAT_TIME) {
        GST_DEBUG_OBJECT (demux, "new pending_newsegment");
        gst_event_replace (&demux->pending_newsegment, event);
        demux->upstream_format_is_time = TRUE;
      } else {
        GST_DEBUG_OBJECT (demux, "Not storing upstream newsegment, "
            "not in time format");

        /* chain will send initial newsegment after pads have been added */
        if (demux->state != QTDEMUX_STATE_MOVIE || !demux->n_streams) {
          GST_DEBUG_OBJECT (demux, "still starting, eating event");
          goto exit;
        }
      }

      /* check if this matches a time seek we received previously
       * FIXME for backwards compatibility reasons we use the
       * seek_offset here to compare. In the future we might want to
       * change this to use the seqnum as it uniquely should identify
       * the segment that corresponds to the seek. */
      GST_DEBUG_OBJECT (demux, "Stored seek offset: %" G_GINT64_FORMAT
          ", received segment offset %" G_GINT64_FORMAT,
          demux->seek_offset, segment.start);
      if (segment.format == GST_FORMAT_BYTES
          && demux->seek_offset == segment.start) {
        GST_OBJECT_LOCK (demux);
        offset = segment.start;

        segment.format = GST_FORMAT_TIME;
        segment.start = demux->push_seek_start;
        segment.stop = demux->push_seek_stop;
        GST_DEBUG_OBJECT (demux, "Replaced segment with stored seek "
            "segment %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
            GST_TIME_ARGS (segment.start), GST_TIME_ARGS (segment.stop));
        GST_OBJECT_UNLOCK (demux);
      }

      /* we only expect a BYTE segment, e.g. following a seek */
      if (segment.format == GST_FORMAT_BYTES) {
        if (GST_CLOCK_TIME_IS_VALID (segment.start)) {
          offset = segment.start;

          gst_qtdemux_find_sample (demux, segment.start, TRUE, FALSE, NULL,
              NULL, (gint64 *) & segment.start);
          if ((gint64) segment.start < 0)
            segment.start = 0;
        }
        if (GST_CLOCK_TIME_IS_VALID (segment.stop)) {
          gst_qtdemux_find_sample (demux, segment.stop, FALSE, FALSE, NULL,
              NULL, (gint64 *) & segment.stop);
          /* keyframe seeking should already arrange for start >= stop,
           * but make sure in other rare cases */
          segment.stop = MAX (segment.stop, segment.start);
        }
      } else if (segment.format == GST_FORMAT_TIME) {
        /* push all data on the adapter before starting this
         * new segment */
        gst_qtdemux_process_adapter (demux, TRUE);
      } else {
        GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
        goto exit;
      }

      /* We shouldn't modify upstream driven TIME FORMAT segment */
      if (!demux->upstream_format_is_time) {
        /* accept upstream's notion of segment and distribute along */
        segment.format = GST_FORMAT_TIME;
        segment.position = segment.time = segment.start;
        segment.duration = demux->segment.duration;
        segment.base = gst_segment_to_running_time (&demux->segment,
            GST_FORMAT_TIME, demux->segment.position);
      }

      gst_segment_copy_into (&segment, &demux->segment);
      GST_DEBUG_OBJECT (demux, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment);

      /* map segment to internal qt segments and push on each stream */
      if (demux->n_streams) {
        if (demux->fragmented) {
          GstEvent *segment_event = gst_event_new_segment (&segment);

          gst_event_replace (&demux->pending_newsegment, NULL);
          gst_event_set_seqnum (segment_event, demux->segment_seqnum);
          gst_qtdemux_push_event (demux, segment_event);
        } else {
          gst_event_replace (&demux->pending_newsegment, NULL);
          gst_qtdemux_map_and_push_segments (demux, &segment);
        }
      }

      /* clear leftover in current segment, if any */
      gst_adapter_clear (demux->adapter);

      /* set up streaming thread */
      demux->offset = offset;
      if (demux->upstream_format_is_time) {
        GST_DEBUG_OBJECT (demux, "Upstream is driving in time format, "
            "set values to restart reading from a new atom");
        demux->neededbytes = 16;
        demux->todrop = 0;
      } else {
        gst_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx,
            NULL);
        if (stream) {
          demux->todrop = stream->samples[idx].offset - offset;
          demux->neededbytes = demux->todrop + stream->samples[idx].size;
        } else {
          /* set up for EOS */
          demux->neededbytes = -1;
          demux->todrop = 0;
        }
      }
    exit:
      gst_event_unref (event);
      res = TRUE;
      goto drop;
    }
    case GST_EVENT_FLUSH_START:
    {
      if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
        gst_event_unref (event);
        goto drop;
      }
      break;
    }
    case GST_EVENT_FLUSH_STOP:
    {
      guint64 dur;

      dur = demux->segment.duration;
      gst_qtdemux_reset (demux, FALSE);
      demux->segment.duration = dur;

      if (gst_event_get_seqnum (event) == demux->offset_seek_seqnum) {
        gst_event_unref (event);
        goto drop;
      }
      break;
    }
    case GST_EVENT_EOS:
      /* If we are in push mode, and get an EOS before we've seen any streams,
       * then error out - we have nowhere to send the EOS */
      if (!demux->pullbased) {
        gint i;
        gboolean has_valid_stream = FALSE;
        for (i = 0; i < demux->n_streams; i++) {
          if (demux->streams[i]->pad != NULL) {
            has_valid_stream = TRUE;
            break;
          }
        }
        if (!has_valid_stream)
          gst_qtdemux_post_no_playable_stream_error (demux);
        else {
          GST_DEBUG_OBJECT (demux, "Data still available after EOS: %u",
              (guint) gst_adapter_available (demux->adapter));
          if (gst_qtdemux_process_adapter (demux, TRUE) != GST_FLOW_OK) {
            res = FALSE;
          }
        }
      }
      break;
    case GST_EVENT_CAPS:{
      GstCaps *caps = NULL;

      gst_event_parse_caps (event, &caps);
      gst_qtdemux_setcaps (demux, caps);
      res = TRUE;
      gst_event_unref (event);
      goto drop;
    }
    case GST_EVENT_PROTECTION:
    {
      const gchar *system_id = NULL;

      gst_event_parse_protection (event, &system_id, NULL, NULL);
      GST_DEBUG_OBJECT (demux, "Received protection event for system ID %s",
          system_id);
      gst_qtdemux_append_protection_system_id (demux, system_id);
      /* save the event for later, for source pads that have not been created */
      g_queue_push_tail (&demux->protection_event_queue, gst_event_ref (event));
      /* send it to all pads that already exist */
      gst_qtdemux_push_event (demux, event);
      res = TRUE;
      goto drop;
    }
    default:
      break;
  }

  res = gst_pad_event_default (demux->sinkpad, parent, event) & res;

drop:
  return res;
}

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

qtdemux_ensure_index (GstQTDemux * qtdemux)
{
  guint i;

  GST_DEBUG_OBJECT (qtdemux, "collecting all metadata for all streams");

  /* Build complete index */
  for (i = 0; i < qtdemux->n_streams; i++) {
    QtDemuxStream *stream = qtdemux->streams[i];

    if (!qtdemux_parse_samples (qtdemux, stream, stream->n_samples - 1))
      goto parse_error;
  }
  return TRUE;

  /* ERRORS */
parse_error:
  {
    GST_LOG_OBJECT (qtdemux,
        "Building complete index of stream %u for seeking failed!", i);
    return FALSE;
  }
}

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

gst_qtdemux_set_index (GstElement * element, GstIndex * index)
{
  GstQTDemux *demux = GST_QTDEMUX (element);

  GST_OBJECT_LOCK (demux);
  if (demux->element_index)
    gst_object_unref (demux->element_index);
  if (index) {
    demux->element_index = gst_object_ref (index);
  } else {
    demux->element_index = NULL;
  }
  GST_OBJECT_UNLOCK (demux);
  /* object lock might be taken again */
  if (index)
    gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
  GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT "for writer id %d",
      demux->element_index, demux->index_id);
}

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

gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw,
    gboolean set, QtDemuxStream ** _stream, gint * _index, gint64 * _time)
{
  gint i, n, index;
  gint64 time, min_time;
  QtDemuxStream *stream;

  min_time = -1;
  stream = NULL;
  index = -1;

  for (n = 0; n < qtdemux->n_streams; ++n) {
    QtDemuxStream *str;
    gint inc;
    gboolean set_sample;

    str = qtdemux->streams[n];
    set_sample = !set;

    if (fw) {
      i = 0;
      inc = 1;
    } else {
      i = str->n_samples - 1;
      inc = -1;
    }

    for (; (i >= 0) && (i < str->n_samples); i += inc) {
      if (str->samples[i].size == 0)
        continue;

      if (fw && (str->samples[i].offset < byte_pos))
        continue;

      if (!fw && (str->samples[i].offset + str->samples[i].size > byte_pos))
        continue;

      /* move stream to first available sample */
      if (set) {
        gst_qtdemux_move_stream (qtdemux, str, i);
        set_sample = TRUE;
      }

      /* avoid index from sparse streams since they might be far away */
      if (!str->sparse) {
        /* determine min/max time */
        time = QTSAMPLE_PTS (str, &str->samples[i]);
        if (min_time == -1 || (!fw && time > min_time) ||
            (fw && time < min_time)) {
          min_time = time;
        }

        /* determine stream with leading sample, to get its position */
        if (!stream ||
            (fw && (str->samples[i].offset < stream->samples[index].offset)) ||
            (!fw && (str->samples[i].offset > stream->samples[index].offset))) {
          stream = str;
          index = i;
        }
      }
      break;
    }

    /* no sample for this stream, mark eos */
    if (!set_sample)
      gst_qtdemux_move_stream (qtdemux, str, str->n_samples);
  }

  if (_time)
    *_time = min_time;
  if (_stream)
    *_stream = stream;
  if (_index)
    *_index = index;
}

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

qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
    QtDemuxStream * stream, guint sample_index)
{
  QtDemuxCencSampleSetInfo *info = NULL;

  g_return_val_if_fail (stream != NULL, NULL);
  g_return_val_if_fail (stream->protected, NULL);
  g_return_val_if_fail (stream->protection_scheme_info != NULL, NULL);

  info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;

  /* Currently, cenc properties for groups of samples are not supported, so
   * simply return a copy of the default sample properties */
  return gst_structure_copy (info->default_properties);
}

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

qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux,
    QtDemuxStream * stream, GNode * container, guint32 * original_fmt)
{
  GNode *sinf;
  GNode *frma;
  GNode *schm;
  GNode *schi;

  g_return_val_if_fail (qtdemux != NULL, FALSE);
  g_return_val_if_fail (stream != NULL, FALSE);
  g_return_val_if_fail (container != NULL, FALSE);
  g_return_val_if_fail (original_fmt != NULL, FALSE);

  sinf = qtdemux_tree_get_child_by_type (container, FOURCC_sinf);
  if (G_UNLIKELY (!sinf)) {
    if (stream->protection_scheme_type == FOURCC_cenc) {
      GST_ERROR_OBJECT (qtdemux, "sinf box does not contain schi box, which is "
          "mandatory for Common Encryption");
      return FALSE;
    }
    return TRUE;
  }

  frma = qtdemux_tree_get_child_by_type (sinf, FOURCC_frma);
  if (G_UNLIKELY (!frma)) {
    GST_ERROR_OBJECT (qtdemux, "sinf box does not contain mandatory frma box");
    return FALSE;
  }

  *original_fmt = QT_FOURCC ((const guint8 *) frma->data + 8);
  GST_DEBUG_OBJECT (qtdemux, "original stream format: '%" GST_FOURCC_FORMAT "'",
      GST_FOURCC_ARGS (*original_fmt));

  schm = qtdemux_tree_get_child_by_type (sinf, FOURCC_schm);
  if (!schm) {
    GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schm box");
    return FALSE;
  }
  stream->protection_scheme_type = QT_FOURCC ((const guint8 *) schm->data + 12);
  stream->protection_scheme_version =
      QT_UINT32 ((const guint8 *) schm->data + 16);

  GST_DEBUG_OBJECT (qtdemux,
      "protection_scheme_type: %" GST_FOURCC_FORMAT ", "
      "protection_scheme_version: %#010x",
      GST_FOURCC_ARGS (stream->protection_scheme_type),
      stream->protection_scheme_version);

  schi = qtdemux_tree_get_child_by_type (sinf, FOURCC_schi);
  if (!schi) {
    GST_DEBUG_OBJECT (qtdemux, "sinf box does not contain schi box");
    return FALSE;
  }
  if (stream->protection_scheme_type == FOURCC_cenc) {
    QtDemuxCencSampleSetInfo *info;
    GNode *tenc;
    const guint8 *tenc_data;
    guint32 isEncrypted;
    guint8 iv_size;
    const guint8 *default_kid;
    GstBuffer *kid_buf;

    if (G_UNLIKELY (!stream->protection_scheme_info))
      stream->protection_scheme_info =
          g_malloc0 (sizeof (QtDemuxCencSampleSetInfo));

    info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;

    tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_tenc);
    if (!tenc) {
      GST_ERROR_OBJECT (qtdemux, "schi box does not contain tenc box, "
          "which is mandatory for Common Encryption");
      return FALSE;
    }
    tenc_data = (const guint8 *) tenc->data + 12;
    isEncrypted = QT_UINT24 (tenc_data);
    iv_size = QT_UINT8 (tenc_data + 3);
    default_kid = (tenc_data + 4);
    kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
    gst_buffer_fill (kid_buf, 0, default_kid, 16);
    if (info->default_properties)
      gst_structure_free (info->default_properties);
    info->default_properties =
        gst_structure_new ("application/x-cenc",
        "iv_size", G_TYPE_UINT, iv_size,
        "encrypted", G_TYPE_BOOLEAN, (isEncrypted == 1),
        "kid", GST_TYPE_BUFFER, kid_buf, NULL);
    GST_DEBUG_OBJECT (qtdemux, "default sample properties: "
        "is_encrypted=%u, iv_size=%u", isEncrypted, iv_size);
    gst_buffer_unref (kid_buf);
  }
  return TRUE;
}

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

gst_qtdemux_stream_segment_get_boundaries (GstQTDemux * qtdemux,
    QtDemuxStream * stream, GstClockTime offset,
    GstClockTime * _start, GstClockTime * _stop, GstClockTime * _time)
{
  GstClockTime seg_time;
  GstClockTime start, stop, time;
  QtDemuxSegment *segment;

  segment = &stream->segments[stream->segment_index];

  /* get time in this segment */
  seg_time = (offset - segment->time) * segment->rate;

  GST_LOG_OBJECT (stream->pad, "seg_time %" GST_TIME_FORMAT,
      GST_TIME_ARGS (seg_time));

  if (G_UNLIKELY (seg_time > segment->duration)) {
    GST_LOG_OBJECT (stream->pad,
        "seg_time > segment->duration %" GST_TIME_FORMAT,
        GST_TIME_ARGS (segment->duration));
    seg_time = segment->duration;
  }

  /* qtdemux->segment.stop is in outside-time-realm, whereas
   * segment->media_stop is in track-time-realm.
   *
   * In order to compare the two, we need to bring segment.stop
   * into the track-time-realm
   *
   * FIXME - does this comment still hold? Don't see any conversion here */

  stop = qtdemux->segment.stop;
  if (stop == GST_CLOCK_TIME_NONE)
    stop = qtdemux->segment.duration;
  if (stop == GST_CLOCK_TIME_NONE)
    stop = segment->media_stop;
  else
    stop =
        MIN (segment->media_stop, stop - segment->time + segment->media_start);

  if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
    start = segment->time + seg_time;
    time = offset;
    stop = start - seg_time + segment->duration;
  } else if (qtdemux->segment.rate >= 0) {
    start = MIN (segment->media_start + seg_time, stop);
    time = offset;
  } else {
    if (segment->media_start >= qtdemux->segment.start) {
      time = segment->time;
    } else {
      time = segment->time + (qtdemux->segment.start - segment->media_start);
    }

    start = MAX (segment->media_start, qtdemux->segment.start);
    stop = MIN (segment->media_start + seg_time, stop);
  }

  *_start = start;
  *_stop = stop;
  *_time = time;
}

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

gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
    QtDemuxStream * stream, gint segment_index, GstClockTime pos)
{
  GstClockTime ts, dur;
  GstEvent *gap;

  ts = pos;
  dur =
      stream->segments[segment_index].duration - (pos -
      stream->segments[segment_index].time);
  gap = gst_event_new_gap (ts, dur);
  stream->time_position += dur;

  GST_DEBUG_OBJECT (stream->pad, "Pushing gap for empty "
      "segment: %" GST_PTR_FORMAT, gap);
  gst_pad_push_event (stream->pad, gap);
}

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

gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent,
    GstEvent * event)
{
  gboolean res = TRUE;
  GstQTDemux *qtdemux = GST_QTDEMUX (parent);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
    {
#ifndef GST_DISABLE_GST_DEBUG
      GstClockTime ts = gst_util_get_timestamp ();
#endif

      if (qtdemux->upstream_format_is_time && qtdemux->fragmented) {
        /* seek should be handled by upstream, we might need to re-download fragments */
        GST_DEBUG_OBJECT (qtdemux,
            "let upstream handle seek for fragmented playback");
        goto upstream;
      }

      /* Build complete index for seeking;
       * if not a fragmented file at least */
      if (!qtdemux->fragmented)
        if (!qtdemux_ensure_index (qtdemux))
          goto index_failed;
#ifndef GST_DISABLE_GST_DEBUG
      ts = gst_util_get_timestamp () - ts;
      GST_INFO_OBJECT (qtdemux,
          "Time taken to parse index %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
#endif
    }
      if (qtdemux->pullbased) {
        res = gst_qtdemux_do_seek (qtdemux, pad, event);
      } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) {
        GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked");
        res = TRUE;
      } else if (qtdemux->state == QTDEMUX_STATE_MOVIE && qtdemux->n_streams
          && !qtdemux->fragmented) {
        res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
      } else {
        GST_DEBUG_OBJECT (qtdemux,
            "ignoring seek in push mode in current state");
        res = FALSE;
      }
      gst_event_unref (event);
      break;
    default:
    upstream:
      res = gst_pad_event_default (pad, parent, event);
      break;
  }

done:
  return res;

  /* ERRORS */
index_failed:
  {
    GST_ERROR_OBJECT (qtdemux, "Index failed");
    gst_event_unref (event);
    res = FALSE;
    goto done;
  }
}

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

qtdemux_tag_add_revdns (GstQTDemux * demux, GstTagList * taglist,
    const char *tag, const char *tag_bis, GNode * node)
{
  GNode *mean;
  GNode *name;
  GNode *data;
  guint32 meansize;
  guint32 namesize;
  guint32 datatype;
  guint32 datasize;
  const gchar *meanstr;
  const gchar *namestr;

  /* checking the whole ---- atom size for consistency */
  if (QT_UINT32 (node->data) <= 4 + 12 + 12 + 16) {
    GST_WARNING_OBJECT (demux, "Tag ---- atom is too small, ignoring");
    return;
  }

  mean = qtdemux_tree_get_child_by_type (node, FOURCC_mean);
  if (!mean) {
    GST_WARNING_OBJECT (demux, "No 'mean' atom found");
    return;
  }

  meansize = QT_UINT32 (mean->data);
  if (meansize <= 12) {
    GST_WARNING_OBJECT (demux, "Small mean atom, ignoring the whole tag");
    return;
  }
  meanstr = ((gchar *) mean->data) + 12;
  meansize -= 12;

  name = qtdemux_tree_get_child_by_type (node, FOURCC_name);
  if (!name) {
    GST_WARNING_OBJECT (demux, "'name' atom not found, ignoring tag");
    return;
  }

  namesize = QT_UINT32 (name->data);
  if (namesize <= 12) {
    GST_WARNING_OBJECT (demux, "'name' atom is too small, ignoring tag");
    return;
  }
  namestr = ((gchar *) name->data) + 12;
  namesize -= 12;

  /*
   * Data atom is:
   * uint32 - size
   * uint32 - name
   * uint8  - version
   * uint24 - data type
   * uint32 - all 0
   * rest   - the data
   */
  data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
  if (!data) {
    GST_WARNING_OBJECT (demux, "No data atom in this tag");
    return;
  }
  datasize = QT_UINT32 (data->data);
  if (datasize <= 16) {
    GST_WARNING_OBJECT (demux, "Data atom too small");
    return;
  }
  datatype = QT_UINT32 (((gchar *) data->data) + 8) & 0xFFFFFF;

  if ((strncmp (meanstr, "com.apple.iTunes", meansize) == 0) ||
      (strncmp (meanstr, "org.hydrogenaudio.replaygain", meansize) == 0)) {
    static const struct
    {
      const gchar name[28];
      const gchar tag[28];
    } tags[] = {
      {
      "replaygain_track_gain", GST_TAG_TRACK_GAIN}, {
      "replaygain_track_peak", GST_TAG_TRACK_PEAK}, {
      "replaygain_album_gain", GST_TAG_ALBUM_GAIN}, {
      "replaygain_album_peak", GST_TAG_ALBUM_PEAK}, {
      "MusicBrainz Track Id", GST_TAG_MUSICBRAINZ_TRACKID}, {
      "MusicBrainz Artist Id", GST_TAG_MUSICBRAINZ_ARTISTID}, {
      "MusicBrainz Album Id", GST_TAG_MUSICBRAINZ_ALBUMID}, {
      "MusicBrainz Album Artist Id", GST_TAG_MUSICBRAINZ_ALBUMARTISTID}
    };
    int i;

    for (i = 0; i < G_N_ELEMENTS (tags); ++i) {
      if (!g_ascii_strncasecmp (tags[i].name, namestr, namesize)) {
        switch (gst_tag_get_type (tags[i].tag)) {
          case G_TYPE_DOUBLE:
            qtdemux_add_double_tag_from_str (demux, taglist, tags[i].tag,
                ((guint8 *) data->data) + 16, datasize - 16);
            break;
          case G_TYPE_STRING:
            qtdemux_tag_add_str (demux, taglist, tags[i].tag, NULL, node);
            break;
          default:
            /* not reached */
            break;
        }
        break;
      }
    }
    if (i == G_N_ELEMENTS (tags))
      goto unknown_tag;
  } else {
    goto unknown_tag;
  }

  return;

/* errors */
unknown_tag:
#ifndef GST_DISABLE_GST_DEBUG
  {
    gchar *namestr_dbg;
    gchar *meanstr_dbg;

    meanstr_dbg = g_strndup (meanstr, meansize);
    namestr_dbg = g_strndup (namestr, namesize);

    GST_WARNING_OBJECT (demux, "This tag %s:%s type:%u is not mapped, "
        "file a bug at bugzilla.gnome.org", meanstr_dbg, namestr_dbg, datatype);

    g_free (namestr_dbg);
    g_free (meanstr_dbg);
  }
#endif
  return;
}

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

gst_qtdemux_stream_send_initial_gap_segments (GstQTDemux * demux,
    QtDemuxStream * stream)
{
  gint i;

  /* Push any initial gap segments before proceeding to the
   * 'real' data */
  for (i = 0; i < stream->n_segments; i++) {
    gst_qtdemux_activate_segment (demux, stream, i, stream->time_position);

    if (QTSEGMENT_IS_EMPTY (&stream->segments[i])) {
      gst_qtdemux_send_gap_for_segment (demux, stream, i,
          stream->time_position);
    } else {
      /* Only support empty segment at the beginning followed by
       * one non-empty segment, this was checked when parsing the
       * edts atom, arriving here is unexpected */
      g_assert (i + 1 == stream->n_segments);
      break;
    }
  }
}

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

qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b)
{
  GstQtReference *ref_a = (GstQtReference *) a;
  GstQtReference *ref_b = (GstQtReference *) b;

  if (ref_b->min_req_qt_version != ref_a->min_req_qt_version)
    return ref_b->min_req_qt_version - ref_a->min_req_qt_version;

  /* known bitrates go before unknown; higher bitrates go first */
  return ref_b->min_req_bitrate - ref_a->min_req_bitrate;
}

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

gst_qtdemux_dispose (GObject * object)
{
  GstQTDemux *qtdemux = GST_QTDEMUX (object);

  if (qtdemux->adapter) {
    g_object_unref (G_OBJECT (qtdemux->adapter));
    qtdemux->adapter = NULL;
  }
  gst_flow_combiner_free (qtdemux->flowcombiner);
  g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
      NULL);
  g_queue_clear (&qtdemux->protection_event_queue);

  g_free (qtdemux->cenc_aux_info_sizes);
  qtdemux->cenc_aux_info_sizes = NULL;

  G_OBJECT_CLASS (parent_class)->dispose (object);
}

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

qtdemux_parse_udta (GstQTDemux * qtdemux, GstTagList * taglist, GNode * udta)
{
  GNode *meta;
  GNode *ilst;
  GNode *xmp_;
  GNode *node;
  gint i;
  GstQtDemuxTagList demuxtaglist;

  demuxtaglist.demux = qtdemux;
  demuxtaglist.taglist = taglist;

  meta = qtdemux_tree_get_child_by_type (udta, FOURCC_meta);
  if (meta != NULL) {
    ilst = qtdemux_tree_get_child_by_type (meta, FOURCC_ilst);
    if (ilst == NULL) {
      GST_LOG_OBJECT (qtdemux, "no ilst");
      return;
    }
  } else {
    ilst = udta;
    GST_LOG_OBJECT (qtdemux, "no meta so using udta itself");
  }

  i = 0;
  while (i < G_N_ELEMENTS (add_funcs)) {
    node = qtdemux_tree_get_child_by_type (ilst, add_funcs[i].fourcc);
    if (node) {
      gint len;

      len = QT_UINT32 (node->data);
      if (len < 12) {
        GST_DEBUG_OBJECT (qtdemux, "too small tag atom %" GST_FOURCC_FORMAT,
            GST_FOURCC_ARGS (add_funcs[i].fourcc));
      } else {
        add_funcs[i].func (qtdemux, taglist, add_funcs[i].gst_tag,
            add_funcs[i].gst_tag_bis, node);
      }
      g_node_destroy (node);
    } else {
      i++;
    }
  }

  /* parsed nodes have been removed, pass along remainder as blob */
  g_node_children_foreach (ilst, G_TRAVERSE_ALL,
      (GNodeForeachFunc) qtdemux_tag_add_blob, &demuxtaglist);

  /* parse up XMP_ node if existing */
  xmp_ = qtdemux_tree_get_child_by_type (udta, FOURCC_XMP_);
  if (xmp_ != NULL) {
    GstBuffer *buf;
    GstTagList *xmptaglist;

    buf = _gst_buffer_new_wrapped (((guint8 *) xmp_->data) + 8,
        QT_UINT32 ((guint8 *) xmp_->data) - 8, NULL);
    xmptaglist = gst_tag_list_from_xmp_buffer (buf);
    gst_buffer_unref (buf);

    qtdemux_handle_xmp_taglist (qtdemux, taglist, xmptaglist);
  } else {
    GST_DEBUG_OBJECT (qtdemux, "No XMP_ node found");
  }
}

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

gst_qtdemux_init (GstQTDemux * qtdemux)
{
  qtdemux->sinkpad =
      gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
  gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
  gst_pad_set_activatemode_function (qtdemux->sinkpad,
      qtdemux_sink_activate_mode);
  gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
  gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
  gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);

  qtdemux->state = QTDEMUX_STATE_INITIAL;
  qtdemux->pullbased = FALSE;
  qtdemux->posted_redirect = FALSE;
  qtdemux->neededbytes = 16;
  qtdemux->todrop = 0;
  qtdemux->adapter = gst_adapter_new ();
  qtdemux->offset = 0;
  qtdemux->first_mdat = -1;
  qtdemux->got_moov = FALSE;
  qtdemux->mdatoffset = -1;
  qtdemux->mdatbuffer = NULL;
  qtdemux->restoredata_buffer = NULL;
  qtdemux->restoredata_offset = -1;
  qtdemux->fragment_start = -1;
  qtdemux->fragment_start_offset = -1;
  qtdemux->media_caps = NULL;
  qtdemux->exposed = FALSE;
  qtdemux->mss_mode = FALSE;
  qtdemux->pending_newsegment = NULL;
  qtdemux->upstream_format_is_time = FALSE;
  qtdemux->have_group_id = FALSE;
  qtdemux->group_id = G_MAXUINT;
  qtdemux->cenc_aux_info_offset = 0;
  qtdemux->cenc_aux_info_sizes = NULL;
  qtdemux->cenc_aux_sample_count = 0;
  qtdemux->protection_system_ids = NULL;
  g_queue_init (&qtdemux->protection_event_queue);
  gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
  qtdemux->flowcombiner = gst_flow_combiner_new ();

  GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE);
}

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

qtdemux_tree_get_sibling_by_type_full (GNode * node, guint32 fourcc,
    GstByteReader * parser)
{
  GNode *child;
  guint8 *buffer;
  guint32 child_fourcc, child_len;

  for (child = g_node_next_sibling (node); child;
      child = g_node_next_sibling (child)) {
    buffer = (guint8 *) child->data;

    child_fourcc = QT_FOURCC (buffer + 4);

    if (child_fourcc == fourcc) {
      if (parser) {
        child_len = QT_UINT32 (buffer);
        if (G_UNLIKELY (child_len < (4 + 4)))
          return NULL;
        /* FIXME: must verify if atom length < parent atom length */
        gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
      }
      return child;
    }
  }
  return NULL;
}

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

gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
    QtDemuxStream * stream, gboolean * empty, guint64 * offset, guint * size,
    GstClockTime * dts, GstClockTime * pts, GstClockTime * duration,
    gboolean * keyframe)
{
  QtDemuxSample *sample;
  GstClockTime time_position;
  guint32 seg_idx;

  g_return_val_if_fail (stream != NULL, FALSE);

  time_position = stream->time_position;
  if (G_UNLIKELY (time_position == GST_CLOCK_TIME_NONE))
    goto eos;

  seg_idx = stream->segment_index;
  if (G_UNLIKELY (seg_idx == -1)) {
    /* find segment corresponding to time_position if we are looking
     * for a segment. */
    seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);
  }

  /* different segment, activate it, sample_index will be set. */
  if (G_UNLIKELY (stream->segment_index != seg_idx))
    gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);

  if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (&stream->segments[stream->
                  segment_index]))) {
    QtDemuxSegment *seg = &stream->segments[stream->segment_index];

    GST_LOG_OBJECT (qtdemux, "Empty segment activated,"
        " prepare empty sample");

    *empty = TRUE;
    *pts = *dts = time_position;
    *duration = seg->duration - (time_position - seg->time);

    return TRUE;
  }

  *empty = FALSE;

  if (stream->sample_index == -1)
    stream->sample_index = 0;

  GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
      stream->sample_index, stream->n_samples);

  if (G_UNLIKELY (stream->sample_index >= stream->n_samples)) {
    if (!qtdemux->fragmented)
      goto eos;

    GST_INFO_OBJECT (qtdemux, "out of samples, trying to add more");
    do {
      GstFlowReturn flow;

      GST_OBJECT_LOCK (qtdemux);
      flow = qtdemux_add_fragmented_samples (qtdemux);
      GST_OBJECT_UNLOCK (qtdemux);

      if (flow != GST_FLOW_OK)
        goto eos;
    }
    while (stream->sample_index >= stream->n_samples);
  }

  if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
    GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!",
        stream->sample_index);
    return FALSE;
  }

  /* now get the info for the sample we're at */
  sample = &stream->samples[stream->sample_index];

  *dts = QTSAMPLE_DTS (stream, sample);
  *pts = QTSAMPLE_PTS (stream, sample);
  *offset = sample->offset;
  *size = sample->size;
  *duration = QTSAMPLE_DUR_DTS (stream, sample, *dts);
  *keyframe = QTSAMPLE_KEYFRAME (stream, sample);

  return TRUE;

  /* special cases */
eos:
  {
    stream->time_position = GST_CLOCK_TIME_NONE;
    return FALSE;
  }
}

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

qtdemux_add_double_tag_from_str (GstQTDemux * demux, GstTagList * taglist,
    const gchar * tag, guint8 * data, guint32 datasize)
{
  gdouble value;
  gchar *datacopy;

  /* make a copy to have \0 at the end */
  datacopy = g_strndup ((gchar *) data, datasize);

  /* convert the str to double */
  if (sscanf (datacopy, "%lf", &value) == 1) {
    GST_DEBUG_OBJECT (demux, "adding tag: %s [%s]", tag, datacopy);
    gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, value, NULL);
  } else {
    GST_WARNING_OBJECT (demux, "Failed to parse double from string: %s",
        datacopy);
  }
  g_free (datacopy);
}

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

gst_qtdemux_stream_flush_samples_data (GstQTDemux * qtdemux,
    QtDemuxStream * stream)
{
  g_free (stream->samples);
  stream->samples = NULL;
  gst_qtdemux_stbl_free (stream);

  /* fragments */
  g_free (stream->ra_entries);
  stream->ra_entries = NULL;
  stream->n_ra_entries = 0;

  stream->sample_index = -1;
  stream->stbl_index = -1;
  stream->n_samples = 0;
  stream->time_position = 0;

  stream->n_samples_moof = 0;
  stream->duration_moof = 0;
  stream->duration_last_moof = 0;
}