[FFmpeg-devel] [PATCH] aadec: alternate mp3 seek handling

Karsten Otto ottoka at posteo.de
Tue Jul 31 22:16:32 EEST 2018


After seeking, determine the offset of the next frame in the decrypted
buffer by scanning the first few bytes for a valid mp3 header.
This significantly improves the listening experience for audio content
with untypical encoding.
---
This is a refinement of an earlier patch iteration, according to lessons
learned and discussions on the mailing list: Scan a limited range to find
the first shifted frame only, check for a very specific bit pattern, and
add extra checks and guards for better code maintainability.

Unfortunately, this variant violates the architectural layering between
demuxer and codec. But I did some more testing with untypical encodings,
where the current estimation mechanism fails, and the resulting audio on
seek was just too horribly annoying.

I believe the better listening experience outweighs the architectural
uglyness, so this should be in the official code base. But if you think
otherwise, just let me know, and I will keep this a private patch.

 libavformat/aadec.c | 45 ++++++++++++++++++++++++++++-----------------
 1 file changed, 28 insertions(+), 17 deletions(-)

diff --git a/libavformat/aadec.c b/libavformat/aadec.c
index d83f283ffe..9b1495c218 100644
--- a/libavformat/aadec.c
+++ b/libavformat/aadec.c
@@ -37,7 +37,7 @@
 #define TEA_BLOCK_SIZE 8
 #define CHAPTER_HEADER_SIZE 8
 #define TIMEPREC 1000
-#define MP3_FRAME_SIZE 104
+#define MP3_FRAME_SIZE 105
 
 typedef struct AADemuxContext {
     AVClass *class;
@@ -51,7 +51,7 @@ typedef struct AADemuxContext {
     int64_t current_chapter_size;
     int64_t content_start;
     int64_t content_end;
-    int seek_offset;
+    int did_seek;
 } AADemuxContext;
 
 static int get_second_size(char *codec_name)
@@ -179,7 +179,7 @@ static int aa_read_header(AVFormatContext *s)
         st->codecpar->sample_rate = 22050;
         st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
         avpriv_set_pts_info(st, 64, 8, 32000 * TIMEPREC);
-        // encoded audio frame is MP3_FRAME_SIZE bytes (+1 with padding, unlikely)
+        // encoded audio frame is MP3_FRAME_SIZE bytes (-1 without padding)
     } else if (!strcmp(codec_name, "acelp85")) {
         st->codecpar->codec_id = AV_CODEC_ID_SIPR;
         st->codecpar->block_align = 19;
@@ -231,7 +231,7 @@ static int aa_read_header(AVFormatContext *s)
     ff_update_cur_dts(s, st, 0);
     avio_seek(pb, start, SEEK_SET);
     c->current_chapter_size = 0;
-    c->seek_offset = 0;
+    c->did_seek = 0;
 
     return 0;
 }
@@ -244,7 +244,7 @@ static int aa_read_packet(AVFormatContext *s, AVPacket *pkt)
     int trailing_bytes;
     int blocks;
     uint8_t buf[MAX_CODEC_SECOND_SIZE * 2];
-    int written = 0;
+    int written = 0, offset = 0;
     int ret;
     AADemuxContext *c = s->priv_data;
     uint64_t pos = avio_tell(s->pb);
@@ -297,16 +297,33 @@ static int aa_read_packet(AVFormatContext *s, AVPacket *pkt)
     if (c->current_chapter_size <= 0)
         c->current_chapter_size = 0;
 
-    if (c->seek_offset > written)
-        c->seek_offset = 0; // ignore wrong estimate
+    if (c->did_seek) {
+        c->did_seek = 0;
+
+        if (s->streams[0]->codecpar->codec_id == AV_CODEC_ID_MP3 && written >= MP3_FRAME_SIZE + 3) {
+            for (offset = 0; offset < MP3_FRAME_SIZE; ++offset) {
+                // find mp3 header: sync, v2, layer3, no crc, 32kbps, 22kHz, mono
+                if ((buf[offset + 0]       ) == 0xff &&
+                    (buf[offset + 1]       ) == 0xf3 &&
+                    (buf[offset + 2] & 0xfc) == 0x40 &&
+                    (buf[offset + 3] & 0xf0) == 0xc0)
+                        break;
+            }
+            if (offset == MP3_FRAME_SIZE) offset = 0; // not found, just use as is
+        }
+
+        ff_update_cur_dts(s, s->streams[0],
+            (pos + offset - c->content_start - CHAPTER_HEADER_SIZE * (c->chapter_idx - 1))
+            * TIMEPREC);
+    }
 
-    ret = av_new_packet(pkt, written - c->seek_offset);
+    if (offset > written) offset = 0;
+    ret = av_new_packet(pkt, written - offset);
     if (ret < 0)
         return ret;
-    memcpy(pkt->data, buf + c->seek_offset, written - c->seek_offset);
+    memcpy(pkt->data, buf + offset, written - offset);
     pkt->pos = pos;
 
-    c->seek_offset = 0;
     return 0;
 }
 
@@ -349,13 +366,7 @@ static int aa_read_seek(AVFormatContext *s,
     c->current_codec_second_size = c->codec_second_size;
     c->current_chapter_size = chapter_size - chapter_pos;
     c->chapter_idx = 1 + chapter_idx;
-
-    // for unaligned frames, estimate offset of first frame in block (assume no padding)
-    if (s->streams[0]->codecpar->codec_id == AV_CODEC_ID_MP3) {
-        c->seek_offset = (MP3_FRAME_SIZE - chapter_pos % MP3_FRAME_SIZE) % MP3_FRAME_SIZE;
-    }
-
-    ff_update_cur_dts(s, s->streams[0], ch->start + (chapter_pos + c->seek_offset) * TIMEPREC);
+    c->did_seek = 1;
 
     return 1;
 }
-- 
2.14.3 (Apple Git-98)



More information about the ffmpeg-devel mailing list