/src/vlc/modules/demux/xiph_metadata.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * xiph_metadata.h: Vorbis Comment parser |
3 | | ***************************************************************************** |
4 | | * Copyright © 2008-2013 VLC authors and VideoLAN |
5 | | * |
6 | | * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org> |
7 | | * Jean-Baptiste Kempf <jb@videolan.org> |
8 | | * |
9 | | * This program is free software; you can redistribute it and/or modify it |
10 | | * under the terms of the GNU Lesser General Public License as published by |
11 | | * the Free Software Foundation; either version 2.1 of the License, or |
12 | | * (at your option) any later version. |
13 | | * |
14 | | * This program is distributed in the hope that it will be useful, |
15 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | * GNU Lesser General Public License for more details. |
18 | | * |
19 | | * You should have received a copy of the GNU Lesser General Public License |
20 | | * along with this program; if not, write to the Free Software Foundation, |
21 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
22 | | *****************************************************************************/ |
23 | | |
24 | | #ifdef HAVE_CONFIG_H |
25 | | # include "config.h" |
26 | | #endif |
27 | | |
28 | | #include <assert.h> |
29 | | |
30 | | #include <vlc_common.h> |
31 | | #include <vlc_arrays.h> |
32 | | #include <vlc_charset.h> |
33 | | #include <vlc_strings.h> |
34 | | #include <vlc_arrays.h> |
35 | | #include <vlc_input.h> |
36 | | #include "xiph_metadata.h" |
37 | | #include "../meta_engine/ID3Pictures.h" |
38 | | |
39 | | input_attachment_t* ParseFlacPicture( const uint8_t *p_data, size_t size, |
40 | | int i_attachments, int *i_cover_score, int *i_cover_idx ) |
41 | 2.55k | { |
42 | 2.55k | uint32_t type, len; |
43 | | |
44 | 2.55k | if( size < 8 ) |
45 | 621 | return NULL; |
46 | 1.93k | #define RM(x) \ |
47 | 12.0k | do { \ |
48 | 12.0k | assert(size >= (x)); \ |
49 | 12.0k | size -= (x); \ |
50 | 12.0k | p_data += (x); \ |
51 | 12.0k | } while (0) |
52 | | |
53 | 1.93k | type = GetDWBE( p_data ); |
54 | 1.93k | RM(4); |
55 | 1.93k | len = GetDWBE( p_data ); |
56 | 1.93k | RM(4); |
57 | | |
58 | 1.93k | if( size < len ) |
59 | 17 | return NULL; |
60 | | |
61 | 1.92k | char *mime = strndup( (const char *)p_data, len ); |
62 | 1.92k | if( unlikely(mime == NULL) ) |
63 | 0 | return NULL; |
64 | 1.92k | RM(len); |
65 | | |
66 | 1.92k | if( size < 4 ) |
67 | 0 | { |
68 | 0 | free( mime ); |
69 | 0 | return NULL; |
70 | 0 | } |
71 | | |
72 | 1.92k | len = GetDWBE( p_data ); |
73 | 1.92k | RM(4); |
74 | | |
75 | 1.92k | if( size < len ) |
76 | 13 | { |
77 | 13 | free( mime ); |
78 | 13 | return NULL; |
79 | 13 | } |
80 | | |
81 | 1.90k | input_attachment_t *p_attachment = NULL; |
82 | 1.90k | char *description = strndup( (const char *)p_data, len ); |
83 | 1.90k | if( unlikely(description == NULL) ) |
84 | 0 | goto error; |
85 | 1.90k | RM(len); |
86 | | |
87 | 1.90k | EnsureUTF8( description ); |
88 | | |
89 | 1.90k | if( size < 20 ) |
90 | 706 | goto error; |
91 | | |
92 | 1.20k | RM(4 * 4); /* skip */ |
93 | | |
94 | 1.20k | len = GetDWBE( p_data ); |
95 | 1.20k | RM(4); |
96 | | |
97 | 1.20k | if( size < len ) |
98 | 137 | goto error; |
99 | | |
100 | | /* printf( "Picture type=%"PRIu32" mime=%s description='%s' " |
101 | | "file length=%zu\n", type, mime, description, len ); */ |
102 | | |
103 | 1.06k | char name[7 + (sizeof (i_attachments) * 3) + 4 + 1]; |
104 | | |
105 | 1.06k | snprintf( name, sizeof (name), "picture%u", i_attachments ); |
106 | | |
107 | 1.06k | if( !strcasecmp( mime, "image/jpeg" ) ) |
108 | 128 | strcat( name, ".jpg" ); |
109 | 937 | else if( !strcasecmp( mime, "image/png" ) ) |
110 | 255 | strcat( name, ".png" ); |
111 | | |
112 | 1.06k | p_attachment = vlc_input_attachment_New( name, mime, description, p_data, |
113 | 1.06k | size /* XXX: len instead? */ ); |
114 | | |
115 | 1.06k | if( type < ARRAY_SIZE(ID3v2_cover_scores) && |
116 | 429 | *i_cover_score < ID3v2_cover_scores[type] ) |
117 | 2 | { |
118 | 2 | *i_cover_idx = i_attachments; |
119 | 2 | *i_cover_score = ID3v2_cover_scores[type]; |
120 | 2 | } |
121 | | |
122 | 1.90k | error: |
123 | 1.90k | free( mime ); |
124 | 1.90k | free( description ); |
125 | 1.90k | return p_attachment; |
126 | 1.06k | } |
127 | | |
128 | | #undef RM |
129 | | #define RM(x) \ |
130 | 58.1k | do { \ |
131 | 58.1k | i_data -= (x); \ |
132 | 58.1k | p_data += (x); \ |
133 | 58.1k | } while (0) |
134 | | |
135 | | |
136 | | typedef struct chapters_array_t |
137 | | { |
138 | | unsigned int i_size; |
139 | | seekpoint_t ** pp_chapters; |
140 | | } chapters_array_t; |
141 | | |
142 | | static seekpoint_t * getChapterEntry( unsigned int i_index, chapters_array_t *p_array ) |
143 | 2.12k | { |
144 | 2.12k | if ( i_index > 4096 ) return NULL; |
145 | 1.73k | if ( i_index >= p_array->i_size ) |
146 | 1.25k | { |
147 | 1.25k | unsigned int i_newsize = p_array->i_size; |
148 | 4.57k | while( i_index >= i_newsize ) i_newsize += 50; |
149 | | |
150 | 1.25k | if ( !p_array->pp_chapters ) |
151 | 1.02k | { |
152 | 1.02k | p_array->pp_chapters = calloc( i_newsize, sizeof( seekpoint_t * ) ); |
153 | 1.02k | if ( !p_array->pp_chapters ) return NULL; |
154 | 1.02k | p_array->i_size = i_newsize; |
155 | 1.02k | } else { |
156 | 232 | seekpoint_t **tmp = calloc( i_newsize, sizeof( seekpoint_t * ) ); |
157 | 232 | if ( !tmp ) return NULL; |
158 | 232 | memcpy( tmp, p_array->pp_chapters, p_array->i_size * sizeof( seekpoint_t * ) ); |
159 | 232 | free( p_array->pp_chapters ); |
160 | 232 | p_array->pp_chapters = tmp; |
161 | 232 | p_array->i_size = i_newsize; |
162 | 232 | } |
163 | 1.25k | } |
164 | 1.73k | if ( !p_array->pp_chapters[i_index] ) |
165 | 1.70k | p_array->pp_chapters[i_index] = vlc_seekpoint_New(); |
166 | 1.73k | return p_array->pp_chapters[i_index]; |
167 | 1.73k | } |
168 | | |
169 | 3.61k | #define XIPHMETA_Title (1 << 0) |
170 | 4.85k | #define XIPHMETA_Artist (1 << 1) |
171 | 3.52k | #define XIPHMETA_Genre (1 << 2) |
172 | 0 | #define XIPHMETA_Copyright (1 << 3) |
173 | 417 | #define XIPHMETA_Album (1 << 4) |
174 | 9.09k | #define XIPHMETA_TrackNum (1 << 5) |
175 | 1.12k | #define XIPHMETA_Description (1 << 6) |
176 | 1.60k | #define XIPHMETA_Rating (1 << 7) |
177 | 2.26k | #define XIPHMETA_Date (1 << 8) |
178 | 0 | #define XIPHMETA_Language (1 << 9) |
179 | 257 | #define XIPHMETA_Publisher (1 << 10) |
180 | 847 | #define XIPHMETA_EncodedBy (1 << 11) |
181 | 641 | #define XIPHMETA_TrackTotal (1 << 12) |
182 | | |
183 | | static char * xiph_ExtractCueSheetMeta( const char *psz_line, |
184 | | const char *psz_tag, int i_tag, |
185 | | bool b_quoted ) |
186 | 7.79k | { |
187 | 7.79k | if( !strncasecmp( psz_line, psz_tag, i_tag ) ) |
188 | 452 | { |
189 | 452 | if( !b_quoted ) |
190 | 161 | return strdup( &psz_line[i_tag] ); |
191 | | |
192 | | /* Unquote string value */ |
193 | 291 | char *psz_value = malloc( strlen( psz_line ) - i_tag + 1 ); |
194 | 291 | if( psz_value ) |
195 | 291 | { |
196 | 291 | char *psz_out = psz_value; |
197 | 291 | psz_line += i_tag; |
198 | 291 | bool b_escaped = false; |
199 | 2.35k | while( *psz_line ) |
200 | 2.06k | { |
201 | 2.06k | switch( *psz_line ) |
202 | 2.06k | { |
203 | 96 | case '\\': |
204 | 96 | if( b_escaped ) |
205 | 0 | { |
206 | 0 | b_escaped = false; |
207 | 0 | *(psz_out++) = *psz_line; |
208 | 0 | } |
209 | 96 | else |
210 | 96 | { |
211 | 96 | b_escaped = true; |
212 | 96 | } |
213 | 96 | break; |
214 | 96 | case '"': |
215 | 96 | if( b_escaped ) |
216 | 96 | { |
217 | 96 | b_escaped = false; |
218 | 96 | *(psz_out++) = *psz_line; |
219 | 96 | } |
220 | 96 | break; |
221 | 1.87k | default: |
222 | 1.87k | *(psz_out++) = *psz_line; |
223 | 1.87k | break; |
224 | 2.06k | } |
225 | 2.06k | psz_line++; |
226 | 2.06k | } |
227 | 291 | *psz_out = 0; |
228 | 291 | return psz_value; |
229 | 291 | } |
230 | 291 | } |
231 | 7.33k | return NULL; |
232 | 7.79k | } |
233 | | |
234 | | static void xiph_ParseCueSheetMeta( unsigned *pi_flags, vlc_meta_t *p_meta, |
235 | | const char *psz_line, |
236 | | int *pi_seekpoint, seekpoint_t ***ppp_seekpoint, |
237 | | seekpoint_t **pp_tmppoint, bool *pb_valid ) |
238 | 2.08k | { |
239 | 2.08k | VLC_UNUSED(pi_seekpoint); |
240 | 2.08k | VLC_UNUSED(ppp_seekpoint); |
241 | | |
242 | 2.08k | seekpoint_t *p_seekpoint = *pp_tmppoint; |
243 | 2.08k | char *psz_string; |
244 | | |
245 | 2.08k | #define TRY_EXTRACT_CUEMETA(var, string, quoted) \ |
246 | 7.46k | if( !(*pi_flags & XIPHMETA_##var) &&\ |
247 | 7.46k | ( psz_string = xiph_ExtractCueSheetMeta( psz_line, string, sizeof(string) - 1, quoted ) ) )\ |
248 | 7.46k | {\ |
249 | 404 | vlc_meta_Set( p_meta, vlc_meta_##var, psz_string );\ |
250 | 404 | free( psz_string );\ |
251 | 404 | *pi_flags |= XIPHMETA_##var;\ |
252 | 404 | } |
253 | | |
254 | 2.08k | TRY_EXTRACT_CUEMETA(Title, "TITLE \"", true) |
255 | 1.84k | else TRY_EXTRACT_CUEMETA(Genre, "REM GENRE ", false) |
256 | 1.84k | else TRY_EXTRACT_CUEMETA(Date, "REM DATE ", false) |
257 | 1.68k | else TRY_EXTRACT_CUEMETA(Artist, "PERFORMER \"", true) |
258 | 1.68k | else if( !strncasecmp( psz_line, " TRACK ", 8 ) ) |
259 | 52 | { |
260 | 52 | if( p_seekpoint ) |
261 | 1 | { |
262 | 1 | if( *pb_valid ) |
263 | 1 | TAB_APPEND( *pi_seekpoint, *ppp_seekpoint, p_seekpoint ); |
264 | 1 | else |
265 | 1 | vlc_seekpoint_Delete( p_seekpoint ); |
266 | 1 | *pb_valid = false; |
267 | 1 | } |
268 | 52 | *pp_tmppoint = p_seekpoint = vlc_seekpoint_New(); |
269 | 52 | } |
270 | 1.63k | else if( p_seekpoint && !strncasecmp( psz_line, " INDEX 01 ", 13 ) ) |
271 | 0 | { |
272 | 0 | unsigned m, s, f; |
273 | 0 | if( sscanf( &psz_line[13], "%u:%u:%u", &m, &s, &f ) == 3 ) |
274 | 0 | { |
275 | 0 | p_seekpoint->i_time_offset = vlc_tick_from_sec(m * 60 + s) + vlc_tick_from_samples(f, 75); |
276 | 0 | *pb_valid = true; |
277 | 0 | } |
278 | 0 | } |
279 | 1.63k | else if( p_seekpoint && !p_seekpoint->psz_name ) |
280 | 982 | { |
281 | 982 | p_seekpoint->psz_name = xiph_ExtractCueSheetMeta( psz_line, " TITLE \"", 11, true ); |
282 | 982 | } |
283 | 2.08k | } |
284 | | |
285 | | static void xiph_ParseCueSheet( unsigned *pi_flags, vlc_meta_t *p_meta, |
286 | | const char *p_data, int i_data, |
287 | | int *pi_seekpoint, seekpoint_t ***ppp_seekpoint ) |
288 | 618 | { |
289 | 618 | seekpoint_t *p_seekpoint = NULL; |
290 | 618 | bool b_valid = false; |
291 | | |
292 | 618 | const char *p_head = p_data; |
293 | 618 | const char *p_tail = p_head; |
294 | 112k | while( p_tail < p_data + i_data ) |
295 | 111k | { |
296 | 111k | if( *p_tail == 0x0D ) |
297 | 2.08k | { |
298 | 2.08k | char *psz = strndup( p_head, p_tail - p_head ); |
299 | 2.08k | if( psz ) |
300 | 2.08k | { |
301 | 2.08k | xiph_ParseCueSheetMeta( pi_flags, p_meta, psz, |
302 | 2.08k | pi_seekpoint, ppp_seekpoint, |
303 | 2.08k | &p_seekpoint, &b_valid ); |
304 | 2.08k | free( psz ); |
305 | 2.08k | } |
306 | 2.08k | if( *(++p_tail) == 0x0A ) |
307 | 582 | p_tail++; |
308 | 2.08k | p_head = p_tail; |
309 | 2.08k | } |
310 | 109k | else |
311 | 109k | { |
312 | 109k | p_tail++; |
313 | 109k | } |
314 | 111k | } |
315 | | |
316 | | |
317 | 618 | if( p_seekpoint ) |
318 | 51 | { |
319 | 51 | if( b_valid ) |
320 | 51 | TAB_APPEND( *pi_seekpoint, *ppp_seekpoint, p_seekpoint ); |
321 | 51 | else |
322 | 51 | vlc_seekpoint_Delete( p_seekpoint ); |
323 | 51 | } |
324 | 618 | } |
325 | | |
326 | | void vorbis_ParseComment( es_format_t *p_fmt, vlc_meta_t **pp_meta, |
327 | | const uint8_t *p_data, size_t i_data, |
328 | | int *i_attachments, input_attachment_t ***attachments, |
329 | | int *i_cover_score, int *i_cover_idx, |
330 | | int *i_seekpoint, seekpoint_t ***ppp_seekpoint ) |
331 | 9.97k | { |
332 | 9.97k | if( i_data < 8 ) |
333 | 36 | return; |
334 | | |
335 | 9.93k | uint32_t vendor_length = GetDWLE(p_data); RM(4); |
336 | | |
337 | 9.93k | if( vendor_length > i_data ) |
338 | 746 | return; /* invalid length */ |
339 | | |
340 | 9.19k | RM(vendor_length); /* TODO: handle vendor payload */ |
341 | | |
342 | 9.19k | if( i_data < 4 ) |
343 | 1 | return; |
344 | | |
345 | 9.19k | uint32_t i_comment = GetDWLE(p_data); RM(4); |
346 | | |
347 | 9.19k | if( i_comment > i_data || i_comment == 0 ) |
348 | 1.06k | return; /* invalid length */ |
349 | | |
350 | | /* */ |
351 | 8.12k | vlc_meta_t *p_meta = *pp_meta; |
352 | 8.12k | if( !p_meta ) |
353 | 209 | *pp_meta = p_meta = vlc_meta_New(); |
354 | | |
355 | 8.12k | if( unlikely( !p_meta ) ) |
356 | 0 | return; |
357 | | |
358 | | /* */ |
359 | 8.12k | unsigned hasMetaFlags = 0; |
360 | | |
361 | 8.12k | chapters_array_t chapters_array = { 0, NULL }; |
362 | | |
363 | 21.1k | for( ; i_comment > 0 && i_data >= 4; i_comment-- ) |
364 | 17.4k | { |
365 | 17.4k | uint32_t comment_size = GetDWLE(p_data); RM(4); |
366 | | |
367 | 17.4k | if( comment_size > i_data ) |
368 | 4.36k | break; |
369 | | |
370 | 13.0k | if( comment_size == 0 ) |
371 | 622 | continue; |
372 | | |
373 | 12.4k | char* psz_comment = malloc( comment_size + 1 ); |
374 | | |
375 | 12.4k | if( unlikely( !psz_comment ) ) |
376 | 0 | goto next_comment; |
377 | | |
378 | 12.4k | memcpy( psz_comment, p_data, comment_size ); |
379 | 12.4k | psz_comment[comment_size] = '\0'; |
380 | | |
381 | 12.4k | #define IF_EXTRACT(txt,var) \ |
382 | 106k | if( !strncasecmp(psz_comment, txt, strlen(txt)) ) \ |
383 | 106k | { \ |
384 | 5.46k | size_t key_length = strlen(txt); \ |
385 | 5.46k | EnsureUTF8( psz_comment + key_length ); \ |
386 | 5.46k | const char *oldval = vlc_meta_Get( p_meta, vlc_meta_ ## var ); \ |
387 | 5.46k | if( oldval && (hasMetaFlags & XIPHMETA_##var)) \ |
388 | 5.46k | { \ |
389 | 1.97k | char * newval; \ |
390 | 1.97k | if( asprintf( &newval, "%s,%s", oldval, &psz_comment[key_length] ) == -1 ) \ |
391 | 1.97k | newval = NULL; \ |
392 | 1.97k | vlc_meta_Set( p_meta, vlc_meta_ ## var, newval ); \ |
393 | 1.97k | free( newval ); \ |
394 | 1.97k | } \ |
395 | 5.46k | else \ |
396 | 5.46k | vlc_meta_Set( p_meta, vlc_meta_ ## var, &psz_comment[key_length] ); \ |
397 | 5.46k | hasMetaFlags |= XIPHMETA_##var; \ |
398 | 5.46k | } |
399 | | |
400 | 12.4k | #define IF_EXTRACT_ONCE_NUMBER(txt,var) \ |
401 | 17.4k | if( !strncasecmp(psz_comment, txt, strlen(txt)) && !(hasMetaFlags & XIPHMETA_##var) ) \ |
402 | 17.4k | { \ |
403 | 385 | bool isnum = true; \ |
404 | 385 | const char *num_str = &psz_comment[strlen(txt)], *c; \ |
405 | 1.90k | for (c = num_str; isnum && *c != '\0'; c++) { \ |
406 | 1.51k | isnum = *c >= '0' && *c <= '9'; \ |
407 | 1.51k | } \ |
408 | 385 | if (isnum) \ |
409 | 385 | { \ |
410 | 256 | vlc_meta_Set( p_meta, vlc_meta_ ## var, num_str ); \ |
411 | 256 | hasMetaFlags |= XIPHMETA_##var; \ |
412 | 256 | } \ |
413 | 385 | } |
414 | | |
415 | 12.4k | IF_EXTRACT("TITLE=", Title ) |
416 | 11.7k | else IF_EXTRACT("ARTIST=", Artist ) |
417 | 10.1k | else IF_EXTRACT("GENRE=", Genre ) |
418 | 9.30k | else IF_EXTRACT("COPYRIGHT=", Copyright ) |
419 | 9.30k | else IF_EXTRACT("ALBUM=", Album ) |
420 | 9.09k | else if( !(hasMetaFlags & XIPHMETA_TrackNum) && !strncasecmp(psz_comment, "TRACKNUMBER=", strlen("TRACKNUMBER=" ) ) ) |
421 | 252 | { |
422 | | /* Yeah yeah, such a clever idea, let's put xx/xx inside TRACKNUMBER |
423 | | * Oh, and let's not use TRACKTOTAL or TOTALTRACKS... */ |
424 | 252 | short unsigned u_track, u_total; |
425 | 252 | int nb_values = sscanf( &psz_comment[strlen("TRACKNUMBER=")], "%hu/%hu", &u_track, &u_total ); |
426 | 252 | if( nb_values >= 1 ) |
427 | 1 | { |
428 | 1 | char str[6]; |
429 | 1 | snprintf(str, 6, "%u", u_track); |
430 | 1 | vlc_meta_Set( p_meta, vlc_meta_TrackNumber, str ); |
431 | 1 | hasMetaFlags |= XIPHMETA_TrackNum; |
432 | 1 | if( nb_values >= 2 ) |
433 | 0 | { |
434 | 0 | snprintf(str, 6, "%u", u_total); |
435 | 0 | vlc_meta_Set( p_meta, vlc_meta_TrackTotal, str ); |
436 | 0 | hasMetaFlags |= XIPHMETA_TrackTotal; |
437 | 0 | } |
438 | 1 | } |
439 | 252 | } |
440 | 8.84k | else IF_EXTRACT_ONCE_NUMBER("TRACKTOTAL=", TrackTotal ) |
441 | 8.58k | else IF_EXTRACT_ONCE_NUMBER("TOTALTRACKS=", TrackTotal ) |
442 | 8.45k | else IF_EXTRACT("DESCRIPTION=", Description ) |
443 | 8.29k | else IF_EXTRACT("COMMENT=", Description ) |
444 | 8.16k | else IF_EXTRACT("COMMENTS=", Description ) |
445 | 7.89k | else IF_EXTRACT("RATING=", Rating ) |
446 | 7.08k | else IF_EXTRACT("DATE=", Date ) |
447 | 6.95k | else if( !strncasecmp(psz_comment, "LANGUAGE=", strlen("LANGUAGE=") ) ) |
448 | 0 | { |
449 | 0 | IF_EXTRACT("LANGUAGE=",Language) |
450 | 0 | if( p_fmt ) |
451 | 0 | { |
452 | 0 | free( p_fmt->psz_language ); |
453 | 0 | p_fmt->psz_language = strdup(&psz_comment[strlen("LANGUAGE=")]); |
454 | 0 | } |
455 | 0 | } |
456 | 6.95k | else IF_EXTRACT("ORGANIZATION=", Publisher ) |
457 | 6.82k | else IF_EXTRACT("ENCODER=", EncodedBy ) |
458 | 6.33k | else if( !strncasecmp( psz_comment, "METADATA_BLOCK_PICTURE=", strlen("METADATA_BLOCK_PICTURE="))) |
459 | 373 | { |
460 | 373 | if( attachments == NULL ) |
461 | 0 | goto next_comment; |
462 | | |
463 | 373 | uint8_t *p_picture; |
464 | 373 | size_t i_size = vlc_b64_decode_binary( &p_picture, &psz_comment[strlen("METADATA_BLOCK_PICTURE=")]); |
465 | 373 | input_attachment_t *p_attachment = ParseFlacPicture( p_picture, |
466 | 373 | i_size, *i_attachments, i_cover_score, i_cover_idx ); |
467 | 373 | free( p_picture ); |
468 | 373 | if( p_attachment ) |
469 | 0 | { |
470 | 0 | TAB_APPEND_CAST( (input_attachment_t**), |
471 | 0 | *i_attachments, *attachments, p_attachment ); |
472 | 0 | } |
473 | 373 | } |
474 | 5.96k | else if( !strncasecmp(psz_comment, "CHAPTER", 7) ) |
475 | 2.45k | { |
476 | 2.45k | unsigned int i_chapt; |
477 | 2.45k | seekpoint_t *p_seekpoint = NULL; |
478 | | |
479 | 40.9k | for( int i = 0; psz_comment[i] && psz_comment[i] != '='; i++ ) |
480 | 38.4k | if( psz_comment[i] >= 'a' && psz_comment[i] <= 'z' ) |
481 | 723 | psz_comment[i] -= 'a' - 'A'; |
482 | | |
483 | 2.45k | if( strstr( psz_comment, "NAME=" ) && |
484 | 2.12k | sscanf( psz_comment, "CHAPTER%uNAME=", &i_chapt ) == 1 ) |
485 | 2.12k | { |
486 | 2.12k | char *p = strchr( psz_comment, '=' ); |
487 | 2.12k | p_seekpoint = getChapterEntry( i_chapt, &chapters_array ); |
488 | 2.12k | if ( !p || ! p_seekpoint ) goto next_comment; |
489 | 1.73k | EnsureUTF8( ++p ); |
490 | 1.73k | if ( ! p_seekpoint->psz_name ) |
491 | 1.70k | p_seekpoint->psz_name = strdup( p ); |
492 | 1.73k | } |
493 | 336 | else if( sscanf( psz_comment, "CHAPTER%u=", &i_chapt ) == 1 ) |
494 | 329 | { |
495 | 329 | unsigned int h, m, s, ms; |
496 | 329 | char *p = strchr( psz_comment, '=' ); |
497 | 329 | if( p && sscanf( ++p, "%u:%u:%u.%u", &h, &m, &s, &ms ) == 4 ) |
498 | 0 | { |
499 | 0 | p_seekpoint = getChapterEntry( i_chapt, &chapters_array ); |
500 | 0 | if ( ! p_seekpoint ) goto next_comment; |
501 | 0 | p_seekpoint->i_time_offset = vlc_tick_from_sec(h * 3600 + m * 60 + s) + VLC_TICK_FROM_MS(ms); |
502 | 0 | } |
503 | 329 | } |
504 | 2.45k | } |
505 | 3.50k | else if( !strncasecmp(psz_comment, "cuesheet=", 9) ) |
506 | 618 | { |
507 | 618 | EnsureUTF8( &psz_comment[9] ); |
508 | 618 | xiph_ParseCueSheet( &hasMetaFlags, p_meta, &psz_comment[9], comment_size - 9, |
509 | 618 | i_seekpoint, ppp_seekpoint ); |
510 | 618 | } |
511 | 2.89k | else if( strchr( psz_comment, '=' ) ) |
512 | 1.59k | { |
513 | | /* generic (PERFORMER/LICENSE/ORGANIZATION/LOCATION/CONTACT/ISRC, |
514 | | * undocumented tags and replay gain ) */ |
515 | 1.59k | char *p = strchr( psz_comment, '=' ); |
516 | 1.59k | *p++ = '\0'; |
517 | 1.59k | EnsureUTF8( p ); |
518 | | |
519 | 14.9k | for( int i = 0; psz_comment[i]; i++ ) |
520 | 13.3k | if( psz_comment[i] >= 'a' && psz_comment[i] <= 'z' ) |
521 | 1.62k | psz_comment[i] -= 'a' - 'A'; |
522 | | |
523 | 1.59k | vlc_meta_SetExtra( p_meta, psz_comment, p ); |
524 | 1.59k | } |
525 | 12.0k | #undef IF_EXTRACT |
526 | 12.4k | next_comment: |
527 | 12.4k | free( psz_comment ); |
528 | 12.4k | RM( comment_size ); |
529 | 12.4k | } |
530 | 8.12k | #undef RM |
531 | | |
532 | 174k | for ( unsigned int i=0; i<chapters_array.i_size; i++ ) |
533 | 166k | { |
534 | 166k | if ( !chapters_array.pp_chapters[i] ) continue; |
535 | 1.70k | TAB_APPEND_CAST( (seekpoint_t**), *i_seekpoint, *ppp_seekpoint, |
536 | 1.70k | chapters_array.pp_chapters[i] ); |
537 | 1.70k | } |
538 | 8.12k | free( chapters_array.pp_chapters ); |
539 | 8.12k | } |
540 | | |
541 | | const char *FindKateCategoryName( const char *psz_tag ) |
542 | 0 | { |
543 | 0 | for( size_t i = 0; i < sizeof(Katei18nCategories)/sizeof(Katei18nCategories[0]); i++ ) |
544 | 0 | { |
545 | 0 | if( !strcmp( psz_tag, Katei18nCategories[i].psz_tag ) ) |
546 | 0 | return Katei18nCategories[i].psz_i18n; |
547 | 0 | } |
548 | 0 | return N_("Unknown category"); |
549 | 0 | } |