ext/ogg/gstoggdemux.*: Properly propagate streaming errors when we are scanning the file for chains so that we don't ...

Original commit message from CVS:
* ext/ogg/gstoggdemux.c: (gst_ogg_demux_submit_buffer),
(gst_ogg_demux_get_data), (gst_ogg_demux_get_next_page),
(gst_ogg_demux_get_prev_page), (gst_ogg_demux_do_seek),
(gst_ogg_demux_perform_seek),
(gst_ogg_demux_bisect_forward_serialno),
(gst_ogg_demux_read_chain), (gst_ogg_demux_read_end_chain),
(gst_ogg_demux_find_chains), (gst_ogg_demux_handle_page),
(gst_ogg_demux_chain), (gst_ogg_demux_combine_flows),
(gst_ogg_demux_loop_reverse), (gst_ogg_demux_loop):
* ext/ogg/gstoggdemux.h:
Properly propagate streaming errors when we are scanning the file for
chains so that we don't crash when shut down. Might fix some crashers
when quickly switching oggs in RB such as #332503 and #378436.
This commit is contained in:
Wim Taymans 2007-01-27 13:32:24 +00:00
parent 3b3320ac38
commit fde9b0096c
3 changed files with 246 additions and 158 deletions

View File

@ -1,3 +1,19 @@
2007-01-27 Wim Taymans <wim@fluendo.com>
* ext/ogg/gstoggdemux.c: (gst_ogg_demux_submit_buffer),
(gst_ogg_demux_get_data), (gst_ogg_demux_get_next_page),
(gst_ogg_demux_get_prev_page), (gst_ogg_demux_do_seek),
(gst_ogg_demux_perform_seek),
(gst_ogg_demux_bisect_forward_serialno),
(gst_ogg_demux_read_chain), (gst_ogg_demux_read_end_chain),
(gst_ogg_demux_find_chains), (gst_ogg_demux_handle_page),
(gst_ogg_demux_chain), (gst_ogg_demux_combine_flows),
(gst_ogg_demux_loop_reverse), (gst_ogg_demux_loop):
* ext/ogg/gstoggdemux.h:
Properly propagate streaming errors when we are scanning the file for
chains so that we don't crash when shut down. Might fix some crashers
when quickly switching oggs in RB such as #332503 and #378436.
2007-01-26 Tim-Philipp Müller <tim at centricular dot net> 2007-01-26 Tim-Philipp Müller <tim at centricular dot net>
* ext/gnomevfs/gstgnomevfssrc.c: (gst_gnome_vfs_src_start): * ext/gnomevfs/gstgnomevfssrc.c: (gst_gnome_vfs_src_start):

View File

