qt: playlist: use item title if available
[vlc.git] / src / input / stream.c
blobb07c3b451d9c2591f5e6a62a2800d60156a004a9
1 /*****************************************************************************
2 * stream.c
3 *****************************************************************************
4 * Copyright (C) 1999-2004 VLC authors and VideoLAN
5 * Copyright 2008-2015 RĂ©mi Denis-Courmont
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
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.
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.
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 *****************************************************************************/
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <assert.h>
29 #include <stdalign.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <limits.h>
33 #include <errno.h>
35 #include <vlc_common.h>
36 #include <vlc_block.h>
37 #include <vlc_access.h>
38 #include <vlc_charset.h>
39 #include <vlc_interrupt.h>
40 #include <vlc_stream_extractor.h>
42 #include <libvlc.h>
43 #include "stream.h"
44 #include "mrl_helpers.h"
46 typedef struct stream_priv_t
48 stream_t stream;
49 void (*destroy)(stream_t *);
50 block_t *block;
51 block_t *peek;
52 uint64_t offset;
53 bool eof;
55 /* UTF-16 and UTF-32 file reading */
56 struct {
57 vlc_iconv_t conv;
58 unsigned char char_width;
59 bool little_endian;
60 } text;
62 max_align_t private_data[];
63 } stream_priv_t;
65 /**
66 * Allocates a VLC stream object
68 stream_t *vlc_stream_CustomNew(vlc_object_t *parent,
69 void (*destroy)(stream_t *), size_t size,
70 const char *type_name)
72 stream_priv_t *priv = vlc_custom_create(parent, sizeof (*priv) + size,
73 type_name);
74 if (unlikely(priv == NULL))
75 return NULL;
77 stream_t *s = &priv->stream;
79 s->psz_url = NULL;
80 s->s = NULL;
81 s->pf_read = NULL;
82 s->pf_block = NULL;
83 s->pf_readdir = NULL;
84 s->pf_seek = NULL;
85 s->pf_control = NULL;
86 s->p_sys = NULL;
87 s->p_input_item = NULL;
88 assert(destroy != NULL);
89 priv->destroy = destroy;
90 priv->block = NULL;
91 priv->peek = NULL;
92 priv->offset = 0;
93 priv->eof = false;
95 /* UTF16 and UTF32 text file conversion */
96 priv->text.conv = (vlc_iconv_t)(-1);
97 priv->text.char_width = 1;
98 priv->text.little_endian = false;
100 return s;
103 void *vlc_stream_Private(stream_t *stream)
105 return ((stream_priv_t *)stream)->private_data;
108 stream_t *vlc_stream_CommonNew(vlc_object_t *parent,
109 void (*destroy)(stream_t *))
111 return vlc_stream_CustomNew(parent, destroy, 0, "stream");
114 void stream_CommonDelete(stream_t *s)
116 stream_priv_t *priv = (stream_priv_t *)s;
118 if (priv->text.conv != (vlc_iconv_t)(-1))
119 vlc_iconv_close(priv->text.conv);
121 if (priv->peek != NULL)
122 block_Release(priv->peek);
123 if (priv->block != NULL)
124 block_Release(priv->block);
126 free(s->psz_url);
127 vlc_object_delete(s);
131 * Destroy a stream
133 void vlc_stream_Delete(stream_t *s)
135 stream_priv_t *priv = (stream_priv_t *)s;
137 priv->destroy(s);
138 stream_CommonDelete(s);
141 stream_t *(vlc_stream_NewURL)(vlc_object_t *p_parent, const char *psz_url)
143 if( !psz_url )
144 return NULL;
146 stream_t *s = stream_AccessNew( p_parent, NULL, NULL, false, psz_url );
147 if( s == NULL )
148 msg_Err( p_parent, "no suitable access module for `%s'", psz_url );
149 else
150 s = stream_FilterAutoNew(s);
151 return s;
154 stream_t *(vlc_stream_NewMRL)(vlc_object_t* parent, const char* mrl )
156 stream_t* stream = vlc_stream_NewURL( parent, mrl );
158 if( stream == NULL )
159 return NULL;
161 char const* anchor = strchr( mrl, '#' );
163 if( anchor == NULL )
164 return stream;
166 char const* extra;
167 if( stream_extractor_AttachParsed( &stream, anchor + 1, &extra ) )
169 msg_Err( parent, "unable to open %s", mrl );
170 vlc_stream_Delete( stream );
171 return NULL;
174 if( extra && *extra )
175 msg_Warn( parent, "ignoring extra fragment data: %s", extra );
177 return stream;
181 * Read from the stream until first newline.
182 * \param s Stream handle to read from
183 * \return A pointer to the allocated output string. You need to free this when you are done.
185 #define STREAM_PROBE_LINE 2048
186 #define STREAM_LINE_MAX (2048*100)
187 char *vlc_stream_ReadLine( stream_t *s )
189 stream_priv_t *priv = (stream_priv_t *)s;
191 /* Let's fail quickly if this is a readdir access */
192 if( s->pf_read == NULL && s->pf_block == NULL )
193 return NULL;
195 /* BOM detection */
196 if( vlc_stream_Tell( s ) == 0 )
198 const uint8_t *p_data;
199 ssize_t i_data = vlc_stream_Peek( s, &p_data, 2 );
201 if( i_data <= 0 )
202 return NULL;
204 if( unlikely(priv->text.conv != (vlc_iconv_t)-1) )
205 { /* seek back to beginning? reset */
206 vlc_iconv_close( priv->text.conv );
207 priv->text.conv = (vlc_iconv_t)-1;
209 priv->text.char_width = 1;
210 priv->text.little_endian = false;
212 if( i_data >= 2 )
214 const char *psz_encoding = NULL;
215 bool little_endian = false;
217 if( !memcmp( p_data, "\xFF\xFE", 2 ) )
219 psz_encoding = "UTF-16LE";
220 little_endian = true;
222 else if( !memcmp( p_data, "\xFE\xFF", 2 ) )
224 psz_encoding = "UTF-16BE";
227 /* Open the converter if we need it */
228 if( psz_encoding != NULL )
230 msg_Dbg( s, "UTF-16 BOM detected" );
231 priv->text.conv = vlc_iconv_open( "UTF-8", psz_encoding );
232 if( unlikely(priv->text.conv == (vlc_iconv_t)-1) )
234 msg_Err( s, "iconv_open failed" );
235 return NULL;
237 priv->text.char_width = 2;
238 priv->text.little_endian = little_endian;
243 size_t i_line = 0;
244 const uint8_t *p_data;
246 for( ;; )
248 size_t i_peek = i_line == 0 ? STREAM_PROBE_LINE
249 : __MIN( i_line * 2, STREAM_LINE_MAX );
251 /* Probe more data */
252 ssize_t i_data = vlc_stream_Peek( s, &p_data, i_peek );
253 if( i_data <= 0 )
254 return NULL;
256 /* Deal here with lone-byte incomplete UTF-16 sequences at EOF
257 that we won't be able to process anyway */
258 if( i_data < priv->text.char_width )
260 assert( priv->text.char_width == 2 );
261 uint8_t inc;
262 ssize_t i_inc = vlc_stream_Read( s, &inc, priv->text.char_width );
263 assert( i_inc == i_data );
264 if( i_inc > 0 )
265 msg_Err( s, "discarding incomplete UTF-16 sequence at EOF: 0x%02x", inc );
266 return NULL;
269 /* Keep to text encoding character width boundary */
270 if( i_data % priv->text.char_width )
271 i_data = i_data - ( i_data % priv->text.char_width );
273 if( (size_t) i_data == i_line )
274 break; /* No more data */
276 assert( (size_t) i_data > i_line );
278 /* Resume search for an EOL where we left off */
279 const uint8_t *p_cur = p_data + i_line, *psz_eol;
281 /* FIXME: <CR> behavior varies depending on where buffer
282 boundaries happen to fall; a <CR><LF> across the boundary
283 creates a bogus empty line. */
284 if( priv->text.char_width == 1 )
286 /* UTF-8: 0A <LF> */
287 psz_eol = memchr( p_cur, '\n', i_data - i_line );
288 if( psz_eol == NULL )
289 /* UTF-8: 0D <CR> */
290 psz_eol = memchr( p_cur, '\r', i_data - i_line );
292 else
294 const uint8_t *p_last = p_data + i_data - priv->text.char_width;
295 uint16_t eol = priv->text.little_endian ? 0x0A00 : 0x000A;
297 assert( priv->text.char_width == 2 );
298 psz_eol = NULL;
299 /* UTF-16: 000A <LF> */
300 for( const uint8_t *p = p_cur; p <= p_last; p += 2 )
302 if( U16_AT( p ) == eol )
304 psz_eol = p;
305 break;
309 if( psz_eol == NULL )
310 { /* UTF-16: 000D <CR> */
311 eol = priv->text.little_endian ? 0x0D00 : 0x000D;
312 for( const uint8_t *p = p_cur; p <= p_last; p += 2 )
314 if( U16_AT( p ) == eol )
316 psz_eol = p;
317 break;
323 if( psz_eol )
325 i_line = (psz_eol - p_data) + priv->text.char_width;
326 /* We have our line */
327 break;
330 i_line = i_data;
332 if( i_line >= STREAM_LINE_MAX )
334 msg_Err( s, "line too long, exceeding %zu bytes",
335 (size_t) STREAM_LINE_MAX );
336 return NULL;
340 if( i_line == 0 ) /* We failed to read any data, probably EOF */
341 return NULL;
343 /* If encoding conversion is required, UTF-8 needs at most 150%
344 as long a buffer as UTF-16 */
345 size_t i_line_conv = priv->text.char_width == 1 ? i_line : i_line * 3 / 2;
346 char *p_line = malloc( i_line_conv + 1 ); /* +1 for easy \0 append */
347 if( !p_line )
348 return NULL;
349 void *p_read = p_line;
351 if( priv->text.char_width > 1 )
353 size_t i_in = i_line, i_out = i_line_conv;
354 const char * p_in = (char *) p_data;
355 char * p_out = p_line;
357 if( vlc_iconv( priv->text.conv, &p_in, &i_in, &p_out, &i_out ) == VLC_ICONV_ERR )
359 msg_Err( s, "conversion error: %s", vlc_strerror_c( errno ) );
360 msg_Dbg( s, "original: %zu, in %zu, out %zu", i_line, i_in, i_out );
361 /* Reset state */
362 size_t r = vlc_iconv( priv->text.conv, NULL, NULL, NULL, NULL );
363 VLC_UNUSED( r );
364 /* FIXME: the rest of the line is discarded and lost */
367 i_line_conv -= i_out;
368 p_read = NULL; /* Line already read, only need to advance the stream */
371 ssize_t i_data = vlc_stream_Read( s, p_read, i_line );
372 assert( i_data > 0 && (size_t) i_data == i_line );
373 if( i_data <= 0 )
375 /* Hmmm */
376 free( p_line );
377 return NULL;
380 i_line = i_line_conv;
382 /* Remove trailing LF/CR */
383 while( i_line >= 1 &&
384 (p_line[i_line - 1] == '\r' || p_line[i_line - 1] == '\n') )
385 i_line--;
387 /* Make sure the \0 is there */
388 p_line[i_line] = '\0';
390 return p_line;
393 static ssize_t vlc_stream_CopyBlock(block_t **restrict pp,
394 void *buf, size_t len)
396 block_t *block = *pp;
398 if (block == NULL)
399 return -1;
401 if (len > block->i_buffer)
402 len = block->i_buffer;
404 if (buf != NULL)
405 memcpy(buf, block->p_buffer, len);
407 block->p_buffer += len;
408 block->i_buffer -= len;
410 if (block->i_buffer == 0)
412 block_Release(block);
413 *pp = NULL;
416 return likely(len > 0) ? (ssize_t)len : -1;
419 static ssize_t vlc_stream_ReadRaw(stream_t *s, void *buf, size_t len)
421 stream_priv_t *priv = (stream_priv_t *)s;
422 ssize_t ret;
424 assert(len <= SSIZE_MAX);
426 if (vlc_killed())
427 return 0;
429 if (s->pf_read != NULL)
431 assert(priv->block == NULL);
432 if (buf == NULL)
434 if (unlikely(len == 0))
435 return 0;
437 char dummy[256];
438 ret = s->pf_read(s, dummy, len <= 256 ? len : 256);
440 else
441 ret = s->pf_read(s, buf, len);
442 return ret;
445 ret = vlc_stream_CopyBlock(&priv->block, buf, len);
446 if (ret >= 0)
447 return ret;
449 if (s->pf_block != NULL)
451 bool eof = false;
453 priv->block = s->pf_block(s, &eof);
454 ret = vlc_stream_CopyBlock(&priv->block, buf, len);
455 if (ret >= 0)
456 return ret;
457 return eof ? 0 : -1;
460 return 0;
463 ssize_t vlc_stream_ReadPartial(stream_t *s, void *buf, size_t len)
465 stream_priv_t *priv = (stream_priv_t *)s;
466 ssize_t ret;
468 ret = vlc_stream_CopyBlock(&priv->peek, buf, len);
469 if (ret >= 0)
471 priv->offset += ret;
472 assert(ret <= (ssize_t)len);
473 return ret;
476 ret = vlc_stream_ReadRaw(s, buf, len);
477 if (ret > 0)
478 priv->offset += ret;
479 if (ret == 0)
480 priv->eof = len != 0;
481 assert(ret <= (ssize_t)len);
482 return ret;
485 ssize_t vlc_stream_Read(stream_t *s, void *buf, size_t len)
487 size_t copied = 0;
489 while (len > 0)
491 ssize_t ret = vlc_stream_ReadPartial(s, buf, len);
492 if (ret < 0)
493 continue;
494 if (ret == 0)
495 break;
497 if (buf != NULL)
498 buf = (char *)buf + ret;
499 assert(len >= (size_t)ret);
500 len -= ret;
501 copied += ret;
504 return copied;
507 ssize_t vlc_stream_Peek(stream_t *s, const uint8_t **restrict bufp, size_t len)
509 stream_priv_t *priv = (stream_priv_t *)s;
510 block_t *peek;
512 peek = priv->peek;
513 if (peek == NULL)
515 peek = priv->block;
516 priv->peek = peek;
517 priv->block = NULL;
520 if (peek == NULL)
522 peek = block_Alloc(len);
523 if (unlikely(peek == NULL))
524 return VLC_ENOMEM;
526 peek->i_buffer = 0;
528 else
529 if (peek->i_buffer < len)
531 size_t avail = peek->i_buffer;
533 peek = block_TryRealloc(peek, 0, len);
534 if (unlikely(peek == NULL))
535 return VLC_ENOMEM;
537 peek->i_buffer = avail;
540 priv->peek = peek;
541 *bufp = peek->p_buffer;
543 while (peek->i_buffer < len)
545 size_t avail = peek->i_buffer;
546 ssize_t ret;
548 ret = vlc_stream_ReadRaw(s, peek->p_buffer + avail, len - avail);
549 if (ret < 0)
550 continue;
552 peek->i_buffer += ret;
554 if (ret == 0)
555 return peek->i_buffer;
558 return len;
561 block_t *vlc_stream_ReadBlock(stream_t *s)
563 stream_priv_t *priv = (stream_priv_t *)s;
564 block_t *block;
566 if (vlc_killed())
568 priv->eof = true;
569 return NULL;
572 if (priv->peek != NULL)
574 block = priv->peek;
575 priv->peek = NULL;
577 else if (priv->block != NULL)
579 block = priv->block;
580 priv->block = NULL;
582 else if (s->pf_block != NULL)
584 priv->eof = false;
585 block = s->pf_block(s, &priv->eof);
587 else
589 block = block_Alloc(4096);
590 if (unlikely(block == NULL))
591 return NULL;
593 ssize_t ret = s->pf_read(s, block->p_buffer, block->i_buffer);
594 if (ret > 0)
595 block->i_buffer = ret;
596 else
598 block_Release(block);
599 block = NULL;
602 priv->eof = !ret;
605 if (block != NULL)
606 priv->offset += block->i_buffer;
608 return block;
611 uint64_t vlc_stream_Tell(const stream_t *s)
613 const stream_priv_t *priv = (const stream_priv_t *)s;
615 return priv->offset;
618 bool vlc_stream_Eof(const stream_t *s)
620 const stream_priv_t *priv = (const stream_priv_t *)s;
622 return priv->eof;
625 int vlc_stream_Seek(stream_t *s, uint64_t offset)
627 stream_priv_t *priv = (stream_priv_t *)s;
629 priv->eof = false;
631 block_t *peek = priv->peek;
632 if (peek != NULL)
634 if (offset >= priv->offset
635 && offset <= (priv->offset + peek->i_buffer))
636 { /* Seeking within the peek buffer */
637 size_t fwd = offset - priv->offset;
639 peek->p_buffer += fwd;
640 peek->i_buffer -= fwd;
641 priv->offset = offset;
643 if (peek->i_buffer == 0)
645 priv->peek = NULL;
646 block_Release(peek);
649 return VLC_SUCCESS;
652 else
654 if (priv->offset == offset)
655 return VLC_SUCCESS; /* Nothing to do! */
658 if (s->pf_seek == NULL)
659 return VLC_EGENERIC;
661 int ret = s->pf_seek(s, offset);
662 if (ret != VLC_SUCCESS)
663 return ret;
665 priv->offset = offset;
667 if (peek != NULL)
669 priv->peek = NULL;
670 block_Release(peek);
673 if (priv->block != NULL)
675 block_Release(priv->block);
676 priv->block = NULL;
679 return VLC_SUCCESS;
683 * Use to control the "stream_t *". Look at #stream_query_e for
684 * possible "i_query" value and format arguments. Return VLC_SUCCESS
685 * if ... succeed ;) and VLC_EGENERIC if failed or unimplemented
687 int vlc_stream_vaControl(stream_t *s, int cmd, va_list args)
689 stream_priv_t *priv = (stream_priv_t *)s;
691 switch (cmd)
693 case STREAM_SET_TITLE:
694 case STREAM_SET_SEEKPOINT:
696 int ret = s->pf_control(s, cmd, args);
697 if (ret != VLC_SUCCESS)
698 return ret;
700 priv->offset = 0;
702 if (priv->peek != NULL)
704 block_Release(priv->peek);
705 priv->peek = NULL;
708 if (priv->block != NULL)
710 block_Release(priv->block);
711 priv->block = NULL;
714 return VLC_SUCCESS;
717 return s->pf_control(s, cmd, args);
721 * Read data into a block.
723 * @param s stream to read data from
724 * @param size number of bytes to read
725 * @return a block of data, or NULL on error
726 @ note The block size may be shorter than requested if the end-of-stream was
727 * reached.
729 block_t *vlc_stream_Block( stream_t *s, size_t size )
731 if( unlikely(size > SSIZE_MAX) )
732 return NULL;
734 block_t *block = block_Alloc( size );
735 if( unlikely(block == NULL) )
736 return NULL;
738 ssize_t val = vlc_stream_Read( s, block->p_buffer, size );
739 if( val <= 0 )
741 block_Release( block );
742 return NULL;
745 block->i_buffer = val;
746 return block;
749 int vlc_stream_ReadDir( stream_t *s, input_item_node_t *p_node )
751 assert(s->pf_readdir != NULL);
752 return s->pf_readdir( s, p_node );