Ejemplo n.º 1
0
static GstFlowReturn
gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout)
{
  /* Get all pads that have data for us and store them in a
   * new list.
   *
   * Calculate the current output offset/timestamp and
   * offset_end/timestamp_end. Allocate a silence buffer
   * for this and store it.
   *
   * For all pads:
   * 1) Once per input buffer (cached)
   *   1) Check discont (flag and timestamp with tolerance)
   *   2) If discont or new, resync. That means:
   *     1) Drop all start data of the buffer that comes before
   *        the current position/offset.
   *     2) Calculate the offset (output segment!) that the first
   *        frame of the input buffer corresponds to. Base this on
   *        the running time.
   *
   * 2) If the current pad's offset/offset_end overlaps with the output
   *    offset/offset_end, mix it at the appropiate position in the output
   *    buffer and advance the pad's position. Remember if this pad needs
   *    a new buffer to advance behind the output offset_end.
   *
   * 3) If we had no pad with a buffer, go EOS.
   *
   * 4) If we had at least one pad that did not advance behind output
   *    offset_end, let collected be called again for the current
   *    output offset/offset_end.
   */
  GstElement *element;
  GstAudioAggregator *aagg;
  GList *iter;
  GstFlowReturn ret;
  GstBuffer *outbuf = NULL;
  gint64 next_offset;
  gint64 next_timestamp;
  gint rate, bpf;
  gboolean dropped = FALSE;
  gboolean is_eos = TRUE;
  gboolean is_done = TRUE;
  guint blocksize;

  element = GST_ELEMENT (agg);
  aagg = GST_AUDIO_AGGREGATOR (agg);

  /* Sync pad properties to the stream time */
  gst_aggregator_iterate_sinkpads (agg,
      (GstAggregatorPadForeachFunc) GST_DEBUG_FUNCPTR (sync_pad_values), NULL);

  GST_AUDIO_AGGREGATOR_LOCK (aagg);
  GST_OBJECT_LOCK (agg);

  /* Update position from the segment start/stop if needed */
  if (agg->segment.position == -1) {
    if (agg->segment.rate > 0.0)
      agg->segment.position = agg->segment.start;
    else
      agg->segment.position = agg->segment.stop;
  }

  if (G_UNLIKELY (aagg->info.finfo->format == GST_AUDIO_FORMAT_UNKNOWN)) {
    if (timeout) {
      GST_DEBUG_OBJECT (aagg,
          "Got timeout before receiving any caps, don't output anything");

      /* Advance position */
      if (agg->segment.rate > 0.0)
        agg->segment.position += aagg->priv->output_buffer_duration;
      else if (agg->segment.position > aagg->priv->output_buffer_duration)
        agg->segment.position -= aagg->priv->output_buffer_duration;
      else
        agg->segment.position = 0;

      GST_OBJECT_UNLOCK (agg);
      GST_AUDIO_AGGREGATOR_UNLOCK (aagg);
      return GST_FLOW_OK;
    } else {
      GST_OBJECT_UNLOCK (agg);
      goto not_negotiated;
    }
  }

  if (aagg->priv->send_caps) {
    GST_OBJECT_UNLOCK (agg);
    gst_aggregator_set_src_caps (agg, aagg->current_caps);
    GST_OBJECT_LOCK (agg);

    aagg->priv->send_caps = FALSE;
  }

  rate = GST_AUDIO_INFO_RATE (&aagg->info);
  bpf = GST_AUDIO_INFO_BPF (&aagg->info);

  if (aagg->priv->offset == -1) {
    aagg->priv->offset =
        gst_util_uint64_scale (agg->segment.position - agg->segment.start, rate,
        GST_SECOND);
    GST_DEBUG_OBJECT (aagg, "Starting at offset %" G_GINT64_FORMAT,
        aagg->priv->offset);
  }

  blocksize = gst_util_uint64_scale (aagg->priv->output_buffer_duration,
      rate, GST_SECOND);
  blocksize = MAX (1, blocksize);

  /* for the next timestamp, use the sample counter, which will
   * never accumulate rounding errors */

  /* FIXME: Reverse mixing does not work at all yet */
  if (agg->segment.rate > 0.0) {
    next_offset = aagg->priv->offset + blocksize;
  } else {
    next_offset = aagg->priv->offset - blocksize;
  }

  next_timestamp =
      agg->segment.start + gst_util_uint64_scale (next_offset, GST_SECOND,
      rate);

  if (aagg->priv->current_buffer == NULL) {
    GST_OBJECT_UNLOCK (agg);
    aagg->priv->current_buffer =
        GST_AUDIO_AGGREGATOR_GET_CLASS (aagg)->create_output_buffer (aagg,
        blocksize);
    /* Be careful, some things could have changed ? */
    GST_OBJECT_LOCK (agg);
    GST_BUFFER_FLAG_SET (aagg->priv->current_buffer, GST_BUFFER_FLAG_GAP);
  }
  outbuf = aagg->priv->current_buffer;

  GST_LOG_OBJECT (agg,
      "Starting to mix %u samples for offset %" G_GINT64_FORMAT
      " with timestamp %" GST_TIME_FORMAT, blocksize,
      aagg->priv->offset, GST_TIME_ARGS (agg->segment.position));

  for (iter = element->sinkpads; iter; iter = iter->next) {
    GstBuffer *inbuf;
    GstAudioAggregatorPad *pad = (GstAudioAggregatorPad *) iter->data;
    GstAggregatorPad *aggpad = (GstAggregatorPad *) iter->data;
    gboolean drop_buf = FALSE;
    gboolean pad_eos = gst_aggregator_pad_is_eos (aggpad);

    if (!pad_eos)
      is_eos = FALSE;

    inbuf = gst_aggregator_pad_get_buffer (aggpad);

    GST_OBJECT_LOCK (pad);
    if (!inbuf) {
      if (timeout) {
        if (pad->priv->output_offset < next_offset) {
          gint64 diff = next_offset - pad->priv->output_offset;
          GST_LOG_OBJECT (pad, "Timeout, missing %" G_GINT64_FORMAT " frames (%"
              GST_TIME_FORMAT ")", diff,
              GST_TIME_ARGS (gst_util_uint64_scale (diff, GST_SECOND,
                      GST_AUDIO_INFO_RATE (&aagg->info))));
        }
      } else if (!pad_eos) {
        is_done = FALSE;
      }
      GST_OBJECT_UNLOCK (pad);
      continue;
    }

    g_assert (!pad->priv->buffer || pad->priv->buffer == inbuf);

    /* New buffer? */
    if (!pad->priv->buffer) {
      /* Takes ownership of buffer */
      if (!gst_audio_aggregator_fill_buffer (aagg, pad, inbuf)) {
        dropped = TRUE;
        GST_OBJECT_UNLOCK (pad);
        gst_aggregator_pad_drop_buffer (aggpad);
        continue;
      }
    } else {
      gst_buffer_unref (inbuf);
    }

    if (!pad->priv->buffer && !dropped && pad_eos) {
      GST_DEBUG_OBJECT (aggpad, "Pad is in EOS state");
      GST_OBJECT_UNLOCK (pad);
      continue;
    }

    g_assert (pad->priv->buffer);

    /* This pad is lacking behind, we need to update the offset
     * and maybe drop the current buffer */
    if (pad->priv->output_offset < aagg->priv->offset) {
      gint64 diff = aagg->priv->offset - pad->priv->output_offset;
      gint64 odiff = diff;

      if (pad->priv->position + diff > pad->priv->size)
        diff = pad->priv->size - pad->priv->position;
      pad->priv->position += diff;
      pad->priv->output_offset += diff;

      if (pad->priv->position == pad->priv->size) {
        GST_LOG_OBJECT (pad, "Buffer was late by %" GST_TIME_FORMAT
            ", dropping %" GST_PTR_FORMAT,
            GST_TIME_ARGS (gst_util_uint64_scale (odiff, GST_SECOND,
                    GST_AUDIO_INFO_RATE (&aagg->info))), pad->priv->buffer);
        /* Buffer done, drop it */
        gst_buffer_replace (&pad->priv->buffer, NULL);
        dropped = TRUE;
        GST_OBJECT_UNLOCK (pad);
        gst_aggregator_pad_drop_buffer (aggpad);
        continue;
      }
    }


    if (pad->priv->output_offset >= aagg->priv->offset
        && pad->priv->output_offset <
        aagg->priv->offset + blocksize && pad->priv->buffer) {
      GST_LOG_OBJECT (aggpad, "Mixing buffer for current offset");
      drop_buf = !gst_audio_aggregator_mix_buffer (aagg, pad, pad->priv->buffer,
          outbuf);
      if (pad->priv->output_offset >= next_offset) {
        GST_DEBUG_OBJECT (pad,
            "Pad is after current offset: %" G_GUINT64_FORMAT " >= %"
            G_GINT64_FORMAT, pad->priv->output_offset, next_offset);
      } else {
        is_done = FALSE;
      }
    }

    GST_OBJECT_UNLOCK (pad);
    if (drop_buf)
      gst_aggregator_pad_drop_buffer (aggpad);

  }
  GST_OBJECT_UNLOCK (agg);

  if (dropped) {
    /* We dropped a buffer, retry */
    GST_INFO_OBJECT (aagg, "A pad dropped a buffer, wait for the next one");
    GST_AUDIO_AGGREGATOR_UNLOCK (aagg);
    return GST_FLOW_OK;
  }

  if (!is_done && !is_eos) {
    /* Get more buffers */
    GST_INFO_OBJECT (aagg,
        "We're not done yet for the current offset," " waiting for more data");
    GST_AUDIO_AGGREGATOR_UNLOCK (aagg);
    return GST_FLOW_OK;
  }

  if (is_eos) {
    gint64 max_offset = 0;

    GST_DEBUG_OBJECT (aagg, "We're EOS");

    GST_OBJECT_LOCK (agg);
    for (iter = GST_ELEMENT (agg)->sinkpads; iter; iter = iter->next) {
      GstAudioAggregatorPad *pad = GST_AUDIO_AGGREGATOR_PAD (iter->data);

      max_offset = MAX ((gint64) max_offset, (gint64) pad->priv->output_offset);
    }
    GST_OBJECT_UNLOCK (agg);

    /* This means EOS or nothing mixed in at all */
    if (aagg->priv->offset == max_offset) {
      gst_buffer_replace (&aagg->priv->current_buffer, NULL);
      GST_AUDIO_AGGREGATOR_UNLOCK (aagg);
      return GST_FLOW_EOS;
    }

    if (max_offset <= next_offset) {
      GST_DEBUG_OBJECT (aagg,
          "Last buffer is incomplete: %" G_GUINT64_FORMAT " <= %"
          G_GINT64_FORMAT, max_offset, next_offset);
      next_offset = max_offset;
      next_timestamp =
          agg->segment.start + gst_util_uint64_scale (next_offset, GST_SECOND,
          rate);

      if (next_offset > aagg->priv->offset)
        gst_buffer_resize (outbuf, 0, (next_offset - aagg->priv->offset) * bpf);
    }
  }

  /* set timestamps on the output buffer */
  GST_OBJECT_LOCK (agg);
  if (agg->segment.rate > 0.0) {
    GST_BUFFER_PTS (outbuf) = agg->segment.position;
    GST_BUFFER_OFFSET (outbuf) = aagg->priv->offset;
    GST_BUFFER_OFFSET_END (outbuf) = next_offset;
    GST_BUFFER_DURATION (outbuf) = next_timestamp - agg->segment.position;
  } else {
    GST_BUFFER_PTS (outbuf) = next_timestamp;
    GST_BUFFER_OFFSET (outbuf) = next_offset;
    GST_BUFFER_OFFSET_END (outbuf) = aagg->priv->offset;
    GST_BUFFER_DURATION (outbuf) = agg->segment.position - next_timestamp;
  }

  GST_OBJECT_UNLOCK (agg);

  /* send it out */
  GST_LOG_OBJECT (aagg,
      "pushing outbuf %p, timestamp %" GST_TIME_FORMAT " offset %"
      G_GINT64_FORMAT, outbuf, GST_TIME_ARGS (GST_BUFFER_PTS (outbuf)),
      GST_BUFFER_OFFSET (outbuf));

  GST_AUDIO_AGGREGATOR_UNLOCK (aagg);

  ret = gst_aggregator_finish_buffer (agg, aagg->priv->current_buffer);
  aagg->priv->current_buffer = NULL;

  GST_LOG_OBJECT (aagg, "pushed outbuf, result = %s", gst_flow_get_name (ret));

  GST_AUDIO_AGGREGATOR_LOCK (aagg);
  GST_OBJECT_LOCK (agg);
  aagg->priv->offset = next_offset;
  agg->segment.position = next_timestamp;

  /* If there was a timeout and there was a gap in data in out of the streams,
   * then it's a very good time to for a resync with the timestamps.
   */
  if (timeout) {
    for (iter = element->sinkpads; iter; iter = iter->next) {
      GstAudioAggregatorPad *pad = GST_AUDIO_AGGREGATOR_PAD (iter->data);

      GST_OBJECT_LOCK (pad);
      if (pad->priv->output_offset < aagg->priv->offset)
        pad->priv->output_offset = -1;
      GST_OBJECT_UNLOCK (pad);
    }
  }
  GST_OBJECT_UNLOCK (agg);
  GST_AUDIO_AGGREGATOR_UNLOCK (aagg);

  return ret;
  /* ERRORS */
not_negotiated:
  {
    GST_AUDIO_AGGREGATOR_UNLOCK (aagg);
    GST_ELEMENT_ERROR (aagg, STREAM, FORMAT, (NULL),
        ("Unknown data received, not negotiated"));
    return GST_FLOW_NOT_NEGOTIATED;
  }
}
Ejemplo n.º 2
0
static GstFlowReturn
gst_mxf_mux_create_metadata (GstMXFMux * mux)
{
  GstFlowReturn ret = GST_FLOW_OK;
  GList *l;
  GArray *tmp;

  GST_DEBUG_OBJECT (mux, "Creating MXF metadata");

  GST_OBJECT_LOCK (mux);

  for (l = GST_ELEMENT_CAST (mux)->sinkpads; l; l = l->next) {
    GstMXFMuxPad *pad = l->data;
    GstCaps *caps;
    GstBuffer *buffer;

    if (!pad || !pad->descriptor) {
      GST_OBJECT_UNLOCK (mux);
      return GST_FLOW_ERROR;
    }

    caps = gst_pad_get_current_caps (GST_PAD_CAST (pad));
    if (!caps) {
      GST_OBJECT_UNLOCK (mux);
      return GST_FLOW_ERROR;
    }

    buffer = gst_aggregator_pad_get_buffer (GST_AGGREGATOR_PAD (pad));
    if (pad->writer->update_descriptor)
      pad->writer->update_descriptor (pad->descriptor,
          caps, pad->mapping_data, buffer);
    if (buffer)
      gst_buffer_unref (buffer);
    gst_caps_unref (caps);
  }

  /* Preface */
  mux->preface =
      (MXFMetadataPreface *) g_object_new (MXF_TYPE_METADATA_PREFACE, NULL);
  mxf_uuid_init (&MXF_METADATA_BASE (mux->preface)->instance_uid,
      mux->metadata);
  g_hash_table_insert (mux->metadata,
      &MXF_METADATA_BASE (mux->preface)->instance_uid, mux->preface);
  mux->metadata_list = g_list_prepend (mux->metadata_list, mux->preface);

  mxf_timestamp_set_now (&mux->preface->last_modified_date);
  mux->preface->version = 258;
  mux->preface->object_model_version = 1;

  mxf_op_set_generalized (&mux->preface->operational_pattern, MXF_OP_1a, TRUE,
      TRUE, FALSE);

  tmp = g_array_new (FALSE, FALSE, sizeof (MXFUL));
  for (l = GST_ELEMENT_CAST (mux)->sinkpads; l; l = l->next) {
    GstMXFMuxPad *pad = l->data;
    guint i;
    gboolean found = FALSE;

    if (!pad || !pad->descriptor ||
        mxf_ul_is_zero (&pad->descriptor->essence_container)) {
      GST_OBJECT_UNLOCK (mux);
      return GST_FLOW_ERROR;
    }

    for (i = 0; i < tmp->len; i++) {
      if (mxf_ul_is_equal (&pad->descriptor->essence_container,
              &g_array_index (tmp, MXFUL, i))) {
        found = TRUE;
        break;
      }
    }

    if (found)
      continue;

    g_array_append_val (tmp, pad->descriptor->essence_container);
  }
  mux->preface->n_essence_containers = tmp->len;
  mux->preface->essence_containers = (MXFUL *) g_array_free (tmp, FALSE);

  /* This will later be used as UID for the material package */
  mxf_uuid_init (&mux->preface->primary_package_uid, mux->metadata);

  /* Identifications */
  {
    MXFMetadataIdentification *identification;
    static const guint8 gst_uid[] = {
      0xe5, 0xde, 0xcd, 0x04, 0x24, 0x90, 0x69, 0x18,
      0x8a, 0xc9, 0xb5, 0xd7, 0x02, 0x58, 0x46, 0x78
    };
    guint major, minor, micro, nano;

    mux->preface->n_identifications = 1;
    mux->preface->identifications = g_new0 (MXFMetadataIdentification *, 1);
    identification = mux->preface->identifications[0] =
        (MXFMetadataIdentification *)
        g_object_new (MXF_TYPE_METADATA_IDENTIFICATION, NULL);

    mxf_uuid_init (&MXF_METADATA_BASE (identification)->instance_uid,
        mux->metadata);
    g_hash_table_insert (mux->metadata,
        &MXF_METADATA_BASE (identification)->instance_uid, identification);
    mux->metadata_list = g_list_prepend (mux->metadata_list, identification);

    mxf_uuid_init (&identification->this_generation_uid, NULL);

    identification->company_name = g_strdup ("GStreamer");
    identification->product_name = g_strdup ("GStreamer Multimedia Framework");

    gst_version (&major, &minor, &micro, &nano);
    identification->product_version.major = major;
    identification->product_version.minor = minor;
    identification->product_version.patch = micro;
    identification->product_version.build = nano;
    identification->product_version.release =
        (nano == 0) ? 1 : (nano == 1) ? 2 : 4;

    identification->version_string =
        g_strdup_printf ("%u.%u.%u.%u", major, minor, micro, nano);
    memcpy (&identification->product_uid, &gst_uid, 16);

    memcpy (&identification->modification_date,
        &mux->preface->last_modified_date, sizeof (MXFTimestamp));
    memcpy (&identification->toolkit_version, &identification->product_version,
        sizeof (MXFProductVersion));

#ifdef HAVE_SYS_UTSNAME_H
    {
      struct utsname sys_details;

      if (uname (&sys_details) == 0) {
        identification->platform = g_strdup_printf ("%s %s %s",
            sys_details.sysname, sys_details.release, sys_details.machine);
      }
    }
#endif

#if defined(G_OS_WIN32)
    if (identification->platform == NULL)
      identification->platform = g_strdup ("Microsoft Windows");
#elif defined(G_OS_BEOS)
    if (identification->platform == NULL)
      identification->platform = g_strdup ("BEOS");
#elif defined(G_OS_UNIX)
    if (identification->platform == NULL)
      identification->platform = g_strdup ("Unix");
#endif
  }

  /* Content storage */
  {
    MXFMetadataContentStorage *cstorage;
    guint i;

    cstorage = mux->preface->content_storage = (MXFMetadataContentStorage *)
        g_object_new (MXF_TYPE_METADATA_CONTENT_STORAGE, NULL);
    mxf_uuid_init (&MXF_METADATA_BASE (cstorage)->instance_uid, mux->metadata);
    g_hash_table_insert (mux->metadata,
        &MXF_METADATA_BASE (cstorage)->instance_uid, cstorage);
    mux->metadata_list = g_list_prepend (mux->metadata_list, cstorage);

    cstorage->n_packages = 2;
    cstorage->packages = g_new0 (MXFMetadataGenericPackage *, 2);

    /* Source package */
    {
      MXFMetadataSourcePackage *p;

      cstorage->packages[1] = (MXFMetadataGenericPackage *)
          g_object_new (MXF_TYPE_METADATA_SOURCE_PACKAGE, NULL);
      mxf_uuid_init (&MXF_METADATA_BASE (cstorage->packages[1])->instance_uid,
          mux->metadata);
      g_hash_table_insert (mux->metadata,
          &MXF_METADATA_BASE (cstorage->packages[1])->instance_uid,
          cstorage->packages[1]);
      mux->metadata_list =
          g_list_prepend (mux->metadata_list, cstorage->packages[1]);
      p = (MXFMetadataSourcePackage *) cstorage->packages[1];

      mxf_umid_init (&p->parent.package_uid);
      p->parent.name = g_strdup ("Source package");
      memcpy (&p->parent.package_creation_date,
          &mux->preface->last_modified_date, sizeof (MXFTimestamp));
      memcpy (&p->parent.package_modified_date,
          &mux->preface->last_modified_date, sizeof (MXFTimestamp));

      p->parent.n_tracks = GST_ELEMENT_CAST (mux)->numsinkpads;
      p->parent.tracks = g_new0 (MXFMetadataTrack *, p->parent.n_tracks);

      if (p->parent.n_tracks > 1) {
        MXFMetadataMultipleDescriptor *d;

        p->descriptor = (MXFMetadataGenericDescriptor *)
            g_object_new (MXF_TYPE_METADATA_MULTIPLE_DESCRIPTOR, NULL);
        d = (MXFMetadataMultipleDescriptor *) p->descriptor;
        d->n_sub_descriptors = p->parent.n_tracks;
        d->sub_descriptors =
            g_new0 (MXFMetadataGenericDescriptor *, p->parent.n_tracks);

        mxf_uuid_init (&MXF_METADATA_BASE (d)->instance_uid, mux->metadata);
        g_hash_table_insert (mux->metadata,
            &MXF_METADATA_BASE (d)->instance_uid, d);
        mux->metadata_list = g_list_prepend (mux->metadata_list, d);
      }

      /* Tracks */
      {
        guint n = 0;

        /* Essence tracks */
        for (l = GST_ELEMENT_CAST (mux)->sinkpads; l; l = l->next) {
          GstMXFMuxPad *pad = l->data;
          MXFMetadataTimelineTrack *track;
          MXFMetadataSequence *sequence;
          MXFMetadataSourceClip *clip;
          GstCaps *caps;
          GstBuffer *buffer;

          p->parent.tracks[n] = (MXFMetadataTrack *)
              g_object_new (MXF_TYPE_METADATA_TIMELINE_TRACK, NULL);
          track = (MXFMetadataTimelineTrack *) p->parent.tracks[n];
          mxf_uuid_init (&MXF_METADATA_BASE (track)->instance_uid,
              mux->metadata);
          g_hash_table_insert (mux->metadata,
              &MXF_METADATA_BASE (track)->instance_uid, track);
          mux->metadata_list = g_list_prepend (mux->metadata_list, track);

          caps = gst_pad_get_current_caps (GST_PAD_CAST (pad));
          buffer = gst_aggregator_pad_get_buffer (GST_AGGREGATOR_PAD (pad));
          track->parent.track_id = n + 1;
          track->parent.track_number =
              pad->writer->get_track_number_template (pad->descriptor,
              caps, pad->mapping_data);

          pad->writer->get_edit_rate (pad->descriptor,
              caps, pad->mapping_data, buffer, p, track, &track->edit_rate);
          if (buffer)
            gst_buffer_unref (buffer);
          gst_caps_unref (caps);

          sequence = track->parent.sequence = (MXFMetadataSequence *)
              g_object_new (MXF_TYPE_METADATA_SEQUENCE, NULL);
          mxf_uuid_init (&MXF_METADATA_BASE (sequence)->instance_uid,
              mux->metadata);
          g_hash_table_insert (mux->metadata,
              &MXF_METADATA_BASE (sequence)->instance_uid, sequence);
          mux->metadata_list = g_list_prepend (mux->metadata_list, sequence);

          memcpy (&sequence->data_definition, &pad->writer->data_definition,
              16);

          sequence->n_structural_components = 1;
          sequence->structural_components =
              g_new0 (MXFMetadataStructuralComponent *, 1);

          clip = (MXFMetadataSourceClip *)
              g_object_new (MXF_TYPE_METADATA_SOURCE_CLIP, NULL);
          sequence->structural_components[0] =
              (MXFMetadataStructuralComponent *) clip;
          mxf_uuid_init (&MXF_METADATA_BASE (clip)->instance_uid,
              mux->metadata);
          g_hash_table_insert (mux->metadata,
              &MXF_METADATA_BASE (clip)->instance_uid, clip);
          mux->metadata_list = g_list_prepend (mux->metadata_list, clip);

          memcpy (&clip->parent.data_definition, &sequence->data_definition,
              16);
          clip->start_position = 0;

          pad->source_package = p;
          pad->source_track = track;
          pad->descriptor->linked_track_id = n + 1;
          if (p->parent.n_tracks == 1) {
            p->descriptor = (MXFMetadataGenericDescriptor *) pad->descriptor;
          } else {
            MXF_METADATA_MULTIPLE_DESCRIPTOR (p->
                descriptor)->sub_descriptors[n] =
                (MXFMetadataGenericDescriptor *) pad->descriptor;
          }

          n++;
        }
      }
    }

    /* Material package */
    {
      MXFMetadataMaterialPackage *p;
      MXFFraction min_edit_rate = { 0, 0 };
      gdouble min_edit_rate_d = G_MAXDOUBLE;

      cstorage->packages[0] = (MXFMetadataGenericPackage *)
          g_object_new (MXF_TYPE_METADATA_MATERIAL_PACKAGE, NULL);
      memcpy (&MXF_METADATA_BASE (cstorage->packages[0])->instance_uid,
          &mux->preface->primary_package_uid, 16);
      g_hash_table_insert (mux->metadata,
          &MXF_METADATA_BASE (cstorage->packages[0])->instance_uid,
          cstorage->packages[0]);
      mux->metadata_list =
          g_list_prepend (mux->metadata_list, cstorage->packages[0]);
      p = (MXFMetadataMaterialPackage *) cstorage->packages[0];

      mxf_umid_init (&p->package_uid);
      p->name = g_strdup ("Material package");
      memcpy (&p->package_creation_date, &mux->preface->last_modified_date,
          sizeof (MXFTimestamp));
      memcpy (&p->package_modified_date, &mux->preface->last_modified_date,
          sizeof (MXFTimestamp));

      p->n_tracks = GST_ELEMENT_CAST (mux)->numsinkpads + 1;
      p->tracks = g_new0 (MXFMetadataTrack *, p->n_tracks);

      /* Tracks */
      {
        guint n;

        n = 1;
        /* Essence tracks */
        for (l = GST_ELEMENT_CAST (mux)->sinkpads; l; l = l->next) {
          GstMXFMuxPad *pad = l->data;
          GstCaps *caps;
          GstBuffer *buffer;
          MXFMetadataSourcePackage *source_package;
          MXFMetadataTimelineTrack *track, *source_track;
          MXFMetadataSequence *sequence;
          MXFMetadataSourceClip *clip;

          source_package = MXF_METADATA_SOURCE_PACKAGE (cstorage->packages[1]);
          source_track =
              MXF_METADATA_TIMELINE_TRACK (source_package->parent.tracks[n -
                  1]);

          p->tracks[n] = (MXFMetadataTrack *)
              g_object_new (MXF_TYPE_METADATA_TIMELINE_TRACK, NULL);
          track = (MXFMetadataTimelineTrack *) p->tracks[n];
          mxf_uuid_init (&MXF_METADATA_BASE (track)->instance_uid,
              mux->metadata);
          g_hash_table_insert (mux->metadata,
              &MXF_METADATA_BASE (track)->instance_uid, track);
          mux->metadata_list = g_list_prepend (mux->metadata_list, track);

          track->parent.track_id = n + 1;
          track->parent.track_number = 0;

          caps = gst_pad_get_current_caps (GST_PAD_CAST (pad));
          buffer = gst_aggregator_pad_get_buffer (GST_AGGREGATOR_PAD (pad));
          pad->writer->get_edit_rate (pad->descriptor,
              caps, pad->mapping_data,
              buffer, source_package, source_track, &track->edit_rate);
          if (buffer)
            gst_buffer_unref (buffer);
          gst_caps_unref (caps);

          if (track->edit_rate.n != source_track->edit_rate.n ||
              track->edit_rate.d != source_track->edit_rate.d) {
            memcpy (&source_track->edit_rate, &track->edit_rate,
                sizeof (MXFFraction));
          }

          if (track->edit_rate.d <= 0 || track->edit_rate.n <= 0) {
            GST_ERROR_OBJECT (mux, "Invalid edit rate");
            GST_OBJECT_UNLOCK (mux);
            return GST_FLOW_ERROR;
          }

          if (min_edit_rate_d >
              ((gdouble) track->edit_rate.n) / ((gdouble) track->edit_rate.d)) {
            min_edit_rate_d =
                ((gdouble) track->edit_rate.n) / ((gdouble) track->edit_rate.d);
            memcpy (&min_edit_rate, &track->edit_rate, sizeof (MXFFraction));
          }

          sequence = track->parent.sequence = (MXFMetadataSequence *)
              g_object_new (MXF_TYPE_METADATA_SEQUENCE, NULL);
          mxf_uuid_init (&MXF_METADATA_BASE (sequence)->instance_uid,
              mux->metadata);
          g_hash_table_insert (mux->metadata,
              &MXF_METADATA_BASE (sequence)->instance_uid, sequence);
          mux->metadata_list = g_list_prepend (mux->metadata_list, sequence);

          memcpy (&sequence->data_definition, &pad->writer->data_definition,
              16);
          sequence->n_structural_components = 1;
          sequence->structural_components =
              g_new0 (MXFMetadataStructuralComponent *, 1);

          clip = (MXFMetadataSourceClip *)
              g_object_new (MXF_TYPE_METADATA_SOURCE_CLIP, NULL);
          sequence->structural_components[0] =
              (MXFMetadataStructuralComponent *) clip;
          mxf_uuid_init (&MXF_METADATA_BASE (clip)->instance_uid,
              mux->metadata);
          g_hash_table_insert (mux->metadata,
              &MXF_METADATA_BASE (clip)->instance_uid, clip);
          mux->metadata_list = g_list_prepend (mux->metadata_list, clip);

          memcpy (&clip->parent.data_definition, &sequence->data_definition,
              16);
          clip->start_position = 0;

          memcpy (&clip->source_package_id, &cstorage->packages[1]->package_uid,
              32);
          clip->source_track_id = n;

          n++;
        }

        n = 0;
        /* Timecode track */
        {
          MXFMetadataTimelineTrack *track;
          MXFMetadataSequence *sequence;
          MXFMetadataTimecodeComponent *component;

          p->tracks[n] = (MXFMetadataTrack *)
              g_object_new (MXF_TYPE_METADATA_TIMELINE_TRACK, NULL);
          track = (MXFMetadataTimelineTrack *) p->tracks[n];
          mxf_uuid_init (&MXF_METADATA_BASE (track)->instance_uid,
              mux->metadata);
          g_hash_table_insert (mux->metadata,
              &MXF_METADATA_BASE (track)->instance_uid, track);
          mux->metadata_list = g_list_prepend (mux->metadata_list, track);

          track->parent.track_id = n + 1;
          track->parent.track_number = 0;
          track->parent.track_name = g_strdup ("Timecode track");
          /* FIXME: Is this correct? */
          memcpy (&track->edit_rate, &min_edit_rate, sizeof (MXFFraction));

          sequence = track->parent.sequence = (MXFMetadataSequence *)
              g_object_new (MXF_TYPE_METADATA_SEQUENCE, NULL);
          mxf_uuid_init (&MXF_METADATA_BASE (sequence)->instance_uid,
              mux->metadata);
          g_hash_table_insert (mux->metadata,
              &MXF_METADATA_BASE (sequence)->instance_uid, sequence);
          mux->metadata_list = g_list_prepend (mux->metadata_list, sequence);

          memcpy (&sequence->data_definition,
              mxf_metadata_track_identifier_get
              (MXF_METADATA_TRACK_TIMECODE_12M_INACTIVE), 16);

          sequence->n_structural_components = 1;
          sequence->structural_components =
              g_new0 (MXFMetadataStructuralComponent *, 1);

          component = (MXFMetadataTimecodeComponent *)
              g_object_new (MXF_TYPE_METADATA_TIMECODE_COMPONENT, NULL);
          sequence->structural_components[0] =
              (MXFMetadataStructuralComponent *) component;
          mxf_uuid_init (&MXF_METADATA_BASE (component)->instance_uid,
              mux->metadata);
          g_hash_table_insert (mux->metadata,
              &MXF_METADATA_BASE (component)->instance_uid, component);
          mux->metadata_list = g_list_prepend (mux->metadata_list, component);

          memcpy (&component->parent.data_definition,
              &sequence->data_definition, 16);

          component->start_timecode = 0;
          if (track->edit_rate.d == 0)
            component->rounded_timecode_base = 1;
          else
            component->rounded_timecode_base =
                (((gdouble) track->edit_rate.n) /
                ((gdouble) track->edit_rate.d) + 0.5);
          /* TODO: drop frame */
        }

        memcpy (&mux->min_edit_rate, &min_edit_rate, sizeof (MXFFraction));
      }
    }

    for (i = 0; i < cstorage->packages[1]->n_tracks; i++) {
      MXFMetadataTrack *track = cstorage->packages[1]->tracks[i];
      guint j;
      guint32 templ;
      guint8 n_type, n;

      if ((track->track_number & 0x00ff00ff) != 0)
        continue;

      templ = track->track_number;
      n_type = 0;

      for (j = 0; j < cstorage->packages[1]->n_tracks; j++) {
        MXFMetadataTrack *tmp = cstorage->packages[1]->tracks[j];

        if (tmp->track_number == templ) {
          n_type++;
        }
      }

      n = 0;
      for (j = 0; j < cstorage->packages[1]->n_tracks; j++) {
        MXFMetadataTrack *tmp = cstorage->packages[1]->tracks[j];

        if (tmp->track_number == templ) {
          n++;
          tmp->track_number |= (n_type << 16) | (n);
        }
      }
    }

    cstorage->n_essence_container_data = 1;
    cstorage->essence_container_data =
        g_new0 (MXFMetadataEssenceContainerData *, 1);
    cstorage->essence_container_data[0] = (MXFMetadataEssenceContainerData *)
        g_object_new (MXF_TYPE_METADATA_ESSENCE_CONTAINER_DATA, NULL);
    mxf_uuid_init (&MXF_METADATA_BASE (cstorage->essence_container_data[0])->
        instance_uid, mux->metadata);
    g_hash_table_insert (mux->metadata,
        &MXF_METADATA_BASE (cstorage->essence_container_data[0])->instance_uid,
        cstorage->essence_container_data[0]);
    mux->metadata_list =
        g_list_prepend (mux->metadata_list,
        cstorage->essence_container_data[0]);

    cstorage->essence_container_data[0]->linked_package =
        MXF_METADATA_SOURCE_PACKAGE (cstorage->packages[1]);
    cstorage->essence_container_data[0]->index_sid = 0;
    cstorage->essence_container_data[0]->body_sid = 1;
  }