FFmpeg
Main Page
Related Pages
Modules
Data Structures
Files
Examples
File List
Globals
All
Data Structures
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
int
ff_id3v2_write_metadata
(
AVFormatContext
*s,
ID3v2EncContext
*id3)
166
{
167
AVDictionaryEntry
*
t
=
NULL
;
168
int
enc = id3->
version
== 3 ?
ID3v2_ENCODING_UTF16BOM
:
169
ID3v2_ENCODING_UTF8
;
170
171
ff_metadata_conv
(&s->
metadata
,
ff_id3v2_34_metadata_conv
,
NULL
);
172
if
(id3->
version
== 3)
173
id3v2_3_metadata_split_date
(&s->
metadata
);
174
else
if
(id3->
version
== 4)
175
ff_metadata_conv
(&s->
metadata
,
ff_id3v2_4_metadata_conv
,
NULL
);
176
177
while
((t =
av_dict_get
(s->
metadata
,
""
, t,
AV_DICT_IGNORE_SUFFIX
))) {
178
int
ret;
179
180
if
((ret =
id3v2_check_write_tag
(id3, s->
pb
, t,
ff_id3v2_tags
, enc)) > 0) {
181
id3->
len
+= ret;
182
continue
;
183
}
184
if
((ret =
id3v2_check_write_tag
(id3, s->
pb
, t, id3->
version
== 3 ?
185
ff_id3v2_3_tags
:
ff_id3v2_4_tags
, enc)) > 0) {
186
id3->
len
+= ret;
187
continue
;
188
}
189
190
/* unknown tag, write as TXXX frame */
191
if
((ret =
id3v2_put_ttag
(id3, s->
pb
, t->
key
, t->
value
,
MKBETAG
(
'T'
,
'X'
,
'X'
,
'X'
), enc)) < 0)
192
return
ret;
193
id3->
len
+= ret;
194
}
195
196
return
0;
197
}
198
199
int
ff_id3v2_write_apic
(
AVFormatContext
*s,
ID3v2EncContext
*id3,
AVPacket
*
pkt
)
200
{
201
AVStream
*st = s->
streams
[pkt->
stream_index
];
202
AVDictionaryEntry
*e;
203
204
AVIOContext
*dyn_buf;
205
uint8_t
*buf;
206
const
CodecMime
*mime =
ff_id3v2_mime_tags
;
207
const
char
*mimetype =
NULL
, *desc =
""
;
208
int
enc = id3->
version
== 3 ?
ID3v2_ENCODING_UTF16BOM
:
209
ID3v2_ENCODING_UTF8
;
210
int
i,
len
, type = 0;
211
212
/* get the mimetype*/
213
while
(mime->
id
!=
AV_CODEC_ID_NONE
) {
214
if
(mime->
id
== st->
codec
->
codec_id
) {
215
mimetype = mime->
str
;
216
break
;
217
}
218
mime++;
219
}
220
if
(!mimetype) {
221
av_log
(s,
AV_LOG_ERROR
,
"No mimetype is known for stream %d, cannot "
222
"write an attached picture.\n"
, st->
index
);
223
return
AVERROR
(EINVAL);
224
}
225
226
/* get the picture type */
227
e =
av_dict_get
(st->
metadata
,
"comment"
,
NULL
, 0);
228
for
(i = 0; e && i <
FF_ARRAY_ELEMS
(
ff_id3v2_picture_types
); i++) {
229
if
(strstr(
ff_id3v2_picture_types
[i], e->
value
) ==
ff_id3v2_picture_types
[i]) {
230
type = i;
231
break
;
232
}
233
}
234
235
/* get the description */
236
if
((e =
av_dict_get
(st->
metadata
,
"title"
,
NULL
, 0)))
237
desc = e->
value
;
238
239
/* start writing */
240
if
(
avio_open_dyn_buf
(&dyn_buf) < 0)
241
return
AVERROR
(ENOMEM);
242
243
avio_w8
(dyn_buf, enc);
244
avio_put_str
(dyn_buf, mimetype);
245
avio_w8
(dyn_buf, type);
246
id3v2_encode_string
(dyn_buf, desc, enc);
247
avio_write
(dyn_buf, pkt->
data
, pkt->
size
);
248
len =
avio_close_dyn_buf
(dyn_buf, &buf);
249
250
avio_wb32
(s->
pb
,
MKBETAG
(
'A'
,
'P'
,
'I'
,
'C'
));
251
if
(id3->
version
== 3)
252
avio_wb32
(s->
pb
, len);
253
else
254
id3v2_put_size
(s->
pb
, len);
255
avio_wb16
(s->
pb
, 0);
256
avio_write
(s->
pb
, buf, len);
257
av_freep
(&buf);
258
259
id3->
len
+= len +
ID3v2_HEADER_SIZE
;
260
261
return
0;
262
}
263
264
void
ff_id3v2_finish
(
ID3v2EncContext
*id3,
AVIOContext
*pb)
265
{
266
int64_t cur_pos =
avio_tell
(pb);
267
avio_seek
(pb, id3->
size_pos
, SEEK_SET);
268
id3v2_put_size
(pb, id3->
len
);
269
avio_seek
(pb, cur_pos, SEEK_SET);
270
}
271
272
int
ff_id3v2_write_simple
(
struct
AVFormatContext
*s,
int
id3v2_version,
273
const
char
*magic)
274
{
275
ID3v2EncContext
id3 = { 0 };
276
int
ret;
277
278
ff_id3v2_start
(&id3, s->
pb
, id3v2_version, magic);
279
if
((ret =
ff_id3v2_write_metadata
(s, &id3)) < 0)
280
return
ret;
281
ff_id3v2_finish
(&id3, s->
pb
);
282
283
return
0;
284
}
Generated on Sat May 25 2013 04:01:17 for FFmpeg by
1.8.2