oggdemux: fix wrong first granule

The code was using the first nonnegative granulepos to seed the
granule tracking, which appeared to work since headers have zero
granulepos. However, this does not work for files with a hole at
start, which are common in live streaming.

The correct behavior is to look for the first granule, and subtract
the duration of all the packets finishing on this page.

The function which does this relies on the fact that the ogg_stream
structure can be duplicated by shallow copy, in order to pull the
packets from the first page(s) on the copy without affecting the
original stream state.
This commit is contained in:
Vincent Penquerc'h 2015-02-04 17:13:44 +00:00
parent 3cd2eb5847
commit ca136e3648

View file

@ -936,9 +936,11 @@ gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
packet->granulepos);
if (granule >= 0) {
if (granule > 0) {
GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule);
pad->current_granule = granule;
} else if (granule == 0) {
/* headers */
} else if (granule != -1) {
GST_ERROR_OBJECT (ogg,
"granulepos %" G_GINT64_FORMAT " yielded granule %" G_GINT64_FORMAT,
@ -1203,6 +1205,62 @@ could_not_submit:
}
}
static void
gst_ogg_demux_setup_first_granule (GstOggDemux * ogg, GstOggPad * pad,
ogg_page * page)
{
/* When we submit a page, we check if we have started tracking granules.
* If not, we calculate the granule corresponding to the first packet
* on the page. */
if (pad->current_granule == -1) {
ogg_int64_t granpos = ogg_page_granulepos (page);
if (granpos > 0) {
ogg_int64_t granule =
gst_ogg_stream_granulepos_to_granule (&pad->map, granpos), duration;
int packets = ogg_page_packets (page), n;
GST_DEBUG_OBJECT (pad,
"This page completes %d packets, granule %" G_GINT64_FORMAT, packets,
granule);
if (packets > 0) {
ogg_stream_state os;
ogg_packet op;
int last_size = pad->map.last_size;
memcpy (&os, &pad->map.stream, sizeof (os));
for (n = 0; n < packets; ++n) {
int ret = ogg_stream_packetout (&os, &op);
if (ret < 0) {
GST_WARNING_OBJECT (pad, "Failed to read packets off first page");
granule = -1;
break;
}
if (ret == 0) {
GST_WARNING_OBJECT (pad,
"Short read getting %d packets off first page", packets);
granule = -1;
break;
}
duration = gst_ogg_stream_get_packet_duration (&pad->map, &op);
GST_DEBUG_OBJECT (pad, "Packet %d has duration %" G_GINT64_FORMAT, n,
duration);
granule -= duration;
}
pad->map.last_size = last_size;
if (granule >= 0) {
pad->current_granule = granule;
GST_INFO_OBJECT (pad, "Starting with first granule %" G_GINT64_FORMAT,
granule);
} else {
GST_WARNING_OBJECT (pad, "Extrapolated first granule is negative");
}
} else {
GST_WARNING_OBJECT (pad,
"Ogg page finishing no packets, but a valid granule");
}
}
}
}
static void
gst_ogg_demux_setup_bisection_bounds (GstOggDemux * ogg)
{
@ -1537,6 +1595,8 @@ gst_ogg_pad_handle_push_mode_state (GstOggPad * pad, ogg_page * page)
GST_PUSH_UNLOCK (ogg);
if (ogg_stream_pagein (&pad->map.stream, page) != 0)
goto choked;
if (pad->current_granule == -1)
gst_ogg_demux_setup_first_granule (ogg, pad, page);
return GST_FLOW_SKIP_PUSH;
}
@ -1556,6 +1616,8 @@ gst_ogg_pad_handle_push_mode_state (GstOggPad * pad, ogg_page * page)
"Not enough timing info collected for sync, waiting for more");
if (ogg_stream_pagein (&pad->map.stream, page) != 0)
goto choked;
if (pad->current_granule == -1)
gst_ogg_demux_setup_first_granule (ogg, pad, page);
return GST_FLOW_SKIP_PUSH;
}
ogg->push_last_seek_time = sync_time;
@ -1803,6 +1865,8 @@ gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page)
if (ogg_stream_pagein (&pad->map.stream, page) != 0)
goto choked;
if (pad->current_granule == -1)
gst_ogg_demux_setup_first_granule (ogg, pad, page);
/* flush all packets in the stream layer, this might not give a packet if
* the page had no packets finishing on the page (npackets == 0). */