Merge branch 'master' into 0.11
authorWim Taymans <wim.taymans@collabora.co.uk>
Mon, 6 Jun 2011 14:27:12 +0000 (16:27 +0200)
committerWim Taymans <wim.taymans@collabora.co.uk>
Mon, 6 Jun 2011 14:27:12 +0000 (16:27 +0200)
Conflicts:
ext/theora/gsttheoraenc.c

1  2 
ext/ogg/gstoggmux.c
ext/theora/gsttheoraenc.c
gst-libs/gst/pbutils/gstdiscoverer.c
gst-libs/gst/tag/gsttagdemux.c
gst/adder/gstadder.c

@@@ -723,6 -779,114 +723,116 @@@ gst_ogg_mux_compare_pads (GstOggMux * o
    return 0;
  }
  
 -  buf = gst_buffer_make_metadata_writable (buf);
+ static GstBuffer *
+ gst_ogg_mux_decorate_buffer (GstOggMux * ogg_mux, GstOggPadData * pad,
+     GstBuffer * buf)
+ {
+   GstClockTime time;
+   gint64 duration, granule, limit;
+   GstClockTime next_time;
+   GstClockTimeDiff diff;
+   ogg_packet packet;
++  gsize size;
+   /* ensure messing with metadata is ok */
 -  packet.packet = GST_BUFFER_DATA (buf);
 -  packet.bytes = GST_BUFFER_SIZE (buf);
++  buf = gst_buffer_make_writable (buf);
+   /* convert time to running time, so we need no longer bother about that */
+   time = GST_BUFFER_TIMESTAMP (buf);
+   if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) {
+     time = gst_segment_to_running_time (&pad->segment, GST_FORMAT_TIME, time);
+     if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) {
+       gst_buffer_unref (buf);
+       return NULL;
+     } else {
+       GST_BUFFER_TIMESTAMP (buf) = time;
+     }
+   }
+   /* now come up with granulepos stuff corresponding to time */
+   if (!pad->have_type ||
+       pad->map.granulerate_n <= 0 || pad->map.granulerate_d <= 0)
+     goto no_granule;
++  packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
++  packet.bytes = size;
+   duration = gst_ogg_stream_get_packet_duration (&pad->map, &packet);
++  gst_buffer_unmap (buf, packet.packet, size);
+   /* give up if no duration can be determined, relying on upstream */
+   if (G_UNLIKELY (duration < 0)) {
+     /* well, if some day we really could handle sparse input ... */
+     if (pad->map.is_sparse) {
+       limit = 1;
+       diff = 2;
+       goto resync;
+     }
+     GST_WARNING_OBJECT (pad->collect.pad,
+         "failed to determine packet duration");
+     goto no_granule;
+   }
+   GST_LOG_OBJECT (pad->collect.pad, "buffer ts %" GST_TIME_FORMAT
+       ", duration %" GST_TIME_FORMAT ", granule duration %" G_GINT64_FORMAT,
+       GST_TIME_ARGS (time), GST_TIME_ARGS (GST_BUFFER_DURATION (buf)),
+       duration);
+   /* determine granule corresponding to time,
+    * using the inverse of oggdemux' granule -> time */
+   /* see if interpolated granule matches good enough */
+   granule = pad->next_granule;
+   next_time = gst_ogg_stream_granule_to_time (&pad->map, pad->next_granule);
+   diff = GST_CLOCK_DIFF (next_time, time);
+   /* we tolerate deviation up to configured or within granule granularity */
+   limit = gst_ogg_stream_granule_to_time (&pad->map, 1) / 2;
+   limit = MAX (limit, ogg_mux->max_tolerance);
+   GST_LOG_OBJECT (pad->collect.pad, "expected granule %" G_GINT64_FORMAT " == "
+       "time %" GST_TIME_FORMAT " --> ts diff %" GST_TIME_FORMAT
+       " < tolerance %" GST_TIME_FORMAT " (?)",
+       granule, GST_TIME_ARGS (next_time), GST_TIME_ARGS (ABS (diff)),
+       GST_TIME_ARGS (limit));
+ resync:
+   /* if not good enough, determine granule based on time */
+   if (diff > limit || diff < -limit) {
+     granule = gst_util_uint64_scale_round (time, pad->map.granulerate_n,
+         GST_SECOND * pad->map.granulerate_d);
+     GST_DEBUG_OBJECT (pad->collect.pad,
+         "resyncing to determined granule %" G_GINT64_FORMAT, granule);
+   }
+   if (pad->map.is_ogm || pad->map.is_sparse) {
+     pad->next_granule = granule;
+   } else {
+     granule += duration;
+     pad->next_granule = granule;
+   }
+   /* track previous keyframe */
+   if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))
+     pad->keyframe_granule = granule;
+   /* determine corresponding time and granulepos */
+   GST_BUFFER_OFFSET (buf) = gst_ogg_stream_granule_to_time (&pad->map, granule);
+   GST_BUFFER_OFFSET_END (buf) =
+       gst_ogg_stream_granule_to_granulepos (&pad->map, granule,
+       pad->keyframe_granule);
+   return buf;
+   /* ERRORS */
+ no_granule:
+   {
+     GST_DEBUG_OBJECT (pad->collect.pad, "could not determine granulepos, "
+         "falling back to upstream provided metadata");
+     return buf;
+   }
+ }
  /* make sure at least one buffer is queued on all pads, two if possible
   * 
   * if pad->buffer == NULL, pad->next_buffer !=  NULL, then
@@@ -781,20 -945,12 +891,15 @@@ gst_ogg_mux_queue_pads (GstOggMux * ogg
            /* and we have one */
            ogg_packet packet;
            gboolean is_header;
 +          gsize size;
  
 -          packet.packet = GST_BUFFER_DATA (buf);
 -          packet.bytes = GST_BUFFER_SIZE (buf);
 +          packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
 +          packet.bytes = size;
  
