oggdec: use ffio_ensure_seekback() to seek back on incorrect data
authorLynne <dev@lynne.ee>
Tue, 28 Apr 2020 11:55:17 +0000 (12:55 +0100)
committerLynne <dev@lynne.ee>
Thu, 30 Apr 2020 22:05:50 +0000 (23:05 +0100)
This cleans up the code and simplifies it.
It also speeds up parsing since the old pb position was incorrect.

libavformat/oggdec.c

index e0188c7..92dcafe 100644 (file)
@@ -206,59 +206,40 @@ static const struct ogg_codec *ogg_find_codec(uint8_t *buf, int size)
  * situation where a new audio stream spawn (identified with a new serial) and
  * must replace the previous one (track switch).
  */
-static int ogg_replace_stream(AVFormatContext *s, uint32_t serial, int size)
+static int ogg_replace_stream(AVFormatContext *s, uint32_t serial, char *magic)
 {
     struct ogg *ogg = s->priv_data;
     struct ogg_stream *os;
     const struct ogg_codec *codec;
     int i = 0;
 
-    if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) {
-        uint8_t magic[8];
-        avio_seek(s->pb, -size, SEEK_CUR);
-        if (avio_read(s->pb, magic, sizeof(magic)) != sizeof(magic))
-            return AVERROR_INVALIDDATA;
-        avio_seek(s->pb, size - sizeof(magic), SEEK_CUR);
-        codec = ogg_find_codec(magic, sizeof(magic));
-        if (!codec) {
-            av_log(s, AV_LOG_ERROR, "Cannot identify new stream\n");
-            return AVERROR_INVALIDDATA;
-        }
-        for (i = 0; i < ogg->nstreams; i++) {
-            if (ogg->streams[i].codec == codec)
-                break;
-        }
-        if (i >= ogg->nstreams)
-            return ogg_new_stream(s, serial);
-    } else if (ogg->nstreams != 1) {
+    if (ogg->nstreams != 1) {
         avpriv_report_missing_feature(s, "Changing stream parameters in multistream ogg");
         return AVERROR_PATCHWELCOME;
     }
 
-    os = &ogg->streams[i];
-
-    os->serial  = serial;
-    return i;
+    /* Check for codecs */
+    codec = ogg_find_codec(magic, 8);
+    if (!codec) {
+        av_log(s, AV_LOG_ERROR, "Cannot identify new stream\n");
+        return AVERROR_INVALIDDATA;
+    }
 
-#if 0
-    buf     = os->buf;
-    bufsize = os->bufsize;
-    codec   = os->codec;
+    /* If the codec matches, then we assume its a replacement */
+    for (i = 0; i < ogg->nstreams; i++) {
+        if (ogg->streams[i].codec == codec)
+            break;
+    }
 
-    if (!ogg->state || ogg->state->streams[i].private != os->private)
-        av_freep(&ogg->streams[i].private);
+    /* Otherwise, create a new stream */
+    if (i >= ogg->nstreams)
+        return ogg_new_stream(s, serial);
 
-    /* Set Ogg stream settings similar to what is done in ogg_new_stream(). We
-     * also re-use the ogg_stream allocated buffer */
-    memset(os, 0, sizeof(*os));
-    os->serial  = serial;
-    os->bufsize = bufsize;
-    os->buf     = buf;
-    os->header  = -1;
-    os->codec   = codec;
+    os = &ogg->streams[i];
+    os->serial = serial;
+    os->codec  = codec;
 
     return i;
-#endif
 }
 
 static int ogg_new_stream(AVFormatContext *s, uint32_t serial)
@@ -325,6 +306,7 @@ static int ogg_read_page(AVFormatContext *s, int *sid)
     uint32_t crc, crc_tmp;
     int size = 0, idx;
     int64_t version, page_pos;
+    int64_t start_pos;
     uint8_t sync[4];
     uint8_t segments[255];
     uint8_t *readout_buf;
@@ -364,6 +346,10 @@ static int ogg_read_page(AVFormatContext *s, int *sid)
     /* 0x4fa9b05f = av_crc(AV_CRC_32_IEEE, 0x0, "OggS", 4) */
     ffio_init_checksum(bc, ff_crc04C11DB7_update, 0x4fa9b05f);
 
+    /* To rewind if checksum is bad/check magic on switches - this is the max packet size */
+    ffio_ensure_seekback(bc, MAX_PAGE_SIZE);
+    start_pos = avio_tell(bc);
+
     version = avio_r8(bc);
     flags   = avio_r8(bc);
     gp      = avio_rl64(bc);
@@ -414,7 +400,7 @@ static int ogg_read_page(AVFormatContext *s, int *sid)
         av_log(s, AV_LOG_ERROR, "CRC mismatch!\n");
         if (idx < 0)
             av_free(readout_buf);
-        avio_seek(bc, -size, SEEK_CUR);
+        avio_seek(bc, start_pos, SEEK_SET);
         return 0;
     }
 
@@ -424,14 +410,14 @@ static int ogg_read_page(AVFormatContext *s, int *sid)
         av_log(s, AV_LOG_ERROR, "Invalid Ogg vers!\n");
         if (idx < 0)
             av_free(readout_buf);
-        avio_seek(bc, -size, SEEK_CUR);
+        avio_seek(bc, start_pos, SEEK_SET);
         return 0;
     }
 
     /* CRC is correct so we can be 99% sure there's an actual change here */
     if (idx < 0) {
         if (data_packets_seen(ogg))
-            idx = ogg_replace_stream(s, serial, size);
+            idx = ogg_replace_stream(s, serial, readout_buf);
         else
             idx = ogg_new_stream(s, serial);