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.
kaffeine/kaffeine/src/input/dvb/ts2rtp.cpp

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"