-           if (GST_BUFFER_OFFSET_END_IS_VALID (buf))
-             packet.granulepos = GST_BUFFER_OFFSET_END (buf);
-           else
-             packet.granulepos = 0;
            /* if we're not yet in data mode, ensure we're setup on the first packet */
            if (!pad->have_type) {
 +            GstCaps *caps;
 +
              /* Use headers in caps, if any; this will allow us to be resilient
               * to starting streams on the fly, and some streams (like VP8
               * at least) do not send headers packets, as other muxers don't
@@@ -1131,6 -1144,83 +1131,89 @@@ theora_enc_write_multipass_cache (GstTh
  }
  
  static GstFlowReturn
 -  theora_enc_init_buffer (ycbcr, &enc->info, GST_BUFFER_DATA (buffer));
+ theora_enc_encode_and_push (GstTheoraEnc * enc, ogg_packet op,
+     GstClockTime timestamp, GstClockTime running_time,
+     GstClockTime duration, GstBuffer * buffer)
+ {
+   GstFlowReturn ret;
+   th_ycbcr_buffer ycbcr;
+   gint res;
++  guint8 *data;
++  gsize size;
 -    gst_buffer_unref (buffer);
 -    return ret;
++  data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
++  theora_enc_init_buffer (ycbcr, &enc->info, data);
+   if (theora_enc_is_discontinuous (enc, running_time, duration)) {
+     theora_enc_reset (enc);
+     enc->granulepos_offset =
+         gst_util_uint64_scale (running_time, enc->fps_n,
+         GST_SECOND * enc->fps_d);
+     enc->timestamp_offset = running_time;
+     enc->next_ts = 0;
+     enc->next_discont = TRUE;
+   }
+   if (enc->multipass_cache_fd
+       && enc->multipass_mode == MULTIPASS_MODE_SECOND_PASS) {
+     if (!theora_enc_read_multipass_cache (enc)) {
+       ret = GST_FLOW_ERROR;
+       goto multipass_read_failed;
+     }
+   }
+   res = th_encode_ycbcr_in (enc->encoder, ycbcr);
+   /* none of the failure cases can happen here */
+   g_assert (res == 0);
+   if (enc->multipass_cache_fd
+       && enc->multipass_mode == MULTIPASS_MODE_FIRST_PASS) {
+     if (!theora_enc_write_multipass_cache (enc, FALSE, FALSE)) {
+       ret = GST_FLOW_ERROR;
+       goto multipass_write_failed;
+     }
+   }
+   ret = GST_FLOW_OK;
+   while (th_encode_packetout (enc->encoder, 0, &op)) {
+     GstClockTime next_time;
+     next_time = th_granule_time (enc->encoder, op.granulepos) * GST_SECOND;
+     ret =
+         theora_push_packet (enc, &op, timestamp, enc->next_ts,
+         next_time - enc->next_ts);
+     enc->next_ts = next_time;
+     if (ret != GST_FLOW_OK)
+       goto data_push;
+   }
++
++done:
++  gst_buffer_unmap (buffer, data, size);
+   gst_buffer_unref (buffer);
+   return ret;
+   /* ERRORS */
+ multipass_read_failed:
+   {
 -    gst_buffer_unref (buffer);
 -    return ret;
++    GST_DEBUG_OBJECT (enc, "multipass read failed");
++    goto done;
+   }
+ multipass_write_failed:
+   {
 -    gst_buffer_unref (buffer);
 -    return ret;
++    GST_DEBUG_OBJECT (enc, "multipass write failed");
++    goto done;
+   }
+ data_push:
+   {
++    GST_DEBUG_OBJECT (enc, "error pushing buffer: %s", gst_flow_get_name (ret));
++    goto done;
+   }
+ }
+ static GstFlowReturn
  theora_enc_chain (GstPad * pad, GstBuffer * buffer)
  {
    GstTheoraEnc *enc;
Simple merge
Simple merge
Simple merge