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 03:58:46 for FFmpeg by
1.8.2