Commit 5c2cf083 authored by Gildas Bazin's avatar Gildas Bazin

* src/stream_output/stream_output.c, include/stream_output.h: new sout_AccessOutRead() funcion.
* modules/access_output/file.c: implemented sout_AccessOutRead().
* modules/access_output/udp.c: coding style cleanup.
* modules/mux/mp4.c: Added support for generating "fast start" files (ie. with the moov header at the beginning of the file).
   Started some code cleanup.
parent 83fb2ffc
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* stream_output.h : stream output module * stream_output.h : stream output module
***************************************************************************** *****************************************************************************
* Copyright (C) 2002 VideoLAN * Copyright (C) 2002 VideoLAN
* $Id: stream_output.h,v 1.18 2003/12/07 17:09:33 gbazin Exp $ * $Id: stream_output.h,v 1.19 2004/01/23 17:56:14 gbazin Exp $
* *
* Authors: Christophe Massiot <massiot@via.ecp.fr> * Authors: Christophe Massiot <massiot@via.ecp.fr>
* Laurent Aimar <fenrir@via.ecp.fr> * Laurent Aimar <fenrir@via.ecp.fr>
...@@ -121,6 +121,8 @@ struct sout_access_out_t ...@@ -121,6 +121,8 @@ struct sout_access_out_t
sout_access_out_sys_t *p_sys; sout_access_out_sys_t *p_sys;
int (* pf_seek )( sout_access_out_t *, int (* pf_seek )( sout_access_out_t *,
off_t ); off_t );
int (* pf_read )( sout_access_out_t *,
sout_buffer_t * );
int (* pf_write )( sout_access_out_t *, int (* pf_write )( sout_access_out_t *,
sout_buffer_t * ); sout_buffer_t * );
}; };
...@@ -295,6 +297,7 @@ VLC_EXPORT( void, sout_BufferChain, ( sout_buffer_t **, sout_buffer_t ...@@ -295,6 +297,7 @@ VLC_EXPORT( void, sout_BufferChain, ( sout_buffer_t **, sout_buffer_t
VLC_EXPORT( sout_access_out_t *, sout_AccessOutNew, ( sout_instance_t *, char *psz_access, char *psz_name ) ); VLC_EXPORT( sout_access_out_t *, sout_AccessOutNew, ( sout_instance_t *, char *psz_access, char *psz_name ) );
VLC_EXPORT( void, sout_AccessOutDelete, ( sout_access_out_t * ) ); VLC_EXPORT( void, sout_AccessOutDelete, ( sout_access_out_t * ) );
VLC_EXPORT( int, sout_AccessOutSeek, ( sout_access_out_t *, off_t ) ); VLC_EXPORT( int, sout_AccessOutSeek, ( sout_access_out_t *, off_t ) );
VLC_EXPORT( int, sout_AccessOutRead, ( sout_access_out_t *, sout_buffer_t * ) );
VLC_EXPORT( int, sout_AccessOutWrite, ( sout_access_out_t *, sout_buffer_t * ) ); VLC_EXPORT( int, sout_AccessOutWrite, ( sout_access_out_t *, sout_buffer_t * ) );
VLC_EXPORT( sout_mux_t *, sout_MuxNew, ( sout_instance_t*, char *, sout_access_out_t * ) ); VLC_EXPORT( sout_mux_t *, sout_MuxNew, ( sout_instance_t*, char *, sout_access_out_t * ) );
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* file.c * file.c
***************************************************************************** *****************************************************************************
* Copyright (C) 2001, 2002 VideoLAN * Copyright (C) 2001, 2002 VideoLAN
* $Id: file.c,v 1.10 2003/12/04 12:33:43 gbazin Exp $ * $Id: file.c,v 1.11 2004/01/23 17:56:14 gbazin Exp $
* *
* Authors: Laurent Aimar <fenrir@via.ecp.fr> * Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org> * Eric Petit <titer@videolan.org>
...@@ -61,6 +61,7 @@ static void Close ( vlc_object_t * ); ...@@ -61,6 +61,7 @@ static void Close ( vlc_object_t * );
static int Write( sout_access_out_t *, sout_buffer_t * ); static int Write( sout_access_out_t *, sout_buffer_t * );
static int Seek ( sout_access_out_t *, off_t ); static int Seek ( sout_access_out_t *, off_t );
static int Read ( sout_access_out_t *, sout_buffer_t * );
/***************************************************************************** /*****************************************************************************
* Module descriptor * Module descriptor
...@@ -97,7 +98,7 @@ static int Open( vlc_object_t *p_this ) ...@@ -97,7 +98,7 @@ static int Open( vlc_object_t *p_this )
msg_Err( p_access, "no file name specified" ); msg_Err( p_access, "no file name specified" );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
i_flags = O_WRONLY|O_CREAT; i_flags = O_RDWR|O_CREAT;
if( sout_cfg_find_value( p_access->p_cfg, "append" ) ) if( sout_cfg_find_value( p_access->p_cfg, "append" ) )
{ {
i_flags |= O_APPEND; i_flags |= O_APPEND;
...@@ -120,8 +121,9 @@ static int Open( vlc_object_t *p_this ) ...@@ -120,8 +121,9 @@ static int Open( vlc_object_t *p_this )
return( VLC_EGENERIC ); return( VLC_EGENERIC );
} }
p_access->pf_write = Write; p_access->pf_write = Write;
p_access->pf_seek = Seek; p_access->pf_read = Read;
p_access->pf_seek = Seek;
msg_Info( p_access, "Open: name:`%s'", p_access->psz_name ); msg_Info( p_access, "Open: name:`%s'", p_access->psz_name );
return VLC_SUCCESS; return VLC_SUCCESS;
...@@ -132,7 +134,7 @@ static int Open( vlc_object_t *p_this ) ...@@ -132,7 +134,7 @@ static int Open( vlc_object_t *p_this )
*****************************************************************************/ *****************************************************************************/
static void Close( vlc_object_t * p_this ) static void Close( vlc_object_t * p_this )
{ {
sout_access_out_t *p_access = (sout_access_out_t*)p_this; sout_access_out_t *p_access = (sout_access_out_t*)p_this;
if( strcmp( p_access->psz_name, "-" ) ) if( strcmp( p_access->psz_name, "-" ) )
{ {
...@@ -149,6 +151,23 @@ static void Close( vlc_object_t * p_this ) ...@@ -149,6 +151,23 @@ static void Close( vlc_object_t * p_this )
/***************************************************************************** /*****************************************************************************
* Read: standard read on a file descriptor. * Read: standard read on a file descriptor.
*****************************************************************************/ *****************************************************************************/
static int Read( sout_access_out_t *p_access, sout_buffer_t *p_buffer )
{
if( strcmp( p_access->psz_name, "-" ) )
{
return read( p_access->p_sys->i_handle, p_buffer->p_buffer,
p_buffer->i_size );
}
else
{
msg_Err( p_access, "cannot seek while using stdout" );
return VLC_EGENERIC;
}
}
/*****************************************************************************
* Write: standard write on a file descriptor.
*****************************************************************************/
static int Write( sout_access_out_t *p_access, sout_buffer_t *p_buffer ) static int Write( sout_access_out_t *p_access, sout_buffer_t *p_buffer )
{ {
size_t i_write = 0; size_t i_write = 0;
...@@ -173,7 +192,7 @@ static int Write( sout_access_out_t *p_access, sout_buffer_t *p_buffer ) ...@@ -173,7 +192,7 @@ static int Write( sout_access_out_t *p_access, sout_buffer_t *p_buffer )
*****************************************************************************/ *****************************************************************************/
static int Seek( sout_access_out_t *p_access, off_t i_pos ) static int Seek( sout_access_out_t *p_access, off_t i_pos )
{ {
msg_Dbg( p_access, "Seek: pos:"I64Fd, (int64_t)i_pos ); //msg_Dbg( p_access, "Seek: pos:"I64Fd, (int64_t)i_pos );
if( strcmp( p_access->psz_name, "-" ) ) if( strcmp( p_access->psz_name, "-" ) )
{ {
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* udp.c * udp.c
***************************************************************************** *****************************************************************************
* Copyright (C) 2001, 2002 VideoLAN * Copyright (C) 2001, 2002 VideoLAN
* $Id: udp.c,v 1.16 2003/11/17 14:46:37 massiot Exp $ * $Id: udp.c,v 1.17 2004/01/23 17:56:14 gbazin Exp $
* *
* Authors: Laurent Aimar <fenrir@via.ecp.fr> * Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org> * Eric Petit <titer@videolan.org>
...@@ -53,8 +53,6 @@ ...@@ -53,8 +53,6 @@
#include "network.h" #include "network.h"
#define DEFAULT_PORT 1234 #define DEFAULT_PORT 1234
#define LATENCY 100000
#define MAX_ERROR 500000
/***************************************************************************** /*****************************************************************************
* Exported prototypes * Exported prototypes
*****************************************************************************/ *****************************************************************************/
...@@ -130,13 +128,14 @@ static int Open( vlc_object_t *p_this ) ...@@ -130,13 +128,14 @@ static int Open( vlc_object_t *p_this )
module_t *p_network; module_t *p_network;
network_socket_t socket_desc; network_socket_t socket_desc;
char *val; vlc_value_t val;
char *psz_val;
if( !( p_sys = p_access->p_sys = if( !( p_sys = p_access->p_sys =
malloc( sizeof( sout_access_out_sys_t ) ) ) ) malloc( sizeof( sout_access_out_sys_t ) ) ) )
{ {
msg_Err( p_access, "Not enough memory" ); msg_Err( p_access, "Not enough memory" );
return( VLC_EGENERIC ); return VLC_EGENERIC;
} }
...@@ -184,7 +183,7 @@ static int Open( vlc_object_t *p_this ) ...@@ -184,7 +183,7 @@ static int Open( vlc_object_t *p_this )
if( !p_sys->p_thread ) if( !p_sys->p_thread )
{ {
msg_Err( p_access, "out of memory" ); msg_Err( p_access, "out of memory" );
return( VLC_EGENERIC ); return VLC_EGENERIC;
} }
p_sys->p_thread->p_sout = p_access->p_sout; p_sys->p_thread->p_sout = p_access->p_sout;
...@@ -198,34 +197,37 @@ static int Open( vlc_object_t *p_this ) ...@@ -198,34 +197,37 @@ static int Open( vlc_object_t *p_this )
socket_desc.psz_bind_addr = ""; socket_desc.psz_bind_addr = "";
socket_desc.i_bind_port = 0; socket_desc.i_bind_port = 0;
socket_desc.i_ttl = 0; socket_desc.i_ttl = 0;
if( ( val = sout_cfg_find_value( p_access->p_cfg, "ttl" ) ) ) if( ( psz_val = sout_cfg_find_value( p_access->p_cfg, "ttl" ) ) )
{ {
socket_desc.i_ttl = atoi( val ); socket_desc.i_ttl = atoi( psz_val );
} }
p_sys->p_thread->p_private = (void*)&socket_desc; p_sys->p_thread->p_private = (void*)&socket_desc;
if( !( p_network = module_Need( p_sys->p_thread, if( !( p_network = module_Need( p_sys->p_thread, "network", "" ) ) )
"network", "" ) ) )
{ {
msg_Err( p_access, "failed to open a connection (udp)" ); msg_Err( p_access, "failed to open a connection (udp)" );
return( VLC_EGENERIC ); return VLC_EGENERIC;
} }
module_Unneed( p_sys->p_thread, p_network ); module_Unneed( p_sys->p_thread, p_network );
p_sys->p_thread->i_handle = socket_desc.i_handle; p_sys->p_thread->i_handle = socket_desc.i_handle;
p_sys->p_thread->i_caching = config_GetInt( p_this, "udp-sout-caching" ) * 1000;
if( ( val = sout_cfg_find_value( p_access->p_cfg, "caching" ) ) ) var_Create( p_this, "udp-sout-caching",
VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Get( p_this, "udp-sout-caching", &val );
p_sys->p_thread->i_caching = val.i_int * 1000;
if( ( psz_val = sout_cfg_find_value( p_access->p_cfg, "caching" ) ) )
{ {
p_sys->p_thread->i_caching = atoll( val ) * 1000; p_sys->p_thread->i_caching = atoll( psz_val ) * 1000;
} }
p_sys->i_mtu = socket_desc.i_mtu; p_sys->i_mtu = socket_desc.i_mtu;
if( vlc_thread_create( p_sys->p_thread, "sout write thread", ThreadWrite, if( vlc_thread_create( p_sys->p_thread, "sout write thread", ThreadWrite,
VLC_THREAD_PRIORITY_OUTPUT, VLC_FALSE ) ) VLC_THREAD_PRIORITY_OUTPUT, VLC_FALSE ) )
{ {
msg_Err( p_access->p_sout, "cannot spawn sout access thread" ); msg_Err( p_access->p_sout, "cannot spawn sout access thread" );
vlc_object_destroy( p_sys->p_thread ); vlc_object_destroy( p_sys->p_thread );
return( VLC_EGENERIC ); return VLC_EGENERIC;
} }
srand( (uint32_t)mdate()); srand( (uint32_t)mdate());
...@@ -235,16 +237,16 @@ static int Open( vlc_object_t *p_this ) ...@@ -235,16 +237,16 @@ static int Open( vlc_object_t *p_this )
if( sout_cfg_find( p_access->p_cfg, "raw" ) ) if( sout_cfg_find( p_access->p_cfg, "raw" ) )
{ {
p_access->pf_write = WriteRaw; p_access->pf_write = WriteRaw;
} }
else else
{ {
p_access->pf_write = Write; p_access->pf_write = Write;
} }
p_access->pf_seek = Seek;
msg_Info( p_access, "Open: addr:`%s' port:`%d'", p_access->pf_seek = Seek;
psz_dst_addr, i_dst_port );
msg_Info( p_access, "Open: addr:`%s' port:`%d'", psz_dst_addr, i_dst_port);
free( psz_dst_addr ); free( psz_dst_addr );
return VLC_SUCCESS; return VLC_SUCCESS;
...@@ -255,14 +257,14 @@ static int Open( vlc_object_t *p_this ) ...@@ -255,14 +257,14 @@ static int Open( vlc_object_t *p_this )
*****************************************************************************/ *****************************************************************************/
static void Close( vlc_object_t * p_this ) static void Close( vlc_object_t * p_this )
{ {
sout_access_out_t *p_access = (sout_access_out_t*)p_this; sout_access_out_t *p_access = (sout_access_out_t*)p_this;
sout_access_out_sys_t *p_sys = p_access->p_sys; sout_access_out_sys_t *p_sys = p_access->p_sys;
int i; int i;
p_sys->p_thread->b_die = 1; p_sys->p_thread->b_die = 1;
for( i = 0; i < 10; i++ ) for( i = 0; i < 10; i++ )
{ {
sout_buffer_t *p_dummy; sout_buffer_t *p_dummy;
p_dummy = sout_BufferNew( p_access->p_sout, p_sys->i_mtu ); p_dummy = sout_BufferNew( p_access->p_sout, p_sys->i_mtu );
p_dummy->i_dts = 0; p_dummy->i_dts = 0;
...@@ -296,7 +298,7 @@ static void Close( vlc_object_t * p_this ) ...@@ -296,7 +298,7 @@ static void Close( vlc_object_t * p_this )
*****************************************************************************/ *****************************************************************************/
static int Write( sout_access_out_t *p_access, sout_buffer_t *p_buffer ) static int Write( sout_access_out_t *p_access, sout_buffer_t *p_buffer )
{ {
sout_access_out_sys_t *p_sys = p_access->p_sys; sout_access_out_sys_t *p_sys = p_access->p_sys;
unsigned int i_write; unsigned int i_write;
while( p_buffer ) while( p_buffer )
...@@ -356,7 +358,6 @@ static int WriteRaw( sout_access_out_t *p_access, sout_buffer_t *p_buffer ) ...@@ -356,7 +358,6 @@ static int WriteRaw( sout_access_out_t *p_access, sout_buffer_t *p_buffer )
*****************************************************************************/ *****************************************************************************/
static int Seek( sout_access_out_t *p_access, off_t i_pos ) static int Seek( sout_access_out_t *p_access, off_t i_pos )
{ {
msg_Err( p_access, "udp sout access cannot seek" ); msg_Err( p_access, "udp sout access cannot seek" );
return( -1 ); return( -1 );
} }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* mp4.c: mp4/mov muxer * mp4.c: mp4/mov muxer
***************************************************************************** *****************************************************************************
* Copyright (C) 2001, 2002, 2003 VideoLAN * Copyright (C) 2001, 2002, 2003 VideoLAN
* $Id: mp4.c,v 1.9 2004/01/13 15:54:09 gbazin Exp $ * $Id: mp4.c,v 1.10 2004/01/23 17:56:14 gbazin Exp $
* *
* Authors: Laurent Aimar <fenrir@via.ecp.fr> * Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Gildas Bazin <gbazin at videolan dot org> * Gildas Bazin <gbazin at videolan dot org>
...@@ -42,8 +42,8 @@ ...@@ -42,8 +42,8 @@
/***************************************************************************** /*****************************************************************************
* Exported prototypes * Exported prototypes
*****************************************************************************/ *****************************************************************************/
static int Open ( vlc_object_t * ); static int Open ( vlc_object_t * );
static void Close ( vlc_object_t * ); static void Close ( vlc_object_t * );
static int Capability(sout_mux_t *, int, void *, void * ); static int Capability(sout_mux_t *, int, void *, void * );
static int AddStream( sout_mux_t *, sout_input_t * ); static int AddStream( sout_mux_t *, sout_input_t * );
...@@ -53,14 +53,27 @@ static int Mux ( sout_mux_t * ); ...@@ -53,14 +53,27 @@ static int Mux ( sout_mux_t * );
/***************************************************************************** /*****************************************************************************
* Module descriptor * Module descriptor
*****************************************************************************/ *****************************************************************************/
#define FASTSTART_TEXT N_("Create \"Fast start\" files")
#define FASTSTART_LONGTEXT N_( \
"When this option is turned on, \"Fast start\" files will be created. " \
"(\"Fast start\" files are optimized for download, allowing the user " \
"to start previewing the file while it is downloading).")
vlc_module_begin(); vlc_module_begin();
set_description( _("MP4/MOV muxer") ); set_description( _("MP4/MOV muxer") );
add_category_hint( "MP4/MOV muxer", NULL, VLC_TRUE );
add_bool( "mp4-faststart", 1, NULL, FASTSTART_TEXT, FASTSTART_LONGTEXT, VLC_TRUE );
set_capability( "sout mux", 5 ); set_capability( "sout mux", 5 );
add_shortcut( "mp4" ); add_shortcut( "mp4" );
add_shortcut( "mov" ); add_shortcut( "mov" );
set_callbacks( Open, Close ); set_callbacks( Open, Close );
vlc_module_end(); vlc_module_end();
/*****************************************************************************
* Local prototypes
*****************************************************************************/
typedef struct typedef struct
{ {
uint64_t i_pos; uint64_t i_pos;
...@@ -84,12 +97,18 @@ typedef struct ...@@ -84,12 +97,18 @@ typedef struct
/* stats */ /* stats */
mtime_t i_duration; mtime_t i_duration;
/* for later stco fix-up (fast start files) */
uint64_t i_stco_pos;
vlc_bool_t b_stco64;
} mp4_stream_t; } mp4_stream_t;
struct sout_mux_sys_t struct sout_mux_sys_t
{ {
vlc_bool_t b_mov; vlc_bool_t b_mov;
vlc_bool_t b_64_ext; vlc_bool_t b_64_ext;
vlc_bool_t b_fast_start;
uint64_t i_mdat_pos; uint64_t i_mdat_pos;
uint64_t i_pos; uint64_t i_pos;
...@@ -100,9 +119,7 @@ struct sout_mux_sys_t ...@@ -100,9 +119,7 @@ struct sout_mux_sys_t
mp4_stream_t **pp_streams; mp4_stream_t **pp_streams;
}; };
typedef struct bo_t
typedef struct bo_t bo_t;
struct bo_t
{ {
vlc_bool_t b_grow; vlc_bool_t b_grow;
...@@ -110,7 +127,7 @@ struct bo_t ...@@ -110,7 +127,7 @@ struct bo_t
int i_buffer; int i_buffer;
uint8_t *p_buffer; uint8_t *p_buffer;
}; } bo_t;
static void bo_init ( bo_t *, int , uint8_t *, vlc_bool_t ); static void bo_init ( bo_t *, int , uint8_t *, vlc_bool_t );
static void bo_add_8 ( bo_t *, uint8_t ); static void bo_add_8 ( bo_t *, uint8_t );
...@@ -121,7 +138,7 @@ static void bo_add_64be ( bo_t *, uint64_t ); ...@@ -121,7 +138,7 @@ static void bo_add_64be ( bo_t *, uint64_t );
static void bo_add_fourcc(bo_t *, char * ); static void bo_add_fourcc(bo_t *, char * );
static void bo_add_bo ( bo_t *, bo_t * ); static void bo_add_bo ( bo_t *, bo_t * );
static void bo_add_mem ( bo_t *, int , uint8_t * ); static void bo_add_mem ( bo_t *, int , uint8_t * );
static void bo_add_descr ( bo_t *, uint8_t , uint32_t ); static void bo_add_descr( bo_t *, uint8_t , uint32_t );
static void bo_fix_32be ( bo_t *, int , uint32_t ); static void bo_fix_32be ( bo_t *, int , uint32_t );
...@@ -135,7 +152,7 @@ static void box_send( sout_mux_t *p_mux, bo_t *box ); ...@@ -135,7 +152,7 @@ static void box_send( sout_mux_t *p_mux, bo_t *box );
static int64_t get_timestamp(); static int64_t get_timestamp();
static sout_buffer_t * bo_to_sout( sout_instance_t *p_sout, bo_t *box ); static sout_buffer_t *bo_to_sout( sout_instance_t *p_sout, bo_t *box );
/***************************************************************************** /*****************************************************************************
* Open: * Open:
...@@ -154,7 +171,7 @@ static int Open( vlc_object_t *p_this ) ...@@ -154,7 +171,7 @@ static int Open( vlc_object_t *p_this )
p_sys->b_mov = p_mux->psz_mux && !strcmp( p_mux->psz_mux, "mov" ); p_sys->b_mov = p_mux->psz_mux && !strcmp( p_mux->psz_mux, "mov" );
p_sys->i_start_dts = 0; p_sys->i_start_dts = 0;
msg_Info( p_mux, "Open" ); msg_Dbg( p_mux, "Open" );
p_mux->pf_capacity = Capability; p_mux->pf_capacity = Capability;
p_mux->pf_addstream = AddStream; p_mux->pf_addstream = AddStream;
...@@ -333,67 +350,61 @@ static bo_t *GetSVQ3Tag( mp4_stream_t *p_stream ) ...@@ -333,67 +350,61 @@ static bo_t *GetSVQ3Tag( mp4_stream_t *p_stream )
return smi; return smi;
} }
/***************************************************************************** static bo_t *GetUdtaTag( sout_mux_t *p_mux )
* Close:
*****************************************************************************/
static uint32_t mvhd_matrix[9] =
{ 0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000 };
static void Close( vlc_object_t * p_this )
{ {
sout_mux_t *p_mux = (sout_mux_t*)p_this; sout_mux_sys_t *p_sys = p_mux->p_sys;
sout_mux_sys_t *p_sys = p_mux->p_sys; bo_t *udta = box_new( "udta" );
sout_buffer_t *p_hdr; int i_track;
bo_t bo;
bo_t *moov, *mvhd;
unsigned int i;
int i_trak, i_index;
uint32_t i_movie_timescale = 90000;
int64_t i_movie_duration = 0;
msg_Info( p_mux, "Close" );
/* create general info */ /* Requirements */
for( i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++ ) for( i_track = 0; i_track < p_sys->i_nb_streams; i_track++ )
{ {
mp4_stream_t *p_stream; mp4_stream_t *p_stream = p_sys->pp_streams[i_track];
p_stream = p_sys->pp_streams[i_trak];
i_movie_duration = __MAX( i_movie_duration, p_stream->i_duration ); if( p_stream->p_fmt->i_codec == VLC_FOURCC('m','p','4','v') ||
p_stream->p_fmt->i_codec == VLC_FOURCC('m','p','4','a') )
{
bo_t *box = box_new( "\251req" );
/* String length */
bo_add_16be( box, sizeof("QuickTime 6.0 or greater") - 1);
bo_add_16be( box, 0 );
bo_add_mem( box, sizeof("QuickTime 6.0 or greater") - 1,
"QuickTime 6.0 or greater" );
box_fix( box );
box_gather( udta, box );
break;
}
} }
msg_Dbg( p_mux, "movie duration %ds",
(uint32_t)( i_movie_duration / (mtime_t)1000000 ) );
i_movie_duration = i_movie_duration *
(int64_t)i_movie_timescale / (int64_t)1000000;
/* *** update mdat size *** */ /* Encoder */
bo_init ( &bo, 0, NULL, VLC_TRUE );
if( p_sys->i_pos - p_sys->i_mdat_pos >= (((uint64_t)1)<<32) )
{ {
/* Extended size */ bo_t *box = box_new( "\251enc" );
bo_add_32be ( &bo, 1 ); /* String length */
bo_add_fourcc( &bo, "mdat" ); bo_add_16be( box, sizeof(PACKAGE_STRING " stream output") - 1);
bo_add_64be ( &bo, p_sys->i_pos - p_sys->i_mdat_pos ); bo_add_16be( box, 0 );
} bo_add_mem( box, sizeof(PACKAGE_STRING " stream output") - 1,
else PACKAGE_STRING " stream output" );
{ box_fix( box );
bo_add_32be ( &bo, 8 ); box_gather( udta, box );
bo_add_fourcc( &bo, "wide" );
bo_add_32be ( &bo, p_sys->i_pos - p_sys->i_mdat_pos - 8 );
bo_add_fourcc( &bo, "mdat" );
} }
p_hdr = bo_to_sout( p_mux->p_sout, &bo ); box_fix( udta );
free( bo.p_buffer ); return udta;
}
/* seek to mdat */ static uint32_t mvhd_matrix[9] =
sout_AccessOutSeek( p_mux->p_access, p_sys->i_mdat_pos ); { 0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000 };
sout_AccessOutWrite( p_mux->p_access, p_hdr );
static bo_t *GetMoovTag( sout_mux_t *p_mux )
{
sout_mux_sys_t *p_sys = p_mux->p_sys;
/* Now create header */ bo_t *moov, *mvhd;
sout_AccessOutSeek( p_mux->p_access, p_sys->i_pos ); unsigned int i;
int i_trak, i_index;
uint32_t i_movie_timescale = 90000;
int64_t i_movie_duration = 0;
moov = box_new( "moov" ); moov = box_new( "moov" );
...@@ -454,6 +465,7 @@ static void Close( vlc_object_t * p_this ) ...@@ -454,6 +465,7 @@ static void Close( vlc_object_t * p_this )
mp4_stream_t *p_stream; mp4_stream_t *p_stream;
uint32_t i_timescale; uint32_t i_timescale;
uint32_t i_chunk_count; uint32_t i_chunk_count;
unsigned int i_chunk;
bo_t *trak; bo_t *trak;
bo_t *tkhd; bo_t *tkhd;
...@@ -466,6 +478,8 @@ static void Close( vlc_object_t * p_this ) ...@@ -466,6 +478,8 @@ static void Close( vlc_object_t * p_this )
bo_t *stbl; bo_t *stbl;
bo_t *stsd; bo_t *stsd;
bo_t *stts; bo_t *stts;
bo_t *stco;
bo_t *stsc;
bo_t *stsz; bo_t *stsz;
p_stream = p_sys->pp_streams[i_trak]; p_stream = p_sys->pp_streams[i_trak];
...@@ -701,7 +715,9 @@ static void Close( vlc_object_t * p_this ) ...@@ -701,7 +715,9 @@ static void Close( vlc_object_t * p_this )
bo_add_16be( soun, 0 ); // revision level (0) bo_add_16be( soun, 0 ); // revision level (0)
bo_add_32be( soun, 0 ); // vendor bo_add_32be( soun, 0 ); // vendor
bo_add_16be( soun, p_stream->p_fmt->audio.i_channels ); // channel-count bo_add_16be( soun, p_stream->p_fmt->audio.i_channels ); // channel-count
bo_add_16be( soun, 16); // FIXME sample size // sample size
bo_add_16be( soun, p_stream->p_fmt->audio.i_bitspersample ?
p_stream->p_fmt->audio.i_bitspersample : 16 );
bo_add_16be( soun, -2 ); // compression id bo_add_16be( soun, -2 ); // compression id
bo_add_16be( soun, 0 ); // packet size (0) bo_add_16be( soun, 0 ); // packet size (0)
bo_add_16be( soun, p_stream->p_fmt->audio.i_rate ); // sampleratehi bo_add_16be( soun, p_stream->p_fmt->audio.i_rate ); // sampleratehi
...@@ -831,7 +847,8 @@ static void Close( vlc_object_t * p_this ) ...@@ -831,7 +847,8 @@ static void Close( vlc_object_t * p_this )
box_fix( stsd ); box_fix( stsd );
box_gather( stbl, stsd ); box_gather( stbl, stsd );
/* we will create chunk table and stsc table FIXME optim stsc table FIXME */ /* we will create chunk table and stsc table
* FIXME optim stsc table FIXME */
i_chunk_count = 0; i_chunk_count = 0;
for( i = 0; i < p_stream->i_entry_count; ) for( i = 0; i < p_stream->i_entry_count; )
{ {
...@@ -851,99 +868,61 @@ static void Close( vlc_object_t * p_this ) ...@@ -851,99 +868,61 @@ static void Close( vlc_object_t * p_this )
} }
/* chunk offset table */ /* chunk offset table */
p_stream->i_stco_pos = stbl->i_buffer + 16;
if( p_sys->i_pos >= (((uint64_t)0x1) << 32) ) if( p_sys->i_pos >= (((uint64_t)0x1) << 32) )
{ {
/* 64 bits version */ /* 64 bits version */
bo_t *co64;
bo_t *stsc;
unsigned int i_chunk;
msg_Dbg( p_mux, "creating %d chunk (co64)", i_chunk_count ); msg_Dbg( p_mux, "creating %d chunk (co64)", i_chunk_count );
p_stream->b_stco64 = VLC_TRUE;
co64 = box_full_new( "co64", 0, 0 ); stco = box_full_new( "co64", 0, 0 );
bo_add_32be( co64, i_chunk_count );
stsc = box_full_new( "stsc", 0, 0 );
bo_add_32be( stsc, i_chunk_count ); // entry-count
for( i_chunk = 0, i = 0; i < p_stream->i_entry_count; i_chunk++ )
{
int i_first;
bo_add_64be( co64, p_stream->entry[i].i_pos );
i_first = i;
while( i < p_stream->i_entry_count )
{
if( i + 1 < p_stream->i_entry_count &&
p_stream->entry[i].i_pos + p_stream->entry[i].i_size
!= p_stream->entry[i + 1].i_pos )
{
i++;
break;
}
i++;
}
bo_add_32be( stsc, 1 + i_chunk ); // first-chunk
bo_add_32be( stsc, i - i_first ) ; // samples-per-chunk
bo_add_32be( stsc, 1 ); // sample-descr-index
}
/* append co64 to stbl */
box_fix( co64 );
box_gather( stbl, co64 );
/* append stsc to stbl */
box_fix( stsc );
box_gather( stbl, stsc );
} }
else else
{ {
/* 32 bits version */ /* 32 bits version */
bo_t *stco;
bo_t *stsc;
unsigned int i_chunk;
msg_Dbg( p_mux, "creating %d chunk (stco)", i_chunk_count ); msg_Dbg( p_mux, "creating %d chunk (stco)", i_chunk_count );
p_stream->b_stco64 = VLC_FALSE;
stco = box_full_new( "stco", 0, 0 ); stco = box_full_new( "stco", 0, 0 );
bo_add_32be( stco, i_chunk_count ); }
stsc = box_full_new( "stsc", 0, 0 ); bo_add_32be( stco, i_chunk_count );
bo_add_32be( stsc, i_chunk_count ); // entry-count
for( i_chunk = 0, i = 0; i < p_stream->i_entry_count; i_chunk++ ) stsc = box_full_new( "stsc", 0, 0 );
{ bo_add_32be( stsc, i_chunk_count ); // entry-count
int i_first; for( i_chunk = 0, i = 0; i < p_stream->i_entry_count; i_chunk++ )
{
int i_first;
if( p_stream->b_stco64 )
bo_add_64be( stco, p_stream->entry[i].i_pos );
else
bo_add_32be( stco, p_stream->entry[i].i_pos ); bo_add_32be( stco, p_stream->entry[i].i_pos );
i_first = i; i_first = i;
while( i < p_stream->i_entry_count ) while( i < p_stream->i_entry_count )
{
if( i + 1 < p_stream->i_entry_count &&
p_stream->entry[i].i_pos + p_stream->entry[i].i_size
!= p_stream->entry[i + 1].i_pos )
{ {
if( i + 1 < p_stream->i_entry_count &&
p_stream->entry[i].i_pos + p_stream->entry[i].i_size
!= p_stream->entry[i + 1].i_pos )
{
i++;
break;
}
i++; i++;
break;
} }
bo_add_32be( stsc, 1 + i_chunk ); // first-chunk
bo_add_32be( stsc, i - i_first ) ; // samples-per-chunk
bo_add_32be( stsc, 1 ); // sample-descr-index
}
/* append stco to stbl */
box_fix( stco );
box_gather( stbl, stco );
/* append stsc to stbl */ i++;
box_fix( stsc ); }
box_gather( stbl, stsc ); bo_add_32be( stsc, 1 + i_chunk ); // first-chunk
bo_add_32be( stsc, i - i_first ) ; // samples-per-chunk
bo_add_32be( stsc, 1 ); // sample-descr-index
} }
/* append stco to stbl */
box_fix( stco );
box_gather( stbl, stco );
/* append stsc to stbl */
box_fix( stsc );
box_gather( stbl, stsc );
/* add stts */ /* add stts */
stts = box_full_new( "stts", 0, 0 ); stts = box_full_new( "stts", 0, 0 );
...@@ -993,30 +972,164 @@ static void Close( vlc_object_t * p_this ) ...@@ -993,30 +972,164 @@ static void Close( vlc_object_t * p_this )
/* append stbl to minf */ /* append stbl to minf */
box_fix( stbl ); box_fix( stbl );
p_stream->i_stco_pos += minf->i_buffer;
box_gather( minf, stbl ); box_gather( minf, stbl );
/* append minf to mdia */ /* append minf to mdia */
box_fix( minf ); box_fix( minf );
p_stream->i_stco_pos += mdia->i_buffer;
box_gather( mdia, minf ); box_gather( mdia, minf );
/* append mdia to trak */ /* append mdia to trak */
box_fix( mdia ); box_fix( mdia );
p_stream->i_stco_pos += trak->i_buffer;
box_gather( trak, mdia ); box_gather( trak, mdia );
/* append trak to moov */ /* append trak to moov */
box_fix( trak ); box_fix( trak );
p_stream->i_stco_pos += moov->i_buffer;
box_gather( moov, trak ); box_gather( moov, trak );
} }
/* Add user data tags */
box_gather( moov, GetUdtaTag( p_mux ) );
box_fix( moov ); box_fix( moov );
box_send( p_mux, moov ); return moov;
}
/* *** release memory *** */ /*****************************************************************************
* Close:
*****************************************************************************/
static void Close( vlc_object_t * p_this )
{
sout_mux_t *p_mux = (sout_mux_t*)p_this;
sout_mux_sys_t *p_sys = p_mux->p_sys;
sout_buffer_t *p_hdr;
bo_t bo, *moov;
vlc_value_t val;
int i_trak;
uint64_t i_moov_pos;
uint32_t i_movie_timescale = 90000;
int64_t i_movie_duration = 0;
msg_Dbg( p_mux, "Close" );
/* Create general info */
for( i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++ ) for( i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++ )
{ {
mp4_stream_t *p_stream; mp4_stream_t *p_stream = p_sys->pp_streams[i_trak];
i_movie_duration = __MAX( i_movie_duration, p_stream->i_duration );
}
msg_Dbg( p_mux, "movie duration %ds",
(uint32_t)( i_movie_duration / (mtime_t)1000000 ) );
p_stream = p_sys->pp_streams[i_trak]; i_movie_duration = i_movie_duration * i_movie_timescale / 1000000;
/* Update mdat size */
bo_init( &bo, 0, NULL, VLC_TRUE );
if( p_sys->i_pos - p_sys->i_mdat_pos >= (((uint64_t)1)<<32) )
{
/* Extended size */
bo_add_32be ( &bo, 1 );
bo_add_fourcc( &bo, "mdat" );
bo_add_64be ( &bo, p_sys->i_pos - p_sys->i_mdat_pos );
}
else
{
bo_add_32be ( &bo, 8 );
bo_add_fourcc( &bo, "wide" );
bo_add_32be ( &bo, p_sys->i_pos - p_sys->i_mdat_pos - 8 );
bo_add_fourcc( &bo, "mdat" );
}
p_hdr = bo_to_sout( p_mux->p_sout, &bo );
free( bo.p_buffer );
sout_AccessOutSeek( p_mux->p_access, p_sys->i_mdat_pos );
sout_AccessOutWrite( p_mux->p_access, p_hdr );
/* Create MOOV header */
i_moov_pos = p_sys->i_pos;
moov = GetMoovTag( p_mux );
/* Check we need to create "fast start" files */
var_Create( p_this, "mp4-faststart", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
var_Get( p_this, "mp4-faststart", &val );
p_sys->b_fast_start = val.b_bool;
while( p_sys->b_fast_start )
{
/* Move data to the end of the file so we can fit the moov header
* at the start */
sout_buffer_t *p_buf;
int64_t i_chunk, i_size = p_sys->i_pos - p_sys->i_mdat_pos;
int i_moov_size = moov->i_buffer;
while( i_size > 0 )
{
i_chunk = __MIN( 32768, i_size );
p_buf = sout_BufferNew( p_mux->p_sout, i_chunk );
sout_AccessOutSeek( p_mux->p_access,
p_sys->i_mdat_pos + i_size - i_chunk );
if( sout_AccessOutRead( p_mux->p_access, p_buf ) < i_chunk )
{
msg_Warn( p_this, "read() not supported by acces output, "
"won't create a fast start file" );
p_sys->b_fast_start = VLC_FALSE;
break;
}
sout_AccessOutSeek( p_mux->p_access, p_sys->i_mdat_pos + i_size +
i_moov_size - i_chunk );
sout_AccessOutWrite( p_mux->p_access, p_buf );
i_size -= i_chunk;
}
if( !p_sys->b_fast_start ) break;
/* Fix-up samples to chunks table in MOOV header */
for( i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++ )
{
mp4_stream_t *p_stream = p_sys->pp_streams[i_trak];
unsigned int i;
int i_chunk;
moov->i_buffer = p_stream->i_stco_pos;
for( i_chunk = 0, i = 0; i < p_stream->i_entry_count; i_chunk++ )
{
if( p_stream->b_stco64 )
bo_add_64be( moov, p_stream->entry[i].i_pos + i_moov_size);
else
bo_add_32be( moov, p_stream->entry[i].i_pos + i_moov_size);
while( i < p_stream->i_entry_count )
{
if( i + 1 < p_stream->i_entry_count &&
p_stream->entry[i].i_pos + p_stream->entry[i].i_size
!= p_stream->entry[i + 1].i_pos )
{
i++;
break;
}
i++;
}
}
}
moov->i_buffer = i_moov_size;
i_moov_pos = p_sys->i_mdat_pos;
p_sys->b_fast_start = VLC_FALSE;
}
/* Write MOOV header */
sout_AccessOutSeek( p_mux->p_access, i_moov_pos );
box_send( p_mux, moov );
/* Clean-up */
for( i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++ )
{
mp4_stream_t *p_stream = p_sys->pp_streams[i_trak];
if( p_stream->p_fmt->p_extra ) if( p_stream->p_fmt->p_extra )
{ {
...@@ -1396,7 +1509,6 @@ static void box_gather ( bo_t *box, bo_t *box2 ) ...@@ -1396,7 +1509,6 @@ static void box_gather ( bo_t *box, bo_t *box2 )
box_free( box2 ); box_free( box2 );
} }
static sout_buffer_t * bo_to_sout( sout_instance_t *p_sout, bo_t *box ) static sout_buffer_t * bo_to_sout( sout_instance_t *p_sout, bo_t *box )
{ {
sout_buffer_t *p_buf; sout_buffer_t *p_buf;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* stream_output.c : stream output module * stream_output.c : stream output module
***************************************************************************** *****************************************************************************
* Copyright (C) 2002-2004 VideoLAN * Copyright (C) 2002-2004 VideoLAN
* $Id: stream_output.c,v 1.37 2004/01/06 12:02:06 zorglub Exp $ * $Id: stream_output.c,v 1.38 2004/01/23 17:56:14 gbazin Exp $
* *
* Authors: Christophe Massiot <massiot@via.ecp.fr> * Authors: Christophe Massiot <massiot@via.ecp.fr>
* Laurent Aimar <fenrir@via.ecp.fr> * Laurent Aimar <fenrir@via.ecp.fr>
...@@ -238,7 +238,8 @@ int sout_InputDelete( sout_packetizer_input_t *p_input ) ...@@ -238,7 +238,8 @@ int sout_InputDelete( sout_packetizer_input_t *p_input )
} }
int sout_InputSendBuffer( sout_packetizer_input_t *p_input, sout_buffer_t *p_buffer ) int sout_InputSendBuffer( sout_packetizer_input_t *p_input,
sout_buffer_t *p_buffer )
{ {
sout_instance_t *p_sout = p_input->p_sout; sout_instance_t *p_sout = p_input->p_sout;
int i_ret; int i_ret;
...@@ -272,7 +273,8 @@ sout_access_out_t *sout_AccessOutNew( sout_instance_t *p_sout, ...@@ -272,7 +273,8 @@ sout_access_out_t *sout_AccessOutNew( sout_instance_t *p_sout,
return NULL; return NULL;
} }
psz_next = sout_cfg_parser( &p_access->psz_access, &p_access->p_cfg, psz_access ); psz_next = sout_cfg_parser( &p_access->psz_access, &p_access->p_cfg,
psz_access );
if( psz_next ) if( psz_next )
{ {
free( psz_next ); free( psz_next );
...@@ -281,6 +283,7 @@ sout_access_out_t *sout_AccessOutNew( sout_instance_t *p_sout, ...@@ -281,6 +283,7 @@ sout_access_out_t *sout_AccessOutNew( sout_instance_t *p_sout,
p_access->p_sout = p_sout; p_access->p_sout = p_sout;
p_access->p_sys = NULL; p_access->p_sys = NULL;
p_access->pf_seek = NULL; p_access->pf_seek = NULL;
p_access->pf_read = NULL;
p_access->pf_write = NULL; p_access->pf_write = NULL;
p_access->p_module = p_access->p_module =
...@@ -317,19 +320,27 @@ void sout_AccessOutDelete( sout_access_out_t *p_access ) ...@@ -317,19 +320,27 @@ void sout_AccessOutDelete( sout_access_out_t *p_access )
/***************************************************************************** /*****************************************************************************
* sout_AccessSeek: * sout_AccessSeek:
*****************************************************************************/ *****************************************************************************/
int sout_AccessOutSeek( sout_access_out_t *p_access, off_t i_pos ) int sout_AccessOutSeek( sout_access_out_t *p_access, off_t i_pos )
{ {
return( p_access->pf_seek( p_access, i_pos ) ); return p_access->pf_seek( p_access, i_pos );
} }
/***************************************************************************** /*****************************************************************************
* sout_AccessWrite: * sout_AccessRead:
*****************************************************************************/ *****************************************************************************/
int sout_AccessOutWrite( sout_access_out_t *p_access, sout_buffer_t *p_buffer ) int sout_AccessOutRead( sout_access_out_t *p_access, sout_buffer_t *p_buffer )
{ {
return( p_access->pf_write( p_access, p_buffer ) ); return ( p_access->pf_read ?
p_access->pf_read( p_access, p_buffer ) : VLC_EGENERIC );
} }
/*****************************************************************************
* sout_AccessWrite:
*****************************************************************************/
int sout_AccessOutWrite( sout_access_out_t *p_access, sout_buffer_t *p_buffer )
{
return p_access->pf_write( p_access, p_buffer );
}
/***************************************************************************** /*****************************************************************************
* MuxNew: allocate a new mux * MuxNew: allocate a new mux
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment