stream.c 17.7 KB
Newer Older
1 2 3
/*****************************************************************************
 * stream.c
 *****************************************************************************
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
4
 * Copyright (C) 1999-2004 VLC authors and VideoLAN
5
 * Copyright 2008-2015 Rémi Denis-Courmont
6
 * $Id$
7 8 9
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 *
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
10 11 12
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
13 14 15 16
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
17 18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
19
 *
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
20 21 22
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 24
 *****************************************************************************/

25 26 27 28
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

29
#include <assert.h>
30 31
#include <stdlib.h>
#include <string.h>
32
#include <limits.h>
33
#include <errno.h>
34

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>
42

43 44
#include <libvlc.h>
#include "stream.h"
45
#include "mrl_helpers.h"
46

47
typedef struct stream_priv_t
Laurent Aimar's avatar
Laurent Aimar committed
48
{
49
    stream_t stream;
50
    void (*destroy)(stream_t *);
51
    block_t *block;
52
    block_t *peek;
53
    uint64_t offset;
54
    bool eof;
55

56 57 58 59 60 61 62
    /* UTF-16 and UTF-32 file reading */
    struct {
        vlc_iconv_t   conv;
        unsigned char char_width;
        bool          little_endian;
    } text;
} stream_priv_t;
63

64 65 66
/**
 * Allocates a VLC stream object
 */
67 68
stream_t *vlc_stream_CommonNew(vlc_object_t *parent,
                               void (*destroy)(stream_t *))
69 70 71
{
    stream_priv_t *priv = vlc_custom_create(parent, sizeof (*priv), "stream");
    if (unlikely(priv == NULL))
72
        return NULL;
73 74 75

    stream_t *s = &priv->stream;

76
    s->p_module = NULL;
77
    s->psz_url = NULL;
78 79
    s->p_source = NULL;
    s->pf_read = NULL;
80
    s->pf_block = NULL;
81
    s->pf_readdir = NULL;
82
    s->pf_seek = NULL;
83
    s->pf_control = NULL;
François Cartegnie's avatar
François Cartegnie committed
84
    s->p_sys = NULL;
85
    s->p_input = NULL;
86 87
    assert(destroy != NULL);
    priv->destroy = destroy;
88
    priv->block = NULL;
89
    priv->peek = NULL;
90
    priv->offset = 0;
91
    priv->eof = false;
92 93

    /* UTF16 and UTF32 text file conversion */
94 95 96
    priv->text.conv = (vlc_iconv_t)(-1);
    priv->text.char_width = 1;
    priv->text.little_endian = false;
97 98 99

    return s;
}
100

101
void stream_CommonDelete(stream_t *s)
102
{
103 104 105 106
    stream_priv_t *priv = (stream_priv_t *)s;

    if (priv->text.conv != (vlc_iconv_t)(-1))
        vlc_iconv_close(priv->text.conv);
107

108 109
    if (priv->peek != NULL)
        block_Release(priv->peek);
110 111
    if (priv->block != NULL)
        block_Release(priv->block);
112

113
    free(s->psz_url);
114
    vlc_object_release(s);
Laurent Aimar's avatar
Laurent Aimar committed
115 116
}

117 118 119
/**
 * Destroy a stream
 */
120
void vlc_stream_Delete(stream_t *s)
121 122 123 124 125 126 127
{
    stream_priv_t *priv = (stream_priv_t *)s;

    priv->destroy(s);
    stream_CommonDelete(s);
}

128
stream_t *(vlc_stream_NewURL)(vlc_object_t *p_parent, const char *psz_url)
129
{
130 131
    if( !psz_url )
        return NULL;
132

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;
137 138
}

139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
stream_t *(vlc_stream_NewMRL)(vlc_object_t* parent, const char* mrl )
{
    stream_t* stream = vlc_stream_NewURL( parent, mrl );

    if( stream == NULL )
        return NULL;

    char const* anchor = strchr( mrl, '#' );

    if( anchor == NULL )
        return stream;

    char const* extra;
    if( stream_extractor_AttachParsed( &stream, anchor + 1, &extra ) )
    {
        msg_Err( parent, "unable to open %s", mrl );
        vlc_stream_Delete( stream );
        return NULL;
    }

    if( extra && *extra )
        msg_Warn( parent, "ignoring extra fragment data: %s", extra );

    return stream;
}

