mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-06-06 07:28:53 +00:00
ext/ogg/gstoggdemux.c: Implement first stab at reverse playback.
Original commit message from CVS: * ext/ogg/gstoggdemux.c: (gst_ogg_pad_submit_page), (gst_ogg_demux_get_prev_page), (gst_ogg_demux_perform_seek), (gst_ogg_demux_handle_page), (gst_ogg_demux_chain), (gst_ogg_demux_loop_forward), (gst_ogg_demux_loop_reverse), (gst_ogg_demux_loop): Implement first stab at reverse playback.
This commit is contained in:
parent
86de56e49d
commit
099f2f0ef7
2 changed files with 214 additions and 136 deletions
|
@ -1,3 +1,12 @@
|
||||||
|
2006-11-09 Christian F.K. Schaller <set EMAIL_ADDRESS environment variable>
|
||||||
|
|
||||||
|
* ext/ogg/gstoggdemux.c: (gst_ogg_pad_submit_page),
|
||||||
|
(gst_ogg_demux_get_prev_page), (gst_ogg_demux_perform_seek),
|
||||||
|
(gst_ogg_demux_handle_page), (gst_ogg_demux_chain),
|
||||||
|
(gst_ogg_demux_loop_forward), (gst_ogg_demux_loop_reverse),
|
||||||
|
(gst_ogg_demux_loop):
|
||||||
|
Implement first stab at reverse playback.
|
||||||
|
|
||||||
2006-11-07 Stefan Kost <ensonic@users.sf.net>
|
2006-11-07 Stefan Kost <ensonic@users.sf.net>
|
||||||
|
|
||||||
* gst-libs/gst/riff/riff-media.c: (gst_riff_create_video_caps),
|
* gst-libs/gst/riff/riff-media.c: (gst_riff_create_video_caps),
|
||||||
|
|
|
@ -209,6 +209,7 @@ static gboolean gst_ogg_demux_collect_chain_info (GstOggDemux * ogg,
|
||||||
GstOggChain * chain);
|
GstOggChain * chain);
|
||||||
static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg,
|
static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg,
|
||||||
GstOggChain * chain, GstEvent * event);
|
GstOggChain * chain, GstEvent * event);
|
||||||
|
static void gst_ogg_chain_mark_discont (GstOggChain * chain);
|
||||||
|
|
||||||
static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg,
|
static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg,
|
||||||
GstEvent * event);
|
GstEvent * event);
|
||||||
|
@ -1200,8 +1201,7 @@ gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page)
|
||||||
done = TRUE;
|
done = TRUE;
|
||||||
break;
|
break;
|
||||||
case -1:
|
case -1:
|
||||||
/* out of sync, could call gst_ogg_pad_reset() here but ogg can decode
|
gst_ogg_chain_mark_discont (pad->chain);
|
||||||
* the packet just fine. We should probably send a DISCONT though. */
|
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
result = gst_ogg_pad_submit_packet (pad, &packet);
|
result = gst_ogg_pad_submit_packet (pad, &packet);
|
||||||
|
@ -1636,7 +1636,7 @@ gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og, gint64 boundary)
|
||||||
|
|
||||||
/* 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 gint
|
static gint64
|
||||||
gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og)
|
gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og)
|
||||||
{
|
{
|
||||||
gint64 begin = ogg->offset;
|
gint64 begin = ogg->offset;
|
||||||
|
@ -1668,7 +1668,7 @@ gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og)
|
||||||
|
|
||||||
/* 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, offset);
|
||||||
ret = gst_ogg_demux_get_next_page (ogg, og, CHUNKSIZE);
|
ret = gst_ogg_demux_get_next_page (ogg, og, -1);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
/* this shouldn't be possible */
|
/* this shouldn't be possible */
|
||||||
return OV_EFAULT;
|
return OV_EFAULT;
|
||||||
|
@ -1989,11 +1989,6 @@ gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
|
||||||
GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
|
GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
/* cannot yet do backwards playback */
|
|
||||||
if (rate <= 0.0) {
|
|
||||||
GST_DEBUG_OBJECT (ogg, "can only seek with positive rate, not %lf", rate);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
GST_DEBUG_OBJECT (ogg, "seek without event");
|
GST_DEBUG_OBJECT (ogg, "seek without event");
|
||||||
|
|
||||||
|
@ -2092,6 +2087,7 @@ gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
|
||||||
GstEvent *event;
|
GstEvent *event;
|
||||||
gint64 stop;
|
gint64 stop;
|
||||||
gint64 start;
|
gint64 start;
|
||||||
|
gint64 last_stop;
|
||||||
|
|
||||||
/* we have to send the flush to the old chain, not the new one */
|
/* we have to send the flush to the old chain, not the new one */
|
||||||
if (flush)
|
if (flush)
|
||||||
|
@ -2099,17 +2095,24 @@ gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
|
||||||
|
|
||||||
if ((stop = ogg->segment.stop) == -1)
|
if ((stop = ogg->segment.stop) == -1)
|
||||||
stop = ogg->segment.duration;
|
stop = ogg->segment.duration;
|
||||||
|
|
||||||
if (stop != -1)
|
if (stop != -1)
|
||||||
stop += chain->segment_start;
|
stop += chain->segment_start;
|
||||||
|
|
||||||
start = ogg->segment.last_stop;
|
start = ogg->segment.start;
|
||||||
if (chain->segment_start != GST_CLOCK_TIME_NONE)
|
if (chain->segment_start != GST_CLOCK_TIME_NONE)
|
||||||
start += chain->segment_start;
|
start += chain->segment_start;
|
||||||
|
|
||||||
|
last_stop = ogg->segment.last_stop;
|
||||||
|
if (chain->segment_start != GST_CLOCK_TIME_NONE)
|
||||||
|
last_stop += chain->segment_start;
|
||||||
|
|
||||||
/* create the segment event we are going to send out */
|
/* create the segment event we are going to send out */
|
||||||
|
if (ogg->segment.rate >= 0.0)
|
||||||
event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
|
event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
|
||||||
ogg->segment.format, start, stop, ogg->segment.time);
|
ogg->segment.format, last_stop, stop, ogg->segment.time);
|
||||||
|
else
|
||||||
|
event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
|
||||||
|
ogg->segment.format, start, last_stop, ogg->segment.time);
|
||||||
|
|
||||||
if (chain != ogg->current_chain) {
|
if (chain != ogg->current_chain) {
|
||||||
/* switch to different chain, send segment on new chain */
|
/* switch to different chain, send segment on new chain */
|
||||||
|
@ -2602,44 +2605,23 @@ no_first_chain:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* streaming mode, receive a buffer, parse it, create pads for
|
|
||||||
* the serialno, submit pages and packets to the oggpads
|
|
||||||
*/
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
|
gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page)
|
||||||
{
|
{
|
||||||
GstOggDemux *ogg;
|
|
||||||
gint ret = -1;
|
|
||||||
GstFlowReturn result = GST_FLOW_OK;
|
|
||||||
guint serialno;
|
|
||||||
|
|
||||||
ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (ogg, "chain");
|
|
||||||
gst_ogg_demux_submit_buffer (ogg, buffer);
|
|
||||||
|
|
||||||
while (ret != 0 && result == GST_FLOW_OK) {
|
|
||||||
ogg_page page;
|
|
||||||
|
|
||||||
ret = ogg_sync_pageout (&ogg->sync, &page);
|
|
||||||
if (ret == 0)
|
|
||||||
/* need more data */
|
|
||||||
break;
|
|
||||||
if (ret == -1) {
|
|
||||||
/* discontinuity in the pages */
|
|
||||||
} else {
|
|
||||||
GstOggPad *pad;
|
GstOggPad *pad;
|
||||||
gint64 granule;
|
gint64 granule;
|
||||||
|
guint serialno;
|
||||||
|
GstFlowReturn result = GST_FLOW_OK;
|
||||||
|
|
||||||
serialno = ogg_page_serialno (&page);
|
serialno = ogg_page_serialno (page);
|
||||||
granule = ogg_page_granulepos (&page);
|
granule = ogg_page_granulepos (page);
|
||||||
|
|
||||||
GST_LOG_OBJECT (ogg,
|
GST_LOG_OBJECT (ogg,
|
||||||
"processing ogg page (serial %08x, pageno %ld, granulepos %"
|
"processing ogg page (serial %08x, pageno %ld, granulepos %"
|
||||||
G_GINT64_FORMAT ", bos %d)",
|
G_GINT64_FORMAT ", bos %d)",
|
||||||
serialno, ogg_page_pageno (&page), granule, ogg_page_bos (&page));
|
serialno, ogg_page_pageno (page), granule, ogg_page_bos (page));
|
||||||
|
|
||||||
if (ogg_page_bos (&page)) {
|
if (ogg_page_bos (page)) {
|
||||||
GstOggChain *chain;
|
GstOggChain *chain;
|
||||||
|
|
||||||
/* first page */
|
/* first page */
|
||||||
|
@ -2694,17 +2676,15 @@ gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
|
||||||
pad = gst_ogg_demux_find_pad (ogg, serialno);
|
pad = gst_ogg_demux_find_pad (ogg, serialno);
|
||||||
}
|
}
|
||||||
if (pad) {
|
if (pad) {
|
||||||
result = gst_ogg_pad_submit_page (pad, &page);
|
result = gst_ogg_pad_submit_page (pad, page);
|
||||||
} else {
|
} else {
|
||||||
/* no pad, this is pretty fatal. This means an ogg page without bos
|
/* no pad, this is pretty fatal. This means an ogg page without bos
|
||||||
* has been seen for this serialno. could just ignore it too... */
|
* has been seen for this serialno. could just ignore it too... */
|
||||||
goto unknown_pad;
|
goto unknown_pad;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
unknown_chain:
|
unknown_chain:
|
||||||
{
|
{
|
||||||
GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
|
GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
|
||||||
|
@ -2721,6 +2701,37 @@ unknown_pad:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* streaming mode, receive a buffer, parse it, create pads for
|
||||||
|
* the serialno, submit pages and packets to the oggpads
|
||||||
|
*/
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
|
||||||
|
{
|
||||||
|
GstOggDemux *ogg;
|
||||||
|
gint ret = -1;
|
||||||
|
GstFlowReturn result = GST_FLOW_OK;
|
||||||
|
|
||||||
|
ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (ogg, "chain");
|
||||||
|
gst_ogg_demux_submit_buffer (ogg, buffer);
|
||||||
|
|
||||||
|
while (ret != 0 && result == GST_FLOW_OK) {
|
||||||
|
ogg_page page;
|
||||||
|
|
||||||
|
ret = ogg_sync_pageout (&ogg->sync, &page);
|
||||||
|
if (ret == 0)
|
||||||
|
/* need more data */
|
||||||
|
break;
|
||||||
|
if (ret == -1) {
|
||||||
|
/* discontinuity in the pages */
|
||||||
|
} else {
|
||||||
|
gst_ogg_demux_handle_page (ogg, &page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
|
gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
|
||||||
{
|
{
|
||||||
|
@ -2778,6 +2789,93 @@ done:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_ogg_demux_loop_forward (GstOggDemux * ogg)
|
||||||
|
{
|
||||||
|
GstFlowReturn ret;
|
||||||
|
GstBuffer *buffer;
|
||||||
|
|
||||||
|
if (ogg->offset == ogg->length) {
|
||||||
|
GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
|
||||||
|
" == %" G_GINT64_FORMAT, ogg->offset, ogg->length);
|
||||||
|
ret = GST_FLOW_UNEXPECTED;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
|
||||||
|
ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer);
|
||||||
|
if (ret != GST_FLOW_OK) {
|
||||||
|
GST_LOG_OBJECT (ogg, "Failed pull_range");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
ogg->offset += GST_BUFFER_SIZE (buffer);
|
||||||
|
|
||||||
|
if (G_UNLIKELY (ogg->newsegment)) {
|
||||||
|
gst_ogg_demux_send_event (ogg, ogg->newsegment);
|
||||||
|
ogg->newsegment = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gst_ogg_demux_chain (ogg->sinkpad, buffer);
|
||||||
|
if (ret != GST_FLOW_OK) {
|
||||||
|
GST_LOG_OBJECT (ogg, "Failed demux_chain");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for the end of the segment */
|
||||||
|
if (ogg->segment.stop != -1 && ogg->segment.last_stop != -1) {
|
||||||
|
if (ogg->segment.last_stop > ogg->segment.stop) {
|
||||||
|
ret = GST_FLOW_UNEXPECTED;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
|
||||||
|
{
|
||||||
|
GstFlowReturn ret;
|
||||||
|
ogg_page page;
|
||||||
|
gint64 offset;
|
||||||
|
|
||||||
|
if (ogg->offset == 0) {
|
||||||
|
GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
|
||||||
|
" == 0", ogg->offset);
|
||||||
|
ret = GST_FLOW_UNEXPECTED;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset);
|
||||||
|
offset = gst_ogg_demux_get_prev_page (ogg, &page);
|
||||||
|
if (offset < 0) {
|
||||||
|
ret = GST_FLOW_ERROR;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
ogg->offset = offset;
|
||||||
|
|
||||||
|
if (G_UNLIKELY (ogg->newsegment)) {
|
||||||
|
gst_ogg_demux_send_event (ogg, ogg->newsegment);
|
||||||
|
ogg->newsegment = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gst_ogg_demux_handle_page (ogg, &page);
|
||||||
|
if (ret != GST_FLOW_OK)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
/* check for the end of the segment */
|
||||||
|
if (ogg->segment.start != -1 && ogg->segment.last_stop != -1) {
|
||||||
|
if (ogg->segment.last_stop <= ogg->segment.start) {
|
||||||
|
ret = GST_FLOW_UNEXPECTED;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* random access code
|
/* random access code
|
||||||
*
|
*
|
||||||
* - first find all the chains and streams by scanning the file.
|
* - first find all the chains and streams by scanning the file.
|
||||||
|
@ -2789,7 +2887,6 @@ gst_ogg_demux_loop (GstOggPad * pad)
|
||||||
{
|
{
|
||||||
GstOggDemux *ogg;
|
GstOggDemux *ogg;
|
||||||
GstFlowReturn ret;
|
GstFlowReturn ret;
|
||||||
GstBuffer *buffer;
|
|
||||||
GstEvent *event;
|
GstEvent *event;
|
||||||
|
|
||||||
ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
|
ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
|
||||||
|
@ -2823,36 +2920,13 @@ gst_ogg_demux_loop (GstOggPad * pad)
|
||||||
goto seek_failed;
|
goto seek_failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ogg->offset == ogg->length) {
|
if (ogg->segment.rate >= 0.0)
|
||||||
GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
|
ret = gst_ogg_demux_loop_forward (ogg);
|
||||||
" == %" G_GINT64_FORMAT, ogg->offset, ogg->length);
|
else
|
||||||
goto eos;
|
ret = gst_ogg_demux_loop_reverse (ogg);
|
||||||
}
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
|
if (ret != GST_FLOW_OK)
|
||||||
ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer);
|
|
||||||
if (ret != GST_FLOW_OK) {
|
|
||||||
GST_LOG_OBJECT (ogg, "Failed pull_range");
|
|
||||||
goto pause;
|
goto pause;
|
||||||
}
|
|
||||||
|
|
||||||
ogg->offset += GST_BUFFER_SIZE (buffer);
|
|
||||||
|
|
||||||
if (G_UNLIKELY (ogg->newsegment)) {
|
|
||||||
gst_ogg_demux_send_event (ogg, ogg->newsegment);
|
|
||||||
ogg->newsegment = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = gst_ogg_demux_chain (ogg->sinkpad, buffer);
|
|
||||||
if (ret != GST_FLOW_OK) {
|
|
||||||
GST_LOG_OBJECT (ogg, "Failed demux_chain");
|
|
||||||
goto pause;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check for the end of the segment */
|
|
||||||
if (ogg->segment.stop != -1 && ogg->segment.last_stop != -1)
|
|
||||||
if (ogg->segment.last_stop > ogg->segment.stop)
|
|
||||||
goto eos;
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -2870,11 +2944,6 @@ seek_failed:
|
||||||
ret = GST_FLOW_ERROR;
|
ret = GST_FLOW_ERROR;
|
||||||
goto pause;
|
goto pause;
|
||||||
}
|
}
|
||||||
eos:
|
|
||||||
{
|
|
||||||
ret = GST_FLOW_UNEXPECTED;
|
|
||||||
goto pause;
|
|
||||||
}
|
|
||||||
pause:
|
pause:
|
||||||
{
|
{
|
||||||
const gchar *reason = gst_flow_get_name (ret);
|
const gchar *reason = gst_flow_get_name (ret);
|
||||||
|
|
Loading…
Reference in a new issue