1 /*****************************************************************************
2 * stream.c test streams and stream filters
3 *****************************************************************************
4 * Copyright (C) 2015 VLC authors and VideoLAN
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
10 *
11 * This program 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 Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
20
21 #include "../../libvlc/test.h"
22 #include "../lib/libvlc_internal.h"
23
24 #include <vlc_md5.h>
25 #include <vlc_stream.h>
26 #include <vlc_rand.h>
27 #include <vlc_fs.h>
28
29 #include <inttypes.h>
30 #include <limits.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34
35 #ifndef TEST_NET
36 #define RAND_FILE_SIZE (25 * 1024 * 1024)
37 #else
38 #define HTTP_URL "http://streams.videolan.org/streams/ogm/MJPEG.ogm"
39 #define HTTP_MD5 "4eaf9e8837759b670694398a33f02bc0"
40 #endif
41
42 struct reader
43 {
44 const char *psz_name;
45 union {
46 FILE *f;
47 stream_t *s;
48 } u;
49 void *p_data;
50
51 void (*pf_close)( struct reader * );
52 uint64_t (*pf_getsize)( struct reader * );
53 ssize_t (*pf_read)( struct reader *, void *, size_t );
54 ssize_t (*pf_peek)( struct reader *, const uint8_t **, size_t );
55 uint64_t (*pf_tell)( struct reader * );
56 int (*pf_seek)( struct reader *, uint64_t );
57 };
58
59 #ifndef TEST_NET
60 static uint64_t
libc_getsize(struct reader * p_reader)61 libc_getsize( struct reader *p_reader )
62 {
63 struct stat st;
64 int i_fd = fileno( p_reader->u.f );
65
66 assert( i_fd >= 0 );
67 assert( fstat( i_fd, &st ) != -1 );
68
69 return st.st_size;
70 }
71
72 static ssize_t
libc_read(struct reader * p_reader,void * p_buf,size_t i_len)73 libc_read( struct reader *p_reader, void *p_buf, size_t i_len )
74 {
75 return fread( p_buf, 1, i_len , p_reader->u.f );
76 }
77
78 static ssize_t
libc_peek(struct reader * p_reader,const uint8_t ** pp_buf,size_t i_len)79 libc_peek( struct reader *p_reader, const uint8_t **pp_buf, size_t i_len )
80 {
81 ssize_t i_ret;
82 long i_last_pos;
83
84 free( p_reader->p_data );
85 p_reader->p_data = malloc( i_len );
86 assert( p_reader->p_data );
87
88 i_last_pos = ftell( p_reader->u.f );
89
90 i_ret = fread( p_reader->p_data, 1, i_len, p_reader->u.f );
91 *pp_buf = p_reader->p_data;
92
93 assert( fseek( p_reader->u.f, i_last_pos, SEEK_SET ) == 0 );
94 return i_ret;
95 }
96
97 static uint64_t
libc_tell(struct reader * p_reader)98 libc_tell( struct reader *p_reader )
99 {
100 long i_ret = ftell( p_reader->u.f );
101 assert( i_ret >= 0 );
102 return i_ret;
103 }
104
105 static int
libc_seek(struct reader * p_reader,uint64_t i_offset)106 libc_seek( struct reader *p_reader, uint64_t i_offset )
107 {
108 return fseek( p_reader->u.f, (long) i_offset, SEEK_SET );
109 }
110
111 static void
libc_close(struct reader * p_reader)112 libc_close( struct reader *p_reader )
113 {
114 fclose( p_reader->u.f );
115 free( p_reader->p_data );
116 free( p_reader );
117 }
118
119 static struct reader *
libc_open(const char * psz_file)120 libc_open( const char *psz_file )
121 {
122 struct reader *p_reader = calloc( 1, sizeof(struct reader) );
123 assert( p_reader );
124
125 p_reader->u.f = fopen( psz_file, "r" );
126 if( !p_reader->u.f )
127 {
128 free( p_reader );
129 return NULL;
130 }
131 p_reader->pf_close = libc_close;
132 p_reader->pf_getsize = libc_getsize;
133 p_reader->pf_read = libc_read;
134 p_reader->pf_peek = libc_peek;
135 p_reader->pf_tell = libc_tell;
136 p_reader->pf_seek = libc_seek;
137 p_reader->psz_name = "libc";
138 assert( p_reader->psz_name );
139 return p_reader;
140 }
141 #endif
142
143 static uint64_t
stream_getsize(struct reader * p_reader)144 stream_getsize( struct reader *p_reader )
145 {
146 uint64_t i_size;
147
148 assert( vlc_stream_GetSize( p_reader->u.s, &i_size ) == 0 );
149 return i_size;
150 }
151
152 static ssize_t
stream_read(struct reader * p_reader,void * p_buf,size_t i_len)153 stream_read( struct reader *p_reader, void *p_buf, size_t i_len )
154 {
155 return vlc_stream_Read( p_reader->u.s, p_buf, i_len );
156 }
157
158 static ssize_t
stream_peek(struct reader * p_reader,const uint8_t ** pp_buf,size_t i_len)159 stream_peek( struct reader *p_reader, const uint8_t **pp_buf, size_t i_len )
160 {
161 return vlc_stream_Peek( p_reader->u.s, pp_buf, i_len );
162 }
163
164 static uint64_t
stream_tell(struct reader * p_reader)165 stream_tell( struct reader *p_reader )
166 {
167 return vlc_stream_Tell( p_reader->u.s );
168 }
169
170 static int
stream_seek(struct reader * p_reader,uint64_t i_offset)171 stream_seek( struct reader *p_reader, uint64_t i_offset )
172 {
173 return vlc_stream_Seek( p_reader->u.s, i_offset );
174 }
175
176 static void
stream_close(struct reader * p_reader)177 stream_close( struct reader *p_reader )
178 {
179 vlc_stream_Delete( p_reader->u.s );
180 libvlc_release( p_reader->p_data );
181 free( p_reader );
182 }
183
184 static struct reader *
stream_open(const char * psz_url)185 stream_open( const char *psz_url )
186 {
187 libvlc_instance_t *p_vlc;
188 struct reader *p_reader;
189 const char * argv[] = {
190 "-v",
191 "--ignore-config",
192 "-I",
193 "dummy",
194 "--no-media-library",
195 "--vout=dummy",
196 "--aout=dummy",
197 };
198
199 p_reader = calloc( 1, sizeof(struct reader) );
200 assert( p_reader );
201
202 p_vlc = libvlc_new( sizeof(argv) / sizeof(argv[0]), argv );
203 assert( p_vlc != NULL );
204
205 p_reader->u.s = vlc_stream_NewURL( p_vlc->p_libvlc_int, psz_url );
206 if( !p_reader->u.s )
207 {
208 libvlc_release( p_vlc );
209 free( p_reader );
210 return NULL;
211 }
212 p_reader->pf_close = stream_close;
213 p_reader->pf_getsize = stream_getsize;
214 p_reader->pf_read = stream_read;
215 p_reader->pf_peek = stream_peek;
216 p_reader->pf_tell = stream_tell;
217 p_reader->pf_seek = stream_seek;
218 p_reader->p_data = p_vlc;
219 p_reader->psz_name = "stream";
220 return p_reader;
221 }
222
223 static ssize_t
read_at(struct reader ** pp_readers,unsigned int i_readers,void * p_buf,uint64_t i_offset,size_t i_read,uint64_t i_size)224 read_at( struct reader **pp_readers, unsigned int i_readers,
225 void *p_buf, uint64_t i_offset,
226 size_t i_read, uint64_t i_size )
227 {
228 void *p_cmp_buf = NULL;
229 ssize_t i_cmp_ret = 0, i_ret = 0;
230
231 for( unsigned i = 0; i < i_readers; ++i )
232 {
233 uint64_t i_last_pos;
234 const uint8_t *p_peek = NULL;
235 struct reader *p_reader = pp_readers[i];
236
237 log( "%s: %s %zu @ %"PRIu64" (size: %" PRIu64 ")\n", p_reader->psz_name,
238 p_buf ? "read" : "peek", i_read, i_offset, i_size );
239 assert( p_reader->pf_seek( p_reader, i_offset ) != -1 );
240
241 i_last_pos = p_reader->pf_tell( p_reader );
242 assert( i_last_pos == i_offset );
243
244 if( p_buf )
245 {
246 i_ret = p_reader->pf_read( p_reader, p_buf, i_read );
247 assert( i_ret >= 0 );
248 assert( p_reader->pf_tell( p_reader ) == i_ret + i_last_pos );
249 }
250 else
251 {
252 i_ret = p_reader->pf_peek( p_reader, &p_peek, i_read );
253 assert( i_ret >= 0 );
254 assert( p_reader->pf_tell( p_reader ) == i_last_pos );
255 if( i_ret > 0 )
256 assert( p_peek );
257 }
258
259 if( i_offset < i_size )
260 assert( (size_t) i_ret == __MIN( i_read, i_size - i_last_pos ) );
261
262 if( i_readers > 1 )
263 {
264 if( i == 0 )
265 {
266 if( i_ret > 0 )
267 {
268 p_cmp_buf = malloc( i_ret );
269 assert( p_cmp_buf );
270 memcpy( p_cmp_buf, p_buf ? p_buf : p_peek, i_ret );
271 }
272 i_cmp_ret = i_ret;
273 }
274 else
275 {
276 assert( i_cmp_ret == i_ret );
277 if( i_ret > 0 )
278 assert( memcmp( p_cmp_buf, p_buf ? p_buf : p_peek, i_ret ) == 0 );
279 }
280 }
281 }
282 free( p_cmp_buf );
283 return i_ret;
284 }
285
286 static void
test(struct reader ** pp_readers,unsigned int i_readers,const char * psz_md5)287 test( struct reader **pp_readers, unsigned int i_readers, const char *psz_md5 )
288 {
289 #define READ_AT( i_offset, i_read ) \
290 read_at( pp_readers, i_readers, p_buf, i_offset, i_read, i_size )
291 #define PEEK_AT( i_offset, i_read ) \
292 read_at( pp_readers, i_readers, NULL, i_offset, i_read, i_size )
293 uint8_t p_buf[4096];
294 ssize_t i_ret = 0;
295 uint64_t i_offset = 0;
296 uint64_t i_size;
297 char *psz_read_md5;
298 struct md5_s md5;
299
300 /* Compare size between each readers */
301 i_size = pp_readers[0]->pf_getsize( pp_readers[0] );
302 assert( i_size > 0 );
303
304 log( "stream size: %"PRIu64"\n", i_size );
305 for( unsigned int i = 1; i < i_readers; ++i )
306 assert( pp_readers[i]->pf_getsize( pp_readers[i] ) == i_size );
307
308 /* Read the whole file and compare between each readers */
309 if( psz_md5 != NULL )
310 InitMD5( &md5 );
311 while( ( i_ret = READ_AT( i_offset, 4096 ) ) > 0 )
312 {
313 i_offset += i_ret;
314 if( psz_md5 != NULL )
315 AddMD5( &md5, p_buf, i_ret );
316 }
317 if( psz_md5 != NULL )
318 {
319 EndMD5( &md5 );
320 psz_read_md5 = psz_md5_hash( &md5 );
321 assert( psz_read_md5 );
322 assert( strcmp( psz_read_md5, psz_md5 ) == 0 );
323 free( psz_read_md5 );
324 }
325
326 /* Test cache skip */
327 i_offset = 9 * i_size / 10;
328 while( i_offset < i_size && ( i_ret = READ_AT( i_offset, 4096 ) ) > 0 )
329 i_offset += i_ret + 1;
330
331 /* Test seek and peek */
332 READ_AT( 0, 42 );
333 READ_AT( i_size - 5, 43 );
334 READ_AT( 1, 45 );
335 READ_AT( 2, 45 );
336 READ_AT( i_size / 2, 45 );
337 READ_AT( 2, 45 );
338 READ_AT( 1, 45 );
339
340 PEEK_AT( 0, 46 );
341 PEEK_AT( i_size - 23, 46 );
342 PEEK_AT( i_size / 2, 46 );
343 PEEK_AT( 0, 46 );
344 }
345
346 #ifndef TEST_NET
347 static void
fill_rand(int i_fd,size_t i_size)348 fill_rand( int i_fd, size_t i_size )
349 {
350 uint8_t p_buf[4096];
351 size_t i_written = 0;
352 while( i_written < i_size )
353 {
354 size_t i_tocopy = __MIN( i_size - i_written, 4096 );
355
356 vlc_rand_bytes(p_buf, i_tocopy);
357 ssize_t i_ret = write( i_fd, p_buf, i_tocopy );
358 assert( i_ret > 0 );
359 i_written += i_ret;
360 }
361 assert( i_written == i_size );
362 }
363 #endif
364
365 int
main(void)366 main( void )
367 {
368 struct reader *pp_readers[3];
369
370 test_init();
371
372 #ifndef TEST_NET
373 char psz_tmp_path[] = "/tmp/libvlc_XXXXXX";
374 char *psz_url;
375 int i_tmp_fd;
376
377 log( "Test random file with libc, and stream\n" );
378 i_tmp_fd = vlc_mkstemp( psz_tmp_path );
379 fill_rand( i_tmp_fd, RAND_FILE_SIZE );
380 assert( i_tmp_fd != -1 );
381 assert( asprintf( &psz_url, "file://%s", psz_tmp_path ) != -1 );
382
383 assert( ( pp_readers[0] = libc_open( psz_tmp_path ) ) );
384 assert( ( pp_readers[1] = stream_open( psz_url ) ) );
385
386 test( pp_readers, 2, NULL );
387 for( unsigned int i = 0; i < 2; ++i )
388 pp_readers[i]->pf_close( pp_readers[i] );
389 free( psz_url );
390
391 close( i_tmp_fd );
392 #else
393
394 log( "Test http url with stream\n" );
395 alarm( 0 );
396 if( !( pp_readers[0] = stream_open( HTTP_URL ) ) )
397 {
398 log( "WARNING: can't test http url" );
399 return 0;
400 }
401
402 test( pp_readers, 1, HTTP_MD5 );
403 for( unsigned int i = 0; i < 1; ++i )
404 pp_readers[i]->pf_close( pp_readers[i] );
405
406 #endif
407
408 return 0;
409 }
410