mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2026-11-06 05:17:42 +00:00
tests/fate: cover chapter subframe alignment and non-synchsafe v2.4 CHAP
Extend fate-id3v2chap to verify that:
- unread trailing bytes after a NUL-terminated text subframe (TPE1) are
skipped so both TPE1 ("artist") and the following embedded subframe
(TIT2) are parsed;
- a non-conforming v2.4 CHAP frame with a plain 32-bit size whose high
bit is set (0x000000b6) is not truncated even when the stripped offset
contains a fitting empty frame header;
- a valid synchsafe v2.4 CHAP frame followed by an empty TPE1 frame and
a TALB frame preserves the subsequent frames;
- an ambiguous plain 256-byte v2.4 CHAP size (0x00000100, all high bits
clear) rejects the 128-byte synchsafe candidate whose fake header
exceeds the remaining tag length.
Signed-off-by: Jun Zhao <barryjzhao@tencent.com>
This commit is contained in:
1 parent
260d32d06d
commit
f5f31760f8
2 files changed
+124
-14
No files matched your search
@@ -34,6 +34,7 @@ static int readback(const uint8_t *buf, int size)
|
||||
FFIOContext read_pb;
|
||||
ID3v2ExtraMeta *extra = NULL;
|
||||
AVDictionary *meta = NULL;
|
||||
const AVDictionaryEntry *album;
|
||||
int ret = 1;
|
||||
|
||||
if (!in)
|
||||
@@ -44,14 +45,22 @@ static int readback(const uint8_t *buf, int size)
|
||||
if (ff_id3v2_parse_chapters(in, extra) < 0)
|
||||
goto done;
|
||||
|
||||
printf("chapters=%u\n", in->nb_chapters);
|
||||
printf("chapters=%u", in->nb_chapters);
|
||||
album = av_dict_get(meta, "album", NULL, 0);
|
||||
if (album)
|
||||
printf(" album=%s", album->value);
|
||||
printf("\n");
|
||||
for (unsigned i = 0; i < in->nb_chapters; i++) {
|
||||
const AVChapter *ch = in->chapters[i];
|
||||
const AVDictionaryEntry *title = av_dict_get(ch->metadata, "title", NULL, 0);
|
||||
const AVDictionaryEntry *title = av_dict_get(ch->metadata, "title", NULL, 0);
|
||||
const AVDictionaryEntry *artist = av_dict_get(ch->metadata, "artist", NULL, 0);
|
||||
|
||||
printf("chapter=%u time_base=%d/%d start=%"PRId64" end=%"PRId64" title=%s\n",
|
||||
printf("chapter=%u time_base=%d/%d start=%"PRId64" end=%"PRId64" title=%s",
|
||||
i, ch->time_base.num, ch->time_base.den, ch->start, ch->end,
|
||||
title ? title->value : "(missing)");
|
||||
if (artist)
|
||||
printf(" artist=%s", artist->value);
|
||||
printf("\n");
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
@@ -64,17 +73,18 @@ done:
|
||||
|
||||
/* The first chapter title is long enough that its TIT2 frame is at least
|
||||
* 128 bytes, so a plain size and a synchsafe size differ. */
|
||||
static int roundtrip(int version)
|
||||
static int roundtrip(int version, int ambiguous)
|
||||
{
|
||||
char long_title[151];
|
||||
char long_title[225];
|
||||
int title_len = ambiguous ? 224 : 150;
|
||||
AVFormatContext *out = NULL;
|
||||
AVIOContext *pb = NULL;
|
||||
ID3v2EncContext id3 = { 0 };
|
||||
uint8_t *buf = NULL;
|
||||
int size, chap_pos = -1, ret = 1;
|
||||
|
||||
memset(long_title, 'L', sizeof(long_title) - 1);
|
||||
long_title[sizeof(long_title) - 1] = 0;
|
||||
memset(long_title, 'L', title_len);
|
||||
long_title[title_len] = 0;
|
||||
|
||||
out = avformat_alloc_context();
|
||||
if (!out ||
|
||||
@@ -86,7 +96,7 @@ static int roundtrip(int version)
|
||||
ff_id3v2_start(&id3, pb, version, ID3v2_DEFAULT_MAGIC);
|
||||
if (ff_id3v2_write_metadata(out, pb, &id3) < 0)
|
||||
goto done;
|
||||
ff_id3v2_finish(&id3, pb, 10);
|
||||
ff_id3v2_finish(&id3, pb, ambiguous ? 300 : 10);
|
||||
size = avio_close_dyn_buf(pb, &buf);
|
||||
pb = NULL;
|
||||
if (size < ID3v2_HEADER_SIZE)
|
||||
@@ -104,12 +114,82 @@ static int roundtrip(int version)
|
||||
if (readback(buf, size))
|
||||
goto done;
|
||||
|
||||
/* Locate the first embedded TIT2 header inside the generated chapter.
|
||||
* Give it an oversized length, leaving the chapter and following CHAP
|
||||
* intact. The reader should retain both chapter timings. */
|
||||
/* A 224-byte title makes the CHAP payload 256 bytes. First test a valid
|
||||
* synchsafe 256-byte CHAP (00 00 02 00, raw 512) followed by an empty
|
||||
* TPE1 frame and a TALB frame inside the 300-byte padding area, so
|
||||
* check_tag() must accept nlen == 0 at the 256-byte boundary rather than
|
||||
* falling through to the raw 512-byte offset in zero padding. Then test
|
||||
* the non-conforming plain size 00 00 01 00, whose high bits are clear
|
||||
* so the synchsafe candidate at 128 bytes must be rejected by checking
|
||||
* the oversized LLLL frame header inside the title. */
|
||||
if (ambiguous) {
|
||||
if (chap_pos >= 0 && chap_pos + 10 + 256 + 45 + 27 <= size) {
|
||||
uint8_t ch1_copy[45];
|
||||
memcpy(ch1_copy, buf + chap_pos + 10 + 256, sizeof(ch1_copy));
|
||||
memcpy(buf + chap_pos + 10 + 256, "TPE1\x00\x00\x00\x00\x00\x00", 10);
|
||||
memcpy(buf + chap_pos + 10 + 266, "TALB\x00\x00\x00\x07\x00\x00\x03World", 17);
|
||||
memcpy(buf + chap_pos + 10 + 283, ch1_copy, sizeof(ch1_copy));
|
||||
printf("empty next frame version=4\n");
|
||||
if (readback(buf, size))
|
||||
goto done;
|
||||
memcpy(buf + chap_pos + 10 + 256, ch1_copy, sizeof(ch1_copy));
|
||||
memset(buf + chap_pos + 10 + 301, 0, 27);
|
||||
|
||||
buf[chap_pos + 6] = 0x01;
|
||||
buf[chap_pos + 7] = 0x00;
|
||||
printf("ambiguous plain CHAP version=4\n");
|
||||
if (readback(buf, size))
|
||||
goto done;
|
||||
}
|
||||
ret = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Non-conforming v2.4 CHAP with a plain 32-bit size (0x000000b6) whose
|
||||
* high bit is set and whose payload contains uppercase ASCII at the
|
||||
* stripped offset (0x36). Also zero the 4 size bytes after "LLLL" at
|
||||
* offset 0x36 once to verify that is_syncsafe() rejects the 0x36
|
||||
* candidate even when check_tag() would accept an empty frame header. */
|
||||
if (version == 4 && chap_pos >= 0) {
|
||||
uint8_t saved[4];
|
||||
buf[chap_pos + 6] = 0x00;
|
||||
buf[chap_pos + 7] = 0xb6;
|
||||
printf("non-synchsafe CHAP version=4\n");
|
||||
if (readback(buf, size))
|
||||
goto done;
|
||||
|
||||
memcpy(saved, buf + chap_pos + 10 + 0x36 + 4, sizeof(saved));
|
||||
memset(buf + chap_pos + 10 + 0x36 + 4, 0, sizeof(saved));
|
||||
printf("high-bit plain CHAP with fitting fake header version=4\n");
|
||||
if (readback(buf, size))
|
||||
goto done;
|
||||
memcpy(buf + chap_pos + 10 + 0x36 + 4, saved, sizeof(saved));
|
||||
buf[chap_pos + 6] = 0x01;
|
||||
buf[chap_pos + 7] = 0x36;
|
||||
}
|
||||
|
||||
/* Split the 162-byte TIT2 subframe into a 22-byte TPE1 subframe with
|
||||
* unread trailing bytes after its first NUL terminator, followed by a
|
||||
* 140-byte TIT2 subframe, then test an oversized subframe length. */
|
||||
if (chap_pos >= 0) {
|
||||
for (int i = chap_pos + 10; i + 10 <= size; i++) {
|
||||
for (int i = chap_pos + 10; i + 33 <= size; i++) {
|
||||
if (!memcmp(buf + i, "TIT2", 4)) {
|
||||
uint8_t enc = buf[i + 10];
|
||||
memcpy(buf + i, "TPE1", 4);
|
||||
buf[i + 6] = 0;
|
||||
buf[i + 7] = 12;
|
||||
buf[i + 13] = 0;
|
||||
memcpy(buf + i + 22, "TIT2\x00\x00", 6);
|
||||
buf[i + 28] = version == 4 ? 0x01 : 0x00;
|
||||
buf[i + 29] = version == 4 ? 0x02 : 0x82;
|
||||
buf[i + 30] = 0;
|
||||
buf[i + 31] = 0;
|
||||
buf[i + 32] = enc;
|
||||
printf("trailing text subframe version=%d\n", version);
|
||||
if (readback(buf, size))
|
||||
goto done;
|
||||
|
||||
memcpy(buf + i, "TIT2", 4);
|
||||
memset(buf + i + 4, 0x7f, 4);
|
||||
printf("invalid subframe version=%d\n", version);
|
||||
if (readback(buf, size))
|
||||
@@ -129,8 +209,9 @@ done:
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int ret = roundtrip(3);
|
||||
int ret = roundtrip(3, 0);
|
||||
|
||||
ret |= roundtrip(4);
|
||||
ret |= roundtrip(4, 0);
|
||||
ret |= roundtrip(4, 1);
|
||||
return ret;
|
||||
}
|
||||
@@ -3,6 +3,10 @@ CHAP size bytes=00 00 00 b6
|
||||
chapters=2
|
||||
chapter=0 time_base=1/1000 start=0 end=1000 title=LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
|
||||
chapter=1 time_base=1/1000 start=1000 end=2000 title=End
|
||||
trailing text subframe version=3
|
||||
chapters=2
|
||||
chapter=0 time_base=1/1000 start=0 end=1000 title=LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL artist=LL
|
||||
chapter=1 time_base=1/1000 start=1000 end=2000 title=End
|
||||
invalid subframe version=3
|
||||
chapters=2
|
||||
chapter=0 time_base=1/1000 start=0 end=1000 title=ch0
|
||||
@@ -12,7 +16,32 @@ CHAP size bytes=00 00 01 36
|
||||
chapters=2
|
||||
chapter=0 time_base=1/1000 start=0 end=1000 title=LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
|
||||
chapter=1 time_base=1/1000 start=1000 end=2000 title=End
|
||||
non-synchsafe CHAP version=4
|
||||
chapters=2
|
||||
chapter=0 time_base=1/1000 start=0 end=1000 title=LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
|
||||
chapter=1 time_base=1/1000 start=1000 end=2000 title=End
|
||||
high-bit plain CHAP with fitting fake header version=4
|
||||
chapters=2
|
||||
chapter=0 time_base=1/1000 start=0 end=1000 title=LLLLLLLLLLLLLLLLLLLLLLLLLLL
|
||||
chapter=1 time_base=1/1000 start=1000 end=2000 title=End
|
||||
trailing text subframe version=4
|
||||
chapters=2
|
||||
chapter=0 time_base=1/1000 start=0 end=1000 title=LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL artist=LL
|
||||
chapter=1 time_base=1/1000 start=1000 end=2000 title=End
|
||||
invalid subframe version=4
|
||||
chapters=2
|
||||
chapter=0 time_base=1/1000 start=0 end=1000 title=ch0
|
||||
chapter=1 time_base=1/1000 start=1000 end=2000 title=End
|
||||
version=4 declared=645 actual=645
|
||||
CHAP size bytes=00 00 02 00
|
||||
chapters=2
|
||||
chapter=0 time_base=1/1000 start=0 end=1000 title=LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
|
||||
chapter=1 time_base=1/1000 start=1000 end=2000 title=End
|
||||
empty next frame version=4
|
||||
chapters=2 album=World
|
||||
chapter=0 time_base=1/1000 start=0 end=1000 title=LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
|
||||
chapter=1 time_base=1/1000 start=1000 end=2000 title=End
|
||||
ambiguous plain CHAP version=4
|
||||
chapters=2
|
||||
chapter=0 time_base=1/1000 start=0 end=1000 title=LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
|
||||
chapter=1 time_base=1/1000 start=1000 end=2000 title=End
|
||||
Reference in new issue
Block a user