Commit 815017d8 by Andy Gatward

Now outputs MPEG-TS instead of MPEG-PS

parent eb36c7e9
......@@ -11,9 +11,12 @@ Tuning PVRoll and selecting inputs
You usually want to supply PVRoll with the parameters for the input (and
frequency to tune to, if appropriate). For instance, for using the S-Video
inpurt on a Hauppauge PVR-150 card:
input on a Hauppauge PVR-150 card:
pvroll -i1 <dest_addr>
The destination address must currently be specified as an IP address, host
name lookups are not supported.
Other options are available - run pvroll -h for more information.
......@@ -4,11 +4,16 @@ Welcome to PVRoll!
PVRoll is a simple and lightweight streaming application designed to allow
the import of content from hardware MPEG-2 encoder cards (so-called 'PVR'
cards) into an IPTV platform.
cards) into an IPTV platform. PVRoll is designed to be the core of a custom
MPEG-2 encoder for importing 'legacy' content, based on a PC with Linux.
PVRoll does not do any processing such as transcoding or remultiplexing, nor
does it stream from plain files or DVB devices If you were looking for
these features, use VLC or DVBlast.
At present, output is only available in MPEG2-PS (program stream)
carried in RTP over UDP. The destination can be IPv4 or IPv6, and
either unicast or multicast.
The output from pvroll is an MPEG2 Transport Stream (MPEG2-TS), carried in
RTP over UDP. The destination can be IPv4 or IPv6, and either unicast or
multicast.
Currently only encoder cards that use the 'ivtv' kernel driver are
supported, and the application checks that this driver is being used.
......
......@@ -5,6 +5,6 @@ To do list for PVRoll
* Auto{make,conf} stuff
* Man page
* MPEG2-TS output (UDP and RTP)
* CX18 card support
* DNS lookup for output address
* UDP output (instead of RTP)
......@@ -4,9 +4,9 @@
CFLAGS += -Wall -O3 -fomit-frame-pointer
CFLAGS += -g
LDFLAGS += -lrt
LDFLAGS_PVROLL += -lpthread
LDFLAGS_PVROLL += -ldvbpsi -lpthread
OBJ_PVROLL = pvroll.o ivtv.o output.o util.o
OBJ_PVROLL = pvroll.o ivtv.o output.o tsmux.o util.o
BIN = $(DESTDIR)/usr/bin
......
......@@ -102,7 +102,10 @@ int ivtv_Open()
void ivtv_Close()
{
if( p_pvr->i_fd >= 0 )
{
close( p_pvr->i_fd );
msg_Dbg( NULL, "Closed handle to encoder card." );
}
}
int ivtv_Read( unsigned char *p_data, uint16_t i_len )
......@@ -144,7 +147,7 @@ int ivtv_InitEncoder()
//ivtv_addV4L2ExtCtrl( &p_ext_ctrl[i_cnt++], V4L2_CID_MPEG_VIDEO_ENCODING, V4L2_MPEG_VIDEO_ENCODING_MPEG_2 );
ivtv_addV4L2ExtCtrl( &p_ext_ctrl[i_cnt++], V4L2_CID_MPEG_AUDIO_ENCODING, V4L2_MPEG_AUDIO_ENCODING_LAYER_2 );
//ivtv_addV4L2ExtCtrl( &p_ext_ctrl[i_cnt++], V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, IVTV_AUDIO_SAMPLE );
ivtv_addV4L2ExtCtrl( &p_ext_ctrl[i_cnt++], V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000 );
ivtv_addV4L2ExtCtrl( &p_ext_ctrl[i_cnt++], V4L2_CID_MPEG_STREAM_TYPE, V4L2_MPEG_STREAM_TYPE_MPEG2_PS );
ivtv_addV4L2ExtCtrl( &p_ext_ctrl[i_cnt++], V4L2_CID_MPEG_AUDIO_MODE, V4L2_MPEG_AUDIO_MODE_STEREO );
......@@ -681,6 +684,8 @@ int ivtv_SetInput()
return( -errno );
}
msg_Dbg( NULL, "Encoder card input %d selected.", p_pvr->i_input );
return( 0 );
}
......
......@@ -49,27 +49,36 @@ static void output_Flush()
{
struct iovec p_iov[( p_output->i_rtp_depth + 1 )];
uint8_t i_rtp_hdr[RTP_SIZE];
uint8_t i;
p_iov[0].iov_base = i_rtp_hdr;
p_iov[0].iov_len = sizeof( i_rtp_hdr );
p_iov[1].iov_base = p_output->p_psdata;
p_iov[1].iov_len = p_output->i_psdata_len;
p_iov[0].iov_len = sizeof( i_rtp_hdr );
rtp_SetHdr( i_rtp_hdr );
for( i = 0; i < p_output->i_rtp_depth; i++ )
{
p_iov[i + 1].iov_base = p_output->pp_blocks[i]->p_ts;
p_iov[i + 1].iov_len = TS_SIZE;
}
if ( writev( p_output->i_fd, p_iov, ( p_output->i_rtp_depth + 1 ) ) < 0 )
{
msg_Err( NULL, "Couldn't write to output: %s", strerror( errno ) );
}
for( i = 0; i < p_output->i_rtp_depth; i++ )
free( p_output->pp_blocks[i] );
p_output->i_rtp_pos = 0;
}
/*****************************************************************************
* output_PutData
* output_PutBlock
*****************************************************************************/
void output_PutData()
void output_PutBlock( block_t *p_block )
{
if( p_output->i_psdata_len )
p_output->pp_blocks[p_output->i_rtp_pos++] = p_block;
if( p_output->i_rtp_pos == p_output->i_rtp_depth )
output_Flush();
}
......@@ -136,10 +145,18 @@ void output_Close()
void output_Init()
{
p_output->i_rtp_seq = rand() & 0xffff;
p_output->i_rtp_ssrc = rand() & 0xffff;
p_output->i_rtp_ssrc = rand() & 0xffffffff;
p_output->i_rtp_tstamp = 0;
p_output->i_rtp_wclock = 0;
p_output->i_rtp_depth = 1;
p_output->i_rtp_depth = ( p_output->i_mtu - RTP_SIZE ) / TS_SIZE;
p_output->i_pcr = 0;
p_output->b_send_pcr = 0;
p_output->i_cc_video = rand() & 0x0f;
p_output->i_cc_audio = rand() & 0x0f;
p_output->i_cc_pat = rand() & 0x0f;
p_output->i_cc_pmt = rand() & 0x0f;
p_output->i_pat_ver = rand() & 0x1f;
p_output->i_pmt_ver = rand() & 0x1f;
}
/*****************************************************************************
......@@ -177,7 +194,7 @@ static void rtp_SetHdr( uint8_t *p_hdr )
}
p_hdr[0] = 0x80;
p_hdr[1] = 96;
p_hdr[1] = 33;
p_hdr[2] = p_output->i_rtp_seq >> 8;
p_hdr[3] = p_output->i_rtp_seq & 0xff;
p_hdr[4] = (i_timestamp >> 24) & 0xff;
......
......@@ -106,8 +106,8 @@ int main( int i_argc, char *p_argv[] )
p_pvr->i_device = DEFAULT_DEVICE;
p_pvr->i_audio_bitrate = DEFAULT_AB;
p_pvr->i_video_bitrate = DEFAULT_VB_AVG;
p_pvr->i_video_bitrate_peak = DEFAULT_VB_MAX;
p_pvr->i_video_bitrate = DEFAULT_VB_AVG * 1024;
p_pvr->i_video_bitrate_peak = DEFAULT_VB_MAX * 1024;
p_pvr->i_video_cbr = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR;
p_pvr->i_freq = 0;
p_pvr->i_gop_size = 0;
......@@ -121,6 +121,11 @@ int main( int i_argc, char *p_argv[] )
p_output->i_mtu = 0; // we use 0 to indicate default
p_output->i_ttl = DEFAULT_TTL;
p_output->i_pid_video = DEFAULT_PID_VIDEO;
p_output->i_pid_audio = DEFAULT_PID_AUDIO;
p_output->i_pid_pmt = DEFAULT_SID;
p_output->i_prognum = DEFAULT_SID;
p_output->i_tsid = DEFAULT_TSID;
// option parsing via getopt (long mode supported)
......@@ -289,6 +294,7 @@ int main( int i_argc, char *p_argv[] )
msg_Warn( NULL, "TTL of %d is not valid, using default.", optarg );
p_output->i_ttl = DEFAULT_TTL;
}
break;
case 'V':
exit( EXIT_SUCCESS );
......@@ -437,17 +443,13 @@ int main( int i_argc, char *p_argv[] )
// set up buffer and start streaming data
output_Init();
p_output->i_psdata_max = p_output->i_mtu - RTP_SIZE;
p_output->p_psdata = malloc( p_output->i_psdata_max );
memset( p_output->p_psdata, 0, p_output->i_psdata_max );
p_output->i_psdata_len = IVTV_READ_BUFFER;
p_output->p_psdata = malloc( p_output->i_psdata_len );
memset( p_output->p_psdata, 0, p_output->i_psdata_len );
i_running = 1;
while( i_running )
{
p_output->i_psdata_len = ivtv_Read( p_output->p_psdata, p_output->i_psdata_max );
if( p_output->i_psdata_len > 0 )
output_PutData();
}
tsmux_Run();
output_Close();
exit( EXIT_SUCCESS );
......
......@@ -24,6 +24,7 @@
#define __PVROLL_H_
#include <netdb.h>
#include <dvbpsi/dvbpsi.h>
/*****************************************************************************
* System-level configuration
......@@ -36,6 +37,8 @@
#define MIN_MTU 200 // Minimum MTU size (188 + 12)
#define MAX_MTU 9000 // Maximum MTU size
#define RTP_SIZE 12 // RTP header size
#define TS_SIZE 188 // Transport stream packet size
#define TS_PAYLOAD_SIZE TS_SIZE - 4
#define DEFAULT_DEVICE 0 // default device (/dev/video0)
#define DEFAULT_INPUT 0 // default input on card
......@@ -54,9 +57,15 @@
#define IVTV_MAX_CTRLS 7 // maximum number of v4l2 ctrls to be set
#define IVTV_AUDIO_SAMPLE 48000 // audio sample rate (48kHz)
#define IVTV_READ_BUFFER 4096 // IVTV read buffer size
typedef int64_t mtime_t;
typedef struct block_t
{
uint8_t p_ts[TS_SIZE];
} block_t;
typedef struct pvr_t
{
int i_fd;
......@@ -79,7 +88,6 @@ typedef struct pvr_t
typedef struct output_t
{
int i_fd;
uint8_t i_flags;
// network information
struct sockaddr_storage *p_addr;
......@@ -87,10 +95,18 @@ typedef struct output_t
uint16_t i_mtu;
uint8_t i_ttl;
// PS data storage
// MPEG2-PS data storage
unsigned char *p_psdata;
uint16_t i_psdata_len;
uint16_t i_psdata_max;
uint32_t i_psdata_len;
uint32_t i_psdata_max;
// MPEG2-TS settings and state machine
block_t *pp_blocks[MAX_MTU / TS_SIZE];
uint8_t i_tsmux_state, i_pat_ver, i_pmt_ver, b_send_pcr;
uint8_t i_cc_video, i_cc_audio, i_cc_pat, i_cc_pmt;
uint16_t i_pid_video, i_pid_audio, i_pid_pmt, i_tsid, i_prognum;
uint64_t i_pcr;
dvbpsi_psi_section_t *p_pat_section, *p_pmt_section;
// RTP handling
uint32_t i_rtp_ssrc;
......@@ -135,9 +151,14 @@ int ivtv_GetAspect();
int ivtv_SetAspect();
/*****************************************************************************
* Functions from tsmux.c
*****************************************************************************/
void tsmux_Run();
/*****************************************************************************
* Functions from output.c
*****************************************************************************/
void output_PutData();
void output_PutBlock( struct block_t *p_block );
int output_Open();
void output_Close();
void output_Init();
......@@ -152,6 +173,5 @@ void msg_Dbg( void *_unused, const char *psz_format, ... );
void msg_Raw( void *_unused, const char *psz_format, ... );
mtime_t mdate( void );
void msleep( mtime_t delay );
void hexDump( uint8_t *p_data, uint32_t i_len );
#endif
This diff is collapsed. Click to expand it.
......@@ -165,53 +165,3 @@ void msleep( mtime_t delay )
while ( nanosleep( &ts, &ts ) && errno == EINTR );
#endif
}
/*****************************************************************************
* hexDump
*****************************************************************************/
void hexDump( uint8_t *p_data, uint32_t i_len )
{
uint16_t i, j;
char *p_outline;
char *p_hrdata;
p_outline = malloc(69);
p_hrdata = malloc(17);
for( i = 0; i < i_len; i += 16 )
{
sprintf( p_outline, "%03x: ", i );
for( j = 0; j < 16; j++ )
{
if( i + j < i_len )
{
sprintf( &p_outline[5 + (3 * j)], "%02x ", p_data[i + j] );
if( p_data[i + j] >= 32 && p_data[i + j] <= 136 )
{
sprintf( &p_hrdata[j], "%c", p_data[i + j] );
}
else {
sprintf( &p_hrdata[j], "." );
}
}
else
{
sprintf( &p_outline[5 + (3 * j)], " " );
sprintf( &p_hrdata[j], " " );
}
}
sprintf( &p_outline[53], "%16s", p_hrdata );
msg_Dbg( NULL, p_outline );
}
free( p_hrdata );
free( p_outline );
}
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