FFmpeg
Main Page
Related Pages
Modules
Namespaces
Data Structures
Files
Examples
File List
Globals
All
Data Structures
Namespaces
Files
Functions
Variables
Typedefs
Enumerations
Enumerator
Macros
Groups
Pages
libavformat
id3v2enc.c
Go to the documentation of this file.
1
/*
2
* ID3v2 header writer
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 <stdint.h>
22
#include <string.h>
23
24
#include "
libavutil/avstring.h
"
25
#include "
libavutil/dict.h
"
26
#include "
libavutil/intreadwrite.h
"
27
#include "
avformat.h
"
28
#include "
avio.h
"
29
#include "
id3v2.h
"
30
31
static
void
id3v2_put_size
(
AVIOContext
*pb,
int
size
)
32
{
33
avio_w8
(pb, size >> 21 & 0x7f);
34
avio_w8
(pb, size >> 14 & 0x7f);
35
avio_w8
(pb, size >> 7 & 0x7f);
36
avio_w8
(pb, size & 0x7f);
37
}
38
39
static
int
string_is_ascii
(
const
uint8_t
*str)
40
{
41
while
(*str && *str < 128) str++;
42
return
!*str;
43
}
44
45
static
void
id3v2_encode_string
(
AVIOContext
*pb,
const
uint8_t
*str,
46
enum
ID3v2Encoding
enc)
47
{
48
int (*put)(
AVIOContext
*,
const
char
*);
49
50
if
(enc ==
ID3v2_ENCODING_UTF16BOM
) {
51
avio_wl16
(pb, 0xFEFF);
/* BOM */
52
put =
avio_put_str16le
;
53
}
else
54
put =
avio_put_str
;
55
56
put(pb, str);
57
}
58
59
/**
60
* Write a text frame with one (normal frames) or two (TXXX frames) strings
61
* according to encoding (only UTF-8 or UTF-16+BOM supported).
62
* @return number of bytes written or a negative error code.
63
*/
64
static
int
id3v2_put_ttag
(
ID3v2EncContext
*id3,
AVIOContext
*avioc,
const
char
*str1,
const
char
*str2,
65
uint32_t
tag
,
enum
ID3v2Encoding
enc)
66
{
67
int
len
;
68
uint8_t
*pb;
69
AVIOContext
*dyn_buf;
70
if
(
avio_open_dyn_buf
(&dyn_buf) < 0)
71
return
AVERROR
(ENOMEM);
72
73
/* check if the strings are ASCII-only and use UTF16 only if
74
* they're not */
75
if
(enc ==
ID3v2_ENCODING_UTF16BOM
&&
string_is_ascii
(str1) &&
76
(!str2 ||
string_is_ascii
(str2)))
77
enc =
ID3v2_ENCODING_ISO8859
;
78
79
avio_w8
(dyn_buf, enc);
80
id3v2_encode_string
(dyn_buf, str1, enc);
81
if
(str2)
82
id3v2_encode_string
(dyn_buf, str2, enc);
83
len =
avio_close_dyn_buf
(dyn_buf, &pb);
84
85
avio_wb32
(avioc, tag);
86
/* ID3v2.3 frame size is not synchsafe */
87
if
(id3->
version
== 3)
88
avio_wb32
(avioc, len);
89
else
90
id3v2_put_size
(avioc, len);
91
avio_wb16
(avioc, 0);
92
avio_write
(avioc, pb, len);
93
94
av_freep
(&pb);
95
return
len +
ID3v2_HEADER_SIZE
;
96
}
97
98
static
int
id3v2_check_write_tag
(
ID3v2EncContext
*id3,
AVIOContext
*pb,
AVDictionaryEntry
*
t
,
99
const
char
table
[][4],
enum
ID3v2Encoding
enc)
100
{
101
uint32_t
tag
;
102
int
i;
103
104
if
(t->
key
[0] !=
'T'
|| strlen(t->
key
) != 4)
105
return
-1;
106
tag =
AV_RB32
(t->
key
);
107
for
(i = 0; *table[i]; i++)
108
if
(tag ==
AV_RB32
(table[i]))
109
return
id3v2_put_ttag
(id3, pb, t->
value
,
NULL
, tag, enc);
110
return
-1;
111
}
112
113
static
void
id3v2_3_metadata_split_date
(
AVDictionary
**pm)
114
{
115
AVDictionaryEntry
*mtag =
NULL
;
116
AVDictionary
*
dst
=
NULL
;
117
const
char
*key, *
value
;
118
char
year[5] = {0}, day_month[5] = {0};
119
int
i;
120
121
while
((mtag =
av_dict_get
(*pm,
""
, mtag,
AV_DICT_IGNORE_SUFFIX
))) {
122
key = mtag->
key
;
123
if
(!
av_strcasecmp
(key,
"date"
)) {
124
/* split date tag using "YYYY-MM-DD" format into year and month/day segments */
125
value = mtag->
value
;
126
i = 0;
127
while
(value[i] >=
'0'
&& value[i] <=
'9'
) i++;
128
if
(value[i] ==
'\0'
|| value[i] ==
'-'
) {
129
av_strlcpy
(year, value,
sizeof
(year));
130
av_dict_set
(&dst,
"TYER"
, year, 0);
131
132
if
(value[i] ==
'-'
&&
133
value[i+1] >=
'0'
&& value[i+1] <=
'1'
&&
134
value[i+2] >=
'0'
&& value[i+2] <=
'9'
&&
135
value[i+3] ==
'-'
&&
136
value[i+4] >=
'0'
&& value[i+4] <=
'3'
&&
137
value[i+5] >=
'0'
&& value[i+5] <=
'9'
&&
138
(value[i+6] ==
'\0'
|| value[i+6] ==
' '
)) {
139
snprintf
(day_month,
sizeof
(day_month),
"%.2s%.2s"
, value + i + 4, value + i + 1);
140
av_dict_set
(&dst,
"TDAT"
, day_month, 0);
141
}
142
}
else
143
av_dict_set
(&dst, key, value, 0);
144
}
else
145
av_dict_set
(&dst, key, mtag->
value
, 0);
146
}
147
av_dict_free
(pm);
148
*pm =
dst
;
149
}
150
151
void
ff_id3v2_start
(
ID3v2EncContext
*id3,
AVIOContext
*pb,
int
id3v2_version,
152
const
char
*magic)
153
{
154
id3->
version
= id3v2_version;
155
156
avio_wb32
(pb,
MKBETAG
(magic[0], magic[1], magic[2], id3v2_version));
157
avio_w8
(pb, 0);
158
avio_w8
(pb, 0);
/* flags */
159
160
/* reserve space for size */
161
id3->
size_pos
=
avio_tell
(pb);
162
avio_wb32
(pb, 0);
163
}
164
165
static
int
write_metadata
(
AVIOContext
*pb,
AVDictionary
**metadata,
166
ID3v2EncContext
*id3,
int
enc)
167
{
168
AVDictionaryEntry
*
t
=
NULL
;
169
int
ret
;
170
171
ff_metadata_conv
(metadata,
ff_id3v2_34_metadata_conv
,
NULL
);
172
if
(id3->
version
== 3)
173
id3v2_3_metadata_split_date
(metadata);
174
else
if
(id3->
version
== 4)
175
ff_metadata_conv
(metadata,
ff_id3v2_4_metadata_conv
,
NULL
);
176
177
while
((t =
av_dict_get
(*metadata,
""
, t,
AV_DICT_IGNORE_SUFFIX
))) {
178
if
((ret =
id3v2_check_write_tag
(id3, pb, t,
ff_id3v2_tags
, enc)) > 0) {
179
id3->
len
+=
ret
;
180
continue
;
181
}
182
if
((ret =
id3v2_check_write_tag
(id3, pb, t, id3->
version
== 3 ?
183
ff_id3v2_3_tags
:
ff_id3v2_4_tags
, enc)) > 0) {
184
id3->
len
+=
ret
;
185
continue
;
186
}
187
188
/* unknown tag, write as TXXX frame */
189
if
((ret =
id3v2_put_ttag
(id3, pb, t->
key
, t->
value
,
MKBETAG
(
'T'
,
'X'
,
'X'
,
'X'
), enc)) < 0)
190
return
ret
;
191
id3->
len
+=
ret
;
192
}
193
194
return
0;
195
}
196
197
static
int
write_chapter
(
AVFormatContext
*
s
,
ID3v2EncContext
*id3,
int
id
,
int
enc)
198
{
199
const
AVRational
time_base = {1, 1000};
200
AVChapter
*ch = s->
chapters
[
id
];
201
uint8_t
*dyn_buf =
NULL
;
202
AVIOContext
*dyn_bc =
NULL
;
203
char
name
[123];
204
int
len
,
start
,
end
,
ret
;
205
206
if
((ret =
avio_open_dyn_buf
(&dyn_bc)) < 0)
207
goto
fail;
208
209
start =
av_rescale_q
(ch->
start
, ch->
time_base
, time_base);
210
end =
av_rescale_q
(ch->
end
, ch->
time_base
, time_base);
211
212
snprintf
(name, 122,
"ch%d"
,
id
);
213
id3->
len
+=
avio_put_str
(dyn_bc, name);
214
avio_wb32
(dyn_bc, start);
215
avio_wb32
(dyn_bc, end);
216
avio_wb32
(dyn_bc, 0xFFFFFFFFu);
217
avio_wb32
(dyn_bc, 0xFFFFFFFFu);
218
219
if
((ret =
write_metadata
(dyn_bc, &ch->
metadata
, id3, enc)) < 0)
220
goto
fail;
221
222
len =
avio_close_dyn_buf
(dyn_bc, &dyn_buf);
223
id3->
len
+= 16 +
ID3v2_HEADER_SIZE
;
224
225
avio_wb32
(s->
pb
,
MKBETAG
(
'C'
,
'H'
,
'A'
,
'P'
));
226
avio_wb32
(s->
pb
, len);
227
avio_wb16
(s->
pb
, 0);
228
avio_write
(s->
pb
, dyn_buf, len);
229
230
fail:
231
if
(dyn_bc && !dyn_buf)
232
avio_close_dyn_buf
(dyn_bc, &dyn_buf);
233
av_freep
(&dyn_buf);
234
235
return
ret
;
236
}
237
238
int
ff_id3v2_write_metadata
(
AVFormatContext
*
s
,
ID3v2EncContext
*id3)
239
{
240
int
enc = id3->
version
== 3 ?
ID3v2_ENCODING_UTF16BOM
:
241
ID3v2_ENCODING_UTF8
;
242
int
i,
ret
;
243
244
if
((ret =
write_metadata
(s->
pb
, &s->
metadata
, id3, enc)) < 0)
245
return
ret
;
246
247
for
(i = 0; i < s->
nb_chapters
; i++) {
248
if
((ret =
write_chapter
(s, id3, i, enc)) < 0)
249
return
ret
;
250
}
251
252
return
0;
253
}
254
255
int
ff_id3v2_write_apic
(
AVFormatContext
*
s
,
ID3v2EncContext
*id3,
AVPacket
*
pkt
)
256
{
257
AVStream
*st = s->
streams
[pkt->
stream_index
];
258
AVDictionaryEntry
*e;
259
260
AVIOContext
*dyn_buf;
261
uint8_t
*
buf
;
262
const
CodecMime
*mime =
ff_id3v2_mime_tags
;
263
const
char
*mimetype =
NULL
, *desc =
""
;
264
int
enc = id3->
version
== 3 ?
ID3v2_ENCODING_UTF16BOM
:
265
ID3v2_ENCODING_UTF8
;
266
int
i,
len
,
type
= 0;
267
268
/* get the mimetype*/
269
while
(mime->
id
!=
AV_CODEC_ID_NONE
) {
270
if
(mime->
id
== st->
codec
->
codec_id
) {
271
mimetype = mime->
str
;
272
break
;
273
}
274
mime++;
275
}
276
if
(!mimetype) {
277
av_log
(s,
AV_LOG_ERROR
,
"No mimetype is known for stream %d, cannot "
278
"write an attached picture.\n"
, st->
index
);
279
return
AVERROR
(EINVAL);
280
}
281
282
/* get the picture type */
283
e =
av_dict_get
(st->
metadata
,
"comment"
,
NULL
, 0);
284
for
(i = 0; e && i <
FF_ARRAY_ELEMS
(
ff_id3v2_picture_types
); i++) {
285
if
(strstr(
ff_id3v2_picture_types
[i], e->
value
) ==
ff_id3v2_picture_types
[i]) {
286
type = i;
287
break
;
288
}
289
}
290
291
/* get the description */
292
if
((e =
av_dict_get
(st->
metadata
,
"title"
,
NULL
, 0)))
293
desc = e->
value
;
294
295
/* start writing */
296
if
(
avio_open_dyn_buf
(&dyn_buf) < 0)
297
return
AVERROR
(ENOMEM);
298
299
avio_w8
(dyn_buf, enc);
300
avio_put_str
(dyn_buf, mimetype);
301
avio_w8
(dyn_buf, type);
302
id3v2_encode_string
(dyn_buf, desc, enc);
303
avio_write
(dyn_buf, pkt->
data
, pkt->
size
);
304
len =
avio_close_dyn_buf
(dyn_buf, &buf);
305
306
avio_wb32
(s->
pb
,
MKBETAG
(
'A'
,
'P'
,
'I'
,
'C'
));
307
if
(id3->
version
== 3)
308
avio_wb32
(s->
pb
, len);
309
else
310
id3v2_put_size
(s->
pb
, len);
311
avio_wb16
(s->
pb
, 0);
312
avio_write
(s->
pb
, buf, len);
313
av_freep
(&buf);
314
315
id3->
len
+= len +
ID3v2_HEADER_SIZE
;
316
317
return
0;
318
}
319
320
void
ff_id3v2_finish
(
ID3v2EncContext
*id3,
AVIOContext
*pb)
321
{
322
int64_t cur_pos =
avio_tell
(pb);
323
avio_seek
(pb, id3->
size_pos
, SEEK_SET);
324
id3v2_put_size
(pb, id3->
len
);
325
avio_seek
(pb, cur_pos, SEEK_SET);
326
}
327
328
int
ff_id3v2_write_simple
(
struct
AVFormatContext
*
s
,
int
id3v2_version,
329
const
char
*magic)
330
{
331
ID3v2EncContext
id3 = { 0 };
332
int
ret
;
333
334
ff_id3v2_start
(&id3, s->
pb
, id3v2_version, magic);
335
if
((ret =
ff_id3v2_write_metadata
(s, &id3)) < 0)
336
return
ret
;
337
ff_id3v2_finish
(&id3, s->
pb
);
338
339
return
0;
340
}
Generated on Sun May 19 2013 19:21:04 for FFmpeg by
1.8.2