From 0be7e1b6ec978833d3b2f368fff1b96cece71b92 Mon Sep 17 00:00:00 2001 From: Chris Tallon Date: Sun, 17 Dec 2006 20:09:50 +0000 Subject: [PATCH] New TS demuxer --- channel.cc | 25 +++++ channel.h | 18 ++++ demuxer.cc | 5 + demuxer.h | 1 + demuxerts.cc | 252 +++++++++++++++++++++++++++++++++++++++++++++++++ demuxerts.h | 51 ++++++++++ objects.mk | 6 +- player.cc | 18 +++- player.h | 4 +- playerradio.cc | 13 ++- playerradio.h | 2 + vdr.cc | 51 ++++++++++ vdr.h | 2 + vfeed.cc | 1 - vvideolive.cc | 10 +- 15 files changed, 450 insertions(+), 9 deletions(-) create mode 100644 demuxerts.cc create mode 100644 demuxerts.h diff --git a/channel.cc b/channel.cc index cc01b23..4de723d 100644 --- a/channel.cc +++ b/channel.cc @@ -19,6 +19,7 @@ */ #include "channel.h" +#include "vdr.h" Channel::Channel() { @@ -27,10 +28,34 @@ Channel::Channel() name = NULL; index = -1; + vpid = 0; + numAPids = 0; } Channel::~Channel() { if (name) delete[] name; index = -1; // just in case + + for(ULONG i = 0; i < numAPids; i++) delete[] apids[i].name; +} + +void Channel::loadPids() +{ + // Clear the list if this is a reload + if (numAPids) + { + for(ULONG i = 0; i < numAPids; i++) delete[] apids[i].name; + apids.clear(); + vpid = 0; + numAPids = 0; + } + + VDR::getInstance()->getChannelPids(this); // FIXME sort out this system + + Log::getInstance()->log("Channel", Log::DEBUG, "C.%lu loaded, VPid=%lu, numApids=%lu", number, vpid, numAPids); + for (ULONG i = 0; i < numAPids; i++) + { + Log::getInstance()->log("Channel", Log::DEBUG, "APid %lu %s", apids[i].pid, apids[i].name); + } } diff --git a/channel.h b/channel.h index e17bdd3..4bb496c 100644 --- a/channel.h +++ b/channel.h @@ -22,20 +22,38 @@ #define CHANNEL_H #include +#include +#include "log.h" #include "defines.h" +// A struct to hold a audio pid pair + +typedef struct _apid +{ + ULONG pid; + char* name; +} apid; + +typedef vector APidList; + class Channel { public: Channel(); ~Channel(); + void loadPids(); + ULONG number; ULONG type; char* name; int index; + + ULONG vpid; + ULONG numAPids; + APidList apids; }; #endif diff --git a/demuxer.cc b/demuxer.cc index dbdf009..a503387 100644 --- a/demuxer.cc +++ b/demuxer.cc @@ -179,6 +179,11 @@ void Demuxer::PESPacket::init(UCHAR type) seq_header = false; } +void Demuxer::PESPacket::truncate() +{ + init(packetType); +} + int Demuxer::PESPacket::write(UCHAR *buf, int len) { if (closed) return 0; diff --git a/demuxer.h b/demuxer.h index 5cd0819..0baa815 100644 --- a/demuxer.h +++ b/demuxer.h @@ -48,6 +48,7 @@ protected: public: PESPacket(); void init(UCHAR type); + void truncate(); int write(UCHAR* buf, int len); int submit(); protected: diff --git a/demuxerts.cc b/demuxerts.cc new file mode 100644 index 0000000..b4bcd2a --- /dev/null +++ b/demuxerts.cc @@ -0,0 +1,252 @@ +/* + Copyright 2006 Mark Calderbank + + This file is part of VOMP. + + VOMP 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. + + VOMP 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 VOMP; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "demuxerts.h" + +DemuxerTS::DemuxerTS(int p_vID = 0, int p_aID = 0) +{ + vID = p_vID; + aID = p_aID; +} + +void DemuxerTS::flush() +{ + partPacket = 0; + Demuxer::flush(); + vPacket.init(0xE0); + aPacket.init(0xC0); +} + +int DemuxerTS::scan(UCHAR *buf, int len) +{ + // Temporarily, just look for the lowest audio stream and return it + + UINT LoPattern = 0x100 | PESTYPE_AUD0, + HiPattern = 0x100 | PESTYPE_AUDMAX; + int ret = 0; + + while (len >= 4) + { + UINT pattern = *(UINT*)buf; + buf++; len--; + + if (pattern < LoPattern || pattern > HiPattern) continue; + ret = pattern & 0xFF; + HiPattern = pattern; + if (HiPattern == LoPattern) break; + } + return ret; +} + +void DemuxerTS::setVID(int p_vID) +{ + vID = p_vID; +} + +void DemuxerTS::setAID(int p_aID) +{ + aID = p_aID; +} + +int DemuxerTS::findPTS(UCHAR* buf, int len, ULLONG* dest) +{ + UINT LoPattern = 0x100 | PESTYPE_VID0, + HiPattern = 0x100 | PESTYPE_VIDMAX; + + while (len >= 14) + { + UINT pattern = *(UINT*)buf; + buf++; len--; + if (pattern < LoPattern || pattern > HiPattern) continue; + + UINT framelength = ((UINT)buf[3] << 8) | buf[4]; + buf += 5; len -= 5; + + if ( buf[1] & 0x80 ) // PTS_DTS_flags indicate that PTS is present + { + *dest = ( (ULLONG)(buf[3] & 0x0E) << 29 ) | + ( (ULLONG)(buf[4]) << 22 ) | + ( (ULLONG)(buf[5] & 0xFE) << 14 ) | + ( (ULLONG)(buf[6]) << 7 ) | + ( (ULLONG)(buf[7] & 0xFE) >> 1 ); + return 1; + } + + buf += framelength; len -= framelength; + } + // No PTS found. + return 0; +} + +int DemuxerTS::put(UCHAR* buf, int len) +{ + int ret = 0; // return number of bytes consumed + + while (partPacket) + { + if (len >= TS_SIZE + 1 - partPacket) + { // Remainder of partial packet is available, plus one + memcpy(store+partPacket, buf, TS_SIZE - partPacket); + ret += TS_SIZE - partPacket; + buf += TS_SIZE - partPacket; + len -= TS_SIZE - partPacket; + partPacket = TS_SIZE; + if (*buf == TS_SIG) + { // Packet is properly terminated + int rc = processTS(store); + if (rc) + partPacket = 0; // Successfully processed + else + return ret; // Try again later. + } + else + { // Packet not terminated. Find another candidate, and shift store + Log::getInstance()->log("TS Demuxer", Log::ERR, "TS Misaligned!"); + int search = 1; + while (search < partPacket && store[search] != TS_SIG) + search++; + partPacket -= search; + if (partPacket) memcpy(store, store+search, partPacket); + } + } + else + { // Still don't have complete packet. Consume what we do have. + memcpy(store+partPacket, buf, len); + partPacket += len; + ret += len; + return ret; + } + } + + // Position ourselves at a candidate TS packet + while (*buf != TS_SIG && len) + { + Log::getInstance()->log("TS Demuxer", Log::ERR, "TS Misaligned!"); + buf++; ret++; len--; + } + + while (len) + { + if (len < TS_SIZE + 1) + { // Not enough data. Store what we have. + memcpy(store, buf, len); + partPacket = len; + ret += len; + return ret; + } + + if (buf[TS_SIZE] != TS_SIG) + { // Not terminated correctly. + while (*buf != TS_SIG && len) + { + buf++; ret++; len--; + } + } + else + { + int rc = processTS(buf); + if (rc) + { // Successfully processed + buf += TS_SIZE; ret += TS_SIZE; len -= TS_SIZE; + } + else + { // Processing failed. + return ret; + } + } + } + return ret; +} + +int DemuxerTS::processTS(UCHAR* buf) +{ + int datalen = TS_SIZE - 4; + int pid = ( (buf[1] & 0x1F) << 8 ) | buf[2]; + UCHAR payload = buf[1] & 0x40; + + if (buf[3] & 0x20) // Adaptation field is present + datalen -= (buf[4] + 1); + if (datalen < 0) // Error in stream TODO log this + return 1; + if (datalen == 0) // Null packet + return 1; + buf += (TS_SIZE - datalen); + + if (payload) + { + int rc = 1; + + if (pid == vID) rc = vPacket.submit(); + if (pid == aID) rc = aPacket.submit(); + if (rc == 0) return 0; + + if (pid == vID) + { + vPacket.init(0xE0); + buf += 6; datalen -= 6; + } + if (pid == aID) + { + aPacket.init(0xC0); + buf += 6; datalen -= 6; + } +// int pesID = -1; +// if (datalen >= 4) pesID = buf[3]; +// switch (pesID) +// { +// case PESTYPE_VID0 ... PESTYPE_VIDMAX: +// if (video_current == -1) video_current = pesID; +// if (video_current == pesID) +// { +// vPID = pid; +// vPacket.init(pesID); +// buf += 6; datalen -= 6; +// } +// break; +// case PESTYPE_AUD0 ... PESTYPE_AUDMAX: +// if (audio_current == -1) audio_current = pesID; +// if (audio_current == pesID) +// { +// aPID = pid; +// aPacket.init(pesID); +// buf += 6; datalen -= 6; +// } +// break; +// } + } + + PESPacket* packet = NULL; + if (pid == vID) packet = &vPacket; + if (pid == aID) packet = &aPacket; + if (packet != NULL) + { + if (packet->write(buf, datalen) == 0) + { // Writing to packet failed. It has overflowed. + Log::getInstance()->log("TS Demuxer", Log::DEBUG, "Packet overflow, pid %d", pid); + if (packet->submit() == 0) + return 0; + packet->truncate(); + packet->write((UCHAR*)"\200\000\000", 3); + packet->write(buf, datalen); + } + } + + return 1; +} diff --git a/demuxerts.h b/demuxerts.h new file mode 100644 index 0000000..0303917 --- /dev/null +++ b/demuxerts.h @@ -0,0 +1,51 @@ +/* + Copyright 2006 Mark Calderbank + + This file is part of VOMP. + + VOMP 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. + + VOMP 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 VOMP; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef DEMUXERTS_H +#define DEMUXERTS_H + +#include "demuxer.h" +#include "defines.h" + +#define TS_SIZE 188 +#define TS_SIG 0x47 + +class DemuxerTS : public Demuxer +{ + public: + DemuxerTS(int p_vID = 0, int p_aID = 0); + void flush(); + int scan(UCHAR* buf, int len); + int findPTS(UCHAR* buf, int len, ULLONG* dest); + void setVID(int p_vID); + void setAID(int p_aID); + int put(UCHAR* buf, int len); + + private: + int processTS(UCHAR* buf); + + UCHAR store[TS_SIZE]; // Storage for partial packets + int partPacket; // Length of partial packet stored from previous put() + PESPacket vPacket; // Video PES packet under construction + PESPacket aPacket; // Audio PES packet under construction + int vID, aID; // TS IDs for video/audio +}; + +#endif diff --git a/objects.mk b/objects.mk index 039988e..08cf8f8 100644 --- a/objects.mk +++ b/objects.mk @@ -3,15 +3,15 @@ OBJECTS1 = command.o log.o tcp.o dsock.o thread.o timers.o i18n.o mutex.o \ vdr.o recman.o recording.o recinfo.o channel.o rectimer.o event.o \ directory.o mark.o \ player.o playerradio.o vfeed.o afeed.o \ - demuxer.o demuxervdr.o stream.o draintarget.o \ + demuxer.o demuxervdr.o demuxerts.o stream.o draintarget.o \ viewman.o box.o region.o colour.o view.o \ vinfo.o vquestion.o vwallpaper.o vrecordinglist.o vlivebanner.o \ - vmute.o vvolume.o voptions.o \ + vmute.o vvolume.o voptions.o \ vtimerlist.o vtimeredit.o voptionsmenu.o vrecordingmenu.o \ vchannellist.o vwelcome.o vvideolive.o vvideorec.o vepgsettimer.o \ vchannelselect.o vserverselect.o vconnect.o vepg.o vrecmove.o \ vradiorec.o vaudioselector.o \ widget.o wselectlist.o wjpeg.o wsymbol.o wbutton.o \ - woptionbox.o wtextbox.o wwss.o \ + woptionbox.o wtextbox.o wwss.o \ fonts/helvB24.o fonts/helvB18.o \ remote.o led.o mtd.o video.o audio.o osd.o surface.o diff --git a/player.cc b/player.cc index e613ca2..20ed763 100644 --- a/player.cc +++ b/player.cc @@ -64,7 +64,10 @@ int Player::init() mutex=CreateMutex(NULL,FALSE,NULL); #endif - demuxer = new DemuxerVDR(); + if (isRecording) + demuxer = new DemuxerVDR(); + else + demuxer = new DemuxerTS(); if (!demuxer) return 0; if (!demuxer->init(this, audio, video, 2097152, 524288)) @@ -157,11 +160,21 @@ void Player::setAudioChannel(int newChannel) // ----------------------------------- Externally called events +void Player::play(ULONG Vpid, ULONG Apid) +{ + // TS mode + DemuxerTS* dts = (DemuxerTS*)demuxer; + dts->setVID(Vpid); + dts->setAID(Apid); + play(); +} + void Player::play() { if (!initted) return; if (state == S_PLAY) return; lock(); + bool doUnlock = false; if (state == S_PAUSE_P) doUnlock = true; switchState(S_PLAY); @@ -901,8 +914,11 @@ void Player::threadFeedLive() thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength); writeLength += thisWrite; +// logger->log("Player", Log::DEBUG, "Player put %u to demuxer", thisWrite); + if (!thisWrite) { +// logger->log("Player", Log::DEBUG, "Demuxer full"); // demuxer is full and can't take anymore threadLock(); threadWaitForSignal(); diff --git a/player.h b/player.h index 2731d37..c76eece 100644 --- a/player.h +++ b/player.h @@ -31,6 +31,7 @@ #include "audio.h" #include "video.h" #include "demuxervdr.h" +#include "demuxerts.h" #include "vfeed.h" #include "afeed.h" #include "remote.h" @@ -58,7 +59,8 @@ class Player : public Thread_TYPE, public Callback void setLengthFrames(ULONG length); void setAudioChannel(int newChannel); - void play(); + void play(); // Recording + void play(ULONG vpid, ULONG apid); // Live TV void stop(); void pause(); void fastForward(); diff --git a/playerradio.cc b/playerradio.cc index 7b88c87..dfe79cf 100644 --- a/playerradio.cc +++ b/playerradio.cc @@ -64,7 +64,10 @@ int PlayerRadio::init(ULLONG tlengthBytes, ULONG tlengthPackets) mutex=CreateMutex(NULL,FALSE,NULL); #endif - demuxer = new DemuxerVDR(); + if (isRecording) + demuxer = new DemuxerVDR(); + else + demuxer = new DemuxerTS(); if (!demuxer) return 0; if (!demuxer->init(this, audio, NULL, 0, 40000)) @@ -190,6 +193,14 @@ ULONG PlayerRadio::getCurrentSeconds() // ----------------------------------- Externally called events +void PlayerRadio::play(ULONG Apid) +{ + // TS mode + DemuxerTS* dts = (DemuxerTS*)demuxer; + dts->setAID(Apid); + play(); +} + void PlayerRadio::play() { if (!initted) return; diff --git a/playerradio.h b/playerradio.h index f70ec18..ea6ac12 100644 --- a/playerradio.h +++ b/playerradio.h @@ -31,6 +31,7 @@ #include "audio.h" #include "video.h" #include "demuxervdr.h" +#include "demuxerts.h" #include "afeed.h" #include "remote.h" #include "vdr.h" @@ -55,6 +56,7 @@ class PlayerRadio : public Thread_TYPE, public Callback void setStartBytes(ULLONG startBytes); void play(); + void play(ULONG apid); // Live radio void stop(); void pause(); void jumpToPercent(int percent); diff --git a/vdr.cc b/vdr.cc index b98c9fc..b0934f5 100644 --- a/vdr.cc +++ b/vdr.cc @@ -1187,3 +1187,54 @@ MarkList* VDR::getMarks(char* fileName) return markList; } + +void VDR::getChannelPids(Channel* channel) +{ + UCHAR buffer[12]; + + *(unsigned long*)&buffer[0] = htonl(8); + *(unsigned long*)&buffer[4] = htonl(VDR_GETCHANNELPIDS); + *(unsigned long*)&buffer[8] = htonl(channel->number); + + MUTEX_LOCK(&mutex); + if (!connected) { MUTEX_UNLOCK(&mutex); return ; } + + int a = tcp->sendPacket(buffer, 12); + + if (a != 12) + { + disconnect(); + MUTEX_UNLOCK(&mutex); + return ; + } + + if (!getPacket()) + { + MUTEX_UNLOCK(&mutex); + return ; + } + + // Format of response + // vpid + // number of apids + // { + // apid + // lang string + // } + + channel->vpid = extractULONG(); + channel->numAPids = extractULONG(); + + for (ULONG i = 0; i < channel->numAPids; i++) + { + apid newapid; + newapid.pid = extractULONG(); + newapid.name = extractString(); + channel->apids.push_back(newapid); + } + + freePacket(); + MUTEX_UNLOCK(&mutex); + + return ; +} diff --git a/vdr.h b/vdr.h index bb1a16e..4e0e64c 100644 --- a/vdr.h +++ b/vdr.h @@ -126,6 +126,7 @@ class VDR ChannelList* getChannelsList(ULONG type); int streamChannel(ULONG number); + void getChannelPids(Channel* channel); UCHAR* getBlock(ULLONG position, UINT maxAmount, UINT* amountReceived); int stopStreaming(); @@ -189,6 +190,7 @@ class VDR const static ULONG VDR_GETNEXTIFRAME = 19; const static ULONG VDR_GETRECINFO = 20; const static ULONG VDR_GETMARKS = 21; + const static ULONG VDR_GETCHANNELPIDS = 22; int getPacket(); void freePacket(); diff --git a/vfeed.cc b/vfeed.cc index a7a885f..c73bd64 100644 --- a/vfeed.cc +++ b/vfeed.cc @@ -64,7 +64,6 @@ void VFeed::threadMethod() if (vlen) { - // Log::getInstance()->log("VFeed", Log::DEBUG, "Written %i", vlen); cb.call(this); } else diff --git a/vvideolive.cc b/vvideolive.cc index e477a85..2d5c05d 100644 --- a/vvideolive.cc +++ b/vvideolive.cc @@ -392,13 +392,19 @@ void VVideoLive::play(int noShowVLB) { if (!noShowVLB) doBanner(false); + // FIXME - change this streamType thingy to the new system using getPids + // FIXME - upgrade PlayerRadio to new getPids + + Channel* toPlay = (*chanList)[currentChannel]; + toPlay->loadPids(); + if (streamType == VDR::RADIO) { - ((PlayerRadio*)player)->play(); + ((PlayerRadio*)player)->play(toPlay->apids[0].pid); } else { - ((Player*)player)->play(); + ((Player*)player)->play(toPlay->vpid, toPlay->apids[0].pid); } } } -- 2.39.2