@ -59,13 +59,7 @@ GST_ELEMENT_DETAILS ("Ogg demuxer",
#define SKELETON_FISHEAD_SIZE 64 #define SKELETON_FISHEAD_SIZE 64
#define SKELETON_FISBONE_MIN_SIZE 52 #define SKELETON_FISBONE_MIN_SIZE 52
enum #define GST_FLOW_LIMIT GST_FLOW_CUSTOM_ERROR
{
OV_EREAD = -1,
OV_EFAULT = -2,
OV_FALSE = -3,
OV_EOF = -4,
};
GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_debug); GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_debug);
GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_setup_debug); GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_setup_debug);
@ -1398,8 +1392,9 @@ static void gst_ogg_demux_finalize (GObject * object);
//static const GstEventMask *gst_ogg_demux_get_event_masks (GstPad * pad); //static const GstEventMask *gst_ogg_demux_get_event_masks (GstPad * pad);
//static const GstQueryType *gst_ogg_demux_get_query_types (GstPad * pad); //static const GstQueryType *gst_ogg_demux_get_query_types (GstPad * pad);
static GstOggChain *gst_ogg_demux_read_chain (GstOggDemux * ogg); static GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg,
static gint gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain ** chain);
static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
GstOggChain * chain); GstOggChain * chain);
static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event); static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event);
@ -1512,24 +1507,49 @@ gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event)
* *
* Returns the number of bytes submited. * Returns the number of bytes submited.
*/ */
static gint static GstFlowReturn
gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer) gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer)
{ {
gint size; gint size;
guint8 *data; guint8 *data;
gchar *oggbuffer; gchar *oggbuffer;
GstFlowReturn ret = GST_FLOW_OK;
size = GST_BUFFER_SIZE (buffer); size = GST_BUFFER_SIZE (buffer);
data = GST_BUFFER_DATA (buffer); data = GST_BUFFER_DATA (buffer);
GST_DEBUG_OBJECT (ogg, "submitting %u bytes", size); GST_DEBUG_OBJECT (ogg, "submitting %u bytes", size);
if (G_UNLIKELY (size == 0))
goto done;
oggbuffer = ogg_sync_buffer (&ogg->sync, size); oggbuffer = ogg_sync_buffer (&ogg->sync, size);
if (G_UNLIKELY (oggbuffer == NULL))
goto no_buffer;
memcpy (oggbuffer, data, size); memcpy (oggbuffer, data, size);
ogg_sync_wrote (&ogg->sync, size); if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
goto write_failed;
done:
gst_buffer_unref (buffer); gst_buffer_unref (buffer);
return size; return ret;
/* ERRORS */
no_buffer:
{
GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
(NULL), ("failed to get ogg sync buffer"));
ret = GST_FLOW_ERROR;
goto done;
}
write_failed:
{
GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
(NULL), ("failed to write %d bytes to the sync buffer", size));
ret = GST_FLOW_ERROR;
goto done;
}
} }
/* in random access mode this code updates the current read position /* in random access mode this code updates the current read position
@ -1547,15 +1567,12 @@ gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset)
/* read more data from the current offset and submit to /* read more data from the current offset and submit to
* the ogg sync layer. * the ogg sync layer.
*
* Return number of bytes written or 0 on EOS or -1 on error.
*/ */
static gint static GstFlowReturn
gst_ogg_demux_get_data (GstOggDemux * ogg) gst_ogg_demux_get_data (GstOggDemux * ogg)
{ {
GstFlowReturn ret; GstFlowReturn ret;
GstBuffer *buffer; GstBuffer *buffer;
gint size;
GST_LOG_OBJECT (ogg, "get data %" G_GINT64_FORMAT " %" G_GINT64_FORMAT, GST_LOG_OBJECT (ogg, "get data %" G_GINT64_FORMAT " %" G_GINT64_FORMAT,
ogg->offset, ogg->length); ogg->offset, ogg->length);
@ -1566,35 +1583,45 @@ gst_ogg_demux_get_data (GstOggDemux * ogg)
if (ret != GST_FLOW_OK) if (ret != GST_FLOW_OK)
goto error; goto error;
size = gst_ogg_demux_submit_buffer (ogg, buffer); ret = gst_ogg_demux_submit_buffer (ogg, buffer);
return size; return ret;
/* ERROR */ /* ERROR */
eos: eos:
{ {
GST_LOG_OBJECT (ogg, "reached EOS"); GST_LOG_OBJECT (ogg, "reached EOS");
return 0; return GST_FLOW_UNEXPECTED;
} }
error: error:
{ {
GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret, GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret,
gst_flow_get_name (ret)); gst_flow_get_name (ret));
ogg->chain_error = ret; return ret;
return -1;
} }
} }
/* Read the next page from the current offset. /* Read the next page from the current offset.
* boundary: number of bytes ahead we allow looking for; * boundary: number of bytes ahead we allow looking for;
* -1 if no boundary * -1 if no boundary
* returns the offset the next page starts at, or OV_FALSE if we couldn't *
* find a new page within the boundary * @offset will contain the offset the next page starts at when this function
* returns GST_FLOW_OK.
*
* GST_FLOW_UNEXPECTED is returned on EOS.
*
* GST_FLOW_LIMIT is returned when we did not find a page before the
* boundary. If @boundary is -1, this is never returned.
*
* Any other error returned while retrieving data from the peer is returned as
* is.
*/ */
static gint64 static GstFlowReturn
gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og, gint64 boundary) gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og, gint64 boundary,
gint64 * offset)
{ {
gint64 end_offset = 0; gint64 end_offset = 0;
GstFlowReturn ret;
GST_LOG_OBJECT (ogg, GST_LOG_OBJECT (ogg,
"get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %" "get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %"
@ -1606,37 +1633,34 @@ gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og, gint64 boundary)
while (TRUE) { while (TRUE) {
glong more; glong more;
if (boundary > 0 && ogg->offset >= end_offset) { if (boundary > 0 && ogg->offset >= end_offset)
GST_LOG_OBJECT (ogg, goto boundary_reached;
"offset %" G_GINT64_FORMAT " >= end_offset %" G_GINT64_FORMAT,
ogg->offset, end_offset);
return OV_FALSE;
}
more = ogg_sync_pageseek (&ogg->sync, og); more = ogg_sync_pageseek (&ogg->sync, og);
GST_LOG_OBJECT (ogg, "pageseek gave %ld", more); GST_LOG_OBJECT (ogg, "pageseek gave %ld", more);
if (more < 0) { if (more < 0) {
GST_LOG_OBJECT (ogg, "skipped %ld bytes", more);
/* skipped n bytes */ /* skipped n bytes */
GST_LOG_OBJECT (ogg, "skipped %ld bytes", more);
ogg->offset -= more; ogg->offset -= more;
} else if (more == 0) { } else if (more == 0) {
gint ret; /* we need more data */
/* send more paramedics */
if (boundary == 0) if (boundary == 0)
return OV_FALSE; goto boundary_reached;
GST_LOG_OBJECT (ogg, "need more data");
ret = gst_ogg_demux_get_data (ogg); ret = gst_ogg_demux_get_data (ogg);
if (ret == 0) if (ret != GST_FLOW_OK)
return OV_EOF; break;
if (ret < 0)
return OV_EREAD;
} else { } else {
gint64 res_offset = ogg->offset;
/* got a page. Return the offset at the page beginning, /* got a page. Return the offset at the page beginning,
advance the internal offset past the page end */ advance the internal offset past the page end */
gint64 ret = ogg->offset; if (offset)
*offset = res_offset;
ret = GST_FLOW_OK;
ogg->offset += more; ogg->offset += more;
/* need to reset as we do not keep track of the bytes we /* need to reset as we do not keep track of the bytes we
@ -1645,54 +1669,77 @@ gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og, gint64 boundary)
GST_LOG_OBJECT (ogg, GST_LOG_OBJECT (ogg,
"got page at %" G_GINT64_FORMAT ", serial %08x, end at %" "got page at %" G_GINT64_FORMAT ", serial %08x, end at %"
G_GINT64_FORMAT ", granule %" G_GINT64_FORMAT, ret, G_GINT64_FORMAT ", granule %" G_GINT64_FORMAT, res_offset,
ogg_page_serialno (og), ogg->offset, ogg_page_granulepos (og)); ogg_page_serialno (og), ogg->offset, ogg_page_granulepos (og));
break;
return ret;
} }
} }
GST_LOG_OBJECT (ogg, "returning %d", ret);
return ret;
/* ERRORS */
boundary_reached:
{
GST_LOG_OBJECT (ogg,
"offset %" G_GINT64_FORMAT " >= end_offset %" G_GINT64_FORMAT,
ogg->offset, end_offset);
return GST_FLOW_LIMIT;
}
} }
/* from the current offset, find the previous page, seeking backwards /* from the current offset, find the previous page, seeking backwards
* until we find the page. */ * until we find the page.
static gint64 */
gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og) static GstFlowReturn
gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og, gint64 * offset)
{ {
GstFlowReturn ret;
gint64 begin = ogg->offset; gint64 begin = ogg->offset;
gint64 end = begin; gint64 end = begin;
gint64 ret; gint64 cur_offset = -1;
gint64 offset = -1;
while (offset == -1) { while (cur_offset == -1) {
begin -= CHUNKSIZE; begin -= CHUNKSIZE;
if (begin < 0) if (begin < 0)
begin = 0; begin = 0;
/* seek CHUNKSIZE back */
gst_ogg_demux_seek (ogg, begin); gst_ogg_demux_seek (ogg, begin);
/* now continue reading until we run out of data, if we find a page /* now continue reading until we run out of data, if we find a page
* start, we save it. It might not be the final page as there could be * start, we save it. It might not be the final page as there could be
* another page after this one. */ * another page after this one. */
while (ogg->offset < end) { while (ogg->offset < end) {
ret = gst_ogg_demux_get_next_page (ogg, og, end - ogg->offset); gint64 new_offset;
if (ret == OV_EREAD)
return OV_EREAD; ret =
if (ret < 0) { gst_ogg_demux_get_next_page (ogg, og, end - ogg->offset, &new_offset);
/* we hit the upper limit, offset contains the last page start */
if (ret == GST_FLOW_LIMIT)
break; break;
} else { /* something went wrong */
offset = ret; if (ret == GST_FLOW_UNEXPECTED)
} new_offset = 0;
else if (ret != GST_FLOW_OK)
return ret;
/* offset is next page start */
cur_offset = new_offset;
} }
} }
/* we have the offset. Actually snork and hold the page now */ /* we have the offset. Actually snork and hold the page now */
gst_ogg_demux_seek (ogg, offset); gst_ogg_demux_seek (ogg, cur_offset);
ret = gst_ogg_demux_get_next_page (ogg, og, -1); ret = gst_ogg_demux_get_next_page (ogg, og, -1, NULL);
if (ret < 0) if (ret != GST_FLOW_OK)
/* this shouldn't be possible */ /* this shouldn't be possible */
return OV_EFAULT; return ret;
return offset; if (offset)
*offset = cur_offset;
return ret;
} }
static gboolean static gboolean
@ -1815,6 +1862,7 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg, gint64 position, gboolean accurate,
gint64 best; gint64 best;
gint64 total; gint64 total;
gint64 result = 0; gint64 result = 0;
GstFlowReturn ret;
gint i; gint i;
/* first find the chain to search in */ /* first find the chain to search in */
@ -1878,14 +1926,12 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg, gint64 position, gboolean accurate,
"after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT
", end %" G_GINT64_FORMAT, bisect, begin, end); ", end %" G_GINT64_FORMAT, bisect, begin, end);
result = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset); ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT, GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
result); result);
if (result == OV_EREAD) {
goto seek_error;
}
if (result < 0) { if (ret == GST_FLOW_LIMIT) {
/* we hit the upper limit, go back a bit */
if (bisect <= begin + 1) { if (bisect <= begin + 1) {
end = begin; /* found it */ end = begin; /* found it */
} else { } else {
@ -1898,7 +1944,7 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg, gint64 position, gboolean accurate,
gst_ogg_demux_seek (ogg, bisect); gst_ogg_demux_seek (ogg, bisect);
} }
} else { } else if (ret == GST_FLOW_OK) {
/* found offset of next ogg page */ /* found offset of next ogg page */
gint64 granulepos; gint64 granulepos;
GstClockTime granuletime; GstClockTime granuletime;
@ -1962,7 +2008,8 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg, gint64 position, gboolean accurate,
} }
} }
} }
} } else
goto seek_error;
} }
} }
@ -2096,6 +2143,9 @@ gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
chain = ogg->current_chain; chain = ogg->current_chain;
} }
if (!chain)
goto no_chain;
/* now we have a new position, prepare for streaming again */ /* now we have a new position, prepare for streaming again */
{ {
GstEvent *event; GstEvent *event;
@ -2164,6 +2214,12 @@ error:
GST_DEBUG_OBJECT (ogg, "seek failed"); GST_DEBUG_OBJECT (ogg, "seek failed");
return FALSE; return FALSE;
} }
no_chain:
{
GST_DEBUG_OBJECT (ogg, "no chain to seek in");
GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
return FALSE;
}
} }
/* finds each bitstream link one at a time using a bisection search /* finds each bitstream link one at a time using a bisection search
@ -2171,14 +2227,15 @@ error:
* Recurses for each link so it can alloc the link storage after * Recurses for each link so it can alloc the link storage after
* finding them all, then unroll and fill the cache at the same time * finding them all, then unroll and fill the cache at the same time
*/ */
static gint static GstFlowReturn
gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg, gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m) gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m)
{ {
gint64 endsearched = end; gint64 endsearched = end;
gint64 next = end; gint64 next = end;
ogg_page og; ogg_page og;
gint64 ret; GstFlowReturn ret;
gint64 offset;
GstOggChain *nextchain; GstOggChain *nextchain;
GST_LOG_OBJECT (ogg, GST_LOG_OBJECT (ogg,
@ -2197,48 +2254,51 @@ gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
} }
gst_ogg_demux_seek (ogg, bisect); gst_ogg_demux_seek (ogg, bisect);
ret = gst_ogg_demux_get_next_page (ogg, &og, -1); ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset);
if (ret == OV_EREAD) {
GST_LOG_OBJECT (ogg, "OV_EREAD");
return OV_EREAD;
}
if (ret < 0) { if (ret == GST_FLOW_UNEXPECTED) {
endsearched = bisect; endsearched = bisect;
} else { } else if (ret == GST_FLOW_OK) {
glong serial = ogg_page_serialno (&og); glong serial = ogg_page_serialno (&og);
if (!gst_ogg_chain_has_stream (chain, serial)) { if (!gst_ogg_chain_has_stream (chain, serial)) {
endsearched = bisect; endsearched = bisect;
next = ret; next = offset;
} else { } else {
searched = ret + og.header_len + og.body_len; searched = offset + og.header_len + og.body_len;
} }
} } else
return ret;
} }
GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched); GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched);
chain->end_offset = searched; chain->end_offset = searched;
gst_ogg_demux_read_end_chain (ogg, chain); ret = gst_ogg_demux_read_end_chain (ogg, chain);
if (ret != GST_FLOW_OK)
return ret;
GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next); GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next);
gst_ogg_demux_seek (ogg, next); gst_ogg_demux_seek (ogg, next);
nextchain = gst_ogg_demux_read_chain (ogg); ret = gst_ogg_demux_read_chain (ogg, &nextchain);
if (ret == GST_FLOW_UNEXPECTED) {
nextchain = NULL;
ret = GST_FLOW_OK;
GST_LOG_OBJECT (ogg, "no next chain");
} else if (ret != GST_FLOW_OK)
goto done;
if (searched < end && nextchain != NULL) { if (searched < end && nextchain != NULL) {
ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset, ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset,
end, nextchain, m + 1); end, nextchain, m + 1);
if (ret != GST_FLOW_OK)
if (ret == OV_EREAD) { goto done;
GST_LOG_OBJECT (ogg, "OV_READ");
return OV_EREAD;
}
} }
g_array_insert_val (ogg->chains, 0, chain); g_array_insert_val (ogg->chains, 0, chain);
return 0; done:
return ret;
} }
/* read a chain from the ogg file. This code will /* read a chain from the ogg file. This code will
@ -2250,9 +2310,10 @@ gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
* decoded the first buffer, we know the timestamp of the first * decoded the first buffer, we know the timestamp of the first
* page in the chain. * page in the chain.
*/ */
static GstOggChain * static GstFlowReturn
gst_ogg_demux_read_chain (GstOggDemux * ogg) gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
{ {
GstFlowReturn ret;
GstOggChain *chain = NULL; GstOggChain *chain = NULL;
gint64 offset = ogg->offset; gint64 offset = ogg->offset;
ogg_page op; ogg_page op;
@ -2266,20 +2327,16 @@ gst_ogg_demux_read_chain (GstOggDemux * ogg)
while (TRUE) { while (TRUE) {
GstOggPad *pad; GstOggPad *pad;
glong serial; glong serial;
gint ret;
ret = gst_ogg_demux_get_next_page (ogg, &op, -1); ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL);
if (ret < 0) { if (ret != GST_FLOW_OK) {
GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret); GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
if (chain) {
gst_ogg_chain_free (chain);
chain = NULL;
}
break; break;
} }
if (!ogg_page_bos (&op)) {
if (!ogg_page_bos (&op)) GST_WARNING_OBJECT (ogg, "page is not BOS page");
break; break;
}
if (chain == NULL) { if (chain == NULL) {
chain = gst_ogg_chain_new (ogg); chain = gst_ogg_chain_new (ogg);
@ -2296,10 +2353,15 @@ gst_ogg_demux_read_chain (GstOggDemux * ogg)
pad = gst_ogg_chain_new_stream (chain, serial); pad = gst_ogg_chain_new_stream (chain, serial);
gst_ogg_pad_submit_page (pad, &op); gst_ogg_pad_submit_page (pad, &op);
} }
if (chain == NULL) {
if (ret != GST_FLOW_OK || chain == NULL) {
GST_WARNING_OBJECT (ogg, "failed to read chain"); GST_WARNING_OBJECT (ogg, "failed to read chain");
return NULL; if (chain) {
gst_ogg_chain_free (chain);
}
return ret;
} }
chain->have_bos = TRUE; chain->have_bos = TRUE;
GST_LOG_OBJECT (ogg, "read bos pages, init decoder now"); GST_LOG_OBJECT (ogg, "read bos pages, init decoder now");
@ -2317,7 +2379,7 @@ gst_ogg_demux_read_chain (GstOggDemux * ogg)
while (!done) { while (!done) {
glong serial; glong serial;
gboolean known_serial = FALSE; gboolean known_serial = FALSE;
gint ret; GstFlowReturn ret;
serial = ogg_page_serialno (&op); serial = ogg_page_serialno (&op);
done = TRUE; done = TRUE;
@ -2351,8 +2413,8 @@ gst_ogg_demux_read_chain (GstOggDemux * ogg)
} }
if (!done) { if (!done) {
ret = gst_ogg_demux_get_next_page (ogg, &op, -1); ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL);
if (ret < 0) if (ret != GST_FLOW_OK)
break; break;
} }
} }
@ -2380,13 +2442,17 @@ gst_ogg_demux_read_chain (GstOggDemux * ogg)
pad->mode = GST_OGG_PAD_MODE_STREAMING; pad->mode = GST_OGG_PAD_MODE_STREAMING;
} }
return chain;
if (res_chain)
*res_chain = chain;
return GST_FLOW_OK;
} }
/* read the last pages from the ogg stream to get the final /* read the last pages from the ogg stream to get the final
* page end_offsets. * page end_offsets.
*/ */
static gint static GstFlowReturn
gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain) gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
{ {
gint64 begin = chain->end_offset; gint64 begin = chain->end_offset;
@ -2394,7 +2460,7 @@ gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
gint64 last_granule = -1; gint64 last_granule = -1;
GstOggPad *last_pad = NULL; GstOggPad *last_pad = NULL;
GstFormat target; GstFormat target;
gint64 ret; GstFlowReturn ret;
gboolean done = FALSE; gboolean done = FALSE;
ogg_page og; ogg_page og;
gint i; gint i;
@ -2410,29 +2476,28 @@ gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
* start, we save it. It might not be the final page as there could be * start, we save it. It might not be the final page as there could be
* another page after this one. */ * another page after this one. */
while (ogg->offset < end) { while (ogg->offset < end) {
ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset); ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, NULL);
if (ret == OV_EREAD)
return OV_EREAD; if (ret == GST_FLOW_LIMIT)
if (ret < 0) {
break; break;
} else { if (ret != GST_FLOW_OK)
for (i = 0; i < chain->streams->len; i++) { return ret;
GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
if (pad->is_skeleton) for (i = 0; i < chain->streams->len; i++) {
continue; GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
if (pad->serialno == ogg_page_serialno (&og)) { if (pad->is_skeleton)
gint64 granulepos = ogg_page_granulepos (&og); continue;
if (last_granule < granulepos) { if (pad->serialno == ogg_page_serialno (&og)) {
last_granule = granulepos; gint64 granulepos = ogg_page_granulepos (&og);
last_pad = pad;
}
done = TRUE; if (last_granule < granulepos) {
break; last_granule = granulepos;
last_pad = pad;
} }
done = TRUE;
break;
} }
} }
} }
@ -2446,7 +2511,7 @@ gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
chain->segment_stop = GST_CLOCK_TIME_NONE; chain->segment_stop = GST_CLOCK_TIME_NONE;
} }
return 0; return GST_FLOW_OK;
} }
/* find a pad with a given serial number /* find a pad with a given serial number
@ -2556,7 +2621,7 @@ gst_ogg_demux_collect_info (GstOggDemux * ogg)
* last page of the ogg stream, if they match then the ogg file has * last page of the ogg stream, if they match then the ogg file has
* just one chain, else we do a binary search for all chains. * just one chain, else we do a binary search for all chains.
*/ */
static gboolean static GstFlowReturn
gst_ogg_demux_find_chains (GstOggDemux * ogg) gst_ogg_demux_find_chains (GstOggDemux * ogg)
{ {
ogg_page og; ogg_page og;
@ -2565,6 +2630,7 @@ gst_ogg_demux_find_chains (GstOggDemux * ogg)
gboolean res; gboolean res;
gulong serialno; gulong serialno;
GstOggChain *chain; GstOggChain *chain;
GstFlowReturn ret;
/* get peer to figure out length */ /* get peer to figure out length */
if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL) if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL)
@ -2582,49 +2648,65 @@ gst_ogg_demux_find_chains (GstOggDemux * ogg)
/* read chain from offset 0, this is the first chain of the /* read chain from offset 0, this is the first chain of the
* ogg file. */ * ogg file. */
gst_ogg_demux_seek (ogg, 0); gst_ogg_demux_seek (ogg, 0);
chain = gst_ogg_demux_read_chain (ogg); ret = gst_ogg_demux_read_chain (ogg, &chain);
if (chain == NULL) if (ret != GST_FLOW_OK)
goto no_first_chain; goto no_first_chain;
/* read page from end offset, we use this page to check if its serial /* read page from end offset, we use this page to check if its serial
* number is contained in the first chain. If this is the case then * number is contained in the first chain. If this is the case then
* this ogg is not a chained ogg and we can skip the scanning. */ * this ogg is not a chained ogg and we can skip the scanning. */
gst_ogg_demux_seek (ogg, ogg->length); gst_ogg_demux_seek (ogg, ogg->length);
gst_ogg_demux_get_prev_page (ogg, &og); ret = gst_ogg_demux_get_prev_page (ogg, &og, NULL);
if (ret != GST_FLOW_OK)
goto no_last_page;
serialno = ogg_page_serialno (&og); serialno = ogg_page_serialno (&og);
if (!gst_ogg_chain_has_stream (chain, serialno)) { if (!gst_ogg_chain_has_stream (chain, serialno)) {
/* the last page is not in the first stream, this means we should /* the last page is not in the first stream, this means we should
* find all the chains in this chained ogg. */ * find all the chains in this chained ogg. */
gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain, 0); ret =
gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain,
0);
} else { } else {
/* we still call this function here but with an empty range so that /* we still call this function here but with an empty range so that
* we can reuse the setup code in this routine. */ * we can reuse the setup code in this routine. */
gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length, ogg->length, ret =
gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length, ogg->length,
chain, 0); chain, 0);
} }
if (ret != GST_FLOW_OK)
goto done;
/* all fine, collect and print */
gst_ogg_demux_collect_info (ogg); gst_ogg_demux_collect_info (ogg);
/* dump our chains and streams */ /* dump our chains and streams */
gst_ogg_print (ogg); gst_ogg_print (ogg);
return TRUE; done:
return ret;
/*** error cases ***/ /*** error cases ***/
no_peer: no_peer:
{ {
GST_DEBUG_OBJECT (ogg, "we don't have a peer"); GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer"));
return FALSE; return GST_FLOW_NOT_LINKED;
} }
no_length: no_length:
{ {
GST_DEBUG_OBJECT (ogg, "can't get file length"); GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length"));
return FALSE; return GST_FLOW_NOT_SUPPORTED;
} }
no_first_chain: no_first_chain:
{ {
GST_DEBUG_OBJECT (ogg, "can't get first chain"); GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain"));
return FALSE; return GST_FLOW_ERROR;
}
no_last_page:
{
GST_DEBUG_OBJECT (ogg, "can't get last page");
return ret;
} }
} }
@ -2712,14 +2794,12 @@ unknown_chain:
{ {
GST_ELEMENT_ERROR (ogg, STREAM, DECODE, GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
(NULL), ("unknown ogg chain for serial %08x detected", serialno)); (NULL), ("unknown ogg chain for serial %08x detected", serialno));
gst_ogg_demux_send_event (ogg, gst_event_new_eos ());
return GST_FLOW_ERROR; return GST_FLOW_ERROR;
} }
unknown_pad: unknown_pad:
{ {
GST_ELEMENT_ERROR (ogg, STREAM, DECODE, GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
(NULL), ("unknown ogg pad for serial %08x detected", serialno)); (NULL), ("unknown ogg pad for serial %08x detected", serialno));
gst_ogg_demux_send_event (ogg, gst_event_new_eos ());
return GST_FLOW_ERROR; return GST_FLOW_ERROR;
} }
} }
@ -2731,15 +2811,15 @@ static GstFlowReturn
gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer) gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
{ {
GstOggDemux *ogg; GstOggDemux *ogg;
gint ret = -1; gint ret;
GstFlowReturn result = GST_FLOW_OK; GstFlowReturn result = GST_FLOW_OK;
ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad)); ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
GST_DEBUG_OBJECT (ogg, "chain"); GST_DEBUG_OBJECT (ogg, "chain");
gst_ogg_demux_submit_buffer (ogg, buffer); result = gst_ogg_demux_submit_buffer (ogg, buffer);
while (ret != 0 && result == GST_FLOW_OK) { while (result == GST_FLOW_OK) {
ogg_page page; ogg_page page;
ret = ogg_sync_pageout (&ogg->sync, &page); ret = ogg_sync_pageout (&ogg->sync, &page);
@ -2748,6 +2828,7 @@ gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
break; break;
if (ret == -1) { if (ret == -1) {
/* discontinuity in the pages */ /* discontinuity in the pages */
GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
} else { } else {
result = gst_ogg_demux_handle_page (ogg, &page); result = gst_ogg_demux_handle_page (ogg, &page);
} }
@ -2782,9 +2863,6 @@ gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad,
/* store the value */ /* store the value */
pad->last_ret = ret; pad->last_ret = ret;
/* if it's success we can return the value right away */
if (GST_FLOW_IS_SUCCESS (ret))
goto done;
/* any other error that is not-linked can be returned right /* any other error that is not-linked can be returned right
* away */ * away */
@ -2879,11 +2957,9 @@ gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
} }
GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset); GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset);
offset = gst_ogg_demux_get_prev_page (ogg, &page); ret = gst_ogg_demux_get_prev_page (ogg, &page, &offset);
if (offset < 0) { if (ret != GST_FLOW_OK)
ret = GST_FLOW_ERROR;
goto done; goto done;
}
ogg->offset = offset; ogg->offset = offset;
@ -2923,15 +2999,13 @@ gst_ogg_demux_loop (GstOggPad * pad)
ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad)); ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
if (ogg->need_chains) { if (ogg->need_chains) {
gboolean got_chains;
gboolean res; gboolean res;
/* this is the only place where we write chains and thus need to lock. */ /* this is the only place where we write chains and thus need to lock. */
GST_CHAIN_LOCK (ogg); GST_CHAIN_LOCK (ogg);
ogg->chain_error = GST_FLOW_OK; ret = gst_ogg_demux_find_chains (ogg);
got_chains = gst_ogg_demux_find_chains (ogg);
GST_CHAIN_UNLOCK (ogg); GST_CHAIN_UNLOCK (ogg);
if (!got_chains) if (ret != GST_FLOW_OK)
goto chain_read_failed; goto chain_read_failed;
ogg->need_chains = FALSE; ogg->need_chains = FALSE;
@ -2964,8 +3038,7 @@ gst_ogg_demux_loop (GstOggPad * pad)
/* ERRORS */ /* ERRORS */
chain_read_failed: chain_read_failed:
{ {
GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("could not read chains")); /* error was posted */
ret = ogg->chain_error;
goto pause; goto pause;
} }
seek_failed: seek_failed:

View File

@ -148,7 +148,6 @@ struct _GstOggDemux
GMutex *chain_lock; /* we need the lock to protect the chains */ GMutex *chain_lock; /* we need the lock to protect the chains */
GArray *chains; /* list of chains we know */ GArray *chains; /* list of chains we know */
GstClockTime total_time; GstClockTime total_time;
GstFlowReturn chain_error; /* error we received while finding chains */
GstOggChain *current_chain; GstOggChain *current_chain;
GstOggChain *building_chain; GstOggChain *building_chain;