You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
486 lines
12 KiB
486 lines
12 KiB
/*
|
|
* ts2rtp.cpp
|
|
*
|
|
* Copyright (C) 2003-2007 Christophe Thommeret <hftom@free.fr>
|
|
*
|
|
* some code from dvbstream (GPL)
|
|
* Copyright (C) 2001,2002 Dave Chapman
|
|
*
|
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <tdemessagebox.h>
|
|
#include <tdelocale.h>
|
|
|
|
#include "ts2rtp.h"
|
|
#include "gdvb.h"
|
|
#include "channeldesc.h"
|
|
|
|
#define TS_SIZE 188
|
|
|
|
|
|
|
|
Ts2Rtp::Ts2Rtp()
|
|
{
|
|
unsigned int i, j=0, k;
|
|
|
|
for( i = 0 ; i < 256 ; i++ ) {
|
|
k = 0;
|
|
for (j = (i << 24) | 0x800000 ; j != 0x80000000 ; j <<= 1) {
|
|
k = (k << 1) ^ (((k ^ j) & 0x80000000) ? 0x04c11db7 : 0);
|
|
}
|
|
CRC32[i] = k;
|
|
}
|
|
|
|
thWrite = 0;
|
|
rtpSocket = 0;
|
|
writePsi = false;
|
|
psiPackets = 0;
|
|
// fill in the MPEG-2 TS deefaults
|
|
// Note: MPEG-2 TS defines a timestamping base frequency of 90000 Hz.
|
|
hRtp.b.v=2;
|
|
hRtp.b.p=0;
|
|
hRtp.b.x=0;
|
|
hRtp.b.cc=0;
|
|
hRtp.b.m=0;
|
|
hRtp.b.pt=33;
|
|
hRtp.b.sequence=rand() & 65535;
|
|
hRtp.timestamp=rand();
|
|
hRtp.ssrc=rand();
|
|
|
|
psiBufferSize = 20*TS_SIZE;
|
|
psiBuffer = (unsigned char*)malloc(psiBufferSize);
|
|
|
|
connect( &psiTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(setPSI()) );
|
|
}
|
|
|
|
|
|
|
|
Ts2Rtp::~Ts2Rtp()
|
|
{
|
|
stop();
|
|
if ( rtpSocket ) {
|
|
close( rtpSocket );
|
|
}
|
|
free( psiBuffer );
|
|
}
|
|
|
|
|
|
|
|
void Ts2Rtp::setSocket( const TQString &addr, int m_port, int m_senderPort )
|
|
{
|
|
address = addr;
|
|
port = m_port;
|
|
senderPort = m_senderPort;
|
|
}
|
|
|
|
|
|
|
|
bool Ts2Rtp::addChannels( TQPtrList<ChannelDesc> *channels )
|
|
{
|
|
int i, j, k, pmtpid=8191;
|
|
ChannelDesc *desc, *d;
|
|
TQValueList<int> pids;
|
|
|
|
if ( !rtpSocket && !makeSocket() )
|
|
return false;
|
|
|
|
sendList = "";
|
|
for ( i=0; i<(int)channels->count(); i++ ) {
|
|
desc = channels->at( i );
|
|
sendList = sendList+desc->name+"|"+TQString().setNum(desc->vpid)+"|"+TQString().setNum(desc->apid[0].pid)+"|";
|
|
if ( desc->apid[0].ac3 ) sendList+= "y|";
|
|
else sendList+= "n|";
|
|
sendList+= TQString().setNum(desc->subpid[0].pid);
|
|
sendList+= "|";
|
|
sendList+= TQString().setNum(desc->subpid[0].page);
|
|
sendList+= "|";
|
|
sendList+= TQString().setNum(desc->subpid[0].id);
|
|
sendList+= "|";
|
|
sendList+= TQString().setNum(desc->subpid[0].type);
|
|
sendList+= "|";
|
|
sendList+= desc->subpid[0].lang+"|";
|
|
for ( j=i; j<(int)channels->count(); j++ ) {
|
|
pids.clear();
|
|
d = channels->at( j );
|
|
if ( d->vpid )
|
|
pids.append( d->vpid );
|
|
for ( k=0; k<d->napid && k<MAX_AUDIO; k++ )
|
|
pids.append( d->apid[k].pid );
|
|
for ( k=0; k<d->nsubpid && k<MAX_DVBSUB; k++ )
|
|
pids.append( d->subpid[k].pid );
|
|
while ( pmtpid==17 || pids.contains( pmtpid ) )
|
|
--pmtpid;
|
|
}
|
|
desc->pmtpid = pmtpid--;
|
|
}
|
|
sendList+="\n";
|
|
psiTables( channels );
|
|
writePsi = true;
|
|
psiTimer.start( 500 );
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
void Ts2Rtp::removeChannels()
|
|
{
|
|
if ( !rtpSocket )
|
|
return;
|
|
stop();
|
|
close( rtpSocket );
|
|
rtpSocket = 0;
|
|
fprintf( stderr, "rtp socket closed\n" );
|
|
thWrite = 0;
|
|
psiPackets = 0;
|
|
if ( psiTimer.isActive() )
|
|
psiTimer.stop();
|
|
}
|
|
|
|
|
|
|
|
bool Ts2Rtp::makeSocket()
|
|
{
|
|
int iRet, iLoop = 1;
|
|
|
|
if ( !makeSenderSocket( address, senderPort ) ) return false;
|
|
|
|
rtpSocket = socket( AF_INET, SOCK_DGRAM, 0 );
|
|
|
|
if ( rtpSocket < 0) {
|
|
KMessageBox::error( 0, i18n("Can't open DVB broadcast socket.") );
|
|
rtpSocket = 0;
|
|
closeSender();
|
|
return false;
|
|
}
|
|
|
|
rtpAddr.sin_family = AF_INET;
|
|
rtpAddr.sin_port = htons( port );
|
|
rtpAddr.sin_addr.s_addr = inet_addr( address.ascii() );
|
|
|
|
iRet = setsockopt( rtpSocket, SOL_SOCKET, SO_BROADCAST, &iLoop, sizeof(iLoop));
|
|
if (iRet < 0) {
|
|
KMessageBox::error( 0, i18n("Can't init DVB broadcast socket.") );
|
|
close( rtpSocket );
|
|
rtpSocket = 0;
|
|
closeSender();
|
|
return false;
|
|
}
|
|
go();
|
|
fprintf( stderr, "rtp socket opened\n" );
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
void Ts2Rtp::process( unsigned char *buf, int size )
|
|
{
|
|
int i, n;
|
|
unsigned char *buffer=buf;
|
|
|
|
if ( writePsi ) {
|
|
i = 0;
|
|
while ( i<psiPackets ) {
|
|
n = psiPackets-i;
|
|
if ( n>8 )
|
|
n = 8;
|
|
sendrtp( (char*)(psiBuffer+(TS_SIZE*i)), TS_SIZE*n );
|
|
i+= n;
|
|
}
|
|
writePsi = false;
|
|
}
|
|
|
|
for ( i=0; i<size; i+=TS_SIZE ) {
|
|
memcpy( thBuf+thWrite, buffer, TS_SIZE );
|
|
thWrite+=TS_SIZE;
|
|
if ( thWrite==(TS_SIZE*8 ) ) {
|
|
sendrtp( (char*)thBuf, TS_SIZE*8 );
|
|
thWrite = 0;
|
|
}
|
|
buffer+=TS_SIZE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* Send a single RTP packet, converting the RTP header to network byte order. */
|
|
void Ts2Rtp::sendrtp( char *data, int len )
|
|
{
|
|
struct timeval tv;
|
|
|
|
gettimeofday( &tv, (struct timezone*) NULL );
|
|
hRtp.timestamp = ((tv.tv_sec%1000000)*1000 + tv.tv_usec/1000)*90;
|
|
|
|
char *buf=(char*)alloca(len+72);
|
|
unsigned int intP;
|
|
char* charP = (char*) &intP;
|
|
int headerSize;
|
|
buf[0] = 0x00;
|
|
buf[0] |= ((((char) hRtp.b.v)<<6)&0xc0);
|
|
buf[0] |= ((((char) hRtp.b.p)<<5)&0x20);
|
|
buf[0] |= ((((char) hRtp.b.x)<<4)&0x10);
|
|
buf[0] |= ((((char) hRtp.b.cc)<<0)&0x0f);
|
|
buf[1] = 0x00;
|
|
buf[1] |= ((((char) hRtp.b.m)<<7)&0x80);
|
|
buf[1] |= ((((char) hRtp.b.pt)<<0)&0x7f);
|
|
intP = htonl(hRtp.b.sequence);
|
|
memcpy(&buf[2],charP+2,2);
|
|
intP = htonl(hRtp.timestamp);
|
|
memcpy(&buf[4],&intP,4);
|
|
/* SSRC: not implemented */
|
|
buf[8] = 0x0f;
|
|
buf[9] = 0x0f;
|
|
buf[10] = 0x0f;
|
|
buf[11] = 0x0f;
|
|
headerSize = 12 + 4*hRtp.b.cc; /* in bytes */
|
|
memcpy(buf+headerSize,data,len);
|
|
|
|
hRtp.b.sequence++;
|
|
if ( rtpSocket ) sendto( rtpSocket, buf, len+headerSize, 0, (struct sockaddr *)&rtpAddr, sizeof(rtpAddr) );
|
|
}
|
|
|
|
|
|
|
|
void Ts2Rtp::setPSI()
|
|
{
|
|
writePsi = true;
|
|
}
|
|
|
|
|
|
void Ts2Rtp::psiTables( TQPtrList<ChannelDesc> *channels )
|
|
{
|
|
unsigned char buf[15000];
|
|
int off, i, j, sectionOff, loopOff, descOff, max;
|
|
int npack=0;
|
|
unsigned short tsid = channels->at(0)->tp.tsid;
|
|
ChannelDesc *desc;
|
|
|
|
psiPackets = 0;
|
|
|
|
// SDT
|
|
off = 0;
|
|
// CRC calculation begins here
|
|
buf[off++] = 0x42; // service description section
|
|
buf[off++] = 0x80;
|
|
sectionOff = off;
|
|
buf[off++] = 0x00; // section_length (12bits)
|
|
buf[off++] = tsid>>8; buf[off++] = tsid&0xff;
|
|
buf[off++] = 0x01; // current_next_indicator
|
|
buf[off++] = 0x00; // section_number
|
|
buf[off++] = 0x00; // last_section_number
|
|
buf[off++] = 0x00; buf[off++] = 0x00; // network_id
|
|
buf[off++] = 0x00; // reserved
|
|
|
|
for ( i=0; i<(int)channels->count(); i++ ) {
|
|
desc = channels->at( i );
|
|
buf[off++] = desc->sid>>8; buf[off++] = desc->sid&0xff; // service_id
|
|
buf[off++] = 0x00; // reserved
|
|
buf[off++] = 0x80; // running_status(3bits) + free_ca(1bit) + descriptors_loop_length
|
|
loopOff = off;
|
|
buf[off++] = 0x00; // descriptors_loop_length
|
|
|
|
buf[off++] = 0x48; // descriptor_tag
|
|
descOff = off;
|
|
buf[off++] = 0x00; //descriptor_length
|
|
buf[off++] = desc->type; //service_type
|
|
buf[off++] = 0x03; // provider_name_length
|
|
buf[off++] = 0x4c; buf[off++] = 0x41; buf[off++] = 0x4e; // provider_name
|
|
buf[off++] = desc->name.length(); // service_name_length
|
|
memcpy( buf+off, desc->name.latin1(), desc->name.length() );
|
|
off+= desc->name.length();
|
|
buf[descOff] = off-descOff-1;
|
|
buf[loopOff] = off-loopOff-1;
|
|
}
|
|
buf[sectionOff-1] |= (off+1)>>8;
|
|
buf[sectionOff] = (off+1)&0xff;
|
|
calculateCRC( buf, buf+off );
|
|
fillPackets( 17, buf, off+4, npack );
|
|
|
|
// PAT
|
|
off = 0;
|
|
// CRC calculation begins here
|
|
buf[off++] = 0x00; // program association section
|
|
buf[off++] = 0x80;
|
|
sectionOff = off;
|
|
buf[off++] = 0x00; // section_length (12bits)
|
|
buf[off++] = tsid>>8; buf[off++] = tsid&0xff;
|
|
buf[off++] = 0x01; // current_next_indicator
|
|
buf[off++] = 0x00; // section_number
|
|
buf[off++] = 0x00; // last_section_number
|
|
|
|
for ( i=0; i<(int)channels->count(); i++ ) {
|
|
desc = channels->at( i );
|
|
buf[off++] = desc->sid>>8; buf[off++] = desc->sid&0xff; // service_id
|
|
buf[off++] = desc->pmtpid>>8; buf[off++] = desc->pmtpid&0xff; // program_map_PID(13bits)
|
|
}
|
|
buf[sectionOff-1] |= (off+1)>>8;
|
|
buf[sectionOff] = (off+1)&0xff;
|
|
calculateCRC( buf, buf+off );
|
|
fillPackets( 0, buf, off+4, npack );
|
|
|
|
// PMT
|
|
for ( i=0; i<(int)channels->count(); i++ ) {
|
|
desc = channels->at( i );
|
|
off = 0;
|
|
// CRC calculation begins here
|
|
buf[off++] = 0x02; // program map section
|
|
buf[off++] = 0x80;
|
|
sectionOff = off;
|
|
buf[off++] = 0x00; // section_length (12bits)
|
|
buf[off++] = desc->sid>>8; buf[off++] = desc->sid&0xff;
|
|
buf[off++] = 0x01; // current_next_indicator
|
|
buf[off++] = 0x00; // section_number
|
|
buf[off++] = 0x00; // last_section_number
|
|
if ( desc->vpid ) {
|
|
buf[off++] = desc->vpid>>8; buf[off++] = desc->vpid&0xff; // PCR pid
|
|
}
|
|
else if ( desc->napid ) {
|
|
buf[off++] = desc->apid[0].pid>>8; buf[off++] = desc->apid[0].pid&0xff; // PCR pid
|
|
}
|
|
buf[off++] = 0x00; buf[off++] = 0x00; // infos_length
|
|
if ( desc->vpid ) {
|
|
buf[off++] = desc->vType; // stream_type
|
|
buf[off++] = desc->vpid>>8; buf[off++] = desc->vpid&0xff; // pid
|
|
buf[off++] = 0x00; buf[off++] = 0x00; // infos_length
|
|
}
|
|
|
|
for ( j=0; j<desc->napid && j<MAX_AUDIO; j++ ) {
|
|
if ( desc->apid[j].ac3 ) {
|
|
buf[off++] = 0x06; // stream type
|
|
buf[off++] = desc->apid[j].pid>>8; buf[off++] = desc->apid[j].pid&0xff;
|
|
buf[off++] = 0x00; buf[off++] = 0x09; // es info length
|
|
buf[off++] = 0x6a; // descriptor tag
|
|
buf[off++] = 0x01; // descriptor length
|
|
buf[off++] = 0x00;
|
|
}
|
|
else {
|
|
buf[off++] = 0x04; // stream type = audio
|
|
buf[off++] = desc->apid[j].pid>>8; buf[off++] = desc->apid[j].pid&0xff;
|
|
buf[off++] = 0x00; buf[off++] = 0x06; // es info length
|
|
}
|
|
buf[off++] = 0x0a; // iso639 descriptor tag
|
|
buf[off++] = 0x04; // descriptor length
|
|
if ( !desc->apid[j].lang.isEmpty() ) {
|
|
buf[off++] = desc->apid[j].lang.constref(0);
|
|
buf[off++] = desc->apid[j].lang.constref(1);
|
|
if ( desc->apid[j].ac3 )
|
|
buf[off++] = '_';
|
|
else
|
|
buf[off++] = desc->apid[j].lang.constref(2);
|
|
}
|
|
else if ( desc->apid[j].ac3 ) {
|
|
buf[off++] = 'd';
|
|
buf[off++] = 'd';
|
|
buf[off++] = 49+j;
|
|
}
|
|
else {
|
|
buf[off++] = 'c';
|
|
buf[off++] = 'h';
|
|
buf[off++] = 49+j;
|
|
}
|
|
buf[off++] = 0x00; // audio type
|
|
}
|
|
|
|
for ( j=0; j<desc->nsubpid && j<MAX_DVBSUB; j++ ) {
|
|
buf[off++] = 0x06; // stream type = ISO_13818_PES_PRIVATE
|
|
buf[off++] = desc->subpid[j].pid>>8; buf[off++] = desc->subpid[j].pid&0xff;
|
|
buf[off++] = 0x00; buf[off++] = 0x0a; // es info length
|
|
buf[off++] = 0x59; //DVB sub tag
|
|
buf[off++] = 0x08; // descriptor length
|
|
if ( !desc->subpid[j].lang.isEmpty() ) {
|
|
buf[off++] = desc->subpid[j].lang.constref(0);
|
|
buf[off++] = desc->subpid[j].lang.constref(1);
|
|
buf[off++] = desc->subpid[j].lang.constref(2);
|
|
}
|
|
else {
|
|
buf[off++] = 'c';
|
|
buf[off++] = 'h';
|
|
buf[off++] = 49+j;
|
|
}
|
|
buf[off++] = desc->subpid[j].type; //sub type
|
|
buf[off++] = desc->subpid[j].page>>8; buf[off++] = desc->subpid[j].page&0xff; // comp_page_id
|
|
buf[off++] = desc->subpid[j].id>>8; buf[off++] = desc->subpid[j].id&0xff; // anc_page_id
|
|
}
|
|
|
|
buf[sectionOff-1] |= (off+1)>>8;
|
|
buf[sectionOff] = (off+1)&0xff;
|
|
calculateCRC( buf, buf+off );
|
|
fillPackets( desc->pmtpid, buf, off+4, npack );
|
|
}
|
|
|
|
psiPackets = npack;
|
|
}
|
|
|
|
|
|
|
|
void Ts2Rtp::fillPackets( unsigned short pid, unsigned char *buf, int len, int &npack )
|
|
{
|
|
int i, off=npack*TS_SIZE, offbuf=0, inc;
|
|
bool pus=true;
|
|
int continuity=0;
|
|
|
|
while ( (off-(npack*TS_SIZE))<len ) {
|
|
if ( (off+TS_SIZE)>(psiBufferSize-(10*TS_SIZE)) ) {
|
|
psiBufferSize+= 10*TS_SIZE;
|
|
psiBuffer = (unsigned char*)realloc( psiBuffer, psiBufferSize );
|
|
fprintf(stderr,"psiBufferSize = %d\n",psiBufferSize);
|
|
}
|
|
psiBuffer[off++] = 0x47; // sync_byte
|
|
if ( pus )
|
|
psiBuffer[off++] = 0x40|(pid>>8);
|
|
else
|
|
psiBuffer[off++] = pid>>8;
|
|
psiBuffer[off++] = pid&0xff; // PID
|
|
psiBuffer[off++] = 0x10|continuity;
|
|
if ( pus ) {
|
|
psiBuffer[off++] = 0x00; // pointer_field
|
|
pus = false;
|
|
}
|
|
inc = TS_SIZE-(off%TS_SIZE);
|
|
if ( (len-offbuf)<inc )
|
|
inc = len-offbuf;
|
|
memcpy( psiBuffer+off, buf+offbuf, inc );
|
|
off+= inc;
|
|
offbuf+= inc;
|
|
++continuity;
|
|
}
|
|
// needed stuffing bytes
|
|
for ( i=0 ; i<(off%TS_SIZE) ; i++)
|
|
psiBuffer[off++] = 0xff;
|
|
npack+= (off-(npack*TS_SIZE))/TS_SIZE;
|
|
}
|
|
|
|
|
|
|
|
void Ts2Rtp::calculateCRC( unsigned char *p_begin, unsigned char *p_end )
|
|
{
|
|
unsigned int i_crc = 0xffffffff;
|
|
|
|
// Calculate the CRC
|
|
while( p_begin < p_end ) {
|
|
i_crc = (i_crc<<8) ^ CRC32[ (i_crc>>24) ^ ((unsigned int)*p_begin) ];
|
|
p_begin++;
|
|
}
|
|
// Store it after the data
|
|
p_end[0] = (i_crc >> 24) & 0xff;
|
|
p_end[1] = (i_crc >> 16) & 0xff;
|
|
p_end[2] = (i_crc >> 8) & 0xff;
|
|
p_end[3] = (i_crc >> 0) & 0xff;
|
|
}
|
|
|
|
#include "ts2rtp.moc"
|