/* * ts2rtp.cpp * * Copyright (C) 2003-2007 Christophe Thommeret * * 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 #include #include #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 *channels ) { int i, j, k, pmtpid=8191; ChannelDesc *desc, *d; TQValueList 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; knapid && kapid[k].pid ); for ( k=0; knsubpid && ksubpid[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 ( i8 ) n = 8; sendrtp( (char*)(psiBuffer+(TS_SIZE*i)), TS_SIZE*n ); i+= n; } writePsi = false; } for ( i=0; i *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; jnapid && japid[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; jnsubpid && jsubpid[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))(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)>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"