[FFmpeg-devel,v2] avformat/aacdec: don't immediately abort if an ADTS frame is not found

Submitted by James Almer on Sept. 6, 2017, 4:48 a.m.

Details

Message ID 20170906044805.8284-1-jamrial@gmail.com
State New
Headers show

Commit Message

James Almer Sept. 6, 2017, 4:48 a.m.
Instead, skip the invalid data in an attempt to find an ADTS frame, and
continue decoding from there.

Fixes ticket #6634

Signed-off-by: James Almer <jamrial@gmail.com>
---
New in v2:

- avpriv_aac_parse_header() used to validate ADTS headers more thoroughly.
- Removed the usage of avio_tell(s->pb) in resync() and replaced it with a
  local counter.

 libavformat/aacdec.c | 77 +++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 58 insertions(+), 19 deletions(-)

Patch hide | download patch | download mbox

diff --git a/libavformat/aacdec.c b/libavformat/aacdec.c
index 364b33404f..e744a9f40d 100644
--- a/libavformat/aacdec.c
+++ b/libavformat/aacdec.c
@@ -21,6 +21,7 @@ 
  */
 
 #include "libavutil/intreadwrite.h"
+#include "libavcodec/aacadtsdec.h"
 #include "avformat.h"
 #include "internal.h"
 #include "id3v1.h"
@@ -77,10 +78,52 @@  static int adts_aac_probe(AVProbeData *p)
         return 0;
 }
 
+static int resync(AVFormatContext *s)
+{
+    AACADTSHeaderInfo hdr;
+    GetBitContext gbc;
+    uint8_t buf[AAC_ADTS_HEADER_SIZE];
+    uint16_t state;
+    int64_t cnt = 1;
+
+    state = avio_r8(s->pb);
+    while (!avio_feof(s->pb) && cnt++ < s->probesize) {
+        int fsize, ret;
+
+        state = ((state << 8) | avio_r8(s->pb));
+        if ((state >> 4) != 0xFFF)
+            continue;
+
+        avio_seek(s->pb, -2, SEEK_CUR);
+        ret = avio_read(s->pb, buf, sizeof(buf));
+        if (ret < 0)
+            return ret;
+        if (ret < AAC_ADTS_HEADER_SIZE)
+            return AVERROR(EIO);
+
+        init_get_bits8(&gbc, buf, sizeof(buf));
+        fsize = avpriv_aac_parse_header(&gbc, &hdr);
+        if (fsize < 0) {
+            // not a valid frame. Seek back to the "state" offset and continue.
+            avio_seek(s->pb, -5, SEEK_CUR);
+            continue;
+        }
+
+        // likely to be a valid frame. Seek back to the start and return.
+        avio_seek(s->pb, -ret, SEEK_CUR);
+        break;
+    }
+
+    if ((state >> 4) != 0xFFF)
+        return avio_feof(s->pb) ? AVERROR_EOF : AVERROR_INVALIDDATA;
+
+    return 0;
+}
+
 static int adts_aac_read_header(AVFormatContext *s)
 {
     AVStream *st;
-    uint16_t state;
+    int ret;
 
     st = avformat_new_stream(s, NULL);
     if (!st)
@@ -99,16 +142,9 @@  static int adts_aac_read_header(AVFormatContext *s)
     }
 
     // skip data until the first ADTS frame is found
-    state = avio_r8(s->pb);
-    while (!avio_feof(s->pb) && avio_tell(s->pb) < s->probesize) {
-        state = (state << 8) | avio_r8(s->pb);
-        if ((state >> 4) != 0xFFF)
-            continue;
-        avio_seek(s->pb, -2, SEEK_CUR);
-        break;
-    }
-    if ((state >> 4) != 0xFFF)
-        return AVERROR_INVALIDDATA;
+    ret = resync(s);
+    if (ret < 0)
+        return ret;
 
     // LCM of all possible ADTS sample rates
     avpriv_set_pts_info(st, 64, 1, 28224000);
@@ -118,6 +154,8 @@  static int adts_aac_read_header(AVFormatContext *s)
 
 static int adts_aac_read_packet(AVFormatContext *s, AVPacket *pkt)
 {
+    AACADTSHeaderInfo hdr;
+    GetBitContext gbc;
     int ret, fsize;
 
     ret = av_get_packet(s->pb, pkt, ADTS_HEADER_SIZE);
@@ -128,15 +166,16 @@  static int adts_aac_read_packet(AVFormatContext *s, AVPacket *pkt)
         return AVERROR(EIO);
     }
 
-    if ((AV_RB16(pkt->data) >> 4) != 0xfff) {
-        av_packet_unref(pkt);
-        return AVERROR_INVALIDDATA;
-    }
-
-    fsize = (AV_RB32(pkt->data + 3) >> 13) & 0x1FFF;
-    if (fsize < ADTS_HEADER_SIZE) {
+    init_get_bits8(&gbc, pkt->data, AAC_ADTS_HEADER_SIZE);
+    fsize = avpriv_aac_parse_header(&gbc, &hdr);
+    if (fsize < 0) {
+        // skip data in an attempt to find an ADTS frame.
         av_packet_unref(pkt);
-        return AVERROR_INVALIDDATA;
+        avio_seek(s->pb, -ret, SEEK_CUR);
+        ret = resync(s);
+        if (ret < 0)
+            return ret;
+        return adts_aac_read_packet(s, pkt);
     }
 
     return av_append_packet(s->pb, pkt, fsize - ADTS_HEADER_SIZE);