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