FFmpeg
Loading...
Searching...
No Matches
id3v2chap.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2026 Jun Zhao
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#include <inttypes.h>
22#include <stdio.h>
23#include <string.h>
24
25#include "libavutil/mem.h"
28#include "libavformat/demux.h"
29#include "libavformat/id3v2.h"
30
31static int readback(const uint8_t *buf, int size)
32{
34 FFIOContext read_pb;
35 ID3v2ExtraMeta *extra = NULL;
36 AVDictionary *meta = NULL;
37 const AVDictionaryEntry *album;
38 int ret = 1;
39
40 if (!in)
41 return ret;
42
43 ffio_init_read_context(&read_pb, buf, size);
44 ff_id3v2_read_dict(in, &read_pb.pub, &meta, ID3v2_DEFAULT_MAGIC, &extra);
45 if (ff_id3v2_parse_chapters(in, extra) < 0)
46 goto done;
47
48 printf("chapters=%u", in->nb_chapters);
49 album = av_dict_get(meta, "album", NULL, 0);
50 if (album)
51 printf(" album=%s", album->value);
52 printf("\n");
53 for (unsigned i = 0; i < in->nb_chapters; i++) {
54 const AVChapter *ch = in->chapters[i];
55 const AVDictionaryEntry *title = av_dict_get(ch->metadata, "title", NULL, 0);
56 const AVDictionaryEntry *artist = av_dict_get(ch->metadata, "artist", NULL, 0);
57
58 printf("chapter=%u time_base=%d/%d start=%"PRId64" end=%"PRId64" title=%s",
59 i, ch->time_base.num, ch->time_base.den, ch->start, ch->end,
60 title ? title->value : "(missing)");
61 if (artist)
62 printf(" artist=%s", artist->value);
63 printf("\n");
64 }
65 ret = 0;
66
67done:
68 av_dict_free(&meta);
71 return ret;
72}
73
74/* The first chapter title is long enough that its TIT2 frame is at least
75 * 128 bytes, so a plain size and a synchsafe size differ. */
76static int roundtrip(int version, int ambiguous)
77{
78 char long_title[225];
79 int title_len = ambiguous ? 224 : 150;
81 AVIOContext *pb = NULL;
82 ID3v2EncContext id3 = { 0 };
83 uint8_t *buf = NULL;
84 int size, chap_pos = -1, ret = 1;
85
86 memset(long_title, 'L', title_len);
87 long_title[title_len] = 0;
88
90 if (!out ||
91 !avpriv_new_chapter(out, 0, av_make_q(1, 1000), 0, 1000, long_title) ||
92 !avpriv_new_chapter(out, 1, av_make_q(1, 1000), 1000, 2000, "End") ||
93 avio_open_dyn_buf(&pb) < 0)
94 goto done;
95
97 if (ff_id3v2_write_metadata(out, pb, &id3) < 0)
98 goto done;
99 ff_id3v2_finish(&id3, pb, ambiguous ? 300 : 10);
100 size = avio_close_dyn_buf(pb, &buf);
101 pb = NULL;
103 goto done;
104
105 printf("version=%d declared=%d actual=%d\n", version, ff_id3v2_tag_len(buf), size);
106 for (int i = 0; i + 10 <= size; i++) {
107 if (!memcmp(buf + i, "CHAP", 4)) {
108 chap_pos = i;
109 printf("CHAP size bytes=%02x %02x %02x %02x\n",
110 buf[i + 4], buf[i + 5], buf[i + 6], buf[i + 7]);
111 break;
112 }
113 }
114 if (readback(buf, size))
115 goto done;
116
117 /* A 224-byte title makes the CHAP payload 256 bytes. First test a valid
118 * synchsafe 256-byte CHAP (00 00 02 00, raw 512) followed by an empty
119 * TPE1 frame and a TALB frame inside the 300-byte padding area, so
120 * check_tag() must accept nlen == 0 at the 256-byte boundary rather than
121 * falling through to the raw 512-byte offset in zero padding. Then test
122 * the non-conforming plain size 00 00 01 00, whose high bits are clear
123 * so the synchsafe candidate at 128 bytes must be rejected by checking
124 * the oversized LLLL frame header inside the title. */
125 if (ambiguous) {
126 if (chap_pos >= 0 && chap_pos + 10 + 256 + 45 + 27 <= size) {
127 uint8_t ch1_copy[45];
128 memcpy(ch1_copy, buf + chap_pos + 10 + 256, sizeof(ch1_copy));
129 memcpy(buf + chap_pos + 10 + 256, "TPE1\x00\x00\x00\x00\x00\x00", 10);
130 memcpy(buf + chap_pos + 10 + 266, "TALB\x00\x00\x00\x07\x00\x00\x03World", 17);
131 memcpy(buf + chap_pos + 10 + 283, ch1_copy, sizeof(ch1_copy));
132 printf("empty next frame version=4\n");
133 if (readback(buf, size))
134 goto done;
135 memcpy(buf + chap_pos + 10 + 256, ch1_copy, sizeof(ch1_copy));
136 memset(buf + chap_pos + 10 + 301, 0, 27);
137
138 buf[chap_pos + 6] = 0x01;
139 buf[chap_pos + 7] = 0x00;
140 printf("ambiguous plain CHAP version=4\n");
141 if (readback(buf, size))
142 goto done;
143 }
144 ret = 0;
145 goto done;
146 }
147
148 /* Non-conforming v2.4 CHAP with a plain 32-bit size (0x000000b6) whose
149 * high bit is set and whose payload contains uppercase ASCII at the
150 * stripped offset (0x36). Also zero the 4 size bytes after "LLLL" at
151 * offset 0x36 once to verify that is_syncsafe() rejects the 0x36
152 * candidate even when check_tag() would accept an empty frame header. */
153 if (version == 4 && chap_pos >= 0) {
154 uint8_t saved[4];
155 buf[chap_pos + 6] = 0x00;
156 buf[chap_pos + 7] = 0xb6;
157 printf("non-synchsafe CHAP version=4\n");
158 if (readback(buf, size))
159 goto done;
160
161 memcpy(saved, buf + chap_pos + 10 + 0x36 + 4, sizeof(saved));
162 memset(buf + chap_pos + 10 + 0x36 + 4, 0, sizeof(saved));
163 printf("high-bit plain CHAP with fitting fake header version=4\n");
164 if (readback(buf, size))
165 goto done;
166 memcpy(buf + chap_pos + 10 + 0x36 + 4, saved, sizeof(saved));
167 buf[chap_pos + 6] = 0x01;
168 buf[chap_pos + 7] = 0x36;
169 }
170
171 /* Split the 162-byte TIT2 subframe into a 22-byte TPE1 subframe with
172 * unread trailing bytes after its first NUL terminator, followed by a
173 * 140-byte TIT2 subframe, then test an oversized subframe length. */
174 if (chap_pos >= 0) {
175 for (int i = chap_pos + 10; i + 33 <= size; i++) {
176 if (!memcmp(buf + i, "TIT2", 4)) {
177 uint8_t enc = buf[i + 10];
178 memcpy(buf + i, "TPE1", 4);
179 buf[i + 6] = 0;
180 buf[i + 7] = 12;
181 buf[i + 13] = 0;
182 memcpy(buf + i + 22, "TIT2\x00\x00", 6);
183 buf[i + 28] = version == 4 ? 0x01 : 0x00;
184 buf[i + 29] = version == 4 ? 0x02 : 0x82;
185 buf[i + 30] = 0;
186 buf[i + 31] = 0;
187 buf[i + 32] = enc;
188 printf("trailing text subframe version=%d\n", version);
189 if (readback(buf, size))
190 goto done;
191
192 memcpy(buf + i, "TIT2", 4);
193 memset(buf + i + 4, 0x7f, 4);
194 printf("invalid subframe version=%d\n", version);
195 if (readback(buf, size))
196 goto done;
197 break;
198 }
199 }
200 }
201 ret = 0;
202
203done:
205 av_free(buf);
207 return ret;
208}
209
210int main(void)
211{
212 int ret = roundtrip(3, 0);
213
214 ret |= roundtrip(4, 0);
215 ret |= roundtrip(4, 1);
216 return ret;
217}
static FILE * out
Main libavformat public API header.
int avio_close_dyn_buf(AVIOContext *s, uint8_t **pbuffer)
Return the written size and a pointer to the buffer.
Definition aviobuf.c:1378
int avio_open_dyn_buf(AVIOContext **s)
Open a write only memory stream.
Definition aviobuf.c:1333
void ffio_free_dyn_buf(AVIOContext **s)
Free a dynamic buffer.
Definition aviobuf.c:1406
void ffio_init_read_context(FFIOContext *s, const uint8_t *buffer, int buffer_size)
Wrap a buffer in an AVIOContext for reading.
Definition aviobuf.c:99
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define NULL
Definition coverity.c:32
__device__ int printf(const char *,...)
AVChapter * avpriv_new_chapter(AVFormatContext *s, int64_t id, AVRational time_base, int64_t start, int64_t end, const char *title)
Add a new chapter.
Definition demux_utils.c:46
int ret
Definition ffplay.c:391
SDL_atomic_t done
Definition ffplay.c:392
AVFormatContext * avformat_alloc_context(void)
Allocate an AVFormatContext.
Definition options.c:165
void avformat_free_context(AVFormatContext *s)
Free an AVFormatContext and all its streams.
Definition avformat.c:165
void av_dict_free(AVDictionary **pm)
Free all the memory allocated for an AVDictionary struct and all keys and values.
Definition dict.c:236
AVDictionaryEntry * av_dict_get(const AVDictionary *m, const char *key, const AVDictionaryEntry *prev, int flags)
Get a dictionary entry with matching key.
Definition dict.c:61
static AVRational av_make_q(int num, int den)
Create an AVRational.
Definition rational.h:71
int ff_id3v2_tag_len(const uint8_t *buf)
Get the length of an ID3v2 tag.
Definition id3v2.c:165
void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta)
Free memory allocated parsing special (non-text) metadata.
Definition id3v2.c:1231
int ff_id3v2_parse_chapters(AVFormatContext *s, ID3v2ExtraMeta *cur)
Create chapters for all CHAP tags found in the ID3v2 header.
Definition id3v2.c:1278
void ff_id3v2_read_dict(AVFormatContext *s, AVIOContext *pb, AVDictionary **metadata, const char *magic, ID3v2ExtraMeta **extra_meta)
Read an ID3v2 tag into specified dictionary and retrieve supported extra metadata.
Definition id3v2.c:1219
void ff_id3v2_start(ID3v2EncContext *id3, AVIOContext *pb, int id3v2_version, const char *magic)
Initialize the encoding context and write the header for a new ID3v2 tag.
Definition id3v2enc.c:355
void ff_id3v2_finish(ID3v2EncContext *id3, AVIOContext *pb, int padding_bytes)
Finalize an opened ID3v2 tag.
Definition id3v2enc.c:563
#define ID3v2_DEFAULT_MAGIC
Default magic bytes for ID3v2 header: "ID3".
Definition id3v2.h:35
#define ID3v2_HEADER_SIZE
Definition id3v2.h:30
int ff_id3v2_write_metadata(AVFormatContext *s, AVIOContext *pb, ID3v2EncContext *id3)
Convert and write all global metadata from s into an ID3v2 tag.
Definition id3v2enc.c:483
static int readback(const uint8_t *buf, int size)
Definition id3v2chap.c:31
static int roundtrip(int version, int ambiguous)
Definition id3v2chap.c:76
int main(void)
Definition id3v2chap.c:210
version
Definition libkvazaar.c:313
Memory handling functions.
int64_t start
Definition avformat.h:1363
AVDictionary * metadata
Definition avformat.h:1364
int64_t end
chapter start/end time in time_base units
Definition avformat.h:1363
AVRational time_base
time base in which the start/end timestamps are specified
Definition avformat.h:1362
char * value
Definition dict.h:92
Format I/O context.
Definition avformat.h:1401
unsigned int nb_chapters
Number of chapters in AVChapter array.
Definition avformat.h:1502
AVChapter ** chapters
Definition avformat.h:1503
Bytestream IO Context.
Definition avio.h:160
int num
Numerator.
Definition rational.h:59
int den
Denominator.
Definition rational.h:60
AVIOContext pub
#define av_free(p)
int size