/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 | 4.28k | { |
42 | 4.28k | uint32_t type, len; |
43 | | |
44 | 4.28k | if( size < 8 ) |
45 | 467 | return NULL; |
46 | 3.81k | #define RM(x) \ |
47 | 23.6k | do { \ |
48 | 23.6k | assert(size >= (x)); \ |
49 | 23.6k | size -= (x); \ |
50 | 23.6k | p_data += (x); \ |
51 | 23.6k | } while (0) |
52 | | |
53 | 3.81k | type = GetDWBE( p_data ); |
54 | 3.81k | RM(4); |
55 | 3.81k | len = GetDWBE( p_data ); |
56 | 3.81k | RM(4); |
57 | | |
58 | 3.81k | if( size < len ) |
59 | 118 | return NULL; |
60 | | |
61 | 3.69k | char *mime = strndup( (const char *)p_data, len ); |
62 | 3.69k | if( unlikely(mime == NULL) ) |
63 | 0 | return NULL; |
64 | 3.69k | RM(len); |
65 | | |
66 | 3.69k | if( size < 4 ) |
67 | 225 | { |
68 | 225 | free( mime ); |
69 | 225 | return NULL; |
70 | 225 | } |
71 | | |
72 | 3.47k | len = GetDWBE( p_data ); |
73 | 3.47k | RM(4); |
74 | | |
75 | 3.47k | if( size < len ) |
76 | 238 | { |
77 | 238 | free( mime ); |
78 | 238 | return NULL; |
79 | 238 | } |
80 | | |
81 | 3.23k | input_attachment_t *p_attachment = NULL; |
82 | 3.23k | char *description = strndup( (const char *)p_data, len ); |
83 | 3.23k | if( unlikely(description == NULL) ) |
84 | 0 | goto error; |
85 | 3.23k | RM(len); |
86 | | |
87 | 3.23k | EnsureUTF8( description ); |
88 | | |
89 | 3.23k | if( size < 20 ) |
90 | 430 | goto error; |
91 | | |
92 | 2.80k | RM(4 * 4); /* skip */ |
93 | | |
94 | 2.80k | len = GetDWBE( p_data ); |
95 | 2.80k | RM(4); |
96 | | |
97 | 2.80k | if( size < len ) |
98 | 482 | goto error; |
99 | | |
100 | | /* printf( "Picture type=%"PRIu32" mime=%s description='%s' " |
101 | | "file length=%zu\n", type, mime, description, len ); */ |
102 | | |
103 | 2.32k | char name[7 + (sizeof (i_attachments) * 3) + 4 + 1]; |
104 | | |
105 | 2.32k | snprintf( name, sizeof (name), "picture%u", i_attachments ); |
106 | | |
107 | 2.32k | if( !strcasecmp( mime, "image/jpeg" ) ) |
108 | 1 | strcat( name, ".jpg" ); |
109 | 2.32k | else if( !strcasecmp( mime, "image/png" ) ) |
110 | 10 | strcat( name, ".png" ); |
111 | | |
112 | 2.32k | p_attachment = vlc_input_attachment_New( name, mime, description, p_data, |
113 | 2.32k | size /* XXX: len instead? */ ); |
114 | | |
115 | 2.32k | if( type < ARRAY_SIZE(ID3v2_cover_scores) && |
116 | 1.67k | *i_cover_score < ID3v2_cover_scores[type] ) |
117 | 45 | { |
118 | 45 | *i_cover_idx = i_attachments; |
119 | 45 | *i_cover_score = ID3v2_cover_scores[type]; |
120 | 45 | } |
121 | | |
122 | 3.23k | error: |
123 | 3.23k | free( mime ); |
124 | 3.23k | free( description ); |
125 | 3.23k | return p_attachment; |
126 | 2.32k | } |
127 | | |
128 | | #undef RM |
129 | | #define RM(x) \ |
130 | 77.6k | do { \ |
131 | 77.6k | i_data -= (x); \ |
132 | 77.6k | p_data += (x); \ |
133 | 77.6k | } 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 | 776 | { |
144 | 776 | if ( i_index > 4096 ) return NULL; |
145 | 716 | if ( i_index >= p_array->i_size ) |
146 | 684 | { |
147 | 684 | unsigned int i_newsize = p_array->i_size; |
148 | 2.80k | while( i_index >= i_newsize ) i_newsize += 50; |
149 | | |
150 | 684 | if ( !p_array->pp_chapters ) |
151 | 664 | { |
152 | 664 | p_array->pp_chapters = calloc( i_newsize, sizeof( seekpoint_t * ) ); |
153 | 664 | if ( !p_array->pp_chapters ) return NULL; |
154 | 664 | p_array->i_size = i_newsize; |
155 | 664 | } else { |
156 | 20 | seekpoint_t **tmp = calloc( i_newsize, sizeof( seekpoint_t * ) ); |
157 | 20 | if ( !tmp ) return NULL; |
158 | 20 | memcpy( tmp, p_array->pp_chapters, p_array->i_size * sizeof( seekpoint_t * ) ); |
159 | 20 | free( p_array->pp_chapters ); |
160 | 20 | p_array->pp_chapters = tmp; |
161 | 20 | p_array->i_size = i_newsize; |
162 | 20 | } |
163 | 684 | } |
164 | 716 | if ( !p_array->pp_chapters[i_index] ) |
165 | 698 | p_array->pp_chapters[i_index] = vlc_seekpoint_New(); |
166 | 716 | return p_array->pp_chapters[i_index]; |
167 | 716 | } |
168 | | |
169 | 5.24k | #define XIPHMETA_Title (1 << 0) |
170 | 4.93k | #define XIPHMETA_Artist (1 << 1) |
171 | 6.08k | #define XIPHMETA_Genre (1 << 2) |
172 | 291 | #define XIPHMETA_Copyright (1 << 3) |
173 | 1.02k | #define XIPHMETA_Album (1 << 4) |
174 | 11.9k | #define XIPHMETA_TrackNum (1 << 5) |
175 | 763 | #define XIPHMETA_Description (1 << 6) |
176 | 1.95k | #define XIPHMETA_Rating (1 << 7) |
177 | 4.62k | #define XIPHMETA_Date (1 << 8) |
178 | 241 | #define XIPHMETA_Language (1 << 9) |
179 | 181 | #define XIPHMETA_Publisher (1 << 10) |
180 | 1.05k | #define XIPHMETA_EncodedBy (1 << 11) |
181 | 783 | #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 | 17.2k | { |
187 | 17.2k | if( !strncasecmp( psz_line, psz_tag, i_tag ) ) |
188 | 282 | { |
189 | 282 | if( !b_quoted ) |
190 | 68 | return strdup( &psz_line[i_tag] ); |
191 | | |
192 | | /* Unquote string value */ |
193 | 214 | char *psz_value = malloc( strlen( psz_line ) - i_tag + 1 ); |
194 | 214 | if( psz_value ) |
195 | 214 | { |
196 | 214 | char *psz_out = psz_value; |
197 | 214 | psz_line += i_tag; |
198 | 214 | bool b_escaped = false; |
199 | 8.47k | while( *psz_line ) |
200 | 8.26k | { |
201 | 8.26k | switch( *psz_line ) |
202 | 8.26k | { |
203 | 600 | case '\\': |
204 | 600 | if( b_escaped ) |
205 | 226 | { |
206 | 226 | b_escaped = false; |
207 | 226 | *(psz_out++) = *psz_line; |
208 | 226 | } |
209 | 374 | else |
210 | 374 | { |
211 | 374 | b_escaped = true; |
212 | 374 | } |
213 | 600 | break; |
214 | 457 | case '"': |
215 | 457 | if( b_escaped ) |
216 | 137 | { |
217 | 137 | b_escaped = false; |
218 | 137 | *(psz_out++) = *psz_line; |
219 | 137 | } |
220 | 457 | break; |
221 | 7.20k | default: |
222 | 7.20k | *(psz_out++) = *psz_line; |
223 | 7.20k | break; |
224 | 8.26k | } |
225 | 8.26k | psz_line++; |
226 | 8.26k | } |
227 | 214 | *psz_out = 0; |
228 | 214 | return psz_value; |
229 | 214 | } |
230 | 214 | } |
231 | 16.9k | return NULL; |
232 | 17.2k | } |
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 | 4.51k | { |
239 | 4.51k | VLC_UNUSED(pi_seekpoint); |
240 | 4.51k | VLC_UNUSED(ppp_seekpoint); |
241 | | |
242 | 4.51k | seekpoint_t *p_seekpoint = *pp_tmppoint; |
243 | 4.51k | char *psz_string; |
244 | | |
245 | 4.51k | #define TRY_EXTRACT_CUEMETA(var, string, quoted) \ |
246 | 17.6k | if( !(*pi_flags & XIPHMETA_##var) &&\ |
247 | 17.6k | ( psz_string = xiph_ExtractCueSheetMeta( psz_line, string, sizeof(string) - 1, quoted ) ) )\ |
248 | 17.6k | {\ |
249 | 220 | vlc_meta_Set( p_meta, vlc_meta_##var, psz_string );\ |
250 | 220 | free( psz_string );\ |
251 | 220 | *pi_flags |= XIPHMETA_##var;\ |
252 | 220 | } |
253 | | |
254 | 4.51k | TRY_EXTRACT_CUEMETA(Title, "TITLE \"", true) |
255 | 4.40k | else TRY_EXTRACT_CUEMETA(Genre, "REM GENRE ", false) |
256 | 4.36k | else TRY_EXTRACT_CUEMETA(Date, "REM DATE ", false) |
257 | 4.33k | else TRY_EXTRACT_CUEMETA(Artist, "PERFORMER \"", true) |
258 | 4.29k | else if( !strncasecmp( psz_line, " TRACK ", 8 ) ) |
259 | 215 | { |
260 | 215 | if( p_seekpoint ) |
261 | 17 | { |
262 | 17 | if( *pb_valid ) |
263 | 17 | TAB_APPEND( *pi_seekpoint, *ppp_seekpoint, p_seekpoint ); |
264 | 17 | else |
265 | 17 | vlc_seekpoint_Delete( p_seekpoint ); |
266 | 17 | *pb_valid = false; |
267 | 17 | } |
268 | 215 | *pp_tmppoint = p_seekpoint = vlc_seekpoint_New(); |
269 | 215 | } |
270 | 4.08k | else if( p_seekpoint && !strncasecmp( psz_line, " INDEX 01 ", 13 ) ) |
271 | 10 | { |
272 | 10 | unsigned m, s, f; |
273 | 10 | 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 | 10 | } |
279 | 4.07k | else if( p_seekpoint && !p_seekpoint->psz_name ) |
280 | 1.09k | { |
281 | 1.09k | p_seekpoint->psz_name = xiph_ExtractCueSheetMeta( psz_line, " TITLE \"", 11, true ); |
282 | 1.09k | } |
283 | 4.51k | } |
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 | 1.28k | { |
289 | 1.28k | seekpoint_t *p_seekpoint = NULL; |
290 | 1.28k | bool b_valid = false; |
291 | | |
292 | 1.28k | const char *p_head = p_data; |
293 | 1.28k | const char *p_tail = p_head; |
294 | 284k | while( p_tail < p_data + i_data ) |
295 | 283k | { |
296 | 283k | if( *p_tail == 0x0D ) |
297 | 4.51k | { |
298 | 4.51k | char *psz = strndup( p_head, p_tail - p_head ); |
299 | 4.51k | if( psz ) |
300 | 4.51k | { |
301 | 4.51k | xiph_ParseCueSheetMeta( pi_flags, p_meta, psz, |
302 | 4.51k | pi_seekpoint, ppp_seekpoint, |
303 | 4.51k | &p_seekpoint, &b_valid ); |
304 | 4.51k | free( psz ); |
305 | 4.51k | } |
306 | 4.51k | if( *(++p_tail) == 0x0A ) |
307 | 140 | p_tail++; |
308 | 4.51k | p_head = p_tail; |
309 | 4.51k | } |
310 | 279k | else |
311 | 279k | { |
312 | 279k | p_tail++; |
313 | 279k | } |
314 | 283k | } |
315 | | |
316 | | |
317 | 1.28k | if( p_seekpoint ) |
318 | 198 | { |
319 | 198 | if( b_valid ) |
320 | 198 | TAB_APPEND( *pi_seekpoint, *ppp_seekpoint, p_seekpoint ); |
321 | 198 | else |
322 | 198 | vlc_seekpoint_Delete( p_seekpoint ); |
323 | 198 | } |
324 | 1.28k | } |
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 | 17.7k | { |
332 | 17.7k | if( i_data < 8 ) |
333 | 1.69k | return; |
334 | | |
335 | 16.0k | uint32_t vendor_length = GetDWLE(p_data); RM(4); |
336 | | |
337 | 16.0k | if( vendor_length > i_data ) |
338 | 2.53k | return; /* invalid length */ |
339 | | |
340 | 13.4k | RM(vendor_length); /* TODO: handle vendor payload */ |
341 | | |
342 | 13.4k | if( i_data < 4 ) |
343 | 69 | return; |
344 | | |
345 | 13.4k | uint32_t i_comment = GetDWLE(p_data); RM(4); |
346 | | |
347 | 13.4k | if( i_comment > i_data || i_comment == 0 ) |
348 | 1.77k | return; /* invalid length */ |
349 | | |
350 | | /* */ |
351 | 11.6k | vlc_meta_t *p_meta = *pp_meta; |
352 | 11.6k | if( !p_meta ) |
353 | 3.40k | *pp_meta = p_meta = vlc_meta_New(); |
354 | | |
355 | 11.6k | if( unlikely( !p_meta ) ) |
356 | 0 | return; |
357 | | |
358 | | /* */ |
359 | 11.6k | unsigned hasMetaFlags = 0; |
360 | | |
361 | 11.6k | chapters_array_t chapters_array = { 0, NULL }; |
362 | | |
363 | 26.5k | for( ; i_comment > 0 && i_data >= 4; i_comment-- ) |
364 | 20.5k | { |
365 | 20.5k | uint32_t comment_size = GetDWLE(p_data); RM(4); |
366 | | |
367 | 20.5k | if( comment_size > i_data ) |
368 | 5.63k | break; |
369 | | |
370 | 14.9k | if( comment_size == 0 ) |
371 | 691 | continue; |
372 | | |
373 | 14.2k | char* psz_comment = malloc( comment_size + 1 ); |
374 | | |
375 | 14.2k | if( unlikely( !psz_comment ) ) |
376 | 0 | goto next_comment; |
377 | | |
378 | 14.2k | memcpy( psz_comment, p_data, comment_size ); |
379 | 14.2k | psz_comment[comment_size] = '\0'; |
380 | | |
381 | 14.2k | #define IF_EXTRACT(txt,var) \ |
382 | 139k | if( !strncasecmp(psz_comment, txt, strlen(txt)) ) \ |
383 | 139k | { \ |
384 | 4.67k | size_t key_length = strlen(txt); \ |
385 | 4.67k | EnsureUTF8( psz_comment + key_length ); \ |
386 | 4.67k | const char *oldval = vlc_meta_Get( p_meta, vlc_meta_ ## var ); \ |
387 | 4.67k | if( oldval && (hasMetaFlags & XIPHMETA_##var)) \ |
388 | 4.67k | { \ |
389 | 431 | char * newval; \ |
390 | 431 | if( asprintf( &newval, "%s,%s", oldval, &psz_comment[key_length] ) == -1 ) \ |
391 | 431 | newval = NULL; \ |
392 | 431 | vlc_meta_Set( p_meta, vlc_meta_ ## var, newval ); \ |
393 | 431 | free( newval ); \ |
394 | 431 | } \ |
395 | 4.67k | else \ |
396 | 4.67k | vlc_meta_Set( p_meta, vlc_meta_ ## var, &psz_comment[key_length] ); \ |
397 | 4.67k | hasMetaFlags |= XIPHMETA_##var; \ |
398 | 4.67k | } |
399 | | |
400 | 14.2k | #define IF_EXTRACT_ONCE_NUMBER(txt,var) \ |
401 | 23.2k | if( !strncasecmp(psz_comment, txt, strlen(txt)) && !(hasMetaFlags & XIPHMETA_##var) ) \ |
402 | 23.2k | { \ |
403 | 534 | bool isnum = true; \ |
404 | 534 | const char *num_str = &psz_comment[strlen(txt)], *c; \ |
405 | 2.34k | for (c = num_str; isnum && *c != '\0'; c++) { \ |
406 | 1.80k | isnum = *c >= '0' && *c <= '9'; \ |
407 | 1.80k | } \ |
408 | 534 | if (isnum) \ |
409 | 534 | { \ |
410 | 245 | vlc_meta_Set( p_meta, vlc_meta_ ## var, num_str ); \ |
411 | 245 | hasMetaFlags |= XIPHMETA_##var; \ |
412 | 245 | } \ |
413 | 534 | } |
414 | | |
415 | 14.2k | IF_EXTRACT("TITLE=", Title ) |
416 | 13.8k | else IF_EXTRACT("ARTIST=", Artist ) |
417 | 13.4k | else IF_EXTRACT("GENRE=", Genre ) |
418 | 12.6k | else IF_EXTRACT("COPYRIGHT=", Copyright ) |
419 | 12.4k | else IF_EXTRACT("ALBUM=", Album ) |
420 | 11.8k | else if( !(hasMetaFlags & XIPHMETA_TrackNum) && !strncasecmp(psz_comment, "TRACKNUMBER=", strlen("TRACKNUMBER=" ) ) ) |
421 | 108 | { |
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 | 108 | short unsigned u_track, u_total; |
425 | 108 | int nb_values = sscanf( &psz_comment[strlen("TRACKNUMBER=")], "%hu/%hu", &u_track, &u_total ); |
426 | 108 | if( nb_values >= 1 ) |
427 | 41 | { |
428 | 41 | char str[6]; |
429 | 41 | snprintf(str, 6, "%u", u_track); |
430 | 41 | vlc_meta_Set( p_meta, vlc_meta_TrackNumber, str ); |
431 | 41 | hasMetaFlags |= XIPHMETA_TrackNum; |
432 | 41 | if( nb_values >= 2 ) |
433 | 3 | { |
434 | 3 | snprintf(str, 6, "%u", u_total); |
435 | 3 | vlc_meta_Set( p_meta, vlc_meta_TrackTotal, str ); |
436 | 3 | hasMetaFlags |= XIPHMETA_TrackTotal; |
437 | 3 | } |
438 | 41 | } |
439 | 108 | } |
440 | 11.7k | else IF_EXTRACT_ONCE_NUMBER("TRACKTOTAL=", TrackTotal ) |
441 | 11.4k | else IF_EXTRACT_ONCE_NUMBER("TOTALTRACKS=", TrackTotal ) |
442 | 11.2k | else IF_EXTRACT("DESCRIPTION=", Description ) |
443 | 11.1k | else IF_EXTRACT("COMMENT=", Description ) |
444 | 10.9k | else IF_EXTRACT("COMMENTS=", Description ) |
445 | 10.8k | else IF_EXTRACT("RATING=", Rating ) |
446 | 9.85k | else IF_EXTRACT("DATE=", Date ) |
447 | 9.73k | else if( !strncasecmp(psz_comment, "LANGUAGE=", strlen("LANGUAGE=") ) ) |
448 | 129 | { |
449 | 129 | IF_EXTRACT("LANGUAGE=",Language) |
450 | 129 | 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 | 129 | } |
456 | 9.60k | else IF_EXTRACT("ORGANIZATION=", Publisher ) |
457 | 9.50k | else IF_EXTRACT("ENCODER=", EncodedBy ) |
458 | 8.89k | else if( !strncasecmp( psz_comment, "METADATA_BLOCK_PICTURE=", strlen("METADATA_BLOCK_PICTURE="))) |
459 | 165 | { |
460 | 165 | if( attachments == NULL ) |
461 | 0 | goto next_comment; |
462 | | |
463 | 165 | uint8_t *p_picture; |
464 | 165 | size_t i_size = vlc_b64_decode_binary( &p_picture, &psz_comment[strlen("METADATA_BLOCK_PICTURE=")]); |
465 | 165 | input_attachment_t *p_attachment = ParseFlacPicture( p_picture, |
466 | 165 | i_size, *i_attachments, i_cover_score, i_cover_idx ); |
467 | 165 | free( p_picture ); |
468 | 165 | if( p_attachment ) |
469 | 1 | { |
470 | 1 | TAB_APPEND_CAST( (input_attachment_t**), |
471 | 1 | *i_attachments, *attachments, p_attachment ); |
472 | 1 | } |
473 | 165 | } |
474 | 8.73k | else if( !strncasecmp(psz_comment, "CHAPTER", 7) ) |
475 | 1.04k | { |
476 | 1.04k | unsigned int i_chapt; |
477 | 1.04k | seekpoint_t *p_seekpoint = NULL; |
478 | | |
479 | 14.7k | for( int i = 0; psz_comment[i] && psz_comment[i] != '='; i++ ) |
480 | 13.7k | if( psz_comment[i] >= 'a' && psz_comment[i] <= 'z' ) |
481 | 548 | psz_comment[i] -= 'a' - 'A'; |
482 | | |
483 | 1.04k | if( strstr( psz_comment, "NAME=" ) && |
484 | 795 | sscanf( psz_comment, "CHAPTER%uNAME=", &i_chapt ) == 1 ) |
485 | 768 | { |
486 | 768 | char *p = strchr( psz_comment, '=' ); |
487 | 768 | p_seekpoint = getChapterEntry( i_chapt, &chapters_array ); |
488 | 768 | if ( !p || ! p_seekpoint ) goto next_comment; |
489 | 708 | EnsureUTF8( ++p ); |
490 | 708 | if ( ! p_seekpoint->psz_name ) |
491 | 698 | p_seekpoint->psz_name = strdup( p ); |
492 | 708 | } |
493 | 276 | else if( sscanf( psz_comment, "CHAPTER%u=", &i_chapt ) == 1 ) |
494 | 155 | { |
495 | 155 | unsigned int h, m, s, ms; |
496 | 155 | char *p = strchr( psz_comment, '=' ); |
497 | 155 | if( p && sscanf( ++p, "%u:%u:%u.%u", &h, &m, &s, &ms ) == 4 ) |
498 | 8 | { |
499 | 8 | p_seekpoint = getChapterEntry( i_chapt, &chapters_array ); |
500 | 8 | if ( ! p_seekpoint ) goto next_comment; |
501 | 8 | p_seekpoint->i_time_offset = vlc_tick_from_sec(h * 3600 + m * 60 + s) + VLC_TICK_FROM_MS(ms); |
502 | 8 | } |
503 | 155 | } |
504 | 1.04k | } |
505 | 7.68k | else if( !strncasecmp(psz_comment, "cuesheet=", 9) ) |
506 | 1.28k | { |
507 | 1.28k | EnsureUTF8( &psz_comment[9] ); |
508 | 1.28k | xiph_ParseCueSheet( &hasMetaFlags, p_meta, &psz_comment[9], comment_size - 9, |
509 | 1.28k | i_seekpoint, ppp_seekpoint ); |
510 | 1.28k | } |
511 | 6.39k | else if( strchr( psz_comment, '=' ) ) |
512 | 4.63k | { |
513 | | /* generic (PERFORMER/LICENSE/ORGANIZATION/LOCATION/CONTACT/ISRC, |
514 | | * undocumented tags and replay gain ) */ |
515 | 4.63k | char *p = strchr( psz_comment, '=' ); |
516 | 4.63k | *p++ = '\0'; |
517 | 4.63k | EnsureUTF8( p ); |
518 | | |
519 | 40.3k | for( int i = 0; psz_comment[i]; i++ ) |
520 | 35.7k | if( psz_comment[i] >= 'a' && psz_comment[i] <= 'z' ) |
521 | 5.24k | psz_comment[i] -= 'a' - 'A'; |
522 | | |
523 | 4.63k | vlc_meta_SetExtra( p_meta, psz_comment, p ); |
524 | 4.63k | } |
525 | 14.1k | #undef IF_EXTRACT |
526 | 14.2k | next_comment: |
527 | 14.2k | free( psz_comment ); |
528 | 14.2k | RM( comment_size ); |
529 | 14.2k | } |
530 | 11.6k | #undef RM |
531 | | |
532 | 117k | for ( unsigned int i=0; i<chapters_array.i_size; i++ ) |
533 | 106k | { |
534 | 106k | if ( !chapters_array.pp_chapters[i] ) continue; |
535 | 698 | TAB_APPEND_CAST( (seekpoint_t**), *i_seekpoint, *ppp_seekpoint, |
536 | 698 | chapters_array.pp_chapters[i] ); |
537 | 698 | } |
538 | 11.6k | free( chapters_array.pp_chapters ); |
539 | 11.6k | } |
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 | } |