diff mbox series

[FFmpeg-devel,v2,3/3] libavformat/oggdec.c: keep reading on empty packets to accommodate for chained bitstreams.

Message ID 20230505132848.18011-3-toots@rastageeks.org
State New
Headers show
Series [FFmpeg-devel,v2,1/3] libavformat/oggparseflac.c: Decode metadata packets. | expand


Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Romain Beauxis May 5, 2023, 1:28 p.m. UTC
From: Romain Beauxis <toots@rastageeks.org>

This patch is the last of a series of patch to enhance FFmpeg's handling of
chained ogg streams.

Documentation for chained (and multiplexed) ogg bitstream can be found here:

My understanding of the current code is that the ogg demuxer will process all
pages from the current ogg track and, after reaching the last page, if another
page follows, will try to match the new stream with the previous one using
its codec. (See ogg_replace_stream in libavformat/oggdec.c).

Meanwhile, the underlying resources for the old stream are destroyed and new
resources are created. There seems to be issues with flushing remaining data
in such cases (see oggvorbis_decode_frame in libavcodec/libvorbisdec.c).

However, on the ogg bitstream side, sending an empty packet as the last packet
of the stream is common practice in ogg/opus streams.

For this reason, the previous patch in this series allowed opus_packet
in libavformat/oggparseopus.c to process empty EOS packets without failing
the stream for having invalid data.

However, if this packet is allowed to go through ogg_read_packet in
libavformat/oggdec.c, then the returned size from the function is 0, which
results in a EINVAL error while reading the stream.

Thus, this patch makes it so that empty packets are ignored. The bitstream
is read further and, if it ends, a proper EOF is returned. Otherwise, a new
chained bitream can begin and the demuxer can try to match it.

Without this patch, typically, decoding a chained ogg/opus stream with a final
empty ogg packet (very common) raises an error on each end of track.

To reproduce or test:
- Try this command: ffmpeg -re -i http://content.radiosega.net:8006/rs-opus-low.ogg \
                           -loglevel verbose -f null -

This a typical log on track change before this series of patch is applied:

<---- LOG BEGIN HERE ---->
[ogg @ 0x13b004080] Packet processing failed: Invalid data found when processing input
Error demuxing input file 0: Invalid data found when processing input
Terminating demuxer thread 0
http://content.radiosega.net:8006/rs-opus-low.ogg: Invalid data found when processing input
No more output streams to write to, finishing.
[out#0/null @ 0x600000b5c180] All streams finished
[out#0/null @ 0x600000b5c180] Terminating muxer thread
size=N/A time=00:00:43.12 bitrate=N/A speed=   1x
video:0kB audio:8089kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
Input file #0 (http://content.radiosega.net:8006/rs-opus-low.ogg):
  Input stream #0:0 (audio): 2161 packets read (229129 bytes); 2157 frames decoded (2070720 samples);
  Total: 2161 packets (229129 bytes) demuxed
Output file #0 (pipe:):
  Output stream #0:0 (audio): 2157 frames encoded (2070720 samples); 2157 packets muxed (8282880 bytes);
  Total: 2157 packets (8282880 bytes) muxed
[AVIOContext @ 0x13b104080] Statistics: 273900 bytes read, 0 seeks
<---- LOG END HERE ---->

(stream processing stops afterward)

This means that, currently, the ffmpeg command line is not able to process
chained ogg/opus that typically end with an empty packet beyong its first

After the patch series is applied: processing continues sucessfully.

 libavformat/oggdec.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff mbox series


diff --git a/libavformat/oggdec.c b/libavformat/oggdec.c
index 3b19e0bd89..b0d850a406 100644
--- a/libavformat/oggdec.c
+++ b/libavformat/oggdec.c
@@ -835,7 +835,7 @@  retry:
         ret = ogg_packet(s, &idx, &pstart, &psize, &fpos);
         if (ret < 0)
             return ret;
-    } while (idx < 0 || !s->streams[idx]);
+    } while (idx < 0 || !s->streams[idx] || psize == 0);
     ogg = s->priv_data;
     os  = ogg->streams + idx;