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
/* 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
}
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;