Main Page
Related Pages
Modules
Data Structures
Files
Examples
File List
Globals
libavformat
id3v2enc.c
Go to the documentation of this file.
1
/*
2
* ID3v2 header writer
3
*
4
* This file is part of Libav.
5
*
6
* Libav 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
* Libav 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 Libav; 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/dict.h
"
25
#include "
libavutil/intreadwrite.h
"
26
#include "
avformat.h
"
27
#include "
avio.h
"
28
#include "
id3v2.h
"
29
30
static
void
id3v2_put_size
(
AVIOContext
*pb,
int
size
)
31
{
32
avio_w8
(pb, size >> 21 & 0x7f);
33
avio_w8
(pb, size >> 14 & 0x7f);
34
avio_w8
(pb, size >> 7 & 0x7f);
35
avio_w8
(pb, size & 0x7f);
36
}
37
38
static
int
string_is_ascii
(
const
uint8_t
*str)
39
{
40
while
(*str && *str < 128) str++;
41
return
!*str;
42
}
43
44
static
void
id3v2_encode_string
(
AVIOContext
*pb,
const
uint8_t
*str,
45
enum
ID3v2Encoding
enc)
46
{
47
int (*
put
)(
AVIOContext
*,
const
char
*);
48
49
if
(enc ==
ID3v2_ENCODING_UTF16BOM
) {
50
avio_wl16
(pb, 0xFEFF);
/* BOM */
51
put
=
avio_put_str16le
;
52
}
else
53
put
=
avio_put_str
;
54
55
put
(pb, str);
56
}
57
63
static
int
id3v2_put_ttag
(
ID3v2EncContext
*id3,
AVIOContext
*avioc,
const
char
*str1,
const
char
*str2,
64
uint32_t
tag
,
enum
ID3v2Encoding
enc)
65
{
66
int
len
;
67
uint8_t
*pb;
68
AVIOContext
*dyn_buf;
69
if
(
avio_open_dyn_buf
(&dyn_buf) < 0)
70
return
AVERROR
(ENOMEM);
71
72
/* check if the strings are ASCII-only and use UTF16 only if
73
* they're not */
74
if
(enc ==
ID3v2_ENCODING_UTF16BOM
&&
string_is_ascii
(str1) &&
75
(!str2 ||
string_is_ascii
(str2)))
76
enc =
ID3v2_ENCODING_ISO8859
;
77
78
avio_w8
(dyn_buf, enc);
79
id3v2_encode_string
(dyn_buf, str1, enc);
80
if
(str2)
81
id3v2_encode_string
(dyn_buf, str2, enc);
82
len =
avio_close_dyn_buf
(dyn_buf, &pb);
83
84
avio_wb32
(avioc, tag);
85
/* ID3v2.3 frame size is not synchsafe */
86
if
(id3->
version
== 3)
87
avio_wb32
(avioc, len);
88
else
89
id3v2_put_size
(avioc, len);
90
avio_wb16
(avioc, 0);
91
avio_write
(avioc, pb, len);
92
93
av_freep
(&pb);
94
return
len +
ID3v2_HEADER_SIZE
;
95
}
96
97
static
int
id3v2_check_write_tag
(
ID3v2EncContext
*id3,
AVIOContext
*pb,
AVDictionaryEntry
*
t
,
98
const
char
table[][4],
enum
ID3v2Encoding
enc)
99
{
100
uint32_t
tag
;
101
int
i;
102
103
if
(t->
key
[0] !=
'T'
|| strlen(t->
key
) != 4)
104
return
-1;
105
tag =
AV_RB32
(t->
key
);
106
for
(i = 0; *table[i]; i++)
107
if
(tag ==
AV_RB32
(table[i]))
108
return
id3v2_put_ttag
(id3, pb, t->
value
,
NULL
, tag, enc);
109
return
-1;
110
}
111
112
void
ff_id3v2_start
(
ID3v2EncContext
*id3,
AVIOContext
*pb,
int
id3v2_version,
113
const
char
*magic)
114
{
115
id3->
version
= id3v2_version;
116
117
avio_wb32
(pb,
MKBETAG
(magic[0], magic[1], magic[2], id3v2_version));
118
avio_w8
(pb, 0);
119
avio_w8
(pb, 0);
/* flags */
120
121
/* reserve space for size */
122
id3->
size_pos
=
avio_tell
(pb);
123
avio_wb32
(pb, 0);
124
}
125
126
int
ff_id3v2_write_metadata
(
AVFormatContext
*s,
ID3v2EncContext
*id3)
127
{
128
AVDictionaryEntry
*
t
=
NULL
;
129
int
enc = id3->
version
== 3 ?
ID3v2_ENCODING_UTF16BOM
:
130
ID3v2_ENCODING_UTF8
;
131
132
ff_metadata_conv
(&s->
metadata
,
ff_id3v2_34_metadata_conv
,
NULL
);
133
if
(id3->
version
== 4)
134
ff_metadata_conv
(&s->
metadata
,
ff_id3v2_4_metadata_conv
,
NULL
);
135
136
while
((t =
av_dict_get
(s->
metadata
,
""
, t,
AV_DICT_IGNORE_SUFFIX
))) {
137
int
ret;
138
139
if
((ret =
id3v2_check_write_tag
(id3, s->
pb
, t,
ff_id3v2_tags
, enc)) > 0) {
140
id3->
len
+= ret;
141
continue
;
142
}
143
if
((ret =
id3v2_check_write_tag
(id3, s->
pb
, t, id3->
version
== 3 ?
144
ff_id3v2_3_tags
:
ff_id3v2_4_tags
, enc)) > 0) {
145
id3->
len
+= ret;
146
continue
;
147
}
148
149
/* unknown tag, write as TXXX frame */
150
if
((ret =
id3v2_put_ttag
(id3, s->
pb
, t->
key
, t->
value
,
MKBETAG
(
'T'
,
'X'
,
'X'
,
'X'
), enc)) < 0)
151
return
ret;
152
id3->
len
+= ret;
153
}
154
155
return
0;
156
}
157
158
int
ff_id3v2_write_apic
(
AVFormatContext
*s,
ID3v2EncContext
*id3,
AVPacket
*pkt)
159
{
160
AVStream
*st = s->
streams
[pkt->
stream_index
];
161
AVDictionaryEntry
*e;
162
163
AVIOContext
*dyn_buf;
164
uint8_t
*buf;
165
const
CodecMime
*mime =
ff_id3v2_mime_tags
;
166
const
char
*mimetype =
NULL
, *desc =
""
;
167
int
enc = id3->
version
== 3 ?
ID3v2_ENCODING_UTF16BOM
:
168
ID3v2_ENCODING_UTF8
;
169
int
i,
len
, type = 0;
170
171
/* get the mimetype*/
172
while
(mime->
id
!=
AV_CODEC_ID_NONE
) {
173
if
(mime->
id
== st->
codec
->
codec_id
) {
174
mimetype = mime->
str
;
175
break
;
176
}
177
mime++;
178
}
179
if
(!mimetype) {
180
av_log
(s,
AV_LOG_ERROR
,
"No mimetype is known for stream %d, cannot "
181
"write an attached picture.\n"
, st->
index
);
182
return
AVERROR
(EINVAL);
183
}
184
185
/* get the picture type */
186
e =
av_dict_get
(st->
metadata
,
"comment"
,
NULL
, 0);
187
for
(i = 0; e && i <
FF_ARRAY_ELEMS
(
ff_id3v2_picture_types
); i++) {
188
if
(strstr(
ff_id3v2_picture_types
[i], e->
value
) ==
ff_id3v2_picture_types
[i]) {
189
type = i;
190
break
;
191
}
192
}
193
194
/* get the description */
195
if
((e =
av_dict_get
(st->
metadata
,
"title"
,
NULL
, 0)))
196
desc = e->
value
;
197
198
/* start writing */
199
if (
avio_open_dyn_buf
(&dyn_buf) < 0)
200
return
AVERROR
(ENOMEM);
201
202
avio_w8
(dyn_buf, enc);
203
avio_put_str
(dyn_buf, mimetype);
204
avio_w8
(dyn_buf, type);
205
id3v2_encode_string
(dyn_buf, desc, enc);
206
avio_write
(dyn_buf, pkt->
data
, pkt->
size
);
207
len =
avio_close_dyn_buf
(dyn_buf, &buf);
208
209
avio_wb32
(s->
pb
,
MKBETAG
(
'A'
,
'P'
,
'I'
,
'C'
));
210
if
(id3->
version
== 3)
211
avio_wb32
(s->
pb
, len);
212
else
213
id3v2_put_size
(s->
pb
, len);
214
avio_wb16
(s->
pb
, 0);
215
avio_write
(s->
pb
, buf, len);
216
av_freep
(&buf);
217
218
id3->
len
+= len +
ID3v2_HEADER_SIZE
;
219
220
return
0;
221
}
222
223
void
ff_id3v2_finish
(
ID3v2EncContext
*id3,
AVIOContext
*pb)
224
{
225
int64_t cur_pos =
avio_tell
(pb);
226
avio_seek
(pb, id3->
size_pos
, SEEK_SET);
227
id3v2_put_size
(pb, id3->
len
);
228
avio_seek
(pb, cur_pos, SEEK_SET);
229
}
230
231
int
ff_id3v2_write_simple
(
struct
AVFormatContext
*s,
int
id3v2_version,
232
const
char
*magic)
233
{
234
ID3v2EncContext
id3 = { 0 };
235
int
ret;
236
237
ff_id3v2_start
(&id3, s->
pb
, id3v2_version, magic);
238
if
((ret =
ff_id3v2_write_metadata
(s, &id3)) < 0)
239
return
ret;
240
ff_id3v2_finish
(&id3, s->
pb
);
241
242
return
0;
243
}