demux: hls: fix unused variable warning
[vlc.git] / src / input / stream.c
blobaffc7b2c0da9699ebc759d73e021ca610e8db544
1 /*****************************************************************************
2 * stream.c
3 *****************************************************************************
4 * Copyright (C) 1999-2004 VLC authors and VideoLAN
5 * Copyright 2008-2015 RĂ©mi Denis-Courmont
6 * $Id$
8 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
29 #include <assert.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_memory.h>
38 #include <vlc_access.h>
39 #include <vlc_charset.h>
40 #include <vlc_interrupt.h>
41 #include <vlc_stream_extractor.h>
43 #include <libvlc.h>
44 #include "stream.h"
45 #include "mrl_helpers.h"
47 typedef struct stream_priv_t
49 stream_t stream;
50 void (*destroy)(stream_t *);
51 block_t *block;
52 block_t *peek;
53 uint64_t offset;
54 bool eof;
56 /* UTF-16 and UTF-32 file reading */
57 struct {
58 vlc_iconv_t conv;
59 unsigned char char_width;
60 bool little_endian;
61 } text;
62 } stream_priv_t;
64 /**
65 * Allocates a VLC stream object
67 stream_t *vlc_stream_CommonNew(vlc_object_t *parent,
68 void (*destroy)(stream_t *))
70 stream_priv_t *priv = vlc_custom_create(parent, sizeof (*priv), "stream");
71 if (unlikely(priv == NULL))
72 return NULL;
74 stream_t *s = &priv->stream;
76 s->p_module = NULL;
77 s->psz_url = NULL;
78 s->p_source = NULL;
79 s->pf_read = NULL;
80 s->pf_block = NULL;
81 s->pf_readdir = NULL;
82 s->pf_seek = NULL;
83 s->pf_control = NULL;
84 s->p_sys = NULL;
85 s->p_input = NULL;
86 assert(destroy != NULL);
87 priv->destroy = destroy;
88 priv->block = NULL;
89 priv->peek = NULL;
90 priv->offset = 0;
91 priv->eof = false;
93 /* UTF16 and UTF32 text file conversion */
94 priv->text.conv = (vlc_iconv_t)(-1);
95 priv->text.char_width = 1;
96 priv->text.little_endian = false;
98 return s;
101 void stream_CommonDelete(stream_t *s)
103 stream_priv_t *priv = (stream_priv_t *)s;
105 if (priv->text.conv != (vlc_iconv_t)(-1))
106 vlc_iconv_close(priv->text.conv);
108 if (priv->peek != NULL)
109 block_Release(priv->peek);
110 if (priv->block != NULL)
111 block_Release(priv->block);
113 free(s->psz_url);
114 vlc_object_release(s);
118 * Destroy a stream
120 void vlc_stream_Delete(stream_t *s)
122 stream_priv_t *priv = (stream_priv_t *)s;
124 priv->destroy(s);
125 stream_CommonDelete(s);
128 stream_t *(vlc_stream_NewURL)(vlc_object_t *p_parent, const char *psz_url)
130 if( !psz_url )
131 return NULL;
133 stream_t *s = stream_AccessNew( p_parent, NULL, false, psz_url );
134 if( s == NULL )
135 msg_Err( p_parent, "no suitable access module for `%s'", psz_url );
136 return s;
139 stream_t *(vlc_stream_NewMRL)(vlc_object_t* parent, const char* mrl )
141 stream_t* stream = vlc_stream_NewURL( parent, mrl );
143 if( stream == NULL )
144 return NULL;
146 char const* anchor = strchr( mrl, '#' );
148 if( anchor == NULL )
149 return stream;
151 char const* extra;
152 if( stream_extractor_AttachParsed( &stream, anchor + 1, &extra ) )
154 msg_Err( parent, "unable to open %s", mrl );
155 vlc_stream_Delete( stream );
156 return NULL;
159 if( extra && *extra )
160 msg_Warn( parent, "ignoring extra fragment data: %s", extra );
162 return stream;
166 * Read from the stream until first newline.
167 * \param s Stream handle to read from
168 * \return A pointer to the allocated output string. You need to free this when you are done.
170 #define STREAM_PROBE_LINE 2048
171 #define STREAM_LINE_MAX (2048*100)
172 char *vlc_stream_ReadLine( stream_t *s )
174 stream_priv_t *priv = (stream_priv_t *)s;
175 char *p_line = NULL;
176 int i_line = 0, i_read = 0;
178 /* Let's fail quickly if this is a readdir access */
179 if( s->pf_read == NULL && s->pf_block == NULL )
180 return NULL;
182 for( ;; )
184 char *psz_eol;
185 const uint8_t *p_data;
186 int i_data;
187 int64_t i_pos;
189 /* Probe new data */
190 i_data = vlc_stream_Peek( s, &p_data, STREAM_PROBE_LINE );
191 if( i_data <= 0 ) break; /* No more data */
193 /* BOM detection */
194 i_pos = vlc_stream_Tell( s );
195 if( i_pos == 0 && i_data >= 2 )
197 const char *psz_encoding = NULL;
199 if( unlikely(priv->text.conv != (vlc_iconv_t)-1) )
200 { /* seek back to beginning? reset */
201 vlc_iconv_close( priv->text.conv );
202 priv->text.conv = (vlc_iconv_t)-1;
205 if( !memcmp( p_data, "\xFF\xFE", 2 ) )
207 psz_encoding = "UTF-16LE";
208 priv->text.little_endian = true;
210 else if( !memcmp( p_data, "\xFE\xFF", 2 ) )
212 psz_encoding = "UTF-16BE";
215 /* Open the converter if we need it */
216 if( psz_encoding != NULL )
218 msg_Dbg( s, "UTF-16 BOM detected" );
219 priv->text.conv = vlc_iconv_open( "UTF-8", psz_encoding );
220 if( unlikely(priv->text.conv == (vlc_iconv_t)-1) )
222 msg_Err( s, "iconv_open failed" );
223 goto error;
225 priv->text.char_width = 2;
229 if( i_data % priv->text.char_width )
231 /* keep i_char_width boundary */
232 i_data = i_data - ( i_data % priv->text.char_width );
233 msg_Warn( s, "the read is not i_char_width compatible");
236 if( i_data == 0 )
237 break;
239 /* Check if there is an EOL */
240 if( priv->text.char_width == 1 )
242 /* UTF-8: 0A <LF> */
243 psz_eol = memchr( p_data, '\n', i_data );
244 if( psz_eol == NULL )
245 /* UTF-8: 0D <CR> */
246 psz_eol = memchr( p_data, '\r', i_data );
248 else
250 const uint8_t *p_last = p_data + i_data - priv->text.char_width;
251 uint16_t eol = priv->text.little_endian ? 0x0A00 : 0x00A0;
253 assert( priv->text.char_width == 2 );
254 psz_eol = NULL;
255 /* UTF-16: 000A <LF> */
256 for( const uint8_t *p = p_data; p <= p_last; p += 2 )
258 if( U16_AT( p ) == eol )
260 psz_eol = (char *)p + 1;
261 break;
265 if( psz_eol == NULL )
266 { /* UTF-16: 000D <CR> */
267 eol = priv->text.little_endian ? 0x0D00 : 0x00D0;
268 for( const uint8_t *p = p_data; p <= p_last; p += 2 )
270 if( U16_AT( p ) == eol )
272 psz_eol = (char *)p + 1;
273 break;
279 if( psz_eol )
281 i_data = (psz_eol - (char *)p_data) + 1;
282 p_line = realloc_or_free( p_line,
283 i_line + i_data + priv->text.char_width ); /* add \0 */
284 if( !p_line )
285 goto error;
286 i_data = vlc_stream_Read( s, &p_line[i_line], i_data );
287 if( i_data <= 0 ) break; /* Hmmm */
288 i_line += i_data - priv->text.char_width; /* skip \n */;
289 i_read += i_data;
291 /* We have our line */
292 break;
295 /* Read data (+1 for easy \0 append) */
296 p_line = realloc_or_free( p_line,
297 i_line + STREAM_PROBE_LINE + priv->text.char_width );
298 if( !p_line )
299 goto error;
300 i_data = vlc_stream_Read( s, &p_line[i_line], STREAM_PROBE_LINE );
301 if( i_data <= 0 ) break; /* Hmmm */
302 i_line += i_data;
303 i_read += i_data;
305 if( i_read >= STREAM_LINE_MAX )
306 goto error; /* line too long */
309 if( i_read > 0 )
311 if( priv->text.char_width > 1 )
313 int i_new_line = 0;
314 size_t i_in = 0, i_out = 0;
315 const char * p_in = NULL;
316 char * p_out = NULL;
317 char * psz_new_line = NULL;
319 /* iconv */
320 /* UTF-8 needs at most 150% of the buffer as many as UTF-16 */
321 i_new_line = i_line * 3 / 2;
322 psz_new_line = malloc( i_new_line );
323 if( psz_new_line == NULL )
324 goto error;
325 i_in = (size_t)i_line;
326 i_out = (size_t)i_new_line;
327 p_in = p_line;
328 p_out = psz_new_line;
330 if( vlc_iconv( priv->text.conv, &p_in, &i_in, &p_out, &i_out ) == (size_t)-1 )
332 msg_Err( s, "conversion error: %s", vlc_strerror_c( errno ) );
333 msg_Dbg( s, "original: %d, in %zu, out %zu", i_line, i_in, i_out );
335 free( p_line );
336 p_line = psz_new_line;
337 i_line = (size_t)i_new_line - i_out; /* does not include \0 */
340 /* Remove trailing LF/CR */
341 while( i_line >= 1 &&
342 (p_line[i_line - 1] == '\r' || p_line[i_line - 1] == '\n') )
343 i_line--;
345 /* Make sure the \0 is there */
346 p_line[i_line] = '\0';
348 return p_line;
351 error:
352 /* We failed to read any data, probably EOF */
353 free( p_line );
355 /* */
356 if( priv->text.conv != (vlc_iconv_t)(-1) )
358 vlc_iconv_close( priv->text.conv );
359 priv->text.conv = (vlc_iconv_t)(-1);
361 return NULL;
364 static ssize_t vlc_stream_CopyBlock(block_t **restrict pp,
365 void *buf, size_t len)
367 block_t *block = *pp;
369 if (block == NULL)
370 return -1;
372 if (len > block->i_buffer)
373 len = block->i_buffer;
375 if (buf != NULL)
376 memcpy(buf, block->p_buffer, len);
378 block->p_buffer += len;
379 block->i_buffer -= len;
381 if (block->i_buffer == 0)
383 block_Release(block);
384 *pp = NULL;
387 return likely(len > 0) ? (ssize_t)len : -1;
390 static ssize_t vlc_stream_ReadRaw(stream_t *s, void *buf, size_t len)
392 stream_priv_t *priv = (stream_priv_t *)s;
393 ssize_t ret;
395 assert(len <= SSIZE_MAX);
397 if (vlc_killed())
398 return 0;
400 if (s->pf_read != NULL)
402 assert(priv->block == NULL);
403 if (buf == NULL)
405 if (unlikely(len == 0))
406 return 0;
408 char dummy[(len <= 256 ? len : 256)];
409 ret = s->pf_read(s, dummy, sizeof (dummy));
411 else
412 ret = s->pf_read(s, buf, len);
413 return ret;
416 ret = vlc_stream_CopyBlock(&priv->block, buf, len);
417 if (ret >= 0)
418 return ret;
420 if (s->pf_block != NULL)
422 bool eof = false;
424 priv->block = s->pf_block(s, &eof);
425 ret = vlc_stream_CopyBlock(&priv->block, buf, len);
426 if (ret >= 0)
427 return ret;
428 return eof ? 0 : -1;
431 return 0;
434 ssize_t vlc_stream_ReadPartial(stream_t *s, void *buf, size_t len)
436 stream_priv_t *priv = (stream_priv_t *)s;
437 ssize_t ret;
439 ret = vlc_stream_CopyBlock(&priv->peek, buf, len);
440 if (ret >= 0)
442 priv->offset += ret;
443 assert(ret <= (ssize_t)len);
444 return ret;
447 ret = vlc_stream_ReadRaw(s, buf, len);
448 if (ret > 0)
449 priv->offset += ret;
450 if (ret == 0)
451 priv->eof = len != 0;
452 assert(ret <= (ssize_t)len);
453 return ret;
456 ssize_t vlc_stream_Read(stream_t *s, void *buf, size_t len)
458 size_t copied = 0;
460 while (len > 0)
462 ssize_t ret = vlc_stream_ReadPartial(s, buf, len);
463 if (ret < 0)
464 continue;
465 if (ret == 0)
466 break;
468 if (buf != NULL)
469 buf = (char *)buf + ret;
470 assert(len >= (size_t)ret);
471 len -= ret;
472 copied += ret;
475 return copied;
478 ssize_t vlc_stream_Peek(stream_t *s, const uint8_t **restrict bufp, size_t len)
480 stream_priv_t *priv = (stream_priv_t *)s;
481 block_t *peek;
483 peek = priv->peek;
484 if (peek == NULL)
486 peek = priv->block;
487 priv->peek = peek;
488 priv->block = NULL;
491 if (peek == NULL)
493 peek = block_Alloc(len);
494 if (unlikely(peek == NULL))
495 return VLC_ENOMEM;
497 peek->i_buffer = 0;
499 else
500 if (peek->i_buffer < len)
502 size_t avail = peek->i_buffer;
504 peek = block_TryRealloc(peek, 0, len);
505 if (unlikely(peek == NULL))
506 return VLC_ENOMEM;
508 peek->i_buffer = avail;
511 priv->peek = peek;
512 *bufp = peek->p_buffer;
514 while (peek->i_buffer < len)
516 size_t avail = peek->i_buffer;
517 ssize_t ret;
519 ret = vlc_stream_ReadRaw(s, peek->p_buffer + avail, len - avail);
520 if (ret < 0)
521 continue;
523 peek->i_buffer += ret;
525 if (ret == 0)
526 return peek->i_buffer;
529 return len;
532 block_t *vlc_stream_ReadBlock(stream_t *s)
534 stream_priv_t *priv = (stream_priv_t *)s;
535 block_t *block;
537 if (vlc_killed())
539 priv->eof = true;
540 return NULL;
543 if (priv->peek != NULL)
545 block = priv->peek;
546 priv->peek = NULL;
548 else if (priv->block != NULL)
550 block = priv->block;
551 priv->block = NULL;
553 else if (s->pf_block != NULL)
555 priv->eof = false;
556 block = s->pf_block(s, &priv->eof);
558 else
560 block = block_Alloc(4096);
561 if (unlikely(block == NULL))
562 return NULL;
564 ssize_t ret = s->pf_read(s, block->p_buffer, block->i_buffer);
565 if (ret > 0)
566 block->i_buffer = ret;
567 else
569 block_Release(block);
570 block = NULL;
573 priv->eof = !ret;
576 if (block != NULL)
577 priv->offset += block->i_buffer;
579 return block;
582 uint64_t vlc_stream_Tell(const stream_t *s)
584 const stream_priv_t *priv = (const stream_priv_t *)s;
586 return priv->offset;
589 bool vlc_stream_Eof(const stream_t *s)
591 const stream_priv_t *priv = (const stream_priv_t *)s;
593 return priv->eof;
596 int vlc_stream_Seek(stream_t *s, uint64_t offset)
598 stream_priv_t *priv = (stream_priv_t *)s;
600 priv->eof = false;
602 block_t *peek = priv->peek;
603 if (peek != NULL)
605 if (offset >= priv->offset
606 && offset <= (priv->offset + peek->i_buffer))
607 { /* Seeking within the peek buffer */
608 size_t fwd = offset - priv->offset;
610 peek->p_buffer += fwd;
611 peek->i_buffer -= fwd;
612 priv->offset = offset;
614 if (peek->i_buffer == 0)
616 priv->peek = NULL;
617 block_Release(peek);
620 return VLC_SUCCESS;
623 else
625 if (priv->offset == offset)
626 return VLC_SUCCESS; /* Nothing to do! */
629 if (s->pf_seek == NULL)
630 return VLC_EGENERIC;
632 int ret = s->pf_seek(s, offset);
633 if (ret != VLC_SUCCESS)
634 return ret;
636 priv->offset = offset;
638 if (peek != NULL)
640 priv->peek = NULL;
641 block_Release(peek);
644 if (priv->block != NULL)
646 block_Release(priv->block);
647 priv->block = NULL;
650 return VLC_SUCCESS;
654 * Use to control the "stream_t *". Look at #stream_query_e for
655 * possible "i_query" value and format arguments. Return VLC_SUCCESS
656 * if ... succeed ;) and VLC_EGENERIC if failed or unimplemented
658 int vlc_stream_vaControl(stream_t *s, int cmd, va_list args)
660 stream_priv_t *priv = (stream_priv_t *)s;
662 switch (cmd)
664 case STREAM_SET_TITLE:
665 case STREAM_SET_SEEKPOINT:
667 int ret = s->pf_control(s, cmd, args);
668 if (ret != VLC_SUCCESS)
669 return ret;
671 priv->offset = 0;
673 if (priv->peek != NULL)
675 block_Release(priv->peek);
676 priv->peek = NULL;
679 if (priv->block != NULL)
681 block_Release(priv->block);
682 priv->block = NULL;
685 return VLC_SUCCESS;
688 return s->pf_control(s, cmd, args);
692 * Read data into a block.
694 * @param s stream to read data from
695 * @param size number of bytes to read
696 * @return a block of data, or NULL on error
697 @ note The block size may be shorter than requested if the end-of-stream was
698 * reached.
700 block_t *vlc_stream_Block( stream_t *s, size_t size )
702 if( unlikely(size > SSIZE_MAX) )
703 return NULL;
705 block_t *block = block_Alloc( size );
706 if( unlikely(block == NULL) )
707 return NULL;
709 ssize_t val = vlc_stream_Read( s, block->p_buffer, size );
710 if( val <= 0 )
712 block_Release( block );
713 return NULL;
716 block->i_buffer = val;
717 return block;
721 * Returns a node containing all the input_item of the directory pointer by
722 * this stream. returns VLC_SUCCESS on success.
724 int vlc_stream_ReadDir( stream_t *s, input_item_node_t *p_node )
726 return s->pf_readdir( s, p_node );