165
/**
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.
169
 */
170 171
#define STREAM_PROBE_LINE 2048
#define STREAM_LINE_MAX (2048*100)
172
char *vlc_stream_ReadLine( stream_t *s )
173
{
174
    stream_priv_t *priv = (stream_priv_t *)s;
175 176
    char *p_line = NULL;
    int i_line = 0, i_read = 0;
177

178
    /* Let's fail quickly if this is a readdir access */
179
    if( s->pf_read == NULL && s->pf_block == NULL )
180 181
        return NULL;

182
    for( ;; )
183
    {
184
        char *psz_eol;
185
        const uint8_t *p_data;
186
        int i_data;
187
        int64_t i_pos;
188 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 */
192

193
        /* BOM detection */
194
        i_pos = vlc_stream_Tell( s );
195
        if( i_pos == 0 && i_data >= 2 )
196
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
197
            const char *psz_encoding = NULL;
198

199 200 201 202 203 204
            if( unlikely(priv->text.conv != (vlc_iconv_t)-1) )
            {   /* seek back to beginning? reset */
                vlc_iconv_close( priv->text.conv );
                priv->text.conv = (vlc_iconv_t)-1;
            }

205
            if( !memcmp( p_data, "\xFF\xFE", 2 ) )
206
            {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
207
                psz_encoding = "UTF-16LE";
208
                priv->text.little_endian = true;
209
            }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
210
            else if( !memcmp( p_data, "\xFE\xFF", 2 ) )
211
            {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
212
                psz_encoding = "UTF-16BE";
213
            }
214 215 216 217

            /* Open the converter if we need it */
            if( psz_encoding != NULL )
            {
218
                msg_Dbg( s, "UTF-16 BOM detected" );
219
                priv->text.conv = vlc_iconv_open( "UTF-8", psz_encoding );
220 221
                if( unlikely(priv->text.conv == (vlc_iconv_t)-1) )
                {
222
                    msg_Err( s, "iconv_open failed" );
223 224 225
                    goto error;
                }
                priv->text.char_width = 2;
226 227 228
            }
        }

229
        if( i_data % priv->text.char_width )
230
        {
231
            /* keep i_char_width boundary */
232
            i_data = i_data - ( i_data % priv->text.char_width );
233 234 235
            msg_Warn( s, "the read is not i_char_width compatible");
        }

236 237 238
        if( i_data == 0 )
            break;

239
        /* Check if there is an EOL */
240
        if( priv->text.char_width == 1 )
241
        {
242 243
            /* UTF-8: 0A <LF> */
            psz_eol = memchr( p_data, '\n', i_data );
244 245 246
            if( psz_eol == NULL )
                /* UTF-8: 0D <CR> */
                psz_eol = memchr( p_data, '\r', i_data );
247 248 249
        }
        else
        {
250 251
            const uint8_t *p_last = p_data + i_data - priv->text.char_width;
            uint16_t eol = priv->text.little_endian ? 0x0A00 : 0x00A0;
252

253
            assert( priv->text.char_width == 2 );
254 255 256
            psz_eol = NULL;
            /* UTF-16: 000A <LF> */
            for( const uint8_t *p = p_data; p <= p_last; p += 2 )
257
            {
258 259 260 261 262
                if( U16_AT( p ) == eol )
                {
                     psz_eol = (char *)p + 1;
                     break;
                }
263
            }
264

265 266
            if( psz_eol == NULL )
            {   /* UTF-16: 000D <CR> */
267
                eol = priv->text.little_endian ? 0x0D00 : 0x00D0;
268 269 270 271 272 273 274 275
                for( const uint8_t *p = p_data; p <= p_last; p += 2 )
                {
                    if( U16_AT( p ) == eol )
                    {
                        psz_eol = (char *)p + 1;
                        break;
                    }
                }
276 277 278
            }
        }

279
        if( psz_eol )
280
        {
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 285
            if( !p_line )
                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 290 291 292
            i_read += i_data;

            /* We have our line */
            break;
293 294 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 299
        if( !p_line )
            goto error;
300
        i_data = vlc_stream_Read( s, &p_line[i_line], STREAM_PROBE_LINE );
301 302 303
        if( i_data <= 0 ) break; /* Hmmm */
        i_line += i_data;
        i_read += i_data;
304 305 306

        if( i_read >= STREAM_LINE_MAX )
            goto error; /* line too long */
307
    }
308

309
    if( i_read > 0 )
310
    {
311
        if( priv->text.char_width > 1 )
312
        {
313
            int i_new_line = 0;
314
            size_t i_in = 0, i_out = 0;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
315
            const char * p_in = NULL;
316 317
            char * p_out = NULL;
            char * psz_new_line = NULL;
318

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 + 1;
322
            psz_new_line = malloc( i_new_line );
323 324
            if( psz_new_line == NULL )
                goto error;
325 326
            i_in = (size_t)i_line;
            i_out = (size_t)i_new_line;
327 328
            p_in = p_line;
            p_out = psz_new_line;
329

330
            if( vlc_iconv( priv->text.conv, &p_in, &i_in, &p_out, &i_out ) == (size_t)-1 )
331
            {
332 333
                msg_Err( s, "conversion error: %s", vlc_strerror_c( errno ) );
                msg_Dbg( s, "original: %d, in %zu, out %zu", i_line, i_in, i_out );
334
            }
335
            free( p_line );
336
            p_line = psz_new_line;
337
            i_line = (size_t)i_new_line - i_out; /* does not include \0 */
338 339 340
        }

        /* Remove trailing LF/CR */
341 342 343
        while( i_line >= 1 &&
               (p_line[i_line - 1] == '\r' || p_line[i_line - 1] == '\n') )
            i_line--;
344 345

        /* Make sure the \0 is there */
346
        p_line[i_line] = '\0';
347

348 349
        return p_line;
    }
350

351
error:
352
    /* We failed to read any data, probably EOF */
353
    free( p_line );
354 355

    /* */
356 357 358 359 360
    if( priv->text.conv != (vlc_iconv_t)(-1) )
    {
        vlc_iconv_close( priv->text.conv );
        priv->text.conv = (vlc_iconv_t)(-1);
    }
361
    return NULL;
362
}
363

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
364 365
static ssize_t vlc_stream_CopyBlock(block_t **restrict pp,
                                    void *buf, size_t len)
