1 /*
2 * Copyright (C) 2000-2019 the xine project
3 *
4 * This file is part of xine, a free video player.
5 *
6 * xine is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * xine 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
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19 *
20 * stream metainfo helper functions
21 * hide some xine engine details from demuxers and reduce code duplication
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <pthread.h>
32
33 #ifdef HAVE_ICONV
34 # include <iconv.h>
35 #endif
36
37 #include <xine/info_helper.h>
38 #include "xine_private.h"
39
40 /* ******************* Stream Info *************************** */
41
42 /*
43 * Check if 'info' is in bounds.
44 */
info_valid(xine_stream_private_t * stream,int info)45 static int info_valid (xine_stream_private_t *stream, int info) {
46 if ((info >= 0) && (info < XINE_STREAM_INFO_MAX))
47 return 1;
48 else {
49 xprintf (stream->s.xine, XINE_VERBOSITY_DEBUG,
50 "info_helper: invalid STREAM_INFO %d. Ignored.\n", info);
51 return 0;
52 }
53 }
54
55 /*
56 * Reset private info.
57 */
_x_stream_info_reset(xine_stream_t * s,int info)58 void _x_stream_info_reset (xine_stream_t *s, int info) {
59 xine_stream_private_t *stream = (xine_stream_private_t *)s;
60 stream = stream->side_streams[0];
61 if (info_valid (stream, info)) {
62 xine_rwlock_wrlock (&stream->info_lock);
63 stream->stream_info[info] = 0;
64 xine_rwlock_unlock (&stream->info_lock);
65 }
66 }
67
68 /*
69 * Reset public info value.
70 */
_x_stream_info_public_reset(xine_stream_t * s,int info)71 void _x_stream_info_public_reset (xine_stream_t *s, int info) {
72 (void)s;
73 (void)info;
74 }
75
76 /*
77 * Set private info value.
78 */
_x_stream_info_set(xine_stream_t * s,int info,int value)79 void _x_stream_info_set (xine_stream_t *s, int info, int value) {
80 xine_stream_private_t *stream = (xine_stream_private_t *)s, *m;
81 m = stream->side_streams[0];
82 if (info_valid (m, info)) {
83 xine_rwlock_wrlock (&m->info_lock);
84 if ((m != stream) &&
85 ((info == XINE_STREAM_INFO_HAS_CHAPTERS) ||
86 (info == XINE_STREAM_INFO_HAS_VIDEO) ||
87 (info == XINE_STREAM_INFO_HAS_AUDIO))) {
88 if (m->stream_info[info] == 0)
89 m->stream_info[info] = value;
90 } else {
91 m->stream_info[info] = value;
92 }
93 xine_rwlock_unlock (&m->info_lock);
94 }
95 }
96
97 /*
98 * Retrieve private info value.
99 */
_x_stream_info_get(xine_stream_t * s,int info)100 uint32_t _x_stream_info_get (xine_stream_t *s, int info) {
101 xine_stream_private_t *stream = (xine_stream_private_t *)s;
102 uint32_t stream_info;
103 stream = stream->side_streams[0];
104 xine_rwlock_rdlock (&stream->info_lock);
105 stream_info = stream->stream_info[info];
106 xine_rwlock_unlock (&stream->info_lock);
107 return stream_info;
108 }
109
110 /*
111 * Retrieve public info value
112 */
_x_stream_info_get_public(xine_stream_t * s,int info)113 uint32_t _x_stream_info_get_public (xine_stream_t *s, int info) {
114 xine_stream_private_t *stream = (xine_stream_private_t *)s;
115 uint32_t stream_info;
116 stream = stream->side_streams[0];
117 xine_rwlock_rdlock (&stream->info_lock);
118 stream_info = stream->stream_info[info];
119 xine_rwlock_unlock (&stream->info_lock);
120 return stream_info;
121 }
122
123 /* **************** Meta Info *********************** */
124
125 /*
126 * Remove trailing separator chars (\n,\r,\t, space,...)
127 * at the end of the string
128 */
meta_info_chomp(char * str)129 static void meta_info_chomp(char *str) {
130 ssize_t i, len;
131
132 len = strlen(str);
133 if (!len)
134 return;
135 i = len - 1;
136
137 while ((i >= 0) && ((unsigned char)str[i] <= 32)) {
138 str[i] = 0;
139 i--;
140 }
141 }
142
143 /*
144 * Check if 'info' is in bounds.
145 */
meta_valid(xine_stream_private_t * stream,int info)146 static int meta_valid (xine_stream_private_t *stream, int info) {
147 if ((info >= 0) && (info < XINE_STREAM_INFO_MAX))
148 return 1;
149 else {
150 xprintf (stream->s.xine, XINE_VERBOSITY_DEBUG,
151 "info_helper: invalid META_INFO %d. Ignored.\n", info);
152 return 0;
153 }
154 }
155
156 /*
157 * Set private meta info to utf-8 string value (can be NULL).
158 */
_meta_info_set_utf8(xine_stream_private_t * stream,int info,const char * value)159 static void _meta_info_set_utf8 (xine_stream_private_t *stream, int info, const char *value) {
160 if (meta_valid (stream, info)) {
161 xine_rwlock_wrlock (&stream->meta_lock);
162 if (( value && !stream->meta_info[info])
163 || ( value && stream->meta_info[info] && strcmp (value, stream->meta_info[info]))
164 || (!value && stream->meta_info[info])) {
165 if (stream->meta_info_public[info] != stream->meta_info[info])
166 free (stream->meta_info[info]);
167 stream->meta_info[info] = (value) ? strdup(value) : NULL;
168 if (stream->meta_info[info])
169 meta_info_chomp (stream->meta_info[info]);
170 }
171 xine_rwlock_unlock (&stream->meta_lock);
172 }
173 }
174
175 #ifdef HAVE_ICONV
is_utf8(const char * s)176 static int is_utf8 (const char *s) {
177 const uint8_t *p = (const uint8_t *)s;
178 while (1) {
179 if ((*p & 0x80) == 0x00) {
180 if (*p == 0x00)
181 break;
182 p += 1;
183 } else if ((*p & 0xe0) == 0xc0) {
184 if ((p[1] & 0xc0) != 0x80)
185 return -1;
186 p += 2;
187 } else if ((*p & 0xf0) == 0xe0) {
188 if (((p[1] & 0xc0) != 0x80)
189 || ((p[2] & 0xc0) != 0x80))
190 return -1;
191 p += 3;
192 } else if ((*p & 0xf8) == 0xf0) {
193 if (((p[1] & 0xc0) != 0x80)
194 || ((p[2] & 0xc0) != 0x80)
195 || ((p[3] & 0xc0) != 0x80))
196 return -1;
197 p += 4;
198 } else if ((*p & 0xfc) == 0xf8) {
199 if (((p[1] & 0xc0) != 0x80)
200 || ((p[2] & 0xc0) != 0x80)
201 || ((p[3] & 0xc0) != 0x80)
202 || ((p[4] & 0xc0) != 0x80))
203 return -1;
204 p += 5;
205 } else if ((*p & 0xfe) == 0xfc) {
206 if (((p[1] & 0xc0) != 0x80)
207 || ((p[2] & 0xc0) != 0x80)
208 || ((p[3] & 0xc0) != 0x80)
209 || ((p[4] & 0xc0) != 0x80)
210 || ((p[5] & 0xc0) != 0x80))
211 return -1;
212 p += 6;
213 } else {
214 return -1;
215 }
216 }
217 return p - (const uint8_t *)s;
218 }
219 #endif
220
221 /*
222 * Set private meta info to value (can be NULL) with a given encoding.
223 * if encoding is NULL assume locale.
224 */
_meta_info_set_encoding(xine_stream_private_t * stream,int info,const char * value,const char * enc)225 static void _meta_info_set_encoding (xine_stream_private_t *stream, int info, const char *value, const char *enc) {
226 const char *buf_set = value;
227 char *buf_free = NULL;
228
229 #ifdef HAVE_ICONV
230 char *system_enc = NULL;
231 iconv_t cd = (iconv_t)-1;
232
233 do {
234 if (!value)
235 break;
236
237 if (enc == NULL) {
238 if ((enc = system_enc = xine_get_system_encoding()) == NULL) {
239 xprintf (stream->s.xine, XINE_VERBOSITY_LOG,
240 _("info_helper: can't find out current locale character set\n"));
241 break;
242 }
243 }
244
245 if (strcmp (enc, "UTF-8")) {
246 /* Don't bother converting if it's already in UTF-8, but the encoding
247 * is badly reported */
248 if (is_utf8 (value) >= 0)
249 break;
250 }
251
252 cd = iconv_open ("UTF-8", enc);
253 if (cd == (iconv_t)-1) {
254 xprintf (stream->s.xine, XINE_VERBOSITY_LOG,
255 _("info_helper: unsupported conversion %s -> UTF-8, no conversion performed\n"), enc);
256 break;
257 }
258
259 {
260 ICONV_CONST char *inbuf;
261 char *outbuf;
262 size_t inbytesleft, outbytesleft;
263
264 if (!strncmp (enc, "UTF-16", 6) || !strncmp (enc, "UCS-2", 5)) {
265 /* strlen() won't work with UTF-16* or UCS-2* */
266 inbytesleft = 0;
267 while (value[inbytesleft] || value[inbytesleft + 1])
268 inbytesleft += 2;
269 } /* ... do we need to handle UCS-4? Probably not. */
270 else
271 inbytesleft = strlen(value);
272 outbytesleft = 4 * inbytesleft; /* estimative (max) */
273 buf_free = malloc (outbytesleft + 1);
274 if (!buf_free)
275 break;
276
277 inbuf = (ICONV_CONST char *)value;
278 outbuf = buf_free;
279 if (iconv (cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft) != (size_t)-1) {
280 *outbuf = '\0';
281 buf_set = buf_free;
282 }
283 }
284 } while (0);
285
286 if (cd != (iconv_t)-1)
287 iconv_close (cd);
288 free (system_enc);
289 #endif
290
291 _meta_info_set_utf8 (stream, info, buf_set);
292 free (buf_free);
293 }
294
295 /*
296 * Reset (nullify) private info value.
297 */
_x_meta_info_reset(xine_stream_t * s,int info)298 void _x_meta_info_reset (xine_stream_t *s, int info) {
299 xine_stream_private_t *stream = (xine_stream_private_t *)s;
300 stream = stream->side_streams[0];
301 _meta_info_set_utf8 (stream, info, NULL);
302 }
303
304 /*
305 * Reset (nullify) public info value.
306 */
_x_meta_info_public_reset(xine_stream_t * s,int info)307 void _x_meta_info_public_reset (xine_stream_t *s, int info) {
308 xine_stream_private_t *stream = (xine_stream_private_t *)s;
309 stream = stream->side_streams[0];
310 if (meta_valid (stream, info)) {
311 xine_rwlock_wrlock (&stream->meta_lock);
312 if (stream->meta_info_public[info] != stream->meta_info[info])
313 _x_freep (&stream->meta_info[info]);
314 xine_rwlock_unlock (&stream->meta_lock);
315 }
316 }
317
318 /*
319 * Set private meta info value using current locale.
320 */
_x_meta_info_set(xine_stream_t * s,int info,const char * str)321 void _x_meta_info_set (xine_stream_t *s, int info, const char *str) {
322 xine_stream_private_t *stream = (xine_stream_private_t *)s;
323 stream = stream->side_streams[0];
324 if (str)
325 _meta_info_set_encoding (stream, info, str, NULL);
326 }
327
328 /*
329 * Set private meta info value using specified encoding.
330 */
_x_meta_info_set_generic(xine_stream_t * s,int info,const char * str,const char * enc)331 void _x_meta_info_set_generic (xine_stream_t *s, int info, const char *str, const char *enc) {
332 xine_stream_private_t *stream = (xine_stream_private_t *)s;
333 stream = stream->side_streams[0];
334 if (str)
335 _meta_info_set_encoding (stream, info, str, enc);
336 }
337
338 /*
339 * Set private meta info value using utf8.
340 */
_x_meta_info_set_utf8(xine_stream_t * s,int info,const char * str)341 void _x_meta_info_set_utf8 (xine_stream_t *s, int info, const char *str) {
342 xine_stream_private_t *stream = (xine_stream_private_t *)s;
343 stream = stream->side_streams[0];
344 if (str)
345 _meta_info_set_utf8 (stream, info, str);
346 }
347
348 /*
349 * Set private meta info from buf, 'len' bytes long.
350 */
_x_meta_info_n_set(xine_stream_t * s,int info,const char * buf,int len)351 void _x_meta_info_n_set (xine_stream_t *s, int info, const char *buf, int len) {
352 xine_stream_private_t *stream = (xine_stream_private_t *)s;
353 stream = stream->side_streams[0];
354 if (meta_valid (stream, info) && len) {
355 char *str = strndup (buf, len);
356 _meta_info_set_encoding (stream, info, str, NULL);
357 free(str);
358 }
359 }
360
361 /*
362 * Set private meta info value, from multiple arguments.
363 */
_x_meta_info_set_multi(xine_stream_t * s,int info,...)364 void _x_meta_info_set_multi (xine_stream_t *s, int info, ...) {
365 xine_stream_private_t *stream = (xine_stream_private_t *)s;
366
367 stream = stream->side_streams[0];
368 if (meta_valid (stream, info)) {
369 va_list ap;
370 char *args[1025];
371 char *buf;
372 size_t n, len;
373
374 len = n = 0;
375
376 va_start(ap, info);
377 while((buf = va_arg(ap, char *)) && (n < 1024)) {
378 len += strlen(buf) + 1;
379 args[n] = buf;
380 n++;
381 }
382 va_end(ap);
383
384 args[n] = NULL;
385
386 if(len) {
387 char *p, *meta;
388
389 p = meta = (char *) malloc(len + 1);
390
391 n = 0;
392 while(args[n]) {
393 strcpy(meta, args[n]);
394 meta += strlen(args[n]) + 1;
395 n++;
396 }
397
398 *meta = '\0';
399
400 xine_rwlock_wrlock (&stream->meta_lock);
401 if (stream->meta_info_public[info] != stream->meta_info[info])
402 free (stream->meta_info[info]);
403 stream->meta_info[info] = p;
404 if (p)
405 meta_info_chomp (p);
406 xine_rwlock_unlock (&stream->meta_lock);
407 }
408 }
409 }
410
411 /*
412 * Retrieve private info value.
413 */
_x_meta_info_get(xine_stream_t * s,int info)414 const char *_x_meta_info_get (xine_stream_t *s, int info) {
415 xine_stream_private_t *stream = (xine_stream_private_t *)s;
416 const char *meta_info;
417 stream = stream->side_streams[0];
418 xine_rwlock_rdlock (&stream->meta_lock);
419 meta_info = stream->meta_info[info];
420 xine_rwlock_unlock (&stream->meta_lock);
421 return meta_info;
422 }
423
424 /*
425 * Retrieve public info value.
426 */
_x_meta_info_get_public(xine_stream_t * s,int info)427 const char *_x_meta_info_get_public (xine_stream_t *s, int info) {
428 xine_stream_private_t *stream = (xine_stream_private_t *)s;
429 char *pub_meta_info = NULL;
430 stream = stream->side_streams[0];
431 if (meta_valid (stream, info)) {
432 xine_rwlock_rdlock (&stream->meta_lock);
433 pub_meta_info = stream->meta_info_public[info];
434 if (pub_meta_info != stream->meta_info[info]) {
435 xine_rwlock_unlock (&stream->meta_lock);
436 xine_rwlock_wrlock (&stream->meta_lock);
437 free (pub_meta_info);
438 stream->meta_info_public[info] = pub_meta_info = stream->meta_info[info];
439 }
440 xine_rwlock_unlock (&stream->meta_lock);
441 }
442 return pub_meta_info;
443 }
444
445