/*****************************************************************************
* stream.c test streams and stream filters
*****************************************************************************
* Copyright (C) 2015 VLC authors and VideoLAN
*
* 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
* (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
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* 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.
*****************************************************************************/
#include "../../libvlc/test.h"
#include "../lib/libvlc_internal.h"
#include <vlc_strings.h>
#include <vlc_hash.h>
#include <vlc_stream.h>
#include <vlc_fs.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#ifndef TEST_NET
#define RAND_FILE_SIZE (
1024 *
1024)
#else
#define HTTP_URL
"http://streams.videolan.org/streams/ogm/MJPEG.ogm"
#define HTTP_MD5
"4eaf9e8837759b670694398a33f02bc0"
#endif
struct reader
{
const char *psz_name;
union {
FILE *f;
stream_t *s;
} u;
void *p_data;
void (*pf_close)(
struct reader * );
uint64_t (*pf_getsize)(
struct reader * );
ssize_t (*pf_read)(
struct reader *,
void *, size_t );
ssize_t (*pf_peek)(
struct reader *,
const uint8_t **, size_t );
uint64_t (*pf_tell)(
struct reader * );
int (*pf_seek)(
struct reader *, uint64_t );
};
#ifndef TEST_NET
static uint64_t
libc_getsize(
struct reader *p_reader )
{
struct stat st;
int i_fd = fileno( p_reader->u.f );
assert( i_fd >=
0 );
assert( fstat( i_fd, &st ) != -
1 );
return st.st_size;
}
static ssize_t
libc_read(
struct reader *p_reader,
void *p_buf, size_t i_len )
{
return fread( p_buf,
1, i_len , p_reader->u.f );
}
static ssize_t
libc_peek(
struct reader *p_reader,
const uint8_t **pp_buf, size_t i_len )
{
ssize_t i_ret;
long i_last_pos;
free( p_reader->p_data );
p_reader->p_data = malloc( i_len );
assert( p_reader->p_data );
i_last_pos = ftell( p_reader->u.f );
i_ret = fread( p_reader->p_data,
1, i_len, p_reader->u.f );
*pp_buf = p_reader->p_data;
assert( fseek( p_reader->u.f, i_last_pos, SEEK_SET ) ==
0 );
return i_ret;
}
static uint64_t
libc_tell(
struct reader *p_reader )
{
long i_ret = ftell( p_reader->u.f );
assert( i_ret >=
0 );
return i_ret;
}
static int
libc_seek(
struct reader *p_reader, uint64_t i_offset )
{
return fseek( p_reader->u.f, (
long) i_offset, SEEK_SET );
}
static void
libc_close(
struct reader *p_reader )
{
fclose( p_reader->u.f );
free( p_reader->p_data );
free( p_reader );
}
static struct reader *
libc_open(
const char *psz_file )
{
struct reader *p_reader = calloc(
1,
sizeof(
struct reader) );
assert( p_reader );
p_reader->u.f = fopen( psz_file,
"r" );
if( !p_reader->u.f )
{
free( p_reader );
return NULL;
}
p_reader->pf_close = libc_close;
p_reader->pf_getsize = libc_getsize;
p_reader->pf_read = libc_read;
p_reader->pf_peek = libc_peek;
p_reader->pf_tell = libc_tell;
p_reader->pf_seek = libc_seek;
p_reader->psz_name =
"libc";
assert( p_reader->psz_name );
return p_reader;
}
#endif
static uint64_t
stream_getsize(
struct reader *p_reader )
{
uint64_t i_size;
assert( vlc_stream_GetSize( p_reader->u.s, &i_size ) ==
0 );
return i_size;
}
static ssize_t
stream_read(
struct reader *p_reader,
void *p_buf, size_t i_len )
{
return vlc_stream_Read( p_reader->u.s, p_buf, i_len );
}
static ssize_t
stream_peek(
struct reader *p_reader,
const uint8_t **pp_buf, size_t i_len )
{
return vlc_stream_Peek( p_reader->u.s, pp_buf, i_len );
}
static uint64_t
stream_tell(
struct reader *p_reader )
{
return vlc_stream_Tell( p_reader->u.s );
}
static int
stream_seek(
struct reader *p_reader, uint64_t i_offset )
{
return vlc_stream_Seek( p_reader->u.s, i_offset );
}
static void
stream_close(
struct reader *p_reader )
{
vlc_stream_Delete( p_reader->u.s );
libvlc_release( p_reader->p_data );
free( p_reader );
}
static struct reader *
stream_open(
const char *psz_url )
{
libvlc_instance_t *p_vlc;
struct reader *p_reader;
const char * argv[] = {
"-v",
"--ignore-config",
"-I",
"dummy",
"--no-media-library",
"--vout=dummy",
"--aout=dummy",
};
p_reader = calloc(
1,
sizeof(
struct reader) );
assert( p_reader );
p_vlc = libvlc_new( ARRAY_SIZE(argv), argv );
assert( p_vlc != NULL );
p_reader->u.s = vlc_stream_NewURL( p_vlc->p_libvlc_int, psz_url );
if( !p_reader->u.s )
{
libvlc_release( p_vlc );
free( p_reader );
return NULL;
}
p_reader->pf_close = stream_close;
p_reader->pf_getsize = stream_getsize;
p_reader->pf_read = stream_read;
p_reader->pf_peek = stream_peek;
p_reader->pf_tell = stream_tell;
p_reader->pf_seek = stream_seek;
p_reader->p_data = p_vlc;
p_reader->psz_name =
"stream";
return p_reader;
}
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 )
{
void *p_cmp_buf = NULL;
ssize_t i_cmp_ret =
0, i_ret =
0;
for(
unsigned i =
0; i < i_readers; ++i )
{
uint64_t i_last_pos;
const uint8_t *p_peek = NULL;
struct reader *p_reader = pp_readers[i];
test_log(
"%s: %s %zu @ %"PRIu64
" (size: %" PRIu64
")\n", p_reader->psz_name,
p_buf ?
"read" :
"peek", i_read, i_offset, i_size );
assert( p_reader->pf_seek( p_reader, i_offset ) != -
1 );
i_last_pos = p_reader->pf_tell( p_reader );
assert( i_last_pos == i_offset );
if( p_buf )
{
i_ret = p_reader->pf_read( p_reader, p_buf, i_read );
assert( i_ret >=
0 );
assert( p_reader->pf_tell( p_reader ) == i_ret + i_last_pos );
}
else
{
i_ret = p_reader->pf_peek( p_reader, &p_peek, i_read );
assert( i_ret >=
0 );
assert( p_reader->pf_tell( p_reader ) == i_last_pos );
if( i_ret >
0 )
assert( p_peek );
}
if( i_offset < i_size )
assert( (size_t) i_ret == __MIN( i_read, i_size - i_last_pos ) );
if( i_readers >
1 )
{
if( i ==
0 )
{
if( i_ret >
0 )
{
p_cmp_buf = malloc( i_ret );
assert( p_cmp_buf );
memcpy( p_cmp_buf, p_buf ? p_buf : p_peek, i_ret );
}
i_cmp_ret = i_ret;
}
else
{
assert( i_cmp_ret == i_ret );
if( i_ret >
0 )
assert( memcmp( p_cmp_buf, p_buf ? p_buf : p_peek, i_ret ) ==
0 );
}
}
}
free( p_cmp_buf );
return i_ret;
}
static void
test(
struct reader **pp_readers,
unsigned int i_readers,
const char *psz_md5 )
{
#define READ_AT( i_offset, i_read ) \
read_at( pp_readers, i_readers, p_buf, i_offset, i_read, i_size )
#define PEEK_AT( i_offset, i_read ) \
read_at( pp_readers, i_readers, NULL, i_offset, i_read, i_size )
uint8_t p_buf[
4096];
ssize_t i_ret =
0;
uint64_t i_offset =
0;
uint64_t i_size;
char psz_read_md5[VLC_HASH_MD5_DIGEST_HEX_SIZE];
vlc_hash_md5_t md5;
/* Compare size between each readers */
i_size = pp_readers[
0]->pf_getsize( pp_readers[
0] );
assert( i_size >
0 );
test_log(
"stream size: %"PRIu64
"\n", i_size );
for(
unsigned int i =
1; i < i_readers; ++i )
assert( pp_readers[i]->pf_getsize( pp_readers[i] ) == i_size );
/* Read the whole file and compare between each readers */
if( psz_md5 != NULL )
vlc_hash_md5_Init( &md5 );
while( ( i_ret = READ_AT( i_offset,
4096 ) ) >
0 )
{
i_offset += i_ret;
if( psz_md5 != NULL )
vlc_hash_md5_Update( &md5, p_buf, i_ret );
}
if( psz_md5 != NULL )
{
vlc_hash_FinishHex( &md5, psz_read_md5 );
assert( strcmp( psz_read_md5, psz_md5 ) ==
0 );
}
/* Test cache skip */
i_offset =
9 * i_size /
10;
while( i_offset < i_size && ( i_ret = READ_AT( i_offset,
4096 ) ) >
0 )
i_offset += i_ret +
1;
/* Test seek and peek */
READ_AT(
0,
42 );
READ_AT( i_size -
5,
43 );
READ_AT(
1,
45 );
READ_AT(
2,
45 );
READ_AT( i_size /
2,
45 );
READ_AT(
2,
45 );
READ_AT(
1,
45 );
PEEK_AT(
0,
46 );
PEEK_AT( i_size -
23,
46 );
PEEK_AT( i_size /
2,
46 );
PEEK_AT(
0,
46 );
}
#ifndef TEST_NET
static void
fill_rand(
int i_fd, size_t i_size )
{
unsigned int seed =
12345;
while( i_size >
0 )
{
uint8_t p_buf[
4096];
size_t i_tocopy = __MIN( i_size,
sizeof (p_buf) );
for (size_t i =
0; i < i_tocopy; i++)
p_buf[i] = rand_r(&seed);
ssize_t i_ret = write( i_fd, p_buf, i_tocopy );
if( i_ret < (ssize_t)i_tocopy ) {
if( i_ret >=
0 )
errno = ENOSPC;
perror(
"write error");
abort();
}
i_size -= i_ret;
}
}
#endif
int
main(
void )
{
struct reader *pp_readers[
3];
test_init();
#ifndef TEST_NET
char psz_tmp_path[] =
"/tmp/libvlc_XXXXXX";
char *psz_url;
int i_tmp_fd;
test_log(
"Generating random file...\n" );
i_tmp_fd = vlc_mkstemp( psz_tmp_path );
fill_rand( i_tmp_fd, RAND_FILE_SIZE );
test_log(
"Testing random file with libc, and stream...\n" );
assert( i_tmp_fd != -
1 );
assert( asprintf( &psz_url,
"file://%s", psz_tmp_path ) != -1 );
assert( ( pp_readers[
0] = libc_open( psz_tmp_path ) ) );
assert( ( pp_readers[
1] = stream_open( psz_url ) ) );
test( pp_readers,
2, NULL );
for(
unsigned int i =
0; i <
2; ++i )
pp_readers[i]->pf_close( pp_readers[i] );
free( psz_url );
close( i_tmp_fd );
#else
test_log(
"Testing http url with stream...\n" );
alarm(
0 );
if( !( pp_readers[
0] = stream_open( HTTP_URL ) ) )
{
test_log(
"WARNING: can't test http url" );
return 0;
}
test( pp_readers,
1, HTTP_MD5 );
for(
unsigned int i =
0; i <
1; ++i )
pp_readers[i]->pf_close( pp_readers[i] );
#endif
return 0;
}