366
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
367
    block_t *block = *pp;
368

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
369 370
    if (block == NULL)
        return -1;
371

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
372 373
    if (len > block->i_buffer)
        len = block->i_buffer;
374

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
375 376
    if (buf != NULL)
        memcpy(buf, block->p_buffer, len);
377

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
378 379
    block->p_buffer += len;
    block->i_buffer -= len;
380

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
381 382 383 384
    if (block->i_buffer == 0)
    {
        block_Release(block);
        *pp = NULL;
385 386
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
387
    return likely(len > 0) ? (ssize_t)len : -1;
388 389
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
390
static ssize_t vlc_stream_ReadRaw(stream_t *s, void *buf, size_t len)
391
{
392
    stream_priv_t *priv = (stream_priv_t *)s;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
393
    ssize_t ret;
394

395 396
    assert(len <= SSIZE_MAX);

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
397
    if (vlc_killed())
398 399
        return 0;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
400
    if (s->pf_read != NULL)
401
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
402
        assert(priv->block == NULL);
403 404 405 406 407 408 409 410 411 412
        if (buf == NULL)
        {
            if (unlikely(len == 0))
                return 0;

            char dummy[(len <= 256 ? len : 256)];
            ret = s->pf_read(s, dummy, sizeof (dummy));
        }
        else
            ret = s->pf_read(s, buf, len);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
413 414
        return ret;
    }
415

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
416 417 418
    ret = vlc_stream_CopyBlock(&priv->block, buf, len);
    if (ret >= 0)
        return ret;
419

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
420 421 422
    if (s->pf_block != NULL)
    {
        bool eof = false;
423

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
424 425 426 427 428
        priv->block = s->pf_block(s, &eof);
        ret = vlc_stream_CopyBlock(&priv->block, buf, len);
        if (ret >= 0)
            return ret;
        return eof ? 0 : -1;
429 430
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
431
    return 0;
432 433
}

434 435 436
ssize_t vlc_stream_ReadPartial(stream_t *s, void *buf, size_t len)
{
    stream_priv_t *priv = (stream_priv_t *)s;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
437
    ssize_t ret;
438

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
439 440
    ret = vlc_stream_CopyBlock(&priv->peek, buf, len);
    if (ret >= 0)
441
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
442
        priv->offset += ret;
443
        assert(ret <= (ssize_t)len);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
444 445
        return ret;
    }
446

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
447 448 449 450 451
    ret = vlc_stream_ReadRaw(s, buf, len);
    if (ret > 0)
        priv->offset += ret;
    if (ret == 0)
        priv->eof = len != 0;
452
    assert(ret <= (ssize_t)len);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
453 454
    return ret;
}
455

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
456 457 458
ssize_t vlc_stream_Read(stream_t *s, void *buf, size_t len)
{
    size_t copied = 0;
459

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
460 461 462 463 464 465 466
    while (len > 0)
    {
        ssize_t ret = vlc_stream_ReadPartial(s, buf, len);
        if (ret < 0)
            continue;
        if (ret == 0)
            break;
467

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
468 469
        if (buf != NULL)
            buf = (char *)buf + ret;
470
        assert(len >= (size_t)ret);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
471 472
        len -= ret;
        copied += ret;
473
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
474 475

    return copied;
476 477
}

478
ssize_t vlc_stream_Peek(stream_t *s, const uint8_t **restrict bufp, size_t len)
479
{
480
    stream_priv_t *priv = (stream_priv_t *)s;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
481
    block_t *peek;
482

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
483 484
    peek = priv->peek;
    if (peek == NULL)
485
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
486 487
        peek = priv->block;
        priv->peek = peek;
488 489 490
        priv->block = NULL;
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
491
    if (peek == NULL)
492
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
493
        peek = block_Alloc(len);
494 495 496
        if (unlikely(peek == NULL))
            return VLC_ENOMEM;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
497
        peek->i_buffer = 0;
498
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
499 500
    else
    if (peek->i_buffer < len)
501
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
502
        size_t avail = peek->i_buffer;
503

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
504
        peek = block_TryRealloc(peek, 0, len);
505 506
        if (unlikely(peek == NULL))
            return VLC_ENOMEM;
507

508
        peek->i_buffer = avail;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
509 510 511 512 513 514 515 516 517
    }

    priv->peek = peek;
    *bufp = peek->p_buffer;

    while (peek->i_buffer < len)
    {
        size_t avail = peek->i_buffer;
        ssize_t ret;
518

519
        ret = vlc_stream_ReadRaw(s, peek->p_buffer + avail, len - avail);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
520 521 522 523 524 525 526
        if (ret < 0)
            continue;

        peek->i_buffer += ret;

        if (ret == 0)
            return peek->i_buffer;
527 528 529 530 531
    }

    return len;
}

