Commit f8bf106d authored by Laurent Aimar's avatar Laurent Aimar

Added stream output. (common work with titer).

parent 3f126864
...@@ -135,6 +135,7 @@ case "x${target_os}" in ...@@ -135,6 +135,7 @@ case "x${target_os}" in
LDFLAGS_access_mms="${LDFLAGS_access_mms} -lws2_32" LDFLAGS_access_mms="${LDFLAGS_access_mms} -lws2_32"
LDFLAGS_access_rtp="${LDFLAGS_access_rtp} -lws2_32" LDFLAGS_access_rtp="${LDFLAGS_access_rtp} -lws2_32"
LDFLAGS_access_udp="${LDFLAGS_access_udp} -lws2_32" LDFLAGS_access_udp="${LDFLAGS_access_udp} -lws2_32"
LDFLAGS_access_output_udp="${LDFLAGS_access_output_udp} -lws2_32"
LDFLAGS_rc="${LDFLAGS_rc} -lws2_32" LDFLAGS_rc="${LDFLAGS_rc} -lws2_32"
LDFLAGS_sap="${LDFLAGS_sap} -lws2_32" LDFLAGS_sap="${LDFLAGS_sap} -lws2_32"
fi fi
...@@ -273,6 +274,7 @@ AC_CHECK_FUNC(send,,[ ...@@ -273,6 +274,7 @@ AC_CHECK_FUNC(send,,[
LDFLAGS_access_rtp="${LDFLAGS_access_rtp} -lsocket" LDFLAGS_access_rtp="${LDFLAGS_access_rtp} -lsocket"
LDFLAGS_access_udp="${LDFLAGS_access_udp} -lsocket" LDFLAGS_access_udp="${LDFLAGS_access_udp} -lsocket"
LDFLAGS_sap="${LDFLAGS_sap} -lsocket" LDFLAGS_sap="${LDFLAGS_sap} -lsocket"
LDFLAGS_access_output_udp="${LDFLAGS_access_output_udp} -lsocket"
)]) )])
AC_CHECK_FUNC(gethostbyname,,[ AC_CHECK_FUNC(gethostbyname,,[
...@@ -865,6 +867,20 @@ AC_ARG_ENABLE(release, ...@@ -865,6 +867,20 @@ AC_ARG_ENABLE(release,
[ --enable-release activate extra optimizations (default disabled)]) [ --enable-release activate extra optimizations (default disabled)])
AM_CONDITIONAL(RELEASE, test "x${enable_release}" = "xyes") AM_CONDITIONAL(RELEASE, test "x${enable_release}" = "xyes")
dnl
dnl Stream output
dnl
AC_ARG_ENABLE(sout,
[ --enable-sout Stream output modules (default enabled)])
if test "x${enable_sout}" != "xno"
then
PLUGINS="${PLUGINS} access_output_dummy access_output_udp access_output_file"
PLUGINS="${PLUGINS} mux_ts mux_ps mux_dummy"
PLUGINS="${PLUGINS} packetizer_mpegaudio packetizer_mpegvideo packetizer_a52"
PLUGINS="${PLUGINS} packetizer_mpeg4video packetizer_copy"
fi
dnl dnl
dnl Input plugins dnl Input plugins
dnl dnl
......
...@@ -2,9 +2,11 @@ ...@@ -2,9 +2,11 @@
* 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.1 2002/08/12 22:12:50 massiot Exp $ * $Id: stream_output.h,v 1.2 2002/12/14 21:32:41 fenrir Exp $
* *
* Authors: Christophe Massiot <massiot@via.ecp.fr> * Authors: Christophe Massiot <massiot@via.ecp.fr>
* Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -24,6 +26,59 @@ ...@@ -24,6 +26,59 @@
/***************************************************************************** /*****************************************************************************
* sout_instance_t: stream output thread descriptor * sout_instance_t: stream output thread descriptor
*****************************************************************************/ *****************************************************************************/
struct sout_buffer_t
{
size_t i_allocated_size;
byte_t *p_buffer;
size_t i_size;
// mtime_t i_date;
mtime_t i_length;
mtime_t i_dts;
mtime_t i_pts;
int i_bitrate;
struct sout_buffer_t *p_next;
};
struct sout_packet_format_t
{
int i_cat; // AUDIO_ES, VIDEO_ES, SPU_ES
vlc_fourcc_t i_fourcc;
void *p_format; // WAVEFORMATEX or BITMAPINFOHEADER
};
struct sout_fifo_t
{
vlc_mutex_t lock; /* fifo data lock */
vlc_cond_t wait; /* fifo data conditional variable */
int i_depth;
sout_buffer_t *p_first;
sout_buffer_t **pp_last;
};
struct sout_input_t
{
vlc_mutex_t lock;
sout_instance_t *p_sout;
sout_packet_format_t input_format;
sout_fifo_t *p_fifo;
void *p_mux_data;
};
#define SOUT_METHOD_NONE 0x00
#define SOUT_METHOD_FILE 0x10
#define SOUT_METHOD_NETWORK 0x20
struct sout_instance_t struct sout_instance_t
{ {
VLC_COMMON_MEMBERS VLC_COMMON_MEMBERS
...@@ -33,10 +88,30 @@ struct sout_instance_t ...@@ -33,10 +88,30 @@ struct sout_instance_t
char * psz_mux; char * psz_mux;
char * psz_name; char * psz_name;
module_t * p_access; module_t *p_access;
module_t * p_mux; int i_method;
void *p_access_data;
int (* pf_write )( sout_instance_t *, sout_buffer_t * );
int (* pf_seek )( sout_instance_t *, off_t );
module_t *p_mux;
void *p_mux_data;
int (* pf_mux_addstream )( sout_instance_t *,
sout_input_t * );
int (* pf_mux_delstream )( sout_instance_t *,
sout_input_t * );
int (* pf_mux ) ( sout_instance_t * );
vlc_mutex_t lock;
int i_nb_inputs;
sout_input_t **pp_inputs;
}; };
/***************************************************************************** /*****************************************************************************
* Prototypes * Prototypes
*****************************************************************************/ *****************************************************************************/
...@@ -44,7 +119,21 @@ struct sout_instance_t ...@@ -44,7 +119,21 @@ struct sout_instance_t
VLC_EXPORT( sout_instance_t *, __sout_NewInstance, ( vlc_object_t *, char * ) ); VLC_EXPORT( sout_instance_t *, __sout_NewInstance, ( vlc_object_t *, char * ) );
VLC_EXPORT( void, sout_DeleteInstance, ( sout_instance_t * ) ); VLC_EXPORT( void, sout_DeleteInstance, ( sout_instance_t * ) );
sout_fifo_t * sout_CreateFifo ( void ); VLC_EXPORT( sout_fifo_t *, sout_FifoCreate, ( sout_instance_t * ) );
void sout_DestroyFifo ( sout_fifo_t * ); VLC_EXPORT( void, sout_FifoDestroy, ( sout_instance_t *, sout_fifo_t * ) );
void sout_FreeFifo ( sout_fifo_t * ); VLC_EXPORT( void, sout_FifoFree, ( sout_instance_t *,sout_fifo_t * ) );
VLC_EXPORT( void, sout_FifoPut, ( sout_fifo_t *, sout_buffer_t* ) );
VLC_EXPORT( sout_buffer_t *, sout_FifoGet, ( sout_fifo_t * ) );
VLC_EXPORT( sout_buffer_t *, sout_FifoShow, ( sout_fifo_t * ) );
#define sout_InputNew( a, b ) __sout_InputNew( VLC_OBJECT(a), b )
VLC_EXPORT( sout_input_t *, __sout_InputNew, ( vlc_object_t *, sout_packet_format_t * ) );
VLC_EXPORT( int, sout_InputDelete, ( sout_input_t * ) );
VLC_EXPORT( int, sout_InputSendBuffer, ( sout_input_t *, sout_buffer_t* ) );
VLC_EXPORT( sout_buffer_t*, sout_BufferNew, ( sout_instance_t *, size_t ) );
VLC_EXPORT( int, sout_BufferRealloc,( sout_instance_t *, sout_buffer_t*, size_t ) );
VLC_EXPORT( int, sout_BufferDelete, ( sout_instance_t *, sout_buffer_t* ) );
VLC_EXPORT( sout_buffer_t*, sout_BufferDuplicate,(sout_instance_t *, sout_buffer_t * ) );
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* Collection of useful common types and macros definitions * Collection of useful common types and macros definitions
***************************************************************************** *****************************************************************************
* Copyright (C) 1998, 1999, 2000 VideoLAN * Copyright (C) 1998, 1999, 2000 VideoLAN
* $Id: vlc_common.h,v 1.43 2002/12/13 01:56:29 gbazin Exp $ * $Id: vlc_common.h,v 1.44 2002/12/14 21:32:41 fenrir Exp $
* *
* Authors: Samuel Hocevar <sam@via.ecp.fr> * Authors: Samuel Hocevar <sam@via.ecp.fr>
* Vincent Seguin <seguin@via.ecp.fr> * Vincent Seguin <seguin@via.ecp.fr>
...@@ -241,6 +241,9 @@ typedef struct subpicture_sys_t subpicture_sys_t; ...@@ -241,6 +241,9 @@ typedef struct subpicture_sys_t subpicture_sys_t;
/* Stream output */ /* Stream output */
typedef struct sout_instance_t sout_instance_t; typedef struct sout_instance_t sout_instance_t;
typedef struct sout_fifo_t sout_fifo_t; typedef struct sout_fifo_t sout_fifo_t;
typedef struct sout_input_t sout_input_t;
typedef struct sout_buffer_t sout_buffer_t;
typedef struct sout_packet_format_t sout_packet_format_t;
/* Decoders */ /* Decoders */
typedef struct decoder_fifo_t decoder_fifo_t; typedef struct decoder_fifo_t decoder_fifo_t;
......
...@@ -8,6 +8,7 @@ EXTRA_DIST = \ ...@@ -8,6 +8,7 @@ EXTRA_DIST = \
access/satellite/Modules.am \ access/satellite/Modules.am \
access/v4l/Modules.am \ access/v4l/Modules.am \
access/vcd/Modules.am \ access/vcd/Modules.am \
access_output/Modules.am \
audio_filter/channel_mixer/Modules.am \ audio_filter/channel_mixer/Modules.am \
audio_filter/converter/Modules.am \ audio_filter/converter/Modules.am \
audio_filter/resampler/Modules.am \ audio_filter/resampler/Modules.am \
...@@ -53,6 +54,9 @@ EXTRA_DIST = \ ...@@ -53,6 +54,9 @@ EXTRA_DIST = \
misc/memcpy/Modules.am \ misc/memcpy/Modules.am \
misc/network/Modules.am \ misc/network/Modules.am \
misc/testsuite/Modules.am \ misc/testsuite/Modules.am \
mux/Modules.am \
mux/mpeg/Modules.am \
packetizer/Modules.am \
video_chroma/Modules.am \ video_chroma/Modules.am \
video_filter/Modules.am \ video_filter/Modules.am \
video_filter/deinterlace/Modules.am \ video_filter/deinterlace/Modules.am \
......
SOURCES_access_output_dummy = modules/access_output/dummy.c
SOURCES_access_output_file = modules/access_output/file.c
SOURCES_access_output_udp = modules/access_output/udp.c
/*****************************************************************************
* dummy.c
*****************************************************************************
* Copyright (C) 2001, 2002 VideoLAN
* $Id: dummy.c,v 1.1 2002/12/14 21:32:41 fenrir Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
#include <vlc/sout.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#elif defined( _MSC_VER ) && defined( _WIN32 ) && !defined( UNDER_CE )
# include <io.h>
#endif
/*****************************************************************************
* Exported prototypes
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close ( vlc_object_t * );
static int Write( sout_instance_t *, sout_buffer_t * );
static int Seek( sout_instance_t *, off_t );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("Dummy stream ouput") );
set_capability( "sout access", 0 );
add_shortcut( "dummy" );
set_callbacks( Open, Close );
vlc_module_end();
/*****************************************************************************
* Open: open the file
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
sout_instance_t *p_sout = (sout_instance_t*)p_this;
p_sout->i_method = SOUT_METHOD_NONE;
p_sout->p_access_data = NULL;
p_sout->pf_write = Write;
p_sout->pf_seek = Seek;
msg_Info( p_sout, "dummy stream output access launched" );
return VLC_SUCCESS;
}
/*****************************************************************************
* Close: close the target
*****************************************************************************/
static void Close( vlc_object_t * p_this )
{
sout_instance_t *p_sout = (sout_instance_t*)p_this;
msg_Info( p_sout, "Close" );
}
/*****************************************************************************
* Read: standard read on a file descriptor.
*****************************************************************************/
static int Write( sout_instance_t *p_sout, sout_buffer_t *p_buffer )
{
size_t i_write = 0;
do
{
sout_buffer_t *p_next;
i_write += p_buffer->i_size;
p_next = p_buffer->p_next;
sout_BufferDelete( p_sout, p_buffer );
p_buffer = p_next;
} while( p_buffer );
msg_Dbg( p_sout, "Dummy Skipped: len:%d", (uint32_t)i_write );
return( i_write );
}
/*****************************************************************************
* Seek: seek to a specific location in a file
*****************************************************************************/
static int Seek( sout_instance_t *p_sout, off_t i_pos )
{
msg_Dbg( p_sout, "Seek: pos:%lld", (int64_t)i_pos );
return( 0 );
}
/*****************************************************************************
* file.c
*****************************************************************************
* Copyright (C) 2001, 2002 VideoLAN
* $Id: file.c,v 1.1 2002/12/14 21:32:41 fenrir Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
#include <vlc/sout.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#elif defined( _MSC_VER ) && defined( _WIN32 ) && !defined( UNDER_CE )
# include <io.h>
#endif
/*****************************************************************************
* Exported prototypes
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close ( vlc_object_t * );
static int Write( sout_instance_t *, sout_buffer_t * );
static int Seek( sout_instance_t *, off_t );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("File stream ouput") );
set_capability( "sout access", 50 );
add_shortcut( "file" );
set_callbacks( Open, Close );
vlc_module_end();
typedef struct sout_access_data_s
{
FILE *p_file;
} sout_access_data_t;
/*****************************************************************************
* Open: open the file
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
sout_instance_t *p_sout = (sout_instance_t*)p_this;
sout_access_data_t *p_access;
char * psz_name = p_sout->psz_name;
p_access = malloc( sizeof( sout_access_data_t ) );
if( !( p_access->p_file = fopen( psz_name, "wb" ) ) )
{
msg_Err( p_sout, "cannot open `%s'", psz_name );
free( p_access );
return( -1 );
}
p_sout->i_method = SOUT_METHOD_FILE;
p_sout->p_access_data = p_access;
p_sout->pf_write = Write;
p_sout->pf_seek = Seek;
msg_Info( p_sout, "Open: name:`%s'", psz_name );
return VLC_SUCCESS;
}
/*****************************************************************************
* Close: close the target
*****************************************************************************/
static void Close( vlc_object_t * p_this )
{
sout_instance_t *p_sout = (sout_instance_t*)p_this;
sout_access_data_t *p_access = (sout_access_data_t*)p_sout->p_access_data;
if( p_access->p_file )
{
fclose( p_access->p_file );
}
msg_Info( p_sout, "Close" );
}
/*****************************************************************************
* Read: standard read on a file descriptor.
*****************************************************************************/
static int Write( sout_instance_t *p_sout, sout_buffer_t *p_buffer )
{
sout_access_data_t *p_access = (sout_access_data_t*)p_sout->p_access_data;
size_t i_write = 0;
do
{
sout_buffer_t *p_next;
i_write += fwrite( p_buffer->p_buffer, 1, p_buffer->i_size,
p_access->p_file );
p_next = p_buffer->p_next;
sout_BufferDelete( p_sout, p_buffer );
p_buffer = p_next;
} while( p_buffer );
msg_Dbg( p_sout, "Write: len:%d", (uint32_t)i_write );
return( i_write );
}
/*****************************************************************************
* Seek: seek to a specific location in a file
*****************************************************************************/
static int Seek( sout_instance_t *p_sout, off_t i_pos )
{
sout_access_data_t *p_access = (sout_access_data_t*)p_sout->p_access_data;
msg_Dbg( p_sout, "Seek: pos:%lld", (int64_t)i_pos );
return( fseek( p_access->p_file, i_pos, SEEK_SET ) );
}
/*****************************************************************************
* udp.c
*****************************************************************************
* Copyright (C) 2001, 2002 VideoLAN
* $Id: udp.c,v 1.1 2002/12/14 21:32:41 fenrir Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
#include <vlc/sout.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#elif defined( _MSC_VER ) && defined( _WIN32 ) && !defined( UNDER_CE )
# include <io.h>
#endif
#ifdef WIN32
# include <winsock2.h>
# include <ws2tcpip.h>
# ifndef IN_MULTICAST
# define IN_MULTICAST(a) IN_CLASSD(a)
# endif
#else
# include <sys/socket.h>
#endif
#include "network.h"
#define DEFAULT_PORT 1234
#define LATENCY 100000
#define MAX_ERROR 500000
/*****************************************************************************
* Exported prototypes
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close ( vlc_object_t * );
static int Write( sout_instance_t *, sout_buffer_t * );
static int Seek( sout_instance_t *, off_t );
static void ThreadWrite( vlc_object_t *p_this );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("UDP stream ouput") );
set_capability( "sout access", 100 );
add_shortcut( "udp" );
add_shortcut( "rtp" ); // Will work only with ts muxer
set_callbacks( Open, Close );
vlc_module_end();
typedef struct sout_access_thread_s
{
VLC_COMMON_MEMBERS
sout_instance_t *p_sout;
sout_fifo_t *p_fifo;
int i_handle;
} sout_access_thread_t;
typedef struct sout_access_data_s
{
int b_rtpts; // 1 if add rtp/ts header
uint16_t i_sequence_number;
uint32_t i_ssrc;
int i_mtu;
sout_buffer_t *p_buffer;
sout_access_thread_t *p_thread;
} sout_access_data_t;
/*****************************************************************************
* Open: open the file
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
sout_instance_t *p_sout = (sout_instance_t*)p_this;
sout_access_data_t *p_access;
char *psz_parser;
char *psz_dst_addr;
int i_dst_port;
module_t *p_network;
network_socket_t socket_desc;
if( !( p_access = malloc( sizeof( sout_access_data_t ) ) ) )
{
msg_Err( p_sout, "Not enough memory" );
return( -1 );
}
if( p_sout->psz_access != NULL &&
!strcmp( p_sout->psz_access, "rtp" ) )
{
if( p_sout->psz_mux != NULL &&
strcmp( p_sout->psz_mux, "ts" ) )
{
msg_Err( p_sout, "rtp ouput work only with ts payload" );
free( p_access );
return( -1 );
}
p_access->b_rtpts = 1;
}
else
{
p_access->b_rtpts = 0;
}
psz_parser = strdup( p_sout->psz_name );
psz_dst_addr = psz_parser;
i_dst_port = 0;
while( *psz_parser && *psz_parser != ':' )
{
psz_parser++;
}
if( *psz_parser == ':' )
{
*psz_parser = '\0';
psz_parser++;
i_dst_port = atoi( psz_parser );
}
if( i_dst_port <= 0 )
{
i_dst_port = DEFAULT_PORT;
}
p_access->p_thread = vlc_object_create( p_sout,
sizeof( sout_access_thread_t ) );
if( !p_access->p_thread )
{
msg_Err( p_sout, "out of memory" );
return( -1 );
}
p_access->p_thread->p_sout = p_sout;
p_access->p_thread->b_die = 0;
p_access->p_thread->b_error= 0;
p_access->p_thread->p_fifo = sout_FifoCreate( p_sout );
socket_desc.i_type = NETWORK_UDP;
socket_desc.psz_server_addr = psz_dst_addr;
socket_desc.i_server_port = i_dst_port;
socket_desc.psz_bind_addr = "";
socket_desc.i_bind_port = 0;
p_access->p_thread->p_private = (void*)&socket_desc;
if( !( p_network = module_Need( p_access->p_thread,
"network", "" ) ) )
{
msg_Err( p_sout, "failed to open a connection (udp)" );
return( -1 );
}
module_Unneed( p_access->p_thread, p_network );
p_access->p_thread->i_handle = socket_desc.i_handle;
p_access->i_mtu = socket_desc.i_mtu;
if( vlc_thread_create( p_access->p_thread, "sout write thread",
ThreadWrite, VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
{
msg_Err( p_sout, "cannot spawn sout access thread" );
vlc_object_destroy( p_access->p_thread );
return( -1 );
}
p_access->p_buffer = NULL;
p_access->i_sequence_number = 12; // FIXME should be random, used by rtp
p_access->i_ssrc = 4212; // FIXME " " " " " "
p_sout->i_method = SOUT_METHOD_NETWORK;
p_sout->p_access_data = (void*)p_access;
p_sout->pf_write = Write;
p_sout->pf_seek = Seek;
msg_Info( p_sout,
"Open: addr:`%s' port:`%d'",
psz_dst_addr, i_dst_port );
free( psz_dst_addr );
return VLC_SUCCESS;
}
/*****************************************************************************
* Close: close the target
*****************************************************************************/
static void Close( vlc_object_t * p_this )
{
sout_instance_t *p_sout = (sout_instance_t*)p_this;
sout_access_data_t *p_access = (sout_access_data_t*)p_sout->p_access_data;
int i;
p_access->p_thread->b_die = 1;
for( i = 0; i < 10; i++ )
{
sout_buffer_t *p_dummy;
p_dummy = sout_BufferNew( p_sout, p_access->i_mtu );
p_dummy->i_dts = 0;
p_dummy->i_pts = 0;
p_dummy->i_length = 0;
sout_FifoPut( p_access->p_thread->p_fifo, p_dummy );
}
vlc_thread_join( p_access->p_thread );
sout_FifoDestroy( p_sout, p_access->p_thread->p_fifo );
if( p_access->p_buffer )
{
sout_BufferDelete( p_sout, p_access->p_buffer );
}
#if defined( UNDER_CE )
CloseHandle( (HANDLE)p_access->p_thread->i_handle );
#elif defined( WIN32 )
closesocket( p_access->p_thread->i_handle );
#else
close( p_access->p_thread->i_handle );
#endif
msg_Info( p_sout, "Close" );
}
/*****************************************************************************
* Read: standard read on a file descriptor.
*****************************************************************************/
static int Write( sout_instance_t *p_sout, sout_buffer_t *p_buffer )
{
sout_access_data_t *p_access = (sout_access_data_t*)p_sout->p_access_data;
int i_write;
while( p_buffer )
{
sout_buffer_t *p_next;
if( p_buffer->i_size > p_access->i_mtu )
{
msg_Warn( p_sout, "arggggggggggggg packet size > mtu" );
i_write = p_access->i_mtu;
}
else
{
i_write = p_buffer->i_size;
}
/* if we have enough data, enque the buffer */
if( p_access->p_buffer &&
p_access->p_buffer->i_size + i_write > p_access->i_mtu )
{
sout_FifoPut( p_access->p_thread->p_fifo, p_access->p_buffer );
p_access->p_buffer = NULL;
}
if( !p_access->p_buffer )
{
p_access->p_buffer = sout_BufferNew( p_sout, p_access->i_mtu );
p_access->p_buffer->i_dts = p_buffer->i_dts;
p_access->p_buffer->i_size = 0;
if( p_access->b_rtpts )
{
mtime_t i_timestamp = p_access->p_buffer->i_dts * 9 / 100;
/* add rtp/ts header */
p_access->p_buffer->p_buffer[0] = 0x80;
p_access->p_buffer->p_buffer[1] = 0x21; // mpeg2-ts
p_access->p_buffer->p_buffer[2] =
( p_access->i_sequence_number >> 8 )&0xff;
p_access->p_buffer->p_buffer[3] =
p_access->i_sequence_number&0xff;
p_access->p_buffer->p_buffer[4] = ( i_timestamp >> 24 )&0xff;
p_access->p_buffer->p_buffer[5] = ( i_timestamp >> 16 )&0xff;
p_access->p_buffer->p_buffer[6] = ( i_timestamp >> 8 )&0xff;
p_access->p_buffer->p_buffer[7] = i_timestamp&0xff;
p_access->p_buffer->p_buffer[ 8] =
( p_access->i_ssrc >> 24 )&0xff;
p_access->p_buffer->p_buffer[ 9] =
( p_access->i_ssrc >> 16 )&0xff;
p_access->p_buffer->p_buffer[10] =
( p_access->i_ssrc >> 8 )&0xff;
p_access->p_buffer->p_buffer[11] = p_access->i_ssrc&0xff;
p_access->p_buffer->i_size = 12;
}
}
if( p_buffer->i_size > 0 )
{
memcpy( p_access->p_buffer->p_buffer + p_access->p_buffer->i_size,
p_buffer->p_buffer,
i_write );
p_access->p_buffer->i_size += i_write;
}
p_next = p_buffer->p_next;
sout_BufferDelete( p_sout, p_buffer );
p_buffer = p_next;
}
return( p_access->p_thread->b_error ? -1 : 0 );
}
/*****************************************************************************
* Seek: seek to a specific location in a file
*****************************************************************************/
static int Seek( sout_instance_t *p_sout, off_t i_pos )
{
msg_Err( p_sout, "udp sout access cannot seek" );
return( -1 );
}
/*****************************************************************************
* ThreadWrite: Write a packet on the network at the good time.
*****************************************************************************/
static void ThreadWrite( vlc_object_t *p_this )
{
sout_access_thread_t *p_thread = (sout_access_thread_t*)p_this;
sout_instance_t *p_sout = p_thread->p_sout;
sout_buffer_t *p_buffer;
mtime_t i_date;
while( !p_thread->b_die && p_thread->p_fifo->i_depth < 5 )
{
msleep( 10000 );
}
if( p_thread->b_die )
{
return;
}
p_buffer = sout_FifoShow( p_thread->p_fifo );
i_date = mdate() - p_buffer->i_dts;
for( ;; )
{
mtime_t i_wait;
p_buffer = sout_FifoGet( p_thread->p_fifo );
if( p_thread->b_die )
{
return;
}
i_wait = i_date + p_buffer->i_dts;
mwait( i_wait );
if( i_wait - mdate() > MAX_ERROR ||
i_wait - mdate() < -MAX_ERROR )
{
msg_Warn( p_sout, "resetting clock" );
i_date = mdate() - p_buffer->i_dts;
}
send( p_thread->i_handle,
p_buffer->p_buffer,
p_buffer->i_size,
0 );
sout_BufferDelete( p_sout, p_buffer );
}
}
SOURCES_mux_dummy = modules/mux/dummy.c
/*****************************************************************************
* dummy.c
*****************************************************************************
* Copyright (C) 2001, 2002 VideoLAN
* $Id: dummy.c,v 1.1 2002/12/14 21:32:41 fenrir Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
#include <vlc/sout.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#elif defined( _MSC_VER ) && defined( _WIN32 ) && !defined( UNDER_CE )
# include <io.h>
#endif
#include "codecs.h"
/*****************************************************************************
* Exported prototypes
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close ( vlc_object_t * );
static int AddStream( sout_instance_t *, sout_input_t * );
static int DelStream( sout_instance_t *, sout_input_t * );
static int Mux ( sout_instance_t * );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("Dummy muxer") );
set_capability( "sout mux", 5 );
add_shortcut( "dummy" );
set_callbacks( Open, Close );
vlc_module_end();
/*****************************************************************************
* Open:
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
sout_instance_t *p_sout = (sout_instance_t*)p_this;
msg_Info( p_sout, "Open" );
p_sout->pf_mux_addstream = AddStream;
p_sout->pf_mux_delstream = DelStream;
p_sout->pf_mux = Mux;
return VLC_SUCCESS;
}
/*****************************************************************************
* Close:
*****************************************************************************/
static void Close( vlc_object_t * p_this )
{
sout_instance_t *p_sout = (sout_instance_t*)p_this;
msg_Info( p_sout, "Close" );
}
static int AddStream( sout_instance_t *p_sout, sout_input_t *p_input )
{
msg_Dbg( p_sout, "adding input" );
return( 0 );
}
static int DelStream( sout_instance_t *p_sout, sout_input_t *p_input )
{
msg_Dbg( p_sout, "removing input" );
return( 0 );
}
static int Mux ( sout_instance_t *p_sout )
{
int i;
for( i = 0; i < p_sout->i_nb_inputs; i++ )
{
int i_count;
sout_fifo_t *p_fifo;
p_fifo = p_sout->pp_inputs[i]->p_fifo;
i_count = p_fifo->i_depth;
while( i_count > 0 )
{
sout_buffer_t *p_data;
p_data = sout_FifoGet( p_fifo );
p_sout->pf_write( p_sout, p_data );
i_count--;
}
}
return( 0 );
}
SOURCES_mux_ps = modules/mux/mpeg/ps.c \
modules/mux/mpeg/pes.c \
modules/mux/mpeg/pes.h \
modules/mux/mpeg/bits.h \
$(NULL)
SOURCES_mux_ts = modules/mux/mpeg/ts.c \
modules/mux/mpeg/pes.c \
modules/mux/mpeg/pes.h \
modules/mux/mpeg/bits.h \
$(NULL)
/*****************************************************************************
* bits.h
*****************************************************************************
* Copyright (C) 2001, 2002 VideoLAN
* $Id: bits.h,v 1.1 2002/12/14 21:32:41 fenrir Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
typedef struct bits_buffer_s
{
int i_size;
int i_data;
uint8_t i_mask;
uint8_t *p_data;
} bits_buffer_t;
static inline int bits_initwrite( bits_buffer_t *p_buffer,
int i_size, void *p_data )
{
p_buffer->i_size = i_size;
p_buffer->i_data = 0;
p_buffer->i_mask = 0x80;
p_buffer->p_data = p_data;
p_buffer->p_data[0] = 0;
if( !p_buffer->p_data )
{
if( !( p_buffer->p_data = malloc( i_size ) ) )
{
return( -1 );
}
else
{
return( 0 );
}
}
else
{
return( 0 );
}
}
static inline void bits_align( bits_buffer_t *p_buffer )
{
if( p_buffer->i_mask != 0x80 && p_buffer->i_data < p_buffer->i_size )
{
p_buffer->i_mask = 0x80;
p_buffer->i_data++;
p_buffer->p_data[p_buffer->i_data] = 0x00;
}
}
static inline void bits_write( bits_buffer_t *p_buffer,
int i_count, uint64_t i_bits )
{
while( i_count > 0 )
{
if( i_bits & ( 1 << ( i_count - 1 ) ) )
{
p_buffer->p_data[p_buffer->i_data] |= p_buffer->i_mask;
}
p_buffer->i_mask >>= 1;
if( p_buffer->i_mask == 0 )
{
p_buffer->i_data++;
if( p_buffer->i_data < p_buffer->i_size )
{
p_buffer->p_data[p_buffer->i_data] = 0;
p_buffer->i_mask = 0x80;
}
}
i_count--;
}
}
/*****************************************************************************
* pes.c
*****************************************************************************
* Copyright (C) 2001, 2002 VideoLAN
* $Id: pes.c,v 1.1 2002/12/14 21:32:41 fenrir Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
#include <vlc/sout.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#elif defined( _MSC_VER ) && defined( _WIN32 ) && !defined( UNDER_CE )
# include <io.h>
#endif
#include "codecs.h"
#include "pes.h"
#include "bits.h"
int EStoPES( sout_instance_t *p_sout,
sout_buffer_t **pp_pes,
sout_buffer_t *p_es,
int i_stream_id,
int b_mpeg2 )
{
sout_buffer_t *p_pes;
bits_buffer_t bits;
mtime_t i_pts, i_dts;
uint8_t *p_data;
int i_size;
i_pts = p_es->i_pts * 9 / 100; // 90000 units clock
i_dts = p_es->i_dts * 9 / 100; // 90000 units clock
i_size = p_es->i_size;
p_data = p_es->p_buffer;
*pp_pes = p_pes = NULL;
do
{
int i_copy;
i_copy = __MIN( i_size, 65500 );
if( *pp_pes == NULL )
{
*pp_pes = p_pes = sout_BufferNew( p_sout, 100 + i_copy);
p_pes->i_dts = p_es->i_dts;
p_pes->i_pts = p_es->i_pts;
p_pes->i_length = p_es->i_length;
}
else
{
p_pes->p_next = sout_BufferNew( p_sout, 100 + i_copy );
p_pes = p_pes->p_next;
p_pes->i_dts = 0;
p_pes->i_pts = 0;
p_pes->i_length = 0;
}
p_pes->i_size = 0;
bits_initwrite( &bits, 100, p_pes->p_buffer );
/* first 4 bytes common pes header */
/* add start code for pes 0x000001 */
bits_write( &bits, 24, 0x01 );
/* add stream id */
bits_write( &bits, 8, i_stream_id );
switch( i_stream_id )
{
case PES_PROGRAM_STREAM_MAP:
case PES_PADDING:
case PES_PRIVATE_STREAM_2:
case PES_ECM:
case PES_EMM:
case PES_PROGRAM_STREAM_DIRECTORY:
case PES_DSMCC_STREAM:
case PES_ITU_T_H222_1_TYPE_E_STREAM:
/* add pes data size */
bits_write( &bits, 16, i_copy );
bits_align( &bits );
break;
default:
/* arg, a little more difficult */
if( b_mpeg2 )
{
int i_pts_dts;
if( i_dts > 0 )
{
bits_write( &bits, 16, i_copy + 13 );
i_pts_dts = 0x03;
}
else
{
bits_write( &bits, 16, i_copy + 8 );
i_pts_dts = 0x02;
}
bits_write( &bits, 2, 0x02 ); // mpeg2 id
bits_write( &bits, 2, 0x00 ); // pes scrambling control
bits_write( &bits, 1, 0x00 ); // pes priority
bits_write( &bits, 1, 0x00 ); // data alignement indicator
bits_write( &bits, 1, 0x00 ); // copyright
bits_write( &bits, 1, 0x00 ); // original or copy
bits_write( &bits, 2, i_pts_dts ); // pts_dts flags
bits_write( &bits, 1, 0x00 ); // escr flags
bits_write( &bits, 1, 0x00 ); // es rate flag
bits_write( &bits, 1, 0x00 ); // dsm trick mode flag
bits_write( &bits, 1, 0x00 ); // additional copy info flag
bits_write( &bits, 1, 0x00 ); // pes crc flag
bits_write( &bits, 1, 0x00 ); // pes extention flags
if( i_pts_dts & 0x01 )
{
bits_write( &bits, 8, 0x0a ); // header size -> pts and dts
}
else
{
bits_write( &bits, 8, 0x05 ); // header size -> pts
}
/* write pts */
bits_write( &bits, 4, i_pts_dts ); // '0010' or '0011'
bits_write( &bits, 3, i_pts >> 30 );
bits_write( &bits, 1, 0x01 ); // marker
bits_write( &bits, 15, i_pts >> 15 );
bits_write( &bits, 1, 0x01 ); // marker
bits_write( &bits, 15, i_pts );
bits_write( &bits, 1, 0x01 ); // marker
/* write i_dts */
if( i_pts_dts & 0x01 )
{
bits_write( &bits, 4, 0x01 ); // '0001'
bits_write( &bits, 3, i_dts >> 30 );
bits_write( &bits, 1, 0x01 ); // marker
bits_write( &bits, 15, i_dts >> 15 );
bits_write( &bits, 1, 0x01 ); // marker
bits_write( &bits, 15, i_dts );
bits_write( &bits, 1, 0x01 ); // marker
i_dts = 0; // write dts only once
}
}
else
{
msg_Warn( p_sout, "es isn't mpeg2 -->corrupted pes" );
}
/* now should be stuffing */
/* and then pes data */
bits_align( &bits );
break;
}
if( i_copy > 0 )
{
memcpy( p_pes->p_buffer + bits.i_data, p_data, i_copy );
}
i_size -= i_copy;
p_data += i_copy;
p_pes->i_size = bits.i_data + i_copy;
} while( i_size > 0 );
sout_BufferDelete( p_sout, p_es );
return( 0 );
}
/*****************************************************************************
* pes.h
*****************************************************************************
* Copyright (C) 2001, 2002 VideoLAN
* $Id: pes.h,v 1.1 2002/12/14 21:32:41 fenrir Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
#define PES_PROGRAM_STREAM_MAP 0xbc
#define PES_PADDING 0xbe
#define PES_PRIVATE_STREAM_2 0xbf
#define PES_ECM 0xb0
#define PES_EMM 0xb1
#define PES_PROGRAM_STREAM_DIRECTORY 0xff
#define PES_DSMCC_STREAM 0xf2
#define PES_ITU_T_H222_1_TYPE_E_STREAM 0xf8
int EStoPES( sout_instance_t *p_sout,
sout_buffer_t **pp_pes, sout_buffer_t *p_es,
int i_stream_id, int b_mpeg2 );
/*****************************************************************************
* ps.c
*****************************************************************************
* Copyright (C) 2001, 2002 VideoLAN
* $Id: ps.c,v 1.1 2002/12/14 21:32:41 fenrir Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
#include <vlc/sout.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#elif defined( _MSC_VER ) && defined( _WIN32 ) && !defined( UNDER_CE )
# include <io.h>
#endif
#include "codecs.h"
#include "bits.h"
#include "pes.h"
/*****************************************************************************
* Exported prototypes
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close ( vlc_object_t * );
static int AddStream( sout_instance_t *, sout_input_t * );
static int DelStream( sout_instance_t *, sout_input_t * );
static int Mux ( sout_instance_t * );
static void SetWBE ( uint8_t *p, uint16_t v )
{
p[0] = ( v >> 8 )&0xff;
p[1] = v&0xff;
}
static void SetDWBE( uint8_t *p, uint32_t v )
{
SetWBE( p, ( v >> 16 )&0xffff );
SetWBE( p + 2, v & 0xffff );
}
#define ADD_DWBE( p_buff, v ) \
SetDWBE( (p_buff)->p_buffer + i_buffer, (v) ); \
i_buffer +=4;
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("PS muxer") );
set_capability( "sout mux", 50 );
add_shortcut( "ps" );
set_callbacks( Open, Close );
vlc_module_end();
typedef struct ps_stream_s
{
int i_ok;
int i_stream_id;
} ps_stream_t;
typedef struct sout_mux_s
{
int i_stream_id_mpga;
int i_stream_id_mpgv;
int i_stream_id_a52;
int i_audio_bound;
int i_video_bound;
int i_pes_count;
int i_system_header;
} sout_mux_t;
/*****************************************************************************
* Open:
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
sout_instance_t *p_sout = (sout_instance_t*)p_this;
sout_mux_t *p_mux;
msg_Info( p_sout, "Open" );
p_mux = malloc( sizeof( sout_mux_t ) );
p_sout->pf_mux_addstream = AddStream;
p_sout->pf_mux_delstream = DelStream;
p_sout->pf_mux = Mux;
p_sout->p_mux_data = (void*)p_mux;
p_mux->i_stream_id_mpga = 0xc0;
p_mux->i_stream_id_a52 = 0x80;
p_mux->i_stream_id_mpgv = 0xe0;
p_mux->i_audio_bound = 0;
p_mux->i_video_bound = 0;
p_mux->i_system_header = 0;
p_mux->i_pes_count = 0;
return VLC_SUCCESS;
}
/*****************************************************************************
* Close:
*****************************************************************************/
static void Close( vlc_object_t * p_this )
{
sout_instance_t *p_sout = (sout_instance_t*)p_this;
sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
sout_buffer_t *p_end;
msg_Info( p_sout, "Close" );
p_end = sout_BufferNew( p_sout, 4 );
SetDWBE( p_end->p_buffer, 0x01b9 );
p_sout->pf_write( p_sout, p_end );
free( p_mux );
p_sout->p_mux_data = NULL;
}
static int AddStream( sout_instance_t *p_sout, sout_input_t *p_input )
{
sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
ps_stream_t *p_stream;
msg_Dbg( p_sout, "adding input", p_sout );
p_input->p_mux_data = (void*)p_stream = malloc( sizeof( ps_stream_t ) );
p_stream->i_ok = 0;
switch( p_input->input_format.i_cat )
{
case VIDEO_ES:
p_stream->i_stream_id = p_mux->i_stream_id_mpgv;
p_mux->i_stream_id_mpgv++;
p_mux->i_video_bound++;
break;
case AUDIO_ES:
if( p_input->input_format.i_fourcc == VLC_FOURCC( 'a', '5', '2', ' ' ) )
{
p_stream->i_stream_id = p_mux->i_stream_id_a52;
p_mux->i_stream_id_a52++;
}
else
{
p_stream->i_stream_id = p_mux->i_stream_id_mpga;
p_mux->i_stream_id_mpga++;
}
p_mux->i_audio_bound++;
break;
default:
return( -1 );
}
p_stream->i_ok = 1;
msg_Dbg( p_sout, "adding input stream_id:0x%x [OK]", p_stream->i_stream_id, p_sout );
return( 0 );
}
static int DelStream( sout_instance_t *p_sout, sout_input_t *p_input )
{
ps_stream_t *p_stream =(ps_stream_t*)p_input->p_mux_data;
msg_Dbg( p_sout, "removing input" );
if( p_stream )
{
free( p_stream );
}
return( 0 );
}
static int MuxWritePackHeader( sout_instance_t *p_sout,
mtime_t i_dts )
{
sout_buffer_t *p_hdr;
bits_buffer_t bits;
mtime_t i_src;
i_src = i_dts * 9 / 100;
p_hdr = sout_BufferNew( p_sout, 18 );
bits_initwrite( &bits, 14, p_hdr->p_buffer );
bits_write( &bits, 32, 0x01ba );
bits_write( &bits, 2, 0x01 ); // FIXME ??
bits_write( &bits, 3, ( i_src >> 30 )&0x07 );
bits_write( &bits, 1, 1 );
bits_write( &bits, 15, ( i_src >> 15 )&0x7fff );
bits_write( &bits, 1, 1 );
bits_write( &bits, 15, i_src&0x7fff );
bits_write( &bits, 1, 1 );
bits_write( &bits, 9, 0 ); // src extention
bits_write( &bits, 1, 1 );
bits_write( &bits, 22, 0/8/50); // FIXME
bits_write( &bits, 1, 1 );
bits_write( &bits, 1, 1 );
bits_write( &bits, 5, 0x1f ); // FIXME reserved
bits_write( &bits, 3, 0 ); // stuffing bytes
p_hdr->i_size = 14;
p_sout->pf_write( p_sout, p_hdr );
return( 0 );
}
static int MuxWriteSystemHeader( sout_instance_t *p_sout )
{
sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
sout_buffer_t *p_hdr;
bits_buffer_t bits;
p_hdr = sout_BufferNew( p_sout, 12 );
bits_initwrite( &bits, 12, p_hdr->p_buffer );
bits_write( &bits, 32, 0x01bb );
bits_write( &bits, 16, 12 - 6);
bits_write( &bits, 1, 1 );
bits_write( &bits, 22, 0 ); // FIXME rate bound
bits_write( &bits, 1, 1 );
bits_write( &bits, 6, p_mux->i_audio_bound );
bits_write( &bits, 1, 0 ); // fixed flag
bits_write( &bits, 1, 0 ); // CSPS flag
bits_write( &bits, 1, 0 ); // system audio lock flag
bits_write( &bits, 1, 0 ); // system video lock flag
bits_write( &bits, 1, 1 ); // marker bit
bits_write( &bits, 5, p_mux->i_video_bound );
bits_write( &bits, 1, 0 ); // packet rate restriction flag
bits_write( &bits, 7, 0x7f ); // reserved bits
/* FIXME missing stream_id ... */
p_sout->pf_write( p_sout, p_hdr );
return( 0 );
}
/* return stream number to be muxed */
static int MuxGetStream( sout_instance_t *p_sout,
int *pi_stream,
mtime_t *pi_dts )
{
mtime_t i_dts;
int i_stream;
int i;
for( i = 0, i_dts = 0, i_stream = -1; i < p_sout->i_nb_inputs; i++ )
{
sout_fifo_t *p_fifo;
p_fifo = p_sout->pp_inputs[i]->p_fifo;
if( p_fifo->i_depth > 1 )
{
sout_buffer_t *p_buf;
p_buf = sout_FifoShow( p_fifo );
if( i_stream < 0 || p_buf->i_dts < i_dts )
{
i_dts = p_buf->i_dts;
i_stream = i;
}
}
else
{
return( -1 ); // wait that all fifo have at least 2 packets
}
}
if( pi_stream )
{
*pi_stream = i_stream;
}
if( pi_dts )
{
*pi_dts = i_dts;
}
return( i_stream );
}
static int Mux ( sout_instance_t *p_sout )
{
sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
mtime_t i_dts;
int i_stream;
for( ;; )
{
sout_input_t *p_input;
ps_stream_t *p_stream;
sout_fifo_t *p_fifo;
sout_buffer_t *p_data;
if( MuxGetStream( p_sout, &i_stream, &i_dts ) < 0 )
{
return( 0 );
}
p_input = p_sout->pp_inputs[i_stream];
p_fifo = p_input->p_fifo;
p_stream = (ps_stream_t*)p_input->p_mux_data;
if( p_mux->i_pes_count % 30 == 0)
{
MuxWritePackHeader( p_sout, i_dts );
}
if( p_mux->i_pes_count % 300 == 0 )
{
// MuxWriteSystemHeader( p_sout );
}
p_data = sout_FifoGet( p_fifo );
EStoPES( p_sout, &p_data, p_data, p_stream->i_stream_id, 1);
p_sout->pf_write( p_sout, p_data );
p_mux->i_pes_count++;
}
return( 0 );
}
/*****************************************************************************
* ts.c
*****************************************************************************
* Copyright (C) 2001, 2002 VideoLAN
* $Id: ts.c,v 1.1 2002/12/14 21:32:41 fenrir Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
#include <vlc/sout.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#elif defined( _MSC_VER ) && defined( _WIN32 ) && !defined( UNDER_CE )
# include <io.h>
#endif
#include "codecs.h"
#include "bits.h"
#include "pes.h"
typedef struct ts_stream_s
{
int i_pid;
int i_stream_type;
int i_stream_id;
int i_continuity_counter;
} ts_stream_t;
typedef struct sout_mux_s
{
int i_pcr_pid;
int i_stream_id_mpga;
int i_stream_id_mpgv;
int i_stream_id_a52;
int i_audio_bound;
int i_video_bound;
int i_pid_free; // first usable pid
int i_pat_version_number;
ts_stream_t pat;
int i_pmt_version_number;
ts_stream_t pmt; // Up to now only one program
int i_ts_packet;// To known when to put pat/mpt
} sout_mux_t;
/*****************************************************************************
* Exported prototypes
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close ( vlc_object_t * );
static int AddStream( sout_instance_t *, sout_input_t * );
static int DelStream( sout_instance_t *, sout_input_t * );
static int Mux ( sout_instance_t * );
/* Reserve a pid and return it */
static int AllocatePID( sout_mux_t *p_mux )
{
return( ++p_mux->i_pid_free );
}
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("TS muxer") );
set_capability( "sout mux", 100 );
add_shortcut( "ts" );
set_callbacks( Open, Close );
vlc_module_end();
/*****************************************************************************
* Open:
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
sout_instance_t *p_sout = (sout_instance_t*)p_this;
sout_mux_t *p_mux;
msg_Info( p_sout, "Open" );
p_mux = malloc( sizeof( sout_mux_t ) );
p_sout->pf_mux_addstream = AddStream;
p_sout->pf_mux_delstream = DelStream;
p_sout->pf_mux = Mux;
p_sout->p_mux_data = (void*)p_mux;
p_mux->i_stream_id_mpga = 0xc0;
p_mux->i_stream_id_a52 = 0x80;
p_mux->i_stream_id_mpgv = 0xe0;
p_mux->i_audio_bound = 0;
p_mux->i_video_bound = 0;
p_mux->i_pat_version_number = 0;
p_mux->pat.i_pid = 0;
p_mux->pat.i_continuity_counter = 0;
p_mux->i_pmt_version_number = 0;
p_mux->pmt.i_pid = 0x10;
p_mux->pmt.i_continuity_counter = 0;
p_mux->i_pid_free = 0x11;
p_mux->i_pcr_pid = 0x1fff;
return VLC_SUCCESS;
}
/*****************************************************************************
* Close:
*****************************************************************************/
static void Close( vlc_object_t * p_this )
{
sout_instance_t *p_sout = (sout_instance_t*)p_this;
sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
msg_Info( p_sout, "Close" );
free( p_mux );
p_sout->p_mux_data = NULL;
}
static int AddStream( sout_instance_t *p_sout, sout_input_t *p_input )
{
sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
ts_stream_t *p_stream;
msg_Dbg( p_sout, "adding input" );
p_input->p_mux_data = (void*)p_stream = malloc( sizeof( ts_stream_t ) );
p_stream->i_pid = AllocatePID( p_mux );
if( p_mux->i_pcr_pid == 0x1fff )
{
p_mux->i_pcr_pid = p_stream->i_pid;
}
p_stream->i_continuity_counter = 0;
switch( p_input->input_format.i_cat )
{
case VIDEO_ES:
switch( p_input->input_format.i_fourcc )
{
case VLC_FOURCC( 'm', 'p','g', 'v' ):
p_stream->i_stream_type = 0x02;
p_stream->i_stream_id = p_mux->i_stream_id_mpgv;
p_mux->i_stream_id_mpgv++;
break;
case VLC_FOURCC( 'm', 'p','4', 'v' ):
p_stream->i_stream_type = 0x10;
p_stream->i_stream_id = 0xfa;
break;
default:
return( -1 );
}
p_mux->i_video_bound++;
break;
case AUDIO_ES:
switch( p_input->input_format.i_fourcc )
{
case VLC_FOURCC( 'a', '5','2', ' ' ):
case VLC_FOURCC( 'a', '5','2', 'b' ):
p_stream->i_stream_type = 0x81;
p_stream->i_stream_id = p_mux->i_stream_id_a52;
p_mux->i_stream_id_a52++;
break;
#if 0
case VLC_FOURCC( 'm', 'p','4', 'a' ):
p_stream->i_stream_type = 0x11;
p_stream->i_stream_id = 0xfa;
p_mux->i_stream_id_mp4a++;
break;
#endif
case VLC_FOURCC( 'm', 'p','g', 'a' ):
p_stream->i_stream_type = 0x04;
p_stream->i_stream_id = p_mux->i_stream_id_mpga;
p_mux->i_stream_id_mpga++;
break;
default:
return( -1 );
}
p_mux->i_audio_bound++;
break;
default:
return( -1 );
}
p_mux->i_ts_packet = 0; // force pat/pmt recreation
p_mux->i_pat_version_number++; p_mux->i_pat_version_number %= 32;
p_mux->i_pmt_version_number++; p_mux->i_pmt_version_number %= 32;
return( 0 );
}
static int DelStream( sout_instance_t *p_sout, sout_input_t *p_input )
{
sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
msg_Dbg( p_sout, "removing input" );
p_mux->i_ts_packet = 0; // force pat/pmt recreation
p_mux->i_pat_version_number++; p_mux->i_pat_version_number %= 32;
p_mux->i_pmt_version_number++; p_mux->i_pmt_version_number %= 32;
return( 0 );
}
static int MuxGetStream( sout_instance_t *p_sout,
int *pi_stream,
mtime_t *pi_dts )
{
mtime_t i_dts;
int i_stream;
int i;
for( i = 0, i_dts = 0, i_stream = -1; i < p_sout->i_nb_inputs; i++ )
{
sout_fifo_t *p_fifo;
p_fifo = p_sout->pp_inputs[i]->p_fifo;
if( p_fifo->i_depth > 1 )
{
sout_buffer_t *p_buf;
p_buf = sout_FifoShow( p_fifo );
if( i_stream < 0 || p_buf->i_dts < i_dts )
{
i_dts = p_buf->i_dts;
i_stream = i;
}
}
else
{
return( -1 ); // wait that all fifo have at least 2 packets
}
}
if( pi_stream )
{
*pi_stream = i_stream;
}
if( pi_dts )
{
*pi_dts = i_dts;
}
return( i_stream );
}
static int PEStoTS( sout_instance_t *p_sout,
sout_buffer_t **pp_ts, sout_buffer_t *p_pes,
ts_stream_t *p_stream )
{
int i_size;
sout_buffer_t *p_last_buffer = NULL;
uint8_t *p_data;
int i_first;
mtime_t i_dts;
int b_new_pes;
/* get PES total size */
i_size = p_pes->i_size;
p_data = p_pes->p_buffer;
if( p_pes->i_dts == 0 && p_pes->i_length > 0 )
{
i_dts = 1; // XXX <french> kludge immonde </french>
}
else
{
i_dts = p_pes->i_dts;
}
for( i_first = 1, b_new_pes = 1; p_pes != NULL; )
{
int i_adaptation_field;
int i_payload;
int i_copy;
bits_buffer_t bits;
sout_buffer_t *p_ts;
p_ts = sout_BufferNew( p_sout, 188 );
p_ts->i_pts = 0;
p_ts->i_dts = i_dts;
i_payload = 184 - ( i_first && i_dts > 0 ? 8 : 0 );
i_copy = __MIN( i_size, i_payload );
i_adaptation_field = ( ( i_first && i_dts > 0 ) ||
i_size < i_payload ) ? 1 : 0;
/* write headers */
bits_initwrite( &bits, 188, p_ts->p_buffer );
bits_write( &bits, 8, 0x47 ); /* sync byte */
bits_write( &bits, 1, 0 ); /* transport_error_indicator */
bits_write( &bits, 1, b_new_pes ? 1 : 0 ); /* payload_unit_start */
b_new_pes = 0;
bits_write( &bits, 1, 0 ); /* transport_priority */
bits_write( &bits, 13, p_stream->i_pid );
bits_write( &bits, 2, 0 ); /* transport_scrambling_control */
bits_write( &bits, 2, ( i_adaptation_field ? 0x03 : 0x01 ) );
bits_write( &bits, 4, /* continuity_counter */
p_stream->i_continuity_counter );
p_stream->i_continuity_counter++;
p_stream->i_continuity_counter %= 16;
if( i_adaptation_field )
{
int i;
int i_stuffing;
if( i_first && i_dts > 0 )
{
i_stuffing = i_payload - i_copy;
bits_write( &bits, 8, 7 + i_stuffing );
bits_write( &bits, 8, 0x10 ); /* various flags */
bits_write( &bits, 33, i_dts * 9 / 100);
bits_write( &bits, 6, 0 );
bits_write( &bits, 9, 0 );
i_dts = 0; /* XXX set dts only for first ts packet */
}
else
{
i_stuffing = i_payload - i_copy;
bits_write( &bits, 8, i_stuffing - 1);
if( i_stuffing - 1 > 0 )
{
bits_write( &bits, 8, 0 );
}
i_stuffing -= 2;
}
/* put stuffing */
for( i = 0; i < i_stuffing; i++ )
{
bits_write( &bits, 8, 0xff );
}
}
/* copy payload */
memcpy( p_ts->p_buffer + bits.i_data,
p_data,
i_copy );
p_data += i_copy;
i_size -= i_copy;
/* chain buffers */
if( i_first )
{
*pp_ts = p_ts;
p_last_buffer = p_ts;
}
else
{
p_last_buffer->p_next = p_ts;
p_last_buffer = p_ts;
}
i_first = 0;
if( i_size <= 0 )
{
sout_buffer_t *p_next;
p_next = p_pes->p_next;
p_pes->p_next = NULL;
sout_BufferDelete( p_sout, p_pes );
p_pes = p_next;
b_new_pes = 1;
if( p_pes )
{
i_size = p_pes->i_size;
p_data = p_pes->p_buffer;
}
else
{
break;
}
}
}
return 0;
}
static int GetPAT( sout_instance_t *p_sout,
sout_buffer_t **pp_ts )
{
sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
sout_buffer_t *p_pat;
bits_buffer_t bits;
p_pat = sout_BufferNew( p_sout, 1024 );
p_pat->i_pts = 0;
p_pat->i_dts = 0;
p_pat->i_length = 0;
bits_initwrite( &bits, 1024, p_pat->p_buffer );
bits_write( &bits, 8, 0 ); // pointer
bits_write( &bits, 8, 0x00 ); // table id
bits_write( &bits, 1, 1 ); // section_syntax_indicator
bits_write( &bits, 1, 0 ); // 0
bits_write( &bits, 2, 0 ); // reserved FIXME
bits_write( &bits, 12, 13 ); // XXX for one program only XXX
bits_write( &bits, 16, 0x01 ); // FIXME stream id
bits_write( &bits, 2, 0 ); // FIXME
bits_write( &bits, 5, p_mux->i_pat_version_number );
bits_write( &bits, 1, 1 ); // current_next_indicator
bits_write( &bits, 8, 0 ); // section number
bits_write( &bits, 8, 0 ); // last section number
bits_write( &bits, 16, 1 ); // program number
bits_write( &bits, 3, 0 ); // reserved
bits_write( &bits, 13, p_mux->pmt.i_pid ); // program map pid
bits_write( &bits, 32, 0 ); // FIXME FIXME FIXME
p_pat->i_size = bits.i_data;
return( PEStoTS( p_sout, pp_ts, p_pat, &p_mux->pat ) );
}
static int GetPMT( sout_instance_t *p_sout,
sout_buffer_t **pp_ts )
{
sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
sout_buffer_t *p_pmt;
bits_buffer_t bits;
int i_stream;
p_pmt = sout_BufferNew( p_sout, 1024 );
p_pmt->i_pts = 0;
p_pmt->i_dts = 0;
p_pmt->i_length = 0;
bits_initwrite( &bits, 1024, p_pmt->p_buffer );
bits_write( &bits, 8, 0 ); // pointer
bits_write( &bits, 8, 0x02 ); // table id
bits_write( &bits, 1, 1 ); // section_syntax_indicator
bits_write( &bits, 1, 0 ); // 0
bits_write( &bits, 2, 0 ); // reserved FIXME
bits_write( &bits, 12, 13 + 5 * p_sout->i_nb_inputs );
bits_write( &bits, 16, 1 ); // FIXME program number
bits_write( &bits, 2, 0 ); // FIXME
bits_write( &bits, 5, p_mux->i_pmt_version_number );
bits_write( &bits, 1, 1 ); // current_next_indicator
bits_write( &bits, 8, 0 ); // section number
bits_write( &bits, 8, 0 ); // last section number
bits_write( &bits, 3, 0 ); // reserved
bits_write( &bits, 13, p_mux->i_pcr_pid ); // FIXME FXIME PCR_PID FIXME
bits_write( &bits, 4, 0 ); // reserved FIXME
bits_write( &bits, 12, 0 ); // program info len FIXME
for( i_stream = 0; i_stream < p_sout->i_nb_inputs; i_stream++ )
{
ts_stream_t *p_stream;
p_stream = (ts_stream_t*)p_sout->pp_inputs[i_stream]->p_mux_data;
bits_write( &bits, 8, p_stream->i_stream_type ); // stream_type
bits_write( &bits, 3, 0 ); // reserved
bits_write( &bits, 13, p_stream->i_pid ); // es pid
bits_write( &bits, 4, 0 ); //reserved
bits_write( &bits, 12, 0 ); // es info len FIXME
}
bits_write( &bits, 32, 0 ); // FIXME FIXME FIXME
p_pmt->i_size = bits.i_data;
return( PEStoTS( p_sout, pp_ts, p_pmt, &p_mux->pmt ) );
}
static void SetTSDate( sout_buffer_t *p_ts, mtime_t i_dts, mtime_t i_length )
{
int i_count;
sout_buffer_t *p_tmp;
mtime_t i_delta;
for( p_tmp = p_ts, i_count = 0; p_tmp != NULL; p_tmp = p_tmp->p_next )
{
i_count++;
}
i_delta = i_length / i_count;
for( p_tmp = p_ts; p_tmp != NULL; p_tmp = p_tmp->p_next )
{
p_tmp->i_dts = i_dts;
p_tmp->i_length = i_delta;
i_dts += i_delta;
}
}
static int Mux( sout_instance_t *p_sout )
{
sout_mux_t *p_mux = (sout_mux_t*)p_sout->p_mux_data;
mtime_t i_dts;
int i_stream;
sout_buffer_t *p_pat, *p_pmt, *p_ts;
for( ;; )
{
mtime_t i_dts, i_length;
sout_input_t *p_input;
ts_stream_t *p_stream;
sout_fifo_t *p_fifo;
sout_buffer_t *p_data;
if( MuxGetStream( p_sout, &i_stream, &i_dts ) < 0 )
{
return( 0 );
}
p_input = p_sout->pp_inputs[i_stream];
p_fifo = p_input->p_fifo;
p_stream = (ts_stream_t*)p_input->p_mux_data;
p_data = sout_FifoGet( p_fifo );
i_dts = p_data->i_dts;
i_length = p_data->i_length;
EStoPES( p_sout, &p_data, p_data, p_stream->i_stream_id, 1);
PEStoTS( p_sout, &p_data, p_data, p_stream );
if( p_mux->i_ts_packet % 30 == 0 )
{
/* create pat/pmt */
GetPAT( p_sout, &p_pat );
GetPMT( p_sout, &p_pmt );
p_ts = p_pat;
p_pat->p_next = p_pmt;
p_pmt->p_next = p_data;
}
else
{
p_pat = NULL;
p_pmt = NULL;
p_ts = p_data;
}
p_mux->i_ts_packet++;
SetTSDate( p_ts, i_dts, i_length );
p_sout->pf_write( p_sout, p_ts );
}
return( 0 );
}
SOURCES_packetizer_copy = modules/packetizer/copy.c
SOURCES_packetizer_a52 = modules/packetizer/a52.c
SOURCES_packetizer_mpegaudio = modules/packetizer/mpegaudio.c
SOURCES_packetizer_mpegvideo = modules/packetizer/mpegvideo.c
SOURCES_packetizer_mpeg4video = modules/packetizer/mpeg4video.c
/*****************************************************************************
* a52.c
*****************************************************************************
* Copyright (C) 2001, 2002 VideoLAN
* $Id: a52.c,v 1.1 2002/12/14 21:32:41 fenrir Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/aout.h>
#include <vlc/decoder.h>
#include <vlc/input.h>
#include <vlc/sout.h>
#include <stdlib.h> /* malloc(), free() */
#include <string.h> /* strdup() */
#include "codecs.h" /* WAVEFORMATEX BITMAPINFOHEADER */
/*****************************************************************************
* Local prototypes
*****************************************************************************/
typedef struct packetizer_s
{
/* Input properties */
decoder_fifo_t *p_fifo;
bit_stream_t bit_stream;
/* Output properties */
sout_input_t *p_sout_input;
sout_packet_format_t output_format;
uint64_t i_samplescount;
} packetizer_t;
static int Open ( vlc_object_t * );
static int Run ( decoder_fifo_t * );
static int InitThread ( packetizer_t * );
static void PacketizeThread ( packetizer_t * );
static void EndThread ( packetizer_t * );
static int SyncInfo( const byte_t *, int *, int *, int * );
#define FREE( p ) if( p ) free( p ); p = NULL
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("A/52 audio packetizer") );
set_capability( "packetizer", 10 );
set_callbacks( Open, NULL );
vlc_module_end();
/*****************************************************************************
* OpenDecoder: probe the packetizer and return score
*****************************************************************************
* Tries to launch a decoder and return score so that the interface is able
* to choose.
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
decoder_fifo_t *p_fifo = (decoder_fifo_t*) p_this;
if( p_fifo->i_fourcc != VLC_FOURCC( 'a', '5', '2', ' ') &&
p_fifo->i_fourcc != VLC_FOURCC( 'a', '5', '2', 'b') )
{
return VLC_EGENERIC;
}
p_fifo->pf_run = Run;
return VLC_SUCCESS;
}
/*****************************************************************************
* RunDecoder: this function is called just after the thread is created
*****************************************************************************/
static int Run( decoder_fifo_t *p_fifo )
{
packetizer_t *p_pack;
int b_error;
msg_Info( p_fifo, "Running A/52 packetizer" );
if( !( p_pack = malloc( sizeof( packetizer_t ) ) ) )
{
msg_Err( p_fifo, "out of memory" );
DecoderError( p_fifo );
return( -1 );
}
memset( p_pack, 0, sizeof( packetizer_t ) );
p_pack->p_fifo = p_fifo;
if( InitThread( p_pack ) != 0 )
{
DecoderError( p_fifo );
return( -1 );
}
while( ( !p_pack->p_fifo->b_die )&&( !p_pack->p_fifo->b_error ) )
{
PacketizeThread( p_pack );
}
if( ( b_error = p_pack->p_fifo->b_error ) )
{
DecoderError( p_pack->p_fifo );
}
EndThread( p_pack );
FREE( p_pack );
if( b_error )
{
return( -1 );
}
return( 0 );
}
/*****************************************************************************
* InitThread: initialize data before entering main loop
*****************************************************************************/
static int InitThread( packetizer_t *p_pack )
{
p_pack->output_format.i_cat = AUDIO_ES;
p_pack->output_format.i_fourcc = VLC_FOURCC( 'a', '5', '2', ' ' );
p_pack->output_format.p_format = NULL;
p_pack->p_sout_input = NULL;
p_pack->i_samplescount = 0;
if( InitBitstream( &p_pack->bit_stream, p_pack->p_fifo,
NULL, NULL ) != VLC_SUCCESS )
{
msg_Err( p_pack->p_fifo, "cannot initialize bitstream" );
return -1;
}
return( 0 );
}
/*****************************************************************************
* PacketizeThread: packetize an unit (here copy a complete pes)
*****************************************************************************/
static void PacketizeThread( packetizer_t *p_pack )
{
sout_buffer_t *p_sout_buffer;
uint8_t p_header[7];
int i_channels, i_samplerate, i_bitrate;
int i_framelength;
/* search a valid start code */
for( ;; )
{
int i_skip = 0;
RealignBits( &p_pack->bit_stream );
while( ShowBits( &p_pack->bit_stream, 16 ) != 0x0b77 &&
!p_pack->p_fifo->b_die && !p_pack->p_fifo->b_error )
{
RemoveBits( &p_pack->bit_stream, 8 );
i_skip++;
}
if( i_skip )
{
msg_Warn( p_pack->p_fifo, "trashing %d bytes", i_skip );
}
if( p_pack->p_fifo->b_die || p_pack->p_fifo->b_error )
{
return;
}
GetChunk( &p_pack->bit_stream, p_header, 7 );
if( p_pack->p_fifo->b_die ) return;
/* Check if frame is valid and get frame info */
i_framelength = SyncInfo( p_header, &i_channels, &i_samplerate,
&i_bitrate );
if( !i_framelength )
{
msg_Warn( p_pack->p_fifo, "invalid header found" );
continue;
}
else
{
// msg_Dbg( p_pack->p_fifo, "frame length %d b", i_framelength );
break;
}
}
if( !p_pack->p_sout_input )
{
/* add a input for the stream ouput */
WAVEFORMATEX *p_wf;
p_wf = malloc( sizeof( WAVEFORMATEX ) );
p_pack->output_format.p_format = (void*)p_wf;
p_wf->wFormatTag = WAVE_FORMAT_A52;
p_wf->nChannels = i_channels;
p_wf->nSamplesPerSec = i_samplerate;
p_wf->nAvgBytesPerSec = 0;
p_wf->nBlockAlign = 1;
p_wf->wBitsPerSample = 0;
p_wf->cbSize = 0;
p_pack->p_sout_input =
sout_InputNew( p_pack->p_fifo,
&p_pack->output_format );
if( !p_pack->p_sout_input )
{
msg_Err( p_pack->p_fifo,
"cannot add a new stream" );
p_pack->p_fifo->b_error = 1;
return;
}
msg_Info( p_pack->p_fifo,
"A/52 channels:%d samplerate:%d bitrate:%d",
i_channels, i_samplerate, i_bitrate );
}
p_sout_buffer =
sout_BufferNew( p_pack->p_sout_input->p_sout, i_framelength );
if( !p_sout_buffer )
{
p_pack->p_fifo->b_error = 1;
return;
}
memcpy( p_sout_buffer->p_buffer, p_header, 7 );
p_sout_buffer->i_bitrate = i_bitrate;
p_sout_buffer->i_pts =
p_sout_buffer->i_dts =
(uint64_t)1000000 *
(uint64_t)p_pack->i_samplescount /
(uint64_t)i_samplerate;
p_sout_buffer->i_length =
(uint64_t)1000000 *
(uint64_t)A52_FRAME_NB /
(uint64_t)i_samplerate;
p_pack->i_samplescount += A52_FRAME_NB;
/* we are already aligned */
GetChunk( &p_pack->bit_stream,
p_sout_buffer->p_buffer + 7,
i_framelength - 7 );
sout_InputSendBuffer( p_pack->p_sout_input,
p_sout_buffer );
}
/*****************************************************************************
* EndThread : packetizer thread destruction
*****************************************************************************/
static void EndThread ( packetizer_t *p_pack)
{
if( p_pack->p_sout_input )
{
sout_InputDelete( p_pack->p_sout_input );
}
}
/*****************************************************************************
* SyncInfo: parse A52 sync info
*****************************************************************************
* This code is borrowed from liba52 by Aaron Holtzman & Michel Lespinasse,
* since we don't want to oblige S/PDIF people to use liba52 just to get
* their SyncInfo...
* fenrir: I've change it to report channels count
*****************************************************************************/
static int SyncInfo( const byte_t * p_buf, int * pi_channels,
int * pi_sample_rate, int * pi_bit_rate )
{
static const uint8_t halfrate[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3 };
static const int rate[] = { 32, 40, 48, 56, 64, 80, 96, 112,
128, 160, 192, 224, 256, 320, 384, 448,
512, 576, 640 };
static const uint8_t lfeon[8] = { 0x10, 0x10, 0x04, 0x04,
0x04, 0x01, 0x04, 0x01 };
int frmsizecod;
int bitrate;
int half;
int acmod;
if ((p_buf[0] != 0x0b) || (p_buf[1] != 0x77)) /* syncword */
return 0;
if (p_buf[5] >= 0x60) /* bsid >= 12 */
return 0;
half = halfrate[p_buf[5] >> 3];
/* acmod, dsurmod and lfeon */
acmod = p_buf[6] >> 5;
if ( (p_buf[6] & 0xf8) == 0x50 )
{
/* Dolby surround = stereo + Dolby */
/* *pi_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
| AOUT_CHAN_DOLBYSTEREO;*/
*pi_channels = 2; /* FIXME ??? */
}
else switch ( acmod )
{
case 0x0:
/* Dual-mono = stereo + dual-mono */
// *pi_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
// | AOUT_CHAN_DUALMONO;
*pi_channels = 2; /* FIXME ??? */
break;
case 0x1:
/* Mono */
*pi_channels = 1;
break;
case 0x2:
/* Stereo */
*pi_channels = 2;
break;
case 0x3: /* 3F */
case 0x4: /* 2F1R */
*pi_channels = 3;
break;
case 0x5: /* 3F1R */
case 0x6: /* 2F2R */
*pi_channels = 4;
break;
case 0x7:
/* 3F2R */
*pi_channels = 5;
break;
default:
return 0;
}
if ( p_buf[6] & lfeon[acmod] ) *pi_channels += 1;//|= AOUT_CHAN_LFEA;
frmsizecod = p_buf[4] & 63;
if (frmsizecod >= 38)
return 0;
bitrate = rate [frmsizecod >> 1];
*pi_bit_rate = (bitrate * 1000) >> half;
switch (p_buf[4] & 0xc0) {
case 0:
*pi_sample_rate = 48000 >> half;
return 4 * bitrate;
case 0x40:
*pi_sample_rate = 44100 >> half;
return 2 * (320 * bitrate / 147 + (frmsizecod & 1));
case 0x80:
*pi_sample_rate = 32000 >> half;
return 6 * bitrate;
default:
return 0;
}
}
/*****************************************************************************
* copy.c
*****************************************************************************
* Copyright (C) 2001, 2002 VideoLAN
* $Id: copy.c,v 1.1 2002/12/14 21:32:41 fenrir Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/aout.h>
#include <vlc/decoder.h>
#include <vlc/input.h>
#include <vlc/sout.h>
#include <stdlib.h> /* malloc(), free() */
#include <string.h> /* strdup() */
/*****************************************************************************
* Local prototypes
*****************************************************************************/
typedef struct packetizer_thread_s
{
/* Input properties */
decoder_fifo_t *p_fifo;
/* Output properties */
sout_input_t *p_sout_input;
sout_packet_format_t output_format;
} packetizer_thread_t;
static int Open ( vlc_object_t * );
static int Run ( decoder_fifo_t * );
static int InitThread ( packetizer_thread_t * );
static void PacketizeThread ( packetizer_thread_t * );
static void EndThread ( packetizer_thread_t * );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("Copy packetizer") );
set_capability( "packetizer", 0 );
set_callbacks( Open, NULL );
vlc_module_end();
/*****************************************************************************
* OpenDecoder: probe the packetizer and return score
*****************************************************************************
* Tries to launch a decoder and return score so that the interface is able
* to choose.
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
decoder_fifo_t *p_fifo = (decoder_fifo_t*) p_this;
p_fifo->pf_run = Run;
return VLC_SUCCESS;
#if 0
if( p_fifo->i_fourcc == VLC_FOURCC( 'm', 'p', 'g', 'a') )
....
#endif
}
/*****************************************************************************
* RunDecoder: this function is called just after the thread is created
*****************************************************************************/
static int Run( decoder_fifo_t *p_fifo )
{
packetizer_thread_t *p_pack;
int b_error;
msg_Info( p_fifo, "Running copy packetizer" );
if( !( p_pack = malloc( sizeof( packetizer_thread_t ) ) ) )
{
msg_Err( p_fifo, "out of memory" );
DecoderError( p_fifo );
return( -1 );
}
memset( p_pack, 0, sizeof( packetizer_thread_t ) );
p_pack->p_fifo = p_fifo;
if( InitThread( p_pack ) != 0 )
{
DecoderError( p_fifo );
return( -1 );
}
while( ( !p_pack->p_fifo->b_die )&&( !p_pack->p_fifo->b_error ) )
{
PacketizeThread( p_pack );
}
if( ( b_error = p_pack->p_fifo->b_error ) )
{
DecoderError( p_pack->p_fifo );
}
EndThread( p_pack );
if( b_error )
{
return( -1 );
}
return( 0 );
}
#define FREE( p ) if( p ) free( p ); p = NULL
/*****************************************************************************
* InitThread: initialize data before entering main loop
*****************************************************************************/
static int InitThread( packetizer_thread_t *p_pack )
{
// p_pack->output_format.i_cat = p_pack->p_fifo->i_cat;
p_pack->output_format.i_fourcc = p_pack->p_fifo->i_fourcc;
p_pack->p_sout_input =
sout_InputNew( p_pack->p_fifo,
&p_pack->output_format );
if( !p_pack->p_sout_input )
{
msg_Err( p_pack->p_fifo,
"cannot add a new stream" );
return( -1 );
}
return( 0 );
}
/*****************************************************************************
* PacketizeThread: packetize an unit (here copy a complete pes)
*****************************************************************************/
static void PacketizeThread( packetizer_thread_t *p_pack )
{
sout_buffer_t *p_sout_buffer;
pes_packet_t *p_pes;
size_t i_size;
/* **** get samples count **** */
input_ExtractPES( p_pack->p_fifo, &p_pes );
if( !p_pes )
{
p_pack->p_fifo->b_error = 1;
return;
}
i_size = p_pes->i_pes_size;
if( i_size > 0 )
{
data_packet_t *p_data;
size_t i_buffer;
p_sout_buffer =
sout_BufferNew( p_pack->p_sout_input->p_sout, i_size );
if( !p_sout_buffer )
{
p_pack->p_fifo->b_error = 1;
return;
}
/* TODO: memcpy of the pes packet */
for( i_buffer = 0, p_data = p_pes->p_first;
p_data != NULL && i_buffer < i_size;
p_data = p_data->p_next)
{
size_t i_copy;
i_copy = __MIN( p_data->p_payload_end - p_data->p_payload_start,
i_size - i_buffer );
if( i_copy > 0 )
{
p_pack->p_fifo->p_vlc->pf_memcpy( p_sout_buffer->p_buffer + i_buffer,
p_data->p_payload_start,
i_copy );
}
i_buffer += i_copy;
}
sout_InputSendBuffer( p_pack->p_sout_input,
p_sout_buffer );
}
input_DeletePES( p_pack->p_fifo->p_packets_mgt, p_pes );
}
/*****************************************************************************
* EndThread : packetizer thread destruction
*****************************************************************************/
static void EndThread ( packetizer_thread_t *p_pack)
{
if( p_pack->p_sout_input )
{
sout_InputDelete( p_pack->p_sout_input );
}
}
/*****************************************************************************
* mpeg4video.c:
*****************************************************************************
* Copyright (C) 2001, 2002 VideoLAN
* $Id: mpeg4video.c,v 1.1 2002/12/14 21:32:41 fenrir Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/aout.h>
#include <vlc/decoder.h>
#include <vlc/input.h>
#include <vlc/sout.h>
#include <stdlib.h> /* malloc(), free() */
#include <string.h> /* strdup() */
/*****************************************************************************
* Local prototypes
*****************************************************************************/
typedef struct packetizer_thread_s
{
/* Input properties */
decoder_fifo_t *p_fifo;
bit_stream_t bit_stream;
mtime_t i_dts;
/* Output properties */
sout_input_t *p_sout_input;
sout_packet_format_t output_format;
sout_buffer_t *p_vol;
int i_vop_since_vol;
} packetizer_thread_t;
static int Open ( vlc_object_t * );
static int Run ( decoder_fifo_t * );
static int InitThread ( packetizer_thread_t * );
static void PacketizeThread ( packetizer_thread_t * );
static void EndThread ( packetizer_thread_t * );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("MPEG-4 packetizer") );
set_capability( "packetizer", 50 );
set_callbacks( Open, NULL );
vlc_module_end();
#define VIDEO_OBJECT_MASK 0x01f
#define VIDEO_OBJECT_LAYER_MASK 0x00f
#define VIDEO_OBJECT_START_CODE 0x100
#define VIDEO_OBJECT_LAYER_START_CODE 0x120
#define VISUAL_OBJECT_SEQUENCE_START_CODE 0x1b0
#define VISUAL_OBJECT_SEQUENCE_END_CODE 0x1b1
#define USER_DATA_START_CODE 0x1b2
#define GROUP_OF_VOP_START_CODE 0x1b3
#define VIDEO_SESSION_ERROR_CODE 0x1b4
#define VISUAL_OBJECT_START_CODE 0x1b5
#define VOP_START_CODE 0x1b6
#define FACE_OBJECT_START_CODE 0x1ba
#define FACE_OBJECT_PLANE_START_CODE 0x1bb
#define MESH_OBJECT_START_CODE 0x1bc
#define MESH_OBJECT_PLANE_START_CODE 0x1bd
#define STILL_TEXTURE_OBJECT_START_CODE 0x1be
#define TEXTURE_SPATIAL_LAYER_START_CODE 0x1bf
#define TEXTURE_SNR_LAYER_START_CODE 0x1c0
/*****************************************************************************
* OpenDecoder: probe the packetizer and return score
*****************************************************************************
* Tries to launch a decoder and return score so that the interface is able
* to choose.
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
decoder_fifo_t *p_fifo = (decoder_fifo_t*) p_this;
p_fifo->pf_run = Run;
switch( p_fifo->i_fourcc )
{
case VLC_FOURCC( 'm', 'p', '4', 'v'):
case VLC_FOURCC( 'D', 'I', 'V', 'X'):
case VLC_FOURCC( 'd', 'i', 'v', 'x'):
case VLC_FOURCC( 'X', 'V', 'I', 'D'):
case VLC_FOURCC( 'X', 'v', 'i', 'D'):
case VLC_FOURCC( 'x', 'v', 'i', 'd'):
case VLC_FOURCC( 'D', 'X', '5', '0'):
return VLC_SUCCESS;
default:
return VLC_EGENERIC;
}
}
/*****************************************************************************
* RunDecoder: this function is called just after the thread is created
*****************************************************************************/
static int Run( decoder_fifo_t *p_fifo )
{
packetizer_thread_t *p_pack;
int b_error;
msg_Info( p_fifo, "Running MPEG-4 packetizer" );
if( !( p_pack = malloc( sizeof( packetizer_thread_t ) ) ) )
{
msg_Err( p_fifo, "out of memory" );
DecoderError( p_fifo );
return( -1 );
}
memset( p_pack, 0, sizeof( packetizer_thread_t ) );
p_pack->p_fifo = p_fifo;
if( InitThread( p_pack ) != 0 )
{
DecoderError( p_fifo );
return( -1 );
}
while( ( !p_pack->p_fifo->b_die )&&( !p_pack->p_fifo->b_error ) )
{
PacketizeThread( p_pack );
}
if( ( b_error = p_pack->p_fifo->b_error ) )
{
DecoderError( p_pack->p_fifo );
}
EndThread( p_pack );
if( b_error )
{
return( -1 );
}
return( 0 );
}
#define FREE( p ) if( p ) free( p ); p = NULL
static int CopyUntilNextStartCode( packetizer_thread_t *p_pack,
sout_buffer_t *p_sout_buffer,
int *pi_pos )
{
int i_copy = 0;
do
{
p_sout_buffer->p_buffer[(*pi_pos)++] =
GetBits( &p_pack->bit_stream, 8 );
i_copy++;
if( *pi_pos + 2048 > p_sout_buffer->i_allocated_size )
{
sout_BufferRealloc( p_pack->p_sout_input->p_sout,
p_sout_buffer,
p_sout_buffer->i_allocated_size + 50 * 1024);
}
} while( ShowBits( &p_pack->bit_stream, 24 ) != 0x01 &&
!p_pack->p_fifo->b_die && !p_pack->p_fifo->b_error );
return( i_copy );
}
static int sout_BufferAddMem( sout_instance_t *p_sout,
sout_buffer_t *p_buffer,
int i_mem,
uint8_t *p_mem )
{
if( p_buffer->i_size + i_mem >= p_buffer->i_allocated_size )
{
sout_BufferRealloc( p_sout,
p_buffer,
p_buffer->i_size + i_mem + 1024 );
}
memcpy( p_buffer->p_buffer + p_buffer->i_size, p_mem, i_mem );
p_buffer->i_size += i_mem;
return( i_mem );
}
/*****************************************************************************
* InitThread: initialize data before entering main loop
*****************************************************************************/
static int InitThread( packetizer_thread_t *p_pack )
{
p_pack->i_dts = 0;
p_pack->p_vol = NULL;
p_pack->i_vop_since_vol = 0;
p_pack->output_format.i_cat = VIDEO_ES;
p_pack->output_format.i_fourcc = VLC_FOURCC( 'm', 'p', '4', 'v' );
if( InitBitstream( &p_pack->bit_stream, p_pack->p_fifo,
NULL, NULL ) != VLC_SUCCESS )
{
msg_Err( p_pack->p_fifo, "cannot initialize bitstream" );
return -1;
}
p_pack->p_sout_input =
sout_InputNew( p_pack->p_fifo,
&p_pack->output_format );
if( !p_pack->p_sout_input )
{
msg_Err( p_pack->p_fifo,
"cannot add a new stream" );
return( -1 );
}
return( 0 );
}
/*****************************************************************************
* PacketizeThread: packetize an unit (here copy a complete pes)
*****************************************************************************/
static void PacketizeThread( packetizer_thread_t *p_pack )
{
sout_instance_t *p_sout = p_pack->p_sout_input->p_sout;
sout_buffer_t *p_frame;
uint32_t i_startcode;
/* Idea: Copy until a vop has been found
* Once a videoobject & videoobjectlayer has been found we save it
*/
p_frame = sout_BufferNew( p_sout, 20*1024 ); // FIXME
p_frame->i_size = 0;
for( ;; )
{
while( ( ( i_startcode = ShowBits( &p_pack->bit_stream, 32 ) )&0xffffff00 ) != 0x00000100 )
{
RemoveBits( &p_pack->bit_stream, 8 );
}
if( i_startcode == VISUAL_OBJECT_SEQUENCE_START_CODE )
{
msg_Dbg( p_pack->p_fifo, "<visuel_object_sequence>" );
RemoveBits32( &p_pack->bit_stream );
}
else if( i_startcode == VISUAL_OBJECT_SEQUENCE_END_CODE )
{
msg_Dbg( p_pack->p_fifo, "</visuel_object_sequence>" );
RemoveBits32( &p_pack->bit_stream );
}
else
{
msg_Dbg( p_pack->p_fifo, "start code:0x%8.8x", i_startcode );
if( ( i_startcode & ~VIDEO_OBJECT_MASK ) == VIDEO_OBJECT_START_CODE )
{
msg_Dbg( p_pack->p_fifo, "<video_object>" );
CopyUntilNextStartCode( p_pack, p_frame, &p_frame->i_size );
}
else if( ( i_startcode & ~VIDEO_OBJECT_LAYER_MASK ) == VIDEO_OBJECT_LAYER_START_CODE )
{
/* first: save it */
if( p_pack->p_vol == NULL )
{
p_pack->p_vol = sout_BufferNew( p_sout, 1024 );
}
p_pack->p_vol->i_size = 0;
CopyUntilNextStartCode( p_pack, p_pack->p_vol, &p_pack->p_vol->i_size );
p_pack->i_vop_since_vol = 0;
/* then: add it to p_frame */
sout_BufferAddMem( p_sout, p_frame,
p_pack->p_vol->i_size,
p_pack->p_vol->p_buffer );
}
else if( i_startcode == GROUP_OF_VOP_START_CODE )
{
msg_Dbg( p_pack->p_fifo, "<group_of_vop>" );
if( p_pack->p_vol && p_pack->i_vop_since_vol > 100 ) // FIXME
{
sout_BufferAddMem( p_sout, p_frame,
p_pack->p_vol->i_size,
p_pack->p_vol->p_buffer );
p_pack->i_vop_since_vol = 0;
}
CopyUntilNextStartCode( p_pack, p_frame, &p_frame->i_size );
}
else if( i_startcode == VOP_START_CODE )
{
msg_Dbg( p_pack->p_fifo, "<vop>" );
if( p_pack->p_vol && p_pack->i_vop_since_vol > 100 ) // FIXME
{
sout_BufferAddMem( p_sout, p_frame,
p_pack->p_vol->i_size,
p_pack->p_vol->p_buffer );
p_pack->i_vop_since_vol = 0;
}
CopyUntilNextStartCode( p_pack, p_frame, &p_frame->i_size );
p_pack->i_vop_since_vol++;
break;
}
else
{
msg_Dbg( p_pack->p_fifo, "unknown start code" );
CopyUntilNextStartCode( p_pack, p_frame, &p_frame->i_size );
}
}
}
p_frame->i_length = 1000000 / 25;
p_frame->i_bitrate= 0;
p_frame->i_dts = p_pack->i_dts;
p_frame->i_pts = p_pack->i_dts;
p_pack->i_dts += 1000000 / 25;
sout_InputSendBuffer( p_pack->p_sout_input, p_frame );
}
/*****************************************************************************
* EndThread : packetizer thread destruction
*****************************************************************************/
static void EndThread ( packetizer_thread_t *p_pack)
{
if( p_pack->p_sout_input )
{
sout_InputDelete( p_pack->p_sout_input );
}
}
/*****************************************************************************
* mpegaudio.c
*****************************************************************************
* Copyright (C) 2001, 2002 VideoLAN
* $Id: mpegaudio.c,v 1.1 2002/12/14 21:32:41 fenrir Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/aout.h>
#include <vlc/decoder.h>
#include <vlc/input.h>
#include <vlc/sout.h>
#include <stdlib.h> /* malloc(), free() */
#include <string.h> /* strdup() */
#include "codecs.h" /* WAVEFORMATEX BITMAPINFOHEADER */
/*****************************************************************************
* Local prototypes
*****************************************************************************/
typedef struct packetizer_s
{
/* Input properties */
decoder_fifo_t *p_fifo;
bit_stream_t bit_stream;
/* Output properties */
sout_input_t *p_sout_input;
sout_packet_format_t output_format;
uint64_t i_samplescount;
uint32_t i_samplespersecond;
} packetizer_t;
static int Open ( vlc_object_t * );
static int Run ( decoder_fifo_t * );
static int InitThread ( packetizer_t * );
static void PacketizeThread ( packetizer_t * );
static void EndThread ( packetizer_t * );
#define FREE( p ) if( p ) free( p ); p = NULL
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("MPEG-I/II audio packetizer") );
set_capability( "packetizer", 50 );
set_callbacks( Open, NULL );
vlc_module_end();
static int mpegaudio_bitrate[2][3][16] =
{
{
/* v1 l1 */
{ 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0},
/* v1 l2 */
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0},
/* v1 l3 */
{ 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0}
},
{
/* v2 l1 */
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0},
/* v2 l2 */
{ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0},
/* v2 l3 */
{ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0}
}
};
static int mpegaudio_samplerate[2][4] = /* version 1 then 2 */
{
{ 44100, 48000, 32000, 0 },
{ 22050, 24000, 16000, 0 }
};
/*****************************************************************************
* OpenDecoder: probe the packetizer and return score
*****************************************************************************
* Tries to launch a decoder and return score so that the interface is able
* to choose.
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
decoder_fifo_t *p_fifo = (decoder_fifo_t*) p_this;
if( p_fifo->i_fourcc != VLC_FOURCC( 'm', 'p', 'g', 'a') )
{
return VLC_EGENERIC;
}
p_fifo->pf_run = Run;
return VLC_SUCCESS;
}
/*****************************************************************************
* RunDecoder: this function is called just after the thread is created
*****************************************************************************/
static int Run( decoder_fifo_t *p_fifo )
{
packetizer_t *p_pack;
int b_error;
msg_Info( p_fifo, "Running mpegaudio packetizer" );
if( !( p_pack = malloc( sizeof( packetizer_t ) ) ) )
{
msg_Err( p_fifo, "out of memory" );
DecoderError( p_fifo );
return( -1 );
}
memset( p_pack, 0, sizeof( packetizer_t ) );
p_pack->p_fifo = p_fifo;
if( InitThread( p_pack ) != 0 )
{
DecoderError( p_fifo );
return( -1 );
}
while( ( !p_pack->p_fifo->b_die )&&( !p_pack->p_fifo->b_error ) )
{
PacketizeThread( p_pack );
}
if( ( b_error = p_pack->p_fifo->b_error ) )
{
DecoderError( p_pack->p_fifo );
}
EndThread( p_pack );
FREE( p_pack );
if( b_error )
{
return( -1 );
}
return( 0 );
}
/*****************************************************************************
* InitThread: initialize data before entering main loop
*****************************************************************************/
static int InitThread( packetizer_t *p_pack )
{
p_pack->output_format.i_cat = AUDIO_ES;
p_pack->output_format.i_fourcc = p_pack->p_fifo->i_fourcc;
p_pack->output_format.p_format = NULL;
p_pack->p_sout_input = NULL;
p_pack->i_samplescount = 0;
p_pack->i_samplespersecond = 0;
if( InitBitstream( &p_pack->bit_stream, p_pack->p_fifo,
NULL, NULL ) != VLC_SUCCESS )
{
msg_Err( p_pack->p_fifo, "cannot initialize bitstream" );
return -1;
}
return( 0 );
}
/*****************************************************************************
* PacketizeThread: packetize an unit (here copy a complete pes)
*****************************************************************************/
static void PacketizeThread( packetizer_t *p_pack )
{
sout_buffer_t *p_sout_buffer;
size_t i_size;
uint32_t i_sync, i_header;
int i_version, i_layer;
int i_channels, i_samplerate, i_bitrate;
int i_samplesperframe, i_framelength;
int i_skip = 0;
/* search a valid start code */
for( ;; )
{
int i_crc, i_bitrate_index, i_samplerate_index;
int i_padding, i_extention, i_mode, i_modeext, i_copyright;
int i_original, i_emphasis;
RealignBits( &p_pack->bit_stream );
while( ShowBits( &p_pack->bit_stream, 12 ) != 0x0fff &&
!p_pack->p_fifo->b_die && !p_pack->p_fifo->b_error )
{
//msg_Warn( p_pack->p_fifo, "trash..." );
RemoveBits( &p_pack->bit_stream, 8 );
i_skip++;
}
if( p_pack->p_fifo->b_die || p_pack->p_fifo->b_error )
{
return;
}
i_sync = GetBits( &p_pack->bit_stream, 12 );
i_header = ShowBits( &p_pack->bit_stream, 20 );
i_version = 1 - GetBits( &p_pack->bit_stream, 1 );
i_layer = 3 - GetBits( &p_pack->bit_stream, 2 );
i_crc = 1 - GetBits( &p_pack->bit_stream, 1 );
i_bitrate_index = GetBits( &p_pack->bit_stream, 4 );
i_samplerate_index = GetBits( &p_pack->bit_stream, 2 );
i_padding = GetBits( &p_pack->bit_stream, 1 );
i_extention = GetBits( &p_pack->bit_stream, 1 );
i_mode = GetBits( &p_pack->bit_stream, 2 );
i_modeext = GetBits( &p_pack->bit_stream, 2 );
i_copyright = GetBits( &p_pack->bit_stream, 1 );
i_original = GetBits( &p_pack->bit_stream, 1 );
i_emphasis = GetBits( &p_pack->bit_stream, 2 );
if( i_layer != 3 &&
i_bitrate_index > 0x00 && i_bitrate_index < 0x0f &&
i_samplerate_index != 0x03 &&
i_emphasis != 0x02 )
{
i_channels = ( i_mode == 3 ) ? 1 : 2;
i_bitrate = mpegaudio_bitrate[i_version][i_layer][i_bitrate_index];
i_samplerate = mpegaudio_samplerate[i_version][i_samplerate_index];
switch( i_layer )
{
case 0:
i_framelength = ( ( i_version ? 6000 : 12000 ) *
i_bitrate / i_samplerate + i_padding ) * 4;
break;
case 1:
case 2:
i_framelength = ( i_version ? 72000 : 144000 ) *
i_bitrate / i_samplerate + i_padding;
break;
default:
i_framelength = 0;
}
switch( i_layer )
{
case 0:
i_samplesperframe = 384;
break;
case 1:
i_samplesperframe = 1152;
break;
case 2:
i_samplesperframe = i_version ? 1152 : 576;
break;
}
break;
}
}
if( !p_pack->p_sout_input )
{
/* add a input for the stream ouput */
WAVEFORMATEX *p_wf;
p_wf = malloc( sizeof( WAVEFORMATEX ) );
p_pack->output_format.p_format = (void*)p_wf;
p_wf->wFormatTag = WAVE_FORMAT_MPEG;
p_wf->nChannels = i_channels;
p_wf->nSamplesPerSec = i_samplerate;
p_wf->nAvgBytesPerSec = 0;
p_wf->nBlockAlign = 1;
p_wf->wBitsPerSample = 0;
p_wf->cbSize = 0; // FIXME there are more field for mpegaudio
p_pack->p_sout_input =
sout_InputNew( p_pack->p_fifo,
&p_pack->output_format );
if( !p_pack->p_sout_input )
{
msg_Err( p_pack->p_fifo,
"cannot add a new stream" );
p_pack->p_fifo->b_error = 1;
return;
}
msg_Dbg( p_pack->p_fifo,
"v:%d l:%d channels:%d samplerate:%d bitrate:%d size:%d",
i_version, i_layer, i_channels, i_samplerate,
i_bitrate, i_framelength );
}
i_size = __MAX( i_framelength, 4 );
// msg_Dbg( p_pack->p_fifo, "frame length %d b", i_size );
p_sout_buffer =
sout_BufferNew( p_pack->p_sout_input->p_sout, i_size );
if( !p_sout_buffer )
{
p_pack->p_fifo->b_error = 1;
return;
}
p_sout_buffer->p_buffer[0] = ( i_sync >> 4 )&0xff;
p_sout_buffer->p_buffer[1] =
( ( i_sync << 4 )&0xf0 ) | ( ( i_header >> 16 )&0x0f );
p_sout_buffer->p_buffer[2] = ( i_header >> 8 )&0xff;
p_sout_buffer->p_buffer[3] = ( i_header )&0xff;
p_sout_buffer->i_bitrate = i_bitrate;
p_sout_buffer->i_pts =
p_sout_buffer->i_dts =
(uint64_t)1000000 *
(uint64_t)p_pack->i_samplescount /
(uint64_t)i_samplerate;
p_sout_buffer->i_length =
(uint64_t)1000000 *
(uint64_t)i_samplesperframe /
(uint64_t)i_samplerate;
p_pack->i_samplescount += i_samplesperframe;
/* we are already aligned */
GetChunk( &p_pack->bit_stream,
p_sout_buffer->p_buffer + 4,
i_size - 4 );
sout_InputSendBuffer( p_pack->p_sout_input,
p_sout_buffer );
}
/*****************************************************************************
* EndThread : packetizer thread destruction
*****************************************************************************/
static void EndThread ( packetizer_t *p_pack)
{
if( p_pack->p_sout_input )
{
sout_InputDelete( p_pack->p_sout_input );
}
}
/*****************************************************************************
* mpegvideo.c
*****************************************************************************
* Copyright (C) 2001, 2002 VideoLAN
* $Id: mpegvideo.c,v 1.1 2002/12/14 21:32:41 fenrir Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/decoder.h>
#include <vlc/input.h>
#include <vlc/sout.h>
#include "codecs.h" /* WAVEFORMATEX BITMAPINFOHEADER */
#include <stdlib.h> /* malloc(), free() */
#include <string.h> /* strdup() */
/*****************************************************************************
* Local prototypes
*****************************************************************************/
typedef struct packetizer_s
{
/* Input properties */
decoder_fifo_t *p_fifo;
bit_stream_t bit_stream;
/* Output properties */
sout_input_t *p_sout_input;
sout_packet_format_t output_format;
mtime_t i_last_dts;
mtime_t i_last_ref_pts;
double d_frame_rate;
uint8_t p_sequence_header[150];
int i_sequence_header_length;
int i_last_sequence_header;
} packetizer_t;
static int Open ( vlc_object_t * );
static int Run ( decoder_fifo_t * );
static int InitThread ( packetizer_t * );
static void PacketizeThread ( packetizer_t * );
static void EndThread ( packetizer_t * );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("MPEG-I/II video packetizer") );
set_capability( "packetizer", 50 );
set_callbacks( Open, NULL );
vlc_module_end();
/*****************************************************************************
* OpenDecoder: probe the packetizer and return score
*****************************************************************************
* Tries to launch a decoder and return score so that the interface is able
* to choose.
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
decoder_fifo_t *p_fifo = (decoder_fifo_t*) p_this;
if( p_fifo->i_fourcc != VLC_FOURCC( 'm', 'p', 'g', 'v') )
{
return VLC_EGENERIC;
}
p_fifo->pf_run = Run;
return VLC_SUCCESS;
}
/*****************************************************************************
* RunDecoder: this function is called just after the thread is created
*****************************************************************************/
static int Run( decoder_fifo_t *p_fifo )
{
packetizer_t *p_pack;
int b_error;
msg_Info( p_fifo, "Running mpegvideo packetizer" );
if( !( p_pack = malloc( sizeof( packetizer_t ) ) ) )
{
msg_Err( p_fifo, "out of memory" );
DecoderError( p_fifo );
return( -1 );
}
memset( p_pack, 0, sizeof( packetizer_t ) );
p_pack->p_fifo = p_fifo;
if( InitThread( p_pack ) != 0 )
{
DecoderError( p_fifo );
return( -1 );
}
while( ( !p_pack->p_fifo->b_die )&&( !p_pack->p_fifo->b_error ) )
{
PacketizeThread( p_pack );
}
if( ( b_error = p_pack->p_fifo->b_error ) )
{
DecoderError( p_pack->p_fifo );
}
EndThread( p_pack );
if( p_pack )
{
free( p_pack );
}
if( b_error )
{
return( -1 );
}
return( 0 );
}
/*****************************************************************************
* InitThread: initialize data before entering main loop
*****************************************************************************/
static int InitThread( packetizer_t *p_pack )
{
p_pack->output_format.i_cat = VIDEO_ES;
p_pack->output_format.i_fourcc = p_pack->p_fifo->i_fourcc;
p_pack->p_sout_input = NULL;
if( InitBitstream( &p_pack->bit_stream, p_pack->p_fifo,
NULL, NULL ) != VLC_SUCCESS )
{
msg_Err( p_pack->p_fifo, "cannot initialize bitstream" );
return -1;
}
return( 0 );
}
/* from ISO 13818-2 */
/* converting frame_rate_code to frame_rate */
static const double pd_frame_rates[16] =
{
0, 24000/1001, 24, 25, 30000/1001, 30, 50, 60000/1001, 60,
0, 0, 0, 0, 0, 0, 0
};
static int CopyUntilNextStartCode( packetizer_t *p_pack,
sout_buffer_t *p_sout_buffer,
int *pi_pos )
{
int i_copy = 0;
do
{
p_sout_buffer->p_buffer[(*pi_pos)++] =
GetBits( &p_pack->bit_stream, 8 );
i_copy++;
if( *pi_pos + 2048 > p_sout_buffer->i_allocated_size )
{
sout_BufferRealloc( p_pack->p_sout_input->p_sout,
p_sout_buffer,
p_sout_buffer->i_allocated_size + 50 * 1024);
}
} while( ShowBits( &p_pack->bit_stream, 24 ) != 0x01 &&
!p_pack->p_fifo->b_die && !p_pack->p_fifo->b_error );
return( i_copy );
}
/*****************************************************************************
* PacketizeThread: packetize an unit (here copy a complete pes)
*****************************************************************************/
static void PacketizeThread( packetizer_t *p_pack )
{
sout_buffer_t *p_sout_buffer = NULL;
int32_t i_pos;
int i_temporal_ref = 0;
int i_skipped;
if( !p_pack->p_sout_input )
{
byte_t p_temp[512];/* 150 bytes is the maximal size
of a sequence_header + sequence_extension */
int i_frame_rate_code;
BITMAPINFOHEADER *p_bih;
p_bih = malloc( sizeof( BITMAPINFOHEADER ) );
p_pack->output_format.p_format = (void*)p_bih;
/* skip data until we find a sequence_header_code */
/* TODO: store skipped somewhere so can send it to the mux
* after the input is created */
i_skipped = 0;
while( ShowBits( &p_pack->bit_stream, 32 ) != 0x1B3 )
{
RemoveBits( &p_pack->bit_stream, 8 );
i_skipped++;
}
msg_Warn( p_pack->p_fifo, "sequence_header_code found (%d skipped)",
i_skipped );
/* copy the start_code */
i_pos = 0;
GetChunk( &p_pack->bit_stream, p_temp, 4 ); i_pos += 4;
p_bih->biSize = sizeof( BITMAPINFOHEADER );
/* horizontal_size_value */
p_bih->biWidth = ShowBits( &p_pack->bit_stream, 12 );
/* vertical_size_value */
p_bih->biHeight = ShowBits( &p_pack->bit_stream, 24 ) & 0xFFF;
/* frame_rate_code */
i_frame_rate_code = ShowBits( &p_pack->bit_stream, 32 ) & 0xF;
p_bih->biPlanes = 1;
p_bih->biBitCount = 0;
p_bih->biCompression = 0x6D706732; /* mpg2 */
p_bih->biSizeImage = 0;
p_bih->biXPelsPerMeter = 0;
p_bih->biYPelsPerMeter = 0;
p_bih->biClrUsed = 0;
p_bih->biClrImportant = 0;
/* copy headers */
GetChunk( &p_pack->bit_stream, p_temp + i_pos, 7 ); i_pos += 7;
/* intra_quantiser_matrix [non_intra_quantiser_matrix] */
if( ShowBits( &p_pack->bit_stream, 7 ) & 0x1 )
{
GetChunk( &p_pack->bit_stream, p_temp + i_pos, 64 ); i_pos += 64;
if( ShowBits( &p_pack->bit_stream, 8 ) & 0x1 )
{
GetChunk( &p_pack->bit_stream, p_temp + i_pos, 65); i_pos += 65;
}
}
/* non_intra_quantiser_matrix */
else if( ShowBits( &p_pack->bit_stream, 8 ) & 0x1 )
{
GetChunk( &p_pack->bit_stream, p_temp + i_pos, 65); i_pos += 65;
}
/* nothing */
else
{
GetChunk( &p_pack->bit_stream, p_temp + i_pos, 1 ); i_pos += 1;
}
/* sequence_extension (10 bytes) */
if( ShowBits( &p_pack->bit_stream, 32 ) != 0x1B5 )
msg_Dbg( p_pack->p_fifo, "ARRGG no extension_start_code" );
else
GetChunk( &p_pack->bit_stream, p_temp + i_pos, 10 ); i_pos += 10;
/* remember sequence_header and sequence_extention */
memcpy( p_pack->p_sequence_header, p_temp, i_pos );
p_pack->i_sequence_header_length = i_pos;
p_pack->d_frame_rate = pd_frame_rates[i_frame_rate_code];
msg_Warn( p_pack->p_fifo,
"creating input (image size %dx%d, frame rate %.2f)",
p_bih->biWidth, p_bih->biHeight, p_pack->d_frame_rate );
/* now we have informations to create the input */
p_pack->p_sout_input = sout_InputNew( p_pack->p_fifo,
&p_pack->output_format );
if( !p_pack->p_sout_input )
{
msg_Err( p_pack->p_fifo, "cannot add a new stream" );
return;
}
p_sout_buffer = sout_BufferNew( p_pack->p_sout_input->p_sout,
100 * 1024 );
p_sout_buffer->i_size = i_pos;
memcpy( p_sout_buffer->p_buffer, p_temp, i_pos );
}
else
{
p_sout_buffer =
sout_BufferNew( p_pack->p_sout_input->p_sout, 100 * 1024 );
i_pos = 0;
}
p_pack->i_last_sequence_header++;
for( ;; )
{
uint32_t i_code;
if( p_pack->p_fifo->b_die || p_pack->p_fifo->b_error )
{
break;
}
i_code = ShowBits( &p_pack->bit_stream, 32 );
if( i_code == 0x1B8 ) /* GOP */
{
/* usefull for bad MPEG-1 : repeat the sequence_header
every second */
if( p_pack->i_last_sequence_header > (int) p_pack->d_frame_rate )
{
memcpy( p_sout_buffer->p_buffer + i_pos,
p_pack->p_sequence_header,
p_pack->i_sequence_header_length );
i_pos += p_pack->i_sequence_header_length;
p_pack->i_last_sequence_header = 0;
}
p_pack->i_last_ref_pts =
p_pack->i_last_dts + 40000; /* FIXME */
CopyUntilNextStartCode( p_pack, p_sout_buffer, &i_pos );
}
else if( i_code == 0x100 ) /* Picture */
{
/* picture_start_code */
GetChunk( &p_pack->bit_stream,
p_sout_buffer->p_buffer + i_pos, 4 ); i_pos += 4;
i_temporal_ref = ShowBits( &p_pack->bit_stream, 10 );
CopyUntilNextStartCode( p_pack, p_sout_buffer, &i_pos );
break;
}
else
{
if( i_code == 0x1B3 )
{
p_pack->i_last_sequence_header = 0;
}
CopyUntilNextStartCode( p_pack, p_sout_buffer, &i_pos );
}
}
sout_BufferRealloc( p_pack->p_sout_input->p_sout,
p_sout_buffer, i_pos );
p_sout_buffer->i_size = i_pos;
p_sout_buffer->i_dts = p_pack->i_last_dts;
p_sout_buffer->i_pts = p_pack->i_last_ref_pts +
i_temporal_ref * (mtime_t)( 1000000 / p_pack->d_frame_rate );
p_sout_buffer->i_length = (uint64_t)1000000 / p_pack->d_frame_rate;
p_sout_buffer->i_bitrate = (int)( 8 * i_pos * p_pack->d_frame_rate );
// msg_Dbg( p_pack->p_fifo, "frame length %d b", i_pos );
sout_InputSendBuffer( p_pack->p_sout_input, p_sout_buffer );
p_pack->i_last_dts += (mtime_t)( 1000000 / p_pack->d_frame_rate );
}
/*****************************************************************************
* EndThread : packetizer thread destruction
*****************************************************************************/
static void EndThread ( packetizer_t *p_pack)
{
if( p_pack->p_sout_input )
{
sout_InputDelete( p_pack->p_sout_input );
}
}
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* input_dec.c: Functions for the management of decoders * input_dec.c: Functions for the management of decoders
***************************************************************************** *****************************************************************************
* Copyright (C) 1999-2001 VideoLAN * Copyright (C) 1999-2001 VideoLAN
* $Id: input_dec.c,v 1.52 2002/12/06 16:34:08 sam Exp $ * $Id: input_dec.c,v 1.53 2002/12/14 21:32:42 fenrir Exp $
* *
* Authors: Christophe Massiot <massiot@via.ecp.fr> * Authors: Christophe Massiot <massiot@via.ecp.fr>
* *
...@@ -44,6 +44,7 @@ static void DeleteDecoderFifo( decoder_fifo_t * ); ...@@ -44,6 +44,7 @@ static void DeleteDecoderFifo( decoder_fifo_t * );
decoder_fifo_t * input_RunDecoder( input_thread_t * p_input, decoder_fifo_t * input_RunDecoder( input_thread_t * p_input,
es_descriptor_t * p_es ) es_descriptor_t * p_es )
{ {
char *psz_sout;
decoder_fifo_t *p_fifo; decoder_fifo_t *p_fifo;
int i_priority; int i_priority;
...@@ -56,8 +57,23 @@ decoder_fifo_t * input_RunDecoder( input_thread_t * p_input, ...@@ -56,8 +57,23 @@ decoder_fifo_t * input_RunDecoder( input_thread_t * p_input,
return NULL; return NULL;
} }
p_fifo->p_module = NULL;
/* If we are in sout mode, search first for packetizer module then
* codec to do transcoding */
psz_sout = config_GetPsz( p_input, "sout" );
if( psz_sout != NULL && *psz_sout != 0 )
{
p_fifo->p_module = module_Need( p_fifo, "packetizer", "$packetizer" );
}
/* default Get a suitable decoder module */
if( p_fifo->p_module == NULL )
{
p_fifo->p_module = module_Need( p_fifo, "decoder", "$codec" );
}
#if 0
/* Get a suitable module */ /* Get a suitable module */
p_fifo->p_module = module_Need( p_fifo, "decoder", "$codec" ); p_fifo->p_module = module_Need( p_fifo, "decoder", "$codec" );
#endif
if( p_fifo->p_module == NULL ) if( p_fifo->p_module == NULL )
{ {
msg_Err( p_fifo, "no suitable decoder module for fourcc `%4.4s'", msg_Err( p_fifo, "no suitable decoder module for fourcc `%4.4s'",
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* libvlc.h: main libvlc header * libvlc.h: main libvlc header
***************************************************************************** *****************************************************************************
* Copyright (C) 1998-2002 VideoLAN * Copyright (C) 1998-2002 VideoLAN
* $Id: libvlc.h,v 1.28 2002/12/09 00:52:42 babal Exp $ * $Id: libvlc.h,v 1.29 2002/12/14 21:32:42 fenrir Exp $
* *
* Authors: Vincent Seguin <seguin@via.ecp.fr> * Authors: Vincent Seguin <seguin@via.ecp.fr>
* Samuel Hocevar <sam@zoy.org> * Samuel Hocevar <sam@zoy.org>
...@@ -283,6 +283,17 @@ ...@@ -283,6 +283,17 @@
#define SOUT_TEXT N_("choose a stream output") #define SOUT_TEXT N_("choose a stream output")
#define SOUT_LONGTEXT N_( \ #define SOUT_LONGTEXT N_( \
"Empty if no stream output.") "Empty if no stream output.")
#define PACKETIZER_TEXT N_("choose prefered packetizer list")
#define PACKETIZER_LONGTEXT N_( \
"This allows you to select the order in which vlc will choose its " \
"packetizers." )
#define MUX_TEXT N_("mux module")
#define MUX_LONGTEXT N_( \
"This is a legacy entry to let you configure mux modules")
#define ACCESS_OUTPUT_TEXT N_("access output module")
#define ACCESS_OUTPUT_LONGTEXT N_( \
"This is a legacy entry to let you configure access output modules")
#define MMX_TEXT N_("enable CPU MMX support") #define MMX_TEXT N_("enable CPU MMX support")
#define MMX_LONGTEXT N_( \ #define MMX_LONGTEXT N_( \
...@@ -475,6 +486,13 @@ vlc_module_begin(); ...@@ -475,6 +486,13 @@ vlc_module_begin();
add_category_hint( N_("Decoders"), NULL ); add_category_hint( N_("Decoders"), NULL );
add_module( "codec", "decoder", NULL, NULL, CODEC_TEXT, CODEC_LONGTEXT ); add_module( "codec", "decoder", NULL, NULL, CODEC_TEXT, CODEC_LONGTEXT );
/* Stream output */
add_category_hint( N_("Stream output"), NULL );
add_module( "packetizer", "packetizer", NULL, NULL, PACKETIZER_TEXT, PACKETIZER_LONGTEXT );
add_module( "mux", "mux", NULL, NULL, MUX_TEXT, MUX_LONGTEXT );
add_module( "access_output", "access_output", NULL, NULL, ACCESS_OUTPUT_TEXT, ACCESS_OUTPUT_LONGTEXT );
add_string( "sout", NULL, NULL, SOUT_TEXT, SOUT_LONGTEXT );
/* CPU options */ /* CPU options */
add_category_hint( N_("CPU"), NULL ); add_category_hint( N_("CPU"), NULL );
#if defined( __i386__ ) #if defined( __i386__ )
...@@ -504,7 +522,6 @@ vlc_module_begin(); ...@@ -504,7 +522,6 @@ vlc_module_begin();
add_module( "memcpy", "memcpy", NULL, NULL, MEMCPY_TEXT, MEMCPY_LONGTEXT ); add_module( "memcpy", "memcpy", NULL, NULL, MEMCPY_TEXT, MEMCPY_LONGTEXT );
add_module( "access", "access", NULL, NULL, ACCESS_TEXT, ACCESS_LONGTEXT ); add_module( "access", "access", NULL, NULL, ACCESS_TEXT, ACCESS_LONGTEXT );
add_module( "demux", "demux", NULL, NULL, DEMUX_TEXT, DEMUX_LONGTEXT ); add_module( "demux", "demux", NULL, NULL, DEMUX_TEXT, DEMUX_LONGTEXT );
add_string( "sout", NULL, NULL, SOUT_TEXT, SOUT_LONGTEXT );
#if defined(WIN32) #if defined(WIN32)
add_bool( "fast-mutex", 0, NULL, FAST_MUTEX_TEXT, FAST_MUTEX_LONGTEXT ); add_bool( "fast-mutex", 0, NULL, FAST_MUTEX_TEXT, FAST_MUTEX_LONGTEXT );
......
...@@ -2,9 +2,11 @@ ...@@ -2,9 +2,11 @@
* stream_output.c : stream output module * stream_output.c : stream output module
***************************************************************************** *****************************************************************************
* Copyright (C) 2002 VideoLAN * Copyright (C) 2002 VideoLAN
* $Id: stream_output.c,v 1.5 2002/11/11 14:39:12 sam Exp $ * $Id: stream_output.c,v 1.6 2002/12/14 21:32:42 fenrir Exp $
* *
* Authors: Christophe Massiot <massiot@via.ecp.fr> * Authors: Christophe Massiot <massiot@via.ecp.fr>
* Laurent Aimar <fenrir@via.ecp.fr>
* Erioc Petit <titer@videolan.org>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -31,7 +33,7 @@ ...@@ -31,7 +33,7 @@
#include <vlc/vlc.h> #include <vlc/vlc.h>
#include <vlc/sout.h> #include <vlc/sout.h>
#undef DEBUG_BUFFER
/***************************************************************************** /*****************************************************************************
* Local prototypes * Local prototypes
*****************************************************************************/ *****************************************************************************/
...@@ -53,13 +55,16 @@ sout_instance_t * __sout_NewInstance ( vlc_object_t *p_parent, ...@@ -53,13 +55,16 @@ sout_instance_t * __sout_NewInstance ( vlc_object_t *p_parent,
return NULL; return NULL;
} }
p_sout->psz_dest = psz_dest; p_sout->psz_dest = strdup( psz_dest );
if ( InitInstance( p_sout ) == -1 ) if ( InitInstance( p_sout ) == -1 )
{ {
vlc_object_destroy( p_sout ); vlc_object_destroy( p_sout );
return NULL; return NULL;
} }
vlc_object_attach( p_sout, p_parent );
return p_sout; return p_sout;
} }
...@@ -72,6 +77,15 @@ static int InitInstance( sout_instance_t * p_sout ) ...@@ -72,6 +77,15 @@ static int InitInstance( sout_instance_t * p_sout )
/* This code is identical to input.c:InitThread. FIXME : factorize it ? */ /* This code is identical to input.c:InitThread. FIXME : factorize it ? */
char * psz_parser = p_sout->psz_dest; char * psz_parser = p_sout->psz_dest;
p_sout->psz_access = "";
p_sout->psz_mux = "";
p_sout->psz_name = "";
p_sout->p_access = NULL;
p_sout->p_mux = NULL;
p_sout->i_nb_inputs = 0;
p_sout->pp_inputs = NULL;
vlc_mutex_init( p_sout, &p_sout->lock );
/* Skip the plug-in names */ /* Skip the plug-in names */
while( *psz_parser && *psz_parser != ':' ) while( *psz_parser && *psz_parser != ':' )
{ {
...@@ -170,6 +184,9 @@ static int InitInstance( sout_instance_t * p_sout ) ...@@ -170,6 +184,9 @@ static int InitInstance( sout_instance_t * p_sout )
return -1; return -1;
} }
p_sout->i_nb_inputs = 0;
p_sout->pp_inputs = NULL;
return 0; return 0;
} }
...@@ -181,8 +198,343 @@ void sout_DeleteInstance( sout_instance_t * p_sout ) ...@@ -181,8 +198,343 @@ void sout_DeleteInstance( sout_instance_t * p_sout )
{ {
/* Unlink object */ /* Unlink object */
vlc_object_detach( p_sout ); vlc_object_detach( p_sout );
if( p_sout->p_mux )
{
module_Unneed( p_sout, p_sout->p_mux );
}
if( p_sout->p_access )
{
module_Unneed( p_sout, p_sout->p_access );
}
vlc_mutex_destroy( &p_sout->lock );
/* Free structure */ /* Free structure */
vlc_object_destroy( p_sout ); vlc_object_destroy( p_sout );
} }
/*****************************************************************************
*
*****************************************************************************/
sout_input_t *__sout_InputNew( vlc_object_t *p_this,
sout_packet_format_t *p_format )
{
sout_instance_t *p_sout = NULL;
sout_input_t *p_input;
int i_try;
/* search an stream output */
for( i_try = 0; i_try < 200; i_try++ )
{
p_sout = vlc_object_find( p_this, VLC_OBJECT_SOUT, FIND_ANYWHERE );
if( !p_sout )
{
msleep( 100*1000 );
msg_Dbg( p_this, "waiting for sout" );
}
else
{
break;
}
}
if( !p_sout )
{
msg_Err( p_this, "cannot find any stream ouput" );
return( NULL );
}
msg_Dbg( p_sout, "adding a new input" );
/* create a new sout input */
p_input = malloc( sizeof( sout_input_t ) );
p_input->p_sout = p_sout;
vlc_mutex_init( p_sout, &p_input->lock );
memcpy( &p_input->input_format,
p_format,
sizeof( sout_packet_format_t ) );
p_input->p_fifo = sout_FifoCreate( p_sout );
p_input->p_mux_data = NULL;
/* add this new one to p_sout */
vlc_mutex_lock( &p_sout->lock );
if( p_sout->i_nb_inputs == 0 )
{
p_sout->pp_inputs = malloc( sizeof( sout_input_t * ) );
}
else
{
p_sout->pp_inputs = realloc( p_sout->pp_inputs,
sizeof( sout_input_t * ) *
( p_sout->i_nb_inputs + 1 ) );
}
p_sout->pp_inputs[p_sout->i_nb_inputs] = p_input;
p_sout->i_nb_inputs++;
if( p_sout->pf_mux_addstream( p_sout, p_input ) < 0 )
{
/* vlc_mutex_unlock( &p_sout->lock ); */
msg_Err( p_sout, "cannot add this stream" );
/* FIXME FIXME */
}
vlc_mutex_unlock( &p_sout->lock );
vlc_object_release( p_sout );
return( p_input );
}
int sout_InputDelete( sout_input_t *p_input )
{
sout_instance_t *p_sout = p_input->p_sout;
int i_input;
vlc_mutex_lock( &p_sout->lock );
for( i_input = 0; i_input < p_sout->i_nb_inputs; i_input++ )
{
if( p_sout->pp_inputs[i_input] == p_input )
{
break;
}
}
if( i_input >= p_sout->i_nb_inputs )
{
msg_Err( p_sout, "cannot find input to delete" );
return( -1 );
}
msg_Dbg( p_sout, "removing an input" );
if( p_sout->pf_mux_delstream( p_sout, p_input ) < 0 )
{
msg_Err( p_sout, "cannot del this stream" );
/* FIXME FIXME */
}
/* remove the entry */
if( p_sout->i_nb_inputs > 1 )
{
memmove( &p_sout->pp_inputs[i_input],
&p_sout->pp_inputs[i_input+1],
(p_sout->i_nb_inputs - i_input - 1) * sizeof( sout_input_t*) );
}
else
{
free( p_sout->pp_inputs );
}
p_sout->i_nb_inputs--;
/* FIXME --> send message to mux to declare this stream removing */
sout_FifoDestroy( p_sout, p_input->p_fifo );
vlc_mutex_destroy( &p_input->lock );
free( p_input );
if( p_sout->i_nb_inputs == 0 )
{
msg_Warn( p_sout, "no more input stream" );
}
vlc_mutex_unlock( &p_sout->lock );
return( 0 );
}
int sout_InputSendBuffer( sout_input_t *p_input, sout_buffer_t *p_buffer )
{
/* msg_Dbg( p_input->p_sout,
"send buffer, size:%d", p_buffer->i_size ); */
sout_FifoPut( p_input->p_fifo, p_buffer );
vlc_mutex_lock( &p_input->p_sout->lock );
p_input->p_sout->pf_mux( p_input->p_sout );
vlc_mutex_unlock( &p_input->p_sout->lock );
return( 0 );
}
sout_fifo_t *sout_FifoCreate( sout_instance_t *p_sout )
{
sout_fifo_t *p_fifo;
if( !( p_fifo = malloc( sizeof( sout_fifo_t ) ) ) )
{
return( NULL );
}
vlc_mutex_init( p_sout, &p_fifo->lock );
vlc_cond_init ( p_sout, &p_fifo->wait );
p_fifo->i_depth = 0;
p_fifo->p_first = NULL;
p_fifo->pp_last = &p_fifo->p_first;
return( p_fifo );
}
void sout_FifoFree( sout_instance_t *p_sout, sout_fifo_t *p_fifo )
{
sout_buffer_t *p_buffer;
vlc_mutex_lock( &p_fifo->lock );
p_buffer = p_fifo->p_first;
while( p_buffer )
{
sout_buffer_t *p_next;
p_next = p_buffer->p_next;
sout_BufferDelete( p_sout, p_buffer );
p_buffer = p_next;
}
vlc_mutex_unlock( &p_fifo->lock );
return;
}
void sout_FifoDestroy( sout_instance_t *p_sout, sout_fifo_t *p_fifo )
{
sout_FifoFree( p_sout, p_fifo );
vlc_mutex_destroy( &p_fifo->lock );
vlc_cond_destroy ( &p_fifo->wait );
free( p_fifo );
}
void sout_FifoPut( sout_fifo_t *p_fifo, sout_buffer_t *p_buffer )
{
vlc_mutex_lock( &p_fifo->lock );
do
{
*p_fifo->pp_last = p_buffer;
p_fifo->pp_last = &p_buffer->p_next;
p_fifo->i_depth++;
p_buffer = p_buffer->p_next;
} while( p_buffer );
/* warm there is data in this fifo */
vlc_cond_signal( &p_fifo->wait );
vlc_mutex_unlock( &p_fifo->lock );
}
sout_buffer_t *sout_FifoGet( sout_fifo_t *p_fifo )
{
sout_buffer_t *p_buffer;
vlc_mutex_lock( &p_fifo->lock );
if( p_fifo->p_first == NULL )
{
vlc_cond_wait( &p_fifo->wait, &p_fifo->lock );
}
p_buffer = p_fifo->p_first;
p_fifo->p_first = p_buffer->p_next;
p_fifo->i_depth--;
if( p_fifo->p_first == NULL )
{
p_fifo->pp_last = &p_fifo->p_first;
}
vlc_mutex_unlock( &p_fifo->lock );
p_buffer->p_next = NULL;
return( p_buffer );
}
sout_buffer_t *sout_FifoShow( sout_fifo_t *p_fifo )
{
sout_buffer_t *p_buffer;
vlc_mutex_lock( &p_fifo->lock );
if( p_fifo->p_first == NULL )
{
vlc_cond_wait( &p_fifo->wait, &p_fifo->lock );
}
p_buffer = p_fifo->p_first;
vlc_mutex_unlock( &p_fifo->lock );
return( p_buffer );
}
sout_buffer_t *sout_BufferNew( sout_instance_t *p_sout, size_t i_size )
{
sout_buffer_t *p_buffer;
#ifdef DEBUG_BUFFER
msg_Dbg( p_sout, "allocating an new buffer, size:%d", (uint32_t)i_size );
#endif
p_buffer = malloc( sizeof( sout_buffer_t ) );
if( i_size > 0 )
{
p_buffer->p_buffer = malloc( i_size );
}
else
{
p_buffer->p_buffer = NULL;
}
p_buffer->i_allocated_size = i_size;
p_buffer->i_size = i_size;
p_buffer->p_next = NULL;
return( p_buffer );
}
int sout_BufferRealloc( sout_instance_t *p_sout, sout_buffer_t *p_buffer, size_t i_size )
{
#ifdef DEBUG_BUFFER
msg_Dbg( p_sout,
"realloc buffer old size:%d new size:%d",
(uint32_t)p_buffer->i_allocated_size,
(uint32_t)i_size );
#endif
if( !( p_buffer->p_buffer = realloc( p_buffer->p_buffer, i_size ) ) )
{
msg_Err( p_sout, "realloc failed" );
p_buffer->i_allocated_size = 0;
p_buffer->i_size = 0;
return( -1 );
}
p_buffer->i_allocated_size = i_size;
return( 0 );
}
int sout_BufferDelete( sout_instance_t *p_sout, sout_buffer_t *p_buffer )
{
#ifdef DEBUG_BUFFER
msg_Dbg( p_sout, "freeing buffer, size:%d", p_buffer->i_size );
#endif
if( p_buffer->p_buffer )
{
free( p_buffer->p_buffer );
}
free( p_buffer );
return( 0 );
}
sout_buffer_t *sout_BufferDuplicate( sout_instance_t *p_sout,
sout_buffer_t *p_buffer )
{
sout_buffer_t *p_dup;
p_dup = sout_BufferNew( p_sout, p_buffer->i_size );
p_dup->i_bitrate= p_buffer->i_bitrate;
p_dup->i_dts = p_buffer->i_dts;
p_dup->i_pts = p_buffer->i_pts;
p_dup->i_length = p_buffer->i_length;
memcpy( p_dup->p_buffer, p_buffer->p_buffer, p_buffer->i_size );
return( p_dup );
}
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