3
3
*****************************************************************************
4
4
* Copyright (C) 1999-2004 the VideoLAN team
5
* $Id: 8947f2393b8787829bcf414a1d757df8c3e45bba $
5
* $Id: aaf7f94169673cea50f9584475718935214fa1e7 $
7
7
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
127
127
stream_read_method_t method; /* method to use */
129
int64_t i_pos; /* Current reading offset */
129
uint64_t i_pos; /* Current reading offset */
131
131
/* Method 1: pf_block */
134
int64_t i_start; /* Offset of block for p_first */
135
int64_t i_offset; /* Offset for data in p_current */
134
uint64_t i_start; /* Offset of block for p_first */
135
uint64_t i_offset; /* Offset for data in p_current */
136
136
block_t *p_current; /* Current block */
138
int i_size; /* Total amount of data in the list */
138
uint64_t i_size; /* Total amount of data in the list */
139
139
block_t *p_first;
140
140
block_t **pp_last;
144
144
/* Method 2: for pf_read */
147
int i_offset; /* Buffer offset in the current track */
148
int i_tk; /* Current track */
147
unsigned i_offset; /* Buffer offset in the current track */
148
int i_tk; /* Current track */
149
149
stream_track_t tk[STREAM_CACHE_TRACK];
151
151
/* Global buffer */
152
152
uint8_t *p_buffer;
155
int i_used; /* Used since last read */
155
unsigned i_used; /* Used since last read */
156
unsigned i_read_size;
188
188
static int AStreamReadBlock( stream_t *s, void *p_read, unsigned int i_read );
189
189
static int AStreamPeekBlock( stream_t *s, const uint8_t **p_peek, unsigned int i_read );
190
static int AStreamSeekBlock( stream_t *s, int64_t i_pos );
190
static int AStreamSeekBlock( stream_t *s, uint64_t i_pos );
191
191
static void AStreamPrebufferBlock( stream_t *s );
192
192
static block_t *AReadBlock( stream_t *s, bool *pb_eof );
195
195
static int AStreamReadStream( stream_t *s, void *p_read, unsigned int i_read );
196
196
static int AStreamPeekStream( stream_t *s, const uint8_t **pp_peek, unsigned int i_read );
197
static int AStreamSeekStream( stream_t *s, int64_t i_pos );
197
static int AStreamSeekStream( stream_t *s, uint64_t i_pos );
198
198
static void AStreamPrebufferStream( stream_t *s );
199
199
static int AReadStream( stream_t *s, void *p_read, unsigned int i_read );
202
202
static int AStreamControl( stream_t *s, int i_query, va_list );
203
203
static void AStreamDestroy( stream_t *s );
204
204
static void UStreamDestroy( stream_t *s );
205
static int ASeek( stream_t *s, int64_t i_pos );
205
static int ASeek( stream_t *s, uint64_t i_pos );
207
207
/****************************************************************************
208
208
* stream_CommonNew: create an empty stream structure
241
242
vlc_object_release( s );
244
246
/****************************************************************************
245
247
* stream_UrlNew: create a stream from a access
246
248
****************************************************************************/
247
stream_t *__stream_UrlNew( vlc_object_t *p_parent, const char *psz_url )
249
stream_t *stream_UrlNew( vlc_object_t *p_parent, const char *psz_url )
249
251
const char *psz_access, *psz_demux;
258
260
strcpy( psz_dup, psz_url );
259
261
input_SplitMRL( &psz_access, &psz_demux, &psz_path, psz_dup );
263
/* Get a weak link to the parent input */
264
/* FIXME: This should probably be removed in favor of a NULL input. */
265
input_thread_t *p_input = (input_thread_t *)vlc_object_find( p_parent, VLC_OBJECT_INPUT, FIND_PARENT );
261
267
/* Now try a real access */
262
p_access = access_New( p_parent, psz_access, psz_demux, psz_path );
268
p_access = access_New( p_parent, p_input, psz_access, psz_demux, psz_path );
271
vlc_object_release((vlc_object_t*)p_input);
264
273
if( p_access == NULL )
351
access_t *p_tmp = access_New( p_access,
361
access_t *p_tmp = access_New( p_access, p_access->p_input,
352
362
p_access->psz_access, "", psz_name );
568
575
stream_sys_t *p_sys = s->p_sys;
569
576
access_t *p_access = p_sys->p_access;
572
int64_t *pi_64, i_64;
579
uint64_t *pi_64, i_64;
575
582
switch( i_query )
577
584
case STREAM_GET_SIZE:
578
pi_64 = (int64_t*)va_arg( args, int64_t * );
585
pi_64 = va_arg( args, uint64_t * );
579
586
if( s->p_sys->i_list )
600
607
case STREAM_GET_POSITION:
601
pi_64 = (int64_t*)va_arg( args, int64_t * );
608
pi_64 = va_arg( args, uint64_t * );
602
609
*pi_64 = p_sys->i_pos;
605
612
case STREAM_SET_POSITION:
606
i_64 = (int64_t)va_arg( args, int64_t );
613
i_64 = va_arg( args, uint64_t );
607
614
switch( p_sys->method )
609
616
case STREAM_METHOD_BLOCK:
796
803
/* We need to create a local copy */
797
804
if( p_sys->i_peek < i_read )
799
p_sys->p_peek = realloc( p_sys->p_peek, i_read );
806
p_sys->p_peek = realloc_or_free( p_sys->p_peek, i_read );
800
807
if( !p_sys->p_peek )
802
809
p_sys->i_peek = 0;
846
static int AStreamSeekBlock( stream_t *s, int64_t i_pos )
853
static int AStreamSeekBlock( stream_t *s, uint64_t i_pos )
848
855
stream_sys_t *p_sys = s->p_sys;
849
856
access_t *p_access = p_sys->p_access;
853
860
/* We already have thoses data, just update p_current/i_offset */
854
if( i_offset >= 0 && i_offset < p_sys->block.i_size )
861
if( i_offset >= 0 && (uint64_t)i_offset < p_sys->block.i_size )
856
863
block_t *b = p_sys->block.p_first;
857
864
int i_current = 0;
859
while( i_current + b->i_buffer < i_offset )
866
while( i_current + b->i_buffer < (uint64_t)i_offset )
861
868
i_current += b->i_buffer;
953
/* Read and skip enough data */
954
if( AStreamRefillBlock( s ) )
957
960
while( p_sys->block.p_current &&
958
p_sys->i_pos + p_sys->block.p_current->i_buffer - p_sys->block.i_offset < i_pos )
961
p_sys->i_pos + p_sys->block.p_current->i_buffer - p_sys->block.i_offset <= i_pos )
960
963
p_sys->i_pos += p_sys->block.p_current->i_buffer - p_sys->block.i_offset;
961
964
p_sys->block.p_current = p_sys->block.p_current->p_next;
962
965
p_sys->block.i_offset = 0;
967
if( !p_sys->block.p_current && AStreamRefillBlock( s ) )
969
if( p_sys->i_pos != i_pos )
965
973
while( p_sys->block.i_start + p_sys->block.i_size < i_pos );
1079
1087
if( i_read > STREAM_CACHE_TRACK_SIZE / 2 )
1080
1088
i_read = STREAM_CACHE_TRACK_SIZE / 2;
1082
while( tk->i_end - tk->i_start - p_sys->stream.i_offset < i_read )
1090
while( tk->i_end < tk->i_start + p_sys->stream.i_offset + i_read )
1084
1092
if( p_sys->stream.i_used <= 1 )
1086
1094
/* Be sure we will read something */
1087
p_sys->stream.i_used += i_read -
1088
(tk->i_end - tk->i_start - p_sys->stream.i_offset);
1095
p_sys->stream.i_used += tk->i_start + p_sys->stream.i_offset + i_read - tk->i_end;
1090
1097
if( AStreamRefillStream( s ) ) break;
1093
if( tk->i_end - tk->i_start - p_sys->stream.i_offset < i_read )
1100
if( tk->i_end < tk->i_start + p_sys->stream.i_offset + i_read )
1094
1102
i_read = tk->i_end - tk->i_start - p_sys->stream.i_offset;
1096
1106
/* Now, direct pointer or a copy ? */
1097
1107
i_off = (tk->i_start + p_sys->stream.i_offset) % STREAM_CACHE_TRACK_SIZE;
1152
1162
access_Control( p_access, ACCESS_CAN_FASTSEEK, &b_afastseek );
1154
1164
/* FIXME compute seek cost (instead of static 'stupid' value) */
1155
int64_t i_skip_threshold;
1165
uint64_t i_skip_threshold;
1157
1167
i_skip_threshold = b_afastseek ? 128 : 3*p_sys->stream.i_read_size;
1166
1176
int i_tk_idx = -1;
1168
1178
/* Prefer the current track */
1169
if( p_current->i_start <= i_pos && i_pos - p_current->i_end <= i_skip_threshold )
1179
if( p_current->i_start <= i_pos && i_pos <= p_current->i_end + i_skip_threshold )
1171
1181
tk = p_current;
1172
1182
i_tk_idx = p_sys->stream.i_tk;
1224
1234
if( ASeek( s, tk->i_end ) )
1225
1235
return VLC_EGENERIC;
1237
else if( i_pos > tk->i_end )
1229
int64_t i_skip = i_pos - tk->i_end;
1239
uint64_t i_skip = i_pos - tk->i_end;
1230
1240
while( i_skip > 0 )
1232
1242
const int i_read_max = __MIN( 10 * STREAM_READ_ATONCE, i_skip );
1257
1267
* - refilling threshold
1258
1268
* - how much to refill
1260
if( (tk->i_end - tk->i_start) - p_sys->stream.i_offset < p_sys->stream.i_read_size )
1270
if( tk->i_end < tk->i_start + p_sys->stream.i_offset + p_sys->stream.i_read_size )
1262
1272
if( p_sys->stream.i_used < STREAM_READ_ATONCE / 2 )
1263
1273
p_sys->stream.i_used = STREAM_READ_ATONCE / 2;
1289
1299
while( i_data < i_read )
1291
int i_off = (tk->i_start + p_sys->stream.i_offset) %
1292
STREAM_CACHE_TRACK_SIZE;
1301
unsigned i_off = (tk->i_start + p_sys->stream.i_offset) % STREAM_CACHE_TRACK_SIZE;
1293
1302
unsigned int i_current =
1294
__MAX(0,__MIN( tk->i_end - tk->i_start - p_sys->stream.i_offset,
1295
STREAM_CACHE_TRACK_SIZE - i_off ));
1303
__MIN( tk->i_end - tk->i_start - p_sys->stream.i_offset,
1304
STREAM_CACHE_TRACK_SIZE - i_off );
1296
1305
int i_copy = __MIN( i_current, i_read - i_data );
1298
1307
if( i_copy <= 0 ) break; /* EOF */
1314
1323
p_sys->stream.i_used += i_copy;
1316
if( tk->i_end - tk->i_start - p_sys->stream.i_offset <= i_read -i_data )
1325
if( tk->i_end + i_data <= tk->i_start + p_sys->stream.i_offset + i_read )
1318
const int i_read_requested = __MAX( __MIN( i_read - i_data,
1319
STREAM_READ_ATONCE * 10 ),
1320
STREAM_READ_ATONCE / 2 );
1327
const unsigned i_read_requested = __MAX( __MIN( i_read - i_data,
1328
STREAM_READ_ATONCE * 10 ),
1329
STREAM_READ_ATONCE / 2 );
1322
1331
if( p_sys->stream.i_used < i_read_requested )
1323
1332
p_sys->stream.i_used = i_read_requested;
1382
1391
tk->i_end += i_read;
1384
1393
/* Windows of STREAM_CACHE_TRACK_SIZE */
1385
if( tk->i_end - tk->i_start > STREAM_CACHE_TRACK_SIZE )
1394
if( tk->i_start + STREAM_CACHE_TRACK_SIZE < tk->i_end )
1387
int i_invalid = tk->i_end - tk->i_start - STREAM_CACHE_TRACK_SIZE;
1396
unsigned i_invalid = tk->i_end - tk->i_start - STREAM_CACHE_TRACK_SIZE;
1389
1398
tk->i_start += i_invalid;
1390
1399
p_sys->stream.i_offset -= i_invalid;
1419
1428
int64_t i_date = mdate();
1430
int i_buffered = tk->i_end - tk->i_start;
1422
if( s->b_die || tk->i_end >= STREAM_CACHE_PREBUFFER_SIZE )
1432
if( s->b_die || i_buffered >= STREAM_CACHE_PREBUFFER_SIZE )
1424
1434
int64_t i_byterate;
1426
1436
/* Update stat */
1427
p_sys->stat.i_bytes = tk->i_end - tk->i_start;
1437
p_sys->stat.i_bytes = i_buffered;
1428
1438
p_sys->stat.i_read_time = i_date - i_start;
1429
1439
i_byterate = ( INT64_C(1000000) * p_sys->stat.i_bytes ) /
1430
1440
(p_sys->stat.i_read_time+1);
1432
1442
msg_Dbg( s, "pre-buffering done %"PRId64" bytes in %"PRId64"s - "
1433
"%"PRId64" kbytes/s",
1434
1444
p_sys->stat.i_bytes,
1435
1445
p_sys->stat.i_read_time / INT64_C(1000000),
1436
1446
i_byterate / 1024 );
1441
i_read = STREAM_CACHE_TRACK_SIZE - tk->i_end;
1442
i_read = __MIN( p_sys->stream.i_read_size, i_read );
1443
i_read = AReadStream( s, &tk->p_buffer[tk->i_end], i_read );
1451
i_read = STREAM_CACHE_TRACK_SIZE - i_buffered;
1452
i_read = __MIN( (int)p_sys->stream.i_read_size, i_read );
1453
i_read = AReadStream( s, &tk->p_buffer[i_buffered], i_read );
1444
1454
if( i_read < 0 )
1446
1456
else if( i_read == 0 )
1535
1545
/* FIXME that's UGLY */
1536
input_thread_t *p_input;
1537
p_input = (input_thread_t *)vlc_object_find( s, VLC_OBJECT_INPUT, FIND_PARENT );
1546
input_thread_t *p_input = s->p_input;
1538
1547
if( p_input != NULL)
1540
1549
var_Create( p_input, "subsdec-encoding", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
1541
1550
var_SetString( p_input, "subsdec-encoding", "UTF-8" );
1542
vlc_object_release( p_input );
1560
1568
/* UTF-8: 0A <LF> */
1561
1569
psz_eol = memchr( p_data, '\n', i_data );
1570
if( psz_eol == NULL )
1571
/* UTF-8: 0D <CR> */
1572
psz_eol = memchr( p_data, '\r', i_data );
1565
const uint8_t *p = p_data;
1566
const uint8_t *p_last = p + i_data - s->p_text->i_char_width;
1568
if( s->p_text->i_char_width == 2 )
1570
if( s->p_text->b_little_endian == true)
1572
/* UTF-16LE: 0A 00 <LF> */
1573
while( p <= p_last && ( p[0] != 0x0A || p[1] != 0x00 ) )
1578
/* UTF-16BE: 00 0A <LF> */
1579
while( p <= p_last && ( p[1] != 0x0A || p[0] != 0x00 ) )
1590
psz_eol = (char *)p + ( s->p_text->i_char_width - 1 );
1576
const uint8_t *p_last = p_data + i_data - s->p_text->i_char_width;
1577
uint16_t eol = s->p_text->b_little_endian ? 0x0A00 : 0x00A0;
1579
assert( s->p_text->i_char_width == 2 );
1581
/* UTF-16: 000A <LF> */
1582
for( const uint8_t *p = p_data; p <= p_last; p += 2 )
1584
if( U16_AT( p ) == eol )
1586
psz_eol = (char *)p + 1;
1591
if( psz_eol == NULL )
1592
{ /* UTF-16: 000D <CR> */
1593
eol = s->p_text->b_little_endian ? 0x0D00 : 0x00D0;
1594
for( const uint8_t *p = p_data; p <= p_last; p += 2 )
1596
if( U16_AT( p ) == eol )
1598
psz_eol = (char *)p + 1;
1596
1607
i_data = (psz_eol - (char *)p_data) + 1;
1597
p_line = realloc( p_line, i_line + i_data + s->p_text->i_char_width ); /* add \0 */
1608
p_line = realloc_or_free( p_line,
1609
i_line + i_data + s->p_text->i_char_width ); /* add \0 */
1600
1612
i_data = stream_Read( s, &p_line[i_line], i_data );
1609
1621
/* Read data (+1 for easy \0 append) */
1610
p_line = realloc( p_line, i_line + STREAM_PROBE_LINE + s->p_text->i_char_width );
1622
p_line = realloc_or_free( p_line,
1623
i_line + STREAM_PROBE_LINE + s->p_text->i_char_width );
1613
1626
i_data = stream_Read( s, &p_line[i_line], STREAM_PROBE_LINE );
1717
1730
msg_Dbg( s, "opening input `%s'", psz_name );
1719
p_list_access = access_New( s, p_access->psz_access, "", psz_name );
1732
p_list_access = access_New( s, s->p_input, p_access->psz_access, "", psz_name );
1721
1734
if( !p_list_access ) return 0;
1789
1802
msg_Dbg( s, "opening input `%s'", psz_name );
1791
p_list_access = access_New( s, p_access->psz_access, "", psz_name );
1804
p_list_access = access_New( s, s->p_input, p_access->psz_access, "", psz_name );
1793
1806
if( !p_list_access ) return 0;
1817
1830
return p_block;
1820
static int ASeek( stream_t *s, int64_t i_pos )
1833
static int ASeek( stream_t *s, uint64_t i_pos )
1822
1835
stream_sys_t *p_sys = s->p_sys;
1823
1836
access_t *p_access = p_sys->p_access;
1826
return VLC_EGENERIC;
1828
1838
/* Check which stream we need to access */
1829
1839
if( p_sys->i_list )
1846
1856
if( i != p_sys->i_list_index && i != 0 )
1848
1858
p_list_access =
1849
access_New( s, p_access->psz_access, "", psz_name );
1859
access_New( s, s->p_input, p_access->psz_access, "", psz_name );
1851
1861
else if( i != p_sys->i_list_index )