532
block_t *vlc_stream_ReadBlock(stream_t *s)
533 534 535 536
{
    stream_priv_t *priv = (stream_priv_t *)s;
    block_t *block;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
537 538 539 540 541 542
    if (vlc_killed())
    {
        priv->eof = true;
        return NULL;
    }

543 544 545 546 547
    if (priv->peek != NULL)
    {
        block = priv->peek;
        priv->peek = NULL;
    }
548 549 550 551 552 553 554 555 556 557
    else if (priv->block != NULL)
    {
        block = priv->block;
        priv->block = NULL;
    }
    else if (s->pf_block != NULL)
    {
        priv->eof = false;
        block = s->pf_block(s, &priv->eof);
    }
558 559 560 561 562 563 564
    else
    {
        block = block_Alloc(4096);
        if (unlikely(block == NULL))
            return NULL;

        ssize_t ret = s->pf_read(s, block->p_buffer, block->i_buffer);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
565
        if (ret > 0)
566 567 568 569 570 571
            block->i_buffer = ret;
        else
        {
            block_Release(block);
            block = NULL;
        }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
572 573

        priv->eof = !ret;
574 575 576 577 578 579 580 581
    }

    if (block != NULL)
        priv->offset += block->i_buffer;

    return block;
}

582
uint64_t vlc_stream_Tell(const stream_t *s)
583 584 585
{
    const stream_priv_t *priv = (const stream_priv_t *)s;

586
    return priv->offset;
587 588
}

589
bool vlc_stream_Eof(const stream_t *s)
590 591 592 593 594 595
{
    const stream_priv_t *priv = (const stream_priv_t *)s;

    return priv->eof;
}

596
int vlc_stream_Seek(stream_t *s, uint64_t offset)
597 598 599
{
    stream_priv_t *priv = (stream_priv_t *)s;

600 601
    priv->eof = false;

602 603 604
    block_t *peek = priv->peek;
    if (peek != NULL)
    {
605 606 607 608
        if (offset >= priv->offset
         && offset <= (priv->offset + peek->i_buffer))
        {   /* Seeking within the peek buffer */
            size_t fwd = offset - priv->offset;
609

610 611 612
            peek->p_buffer += fwd;
            peek->i_buffer -= fwd;
            priv->offset = offset;
613

614 615 616 617
            if (peek->i_buffer == 0)
            {
                priv->peek = NULL;
                block_Release(peek);
618
            }
619 620

            return VLC_SUCCESS;
621 622 623 624 625 626 627 628
        }
    }
    else
    {
        if (priv->offset == offset)
            return VLC_SUCCESS; /* Nothing to do! */
    }

629 630 631 632 633 634 635 636 637 638 639 640 641 642 643
    if (s->pf_seek == NULL)
        return VLC_EGENERIC;

    int ret = s->pf_seek(s, offset);
    if (ret != VLC_SUCCESS)
        return ret;

    priv->offset = offset;

    if (peek != NULL)
    {
        priv->peek = NULL;
        block_Release(peek);
    }

644 645 646 647 648 649
    if (priv->block != NULL)
    {
        block_Release(priv->block);
        priv->block = NULL;
    }

650
    return VLC_SUCCESS;
651 652
}

653 654 655 656 657
/**
 * Use to control the "stream_t *". Look at #stream_query_e for
 * possible "i_query" value and format arguments.  Return VLC_SUCCESS
 * if ... succeed ;) and VLC_EGENERIC if failed or unimplemented
 */
658
int vlc_stream_vaControl(stream_t *s, int cmd, va_list args)
659
{
660 661 662 663
    stream_priv_t *priv = (stream_priv_t *)s;

    switch (cmd)
    {
664 665 666 667 668 669 670 671 672 673 674 675 676 677
        case STREAM_SET_TITLE:
        case STREAM_SET_SEEKPOINT:
        {
            int ret = s->pf_control(s, cmd, args);
            if (ret != VLC_SUCCESS)
                return ret;

            priv->offset = 0;

            if (priv->peek != NULL)
            {
                block_Release(priv->peek);
                priv->peek = NULL;
            }
678 679 680 681 682 683 684

            if (priv->block != NULL)
            {
                block_Release(priv->block);
                priv->block = NULL;
            }

685 686
            return VLC_SUCCESS;
        }
687 688
    }
    return s->pf_control(s, cmd, args);
689 690 691
}

/**
692 693 694 695 696 697 698
 * Read data into a block.
 *
 * @param s stream to read data from
 * @param size number of bytes to read
 * @return a block of data, or NULL on error
 @ note The block size may be shorter than requested if the end-of-stream was
 * reached.
699
 */
700
block_t *vlc_stream_Block( stream_t *s, size_t size )
701
{
702 703
    if( unlikely(size > SSIZE_MAX) )
        return NULL;
704

705 706 707 708
    block_t *block = block_Alloc( size );
    if( unlikely(block == NULL) )
        return NULL;

709
    ssize_t val = vlc_stream_Read( s, block->p_buffer, size );
710
    if( val <= 0 )
711
    {
712 713
        block_Release( block );
        return NULL;
714
    }
715 716 717

    block->i_buffer = val;
    return block;
718
}
719

720
/**
721 722
 * Returns a node containing all the input_item of the directory pointer by
 * this stream. returns VLC_SUCCESS on success.
723
 */
724
int vlc_stream_ReadDir( stream_t *s, input_item_node_t *p_node )
725
{
726
    return s->pf_readdir( s, p_node );
727
}