Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

1226 lines (1114 sloc) 37.981 kB
/*
* vdr-plugin-vnsi - XBMC server plugin for VDR
*
* Copyright (C) 2010 Alwin Esch (Team XBMC)
* Copyright (C) 2010, 2011 Alexander Pipelka
*
* http://www.xbmc.org
*
* 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, 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 XBMC; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/gpl.html
*
*/
#include <stdlib.h>
#include <sys/ioctl.h>
#include <time.h>
#include <libsi/section.h>
#include <libsi/descriptor.h>
#include <vdr/remux.h>
#include <vdr/channels.h>
#include <asm/byteorder.h>
#include "config.h"
#include "receiver.h"
#include "cxsocket.h"
#include "vnsicommand.h"
#include "responsepacket.h"
// --- cLiveReceiver -------------------------------------------------
class cLiveReceiver: public cReceiver
{
friend class cLiveStreamer;
private:
cLiveStreamer *m_Streamer;
protected:
virtual void Activate(bool On);
virtual void Receive(uchar *Data, int Length);
public:
cLiveReceiver(cLiveStreamer *Streamer, const cChannel *Channel, int Priority, const int *Pids);
virtual ~cLiveReceiver();
};
cLiveReceiver::cLiveReceiver(cLiveStreamer *Streamer, const cChannel *Channel, int Priority, const int *Pids)
: cReceiver(Channel, Priority)
, m_Streamer(Streamer)
{
DEBUGLOG("Starting live receiver");
}
cLiveReceiver::~cLiveReceiver()
{
DEBUGLOG("Killing live receiver");
}
void cLiveReceiver::Receive(uchar *Data, int Length)
{
int p = m_Streamer->Put(Data, Length);
if (p != Length)
m_Streamer->ReportOverflow(Length - p);
}
inline void cLiveReceiver::Activate(bool On)
{
m_Streamer->Activate(On);
}
// --- cLivePatFilter ----------------------------------------------------
class cLivePatFilter : public cFilter
{
private:
int m_pmtPid;
int m_pmtSid;
int m_pmtVersion;
const cChannel *m_Channel;
cLiveStreamer *m_Streamer;
int GetPid(SI::PMT::Stream& stream, eStreamType *type, char *langs, int *subtitlingType, int *compositionPageId, int *ancillaryPageId);
void GetLanguage(SI::PMT::Stream& stream, char *langs);
virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
public:
cLivePatFilter(cLiveStreamer *Streamer, const cChannel *Channel);
};
cLivePatFilter::cLivePatFilter(cLiveStreamer *Streamer, const cChannel *Channel)
{
DEBUGLOG("cStreamdevPatFilter(\"%s\")", Channel->Name());
m_Channel = Channel;
m_Streamer = Streamer;
m_pmtPid = 0;
m_pmtSid = 0;
m_pmtVersion = -1;
Set(0x00, 0x00); // PAT
}
static const char * const psStreamTypes[] = {
"UNKNOWN",
"ISO/IEC 11172 Video",
"ISO/IEC 13818-2 Video",
"ISO/IEC 11172 Audio",
"ISO/IEC 13818-3 Audio",
"ISO/IEC 13818-1 Privete sections",
"ISO/IEC 13818-1 Private PES data",
"ISO/IEC 13512 MHEG",
"ISO/IEC 13818-1 Annex A DSM CC",
"0x09",
"ISO/IEC 13818-6 Multiprotocol encapsulation",
"ISO/IEC 13818-6 DSM-CC U-N Messages",
"ISO/IEC 13818-6 Stream Descriptors",
"ISO/IEC 13818-6 Sections (any type, including private data)",
"ISO/IEC 13818-1 auxiliary",
"ISO/IEC 13818-7 Audio with ADTS transport sytax",
"ISO/IEC 14496-2 Visual (MPEG-4)",
"ISO/IEC 14496-3 Audio with LATM transport syntax",
"0x12", "0x13", "0x14", "0x15", "0x16", "0x17", "0x18", "0x19", "0x1a",
"ISO/IEC 14496-10 Video (MPEG-4 part 10/AVC, aka H.264)",
"",
};
void cLivePatFilter::GetLanguage(SI::PMT::Stream& stream, char *langs)
{
SI::Descriptor *d;
for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); )
{
switch (d->getDescriptorTag())
{
case SI::ISO639LanguageDescriptorTag:
{
SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
strn0cpy(langs, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1);
break;
}
default: ;
}
delete d;
}
}
int cLivePatFilter::GetPid(SI::PMT::Stream& stream, eStreamType *type, char *langs, int *subtitlingType, int *compositionPageId, int *ancillaryPageId)
{
SI::Descriptor *d;
*langs = 0;
if (!stream.getPid())
return 0;
if(m_Channel->Tpid() == stream.getPid())
{
DEBUGLOG("cStreamdevPatFilter PMT scanner: adding PID %d %s\n", stream.getPid(), "Teletext");
*type = stTELETEXT;
return stream.getPid();
}
switch (stream.getStreamType())
{
case 0x01: // ISO/IEC 11172 Video
case 0x02: // ISO/IEC 13818-2 Video
case 0x80: // ATSC Video MPEG2 (ATSC DigiCipher QAM)
DEBUGLOG("cStreamdevPatFilter PMT scanner adding PID %d (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()]);
*type = stMPEG2VIDEO;
return stream.getPid();
case 0x03: // ISO/IEC 11172 Audio
case 0x04: // ISO/IEC 13818-3 Audio
*type = stMPEG2AUDIO;
GetLanguage(stream, langs);
DEBUGLOG("cStreamdevPatFilter PMT scanner adding PID %d (%s) (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], langs);
return stream.getPid();
case 0x0f: // ISO/IEC 13818-7 Audio with ADTS transport syntax
case 0x11: // ISO/IEC 14496-3 Audio with LATM transport syntax
*type = stAAC;
GetLanguage(stream, langs);
DEBUGLOG("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "AAC", langs);
return stream.getPid();
#if 1
case 0x07: // ISO/IEC 13512 MHEG
case 0x08: // ISO/IEC 13818-1 Annex A DSM CC
case 0x0a: // ISO/IEC 13818-6 Multiprotocol encapsulation
case 0x0b: // ISO/IEC 13818-6 DSM-CC U-N Messages
case 0x0c: // ISO/IEC 13818-6 Stream Descriptors
case 0x0d: // ISO/IEC 13818-6 Sections (any type, including private data)
case 0x0e: // ISO/IEC 13818-1 auxiliary
#endif
case 0x10: // ISO/IEC 14496-2 Visual (MPEG-4)
DEBUGLOG("cStreamdevPatFilter PMT scanner: Not adding PID %d (%s) (skipped)\n", stream.getPid(), psStreamTypes[stream.getStreamType()]);
break;
case 0x1b: // ISO/IEC 14496-10 Video (MPEG-4 part 10/AVC, aka H.264)
DEBUGLOG("cStreamdevPatFilter PMT scanner adding PID %d (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()]);
*type = stH264;
return stream.getPid();
case 0x05: // ISO/IEC 13818-1 private sections
case 0x06: // ISO/IEC 13818-1 PES packets containing private data
for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); )
{
switch (d->getDescriptorTag())
{
case SI::AC3DescriptorTag:
DEBUGLOG("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "AC3", langs);
*type = stAC3;
GetLanguage(stream, langs);
delete d;
return stream.getPid();
case SI::EnhancedAC3DescriptorTag:
DEBUGLOG("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "EAC3", langs);
*type = stEAC3;
GetLanguage(stream, langs);
delete d;
return stream.getPid();
case SI::DTSDescriptorTag:
DEBUGLOG("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "DTS", langs);
*type = stDTS;
GetLanguage(stream, langs);
delete d;
return stream.getPid();
case SI::AACDescriptorTag:
DEBUGLOG("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "AAC", langs);
*type = stAAC;
GetLanguage(stream, langs);
delete d;
return stream.getPid();
case SI::TeletextDescriptorTag:
DEBUGLOG("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "Teletext");
*type = stTELETEXT;
delete d;
return stream.getPid();
case SI::SubtitlingDescriptorTag:
{
*type = stDVBSUB;
*langs = 0;
*subtitlingType = 0;
*compositionPageId = 0;
*ancillaryPageId = 0;
SI::SubtitlingDescriptor *sd = (SI::SubtitlingDescriptor *)d;
SI::SubtitlingDescriptor::Subtitling sub;
char *s = langs;
int n = 0;
for (SI::Loop::Iterator it; sd->subtitlingLoop.getNext(sub, it); )
{
if (sub.languageCode[0])
{
*subtitlingType = sub.getSubtitlingType();
*compositionPageId = sub.getCompositionPageId();
*ancillaryPageId = sub.getAncillaryPageId();
if (n > 0)
*s++ = '+';
strn0cpy(s, I18nNormalizeLanguageCode(sub.languageCode), MAXLANGCODE1);
s += strlen(s);
if (n++ > 1)
break;
}
}
delete d;
DEBUGLOG("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "DVBSUB");
return stream.getPid();
}
default:
DEBUGLOG("cStreamdevPatFilter PMT scanner: NOT adding PID %d (%s) %s (%i)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "UNKNOWN", d->getDescriptorTag());
break;
}
delete d;
}
break;
default:
/* This following section handles all the cases where the audio track
* info is stored in PMT user info with stream id >= 0x81
* we check the registration format identifier to see if it
* holds "AC-3"
*/
if (stream.getStreamType() >= 0x81)
{
bool found = false;
for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); )
{
switch (d->getDescriptorTag())
{
case SI::RegistrationDescriptorTag:
/* unfortunately libsi does not implement RegistrationDescriptor */
if (d->getLength() >= 4)
{
found = true;
SI::CharArray rawdata = d->getData();
if (/*rawdata[0] == 5 && rawdata[1] >= 4 && */
rawdata[2] == 'A' && rawdata[3] == 'C' &&
rawdata[4] == '-' && rawdata[5] == '3')
{
DEBUGLOG("cStreamdevPatFilter PMT scanner: Adding pid %d (type 0x%x) RegDesc len %d (%c%c%c%c)\n",
stream.getPid(), stream.getStreamType(), d->getLength(), rawdata[2], rawdata[3], rawdata[4], rawdata[5]);
*type = stAC3;
delete d;
return stream.getPid();
}
}
break;
default:
break;
}
delete d;
}
if (!found)
{
DEBUGLOG("NOT adding PID %d (type 0x%x) RegDesc not found -> UNKNOWN\n", stream.getPid(), stream.getStreamType());
}
}
DEBUGLOG("cStreamdevPatFilter PMT scanner: NOT adding PID %d (%s) %s\n", stream.getPid(), psStreamTypes[stream.getStreamType()<0x1c?stream.getStreamType():0], "UNKNOWN");
break;
}
*type = stNone;
return 0;
}
void cLivePatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
{
if (Pid == 0x00)
{
if (Tid == 0x00)
{
SI::PAT pat(Data, false);
if (!pat.CheckCRCAndParse())
return;
SI::PAT::Association assoc;
for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); )
{
if (!assoc.isNITPid())
{
const cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId());
if (Channel && (Channel == m_Channel))
{
int prevPmtPid = m_pmtPid;
if (0 != (m_pmtPid = assoc.getPid()))
{
m_pmtSid = assoc.getServiceId();
if (m_pmtPid != prevPmtPid)
{
Add(m_pmtPid, 0x02);
m_pmtVersion = -1;
}
return;
}
}
}
}
}
}
else if (Pid == m_pmtPid && Tid == SI::TableIdPMT && Source() && Transponder())
{
SI::PMT pmt(Data, false);
if (!pmt.CheckCRCAndParse())
return;
if (pmt.getServiceId() != m_pmtSid)
return; // skip broken PMT records
if (m_pmtVersion != -1)
{
if (m_pmtVersion != pmt.getVersionNumber())
{
// printf("cStreamdevPatFilter: PMT version changed, detaching all pids\n");
cFilter::Del(m_pmtPid, 0x02);
m_pmtPid = 0; // this triggers PAT scan
}
return;
}
m_pmtVersion = pmt.getVersionNumber();
SI::PMT::Stream stream;
int pids[MAXRECEIVEPIDS + 1];
eStreamType types[MAXRECEIVEPIDS + 1];
char langs[MAXRECEIVEPIDS + 1][MAXLANGCODE2];
int subtitlingType[MAXRECEIVEPIDS + 1];
int compositionPageId[MAXRECEIVEPIDS + 1];
int ancillaryPageId[MAXRECEIVEPIDS + 1];
int streams = 0;
for (SI::Loop::Iterator it; pmt.streamLoop.getNext(stream, it); )
{
eStreamType type;
int pid = GetPid(stream, &type, langs[streams], &subtitlingType[streams], &compositionPageId[streams], &ancillaryPageId[streams]);
if (0 != pid && streams < MAXRECEIVEPIDS)
{
pids[streams] = pid;
types[streams] = type;
streams++;
}
}
pids[streams] = 0;
int newstreams = 0;
for (int i = 0; i < streams; i++)
{
if (m_Streamer->HaveStreamDemuxer(pids[i], types[i]) == -1)
newstreams++;
}
if (newstreams > 0)
{
if (m_Streamer->m_Receiver)
{
DEBUGLOG("Detaching Live Receiver");
m_Streamer->m_Device->Detach(m_Streamer->m_Receiver);
DELETENULL(m_Streamer->m_Receiver);
}
for (int idx = 0; idx < MAXRECEIVEPIDS; ++idx)
{
if (m_Streamer->m_Streams[idx])
{
DELETENULL(m_Streamer->m_Streams[idx]);
m_Streamer->m_Pids[idx] = 0;
}
}
m_Streamer->m_NumStreams = 0;
m_Streamer->m_streamReady = false;
m_Streamer->m_IFrameSeen = false;
for (int i = 0; i < streams; i++)
{
switch (types[i])
{
case stMPEG2AUDIO:
{
m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stMPEG2AUDIO, pids[i]);
m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
m_Streamer->m_NumStreams++;
break;
}
case stMPEG2VIDEO:
{
m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stMPEG2VIDEO, pids[i]);
m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
m_Streamer->m_NumStreams++;
break;
}
case stH264:
{
m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stH264, pids[i]);
m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
m_Streamer->m_NumStreams++;
break;
}
case stAC3:
{
m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stAC3, pids[i]);
m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
m_Streamer->m_NumStreams++;
break;
}
case stEAC3:
{
m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stEAC3, pids[i]);
m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
m_Streamer->m_NumStreams++;
break;
}
case stDTS:
{
m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stDTS, pids[i]);
m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
m_Streamer->m_NumStreams++;
break;
}
case stAAC:
{
m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stAAC, pids[i]);
m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
m_Streamer->m_NumStreams++;
break;
}
case stDVBSUB:
{
m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stDVBSUB, pids[i]);
m_Streamer->m_Streams[m_Streamer->m_NumStreams]->SetLanguage(langs[i]);
m_Streamer->m_Streams[m_Streamer->m_NumStreams]->SetSubtitlingDescriptor(subtitlingType[i], compositionPageId[i], ancillaryPageId[i]);
m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
m_Streamer->m_NumStreams++;
break;
}
case stTELETEXT:
{
m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stTELETEXT, pids[i]);
m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
m_Streamer->m_NumStreams++;
break;
}
default:
break;
}
}
m_Streamer->m_Receiver = new cLiveReceiver(m_Streamer, m_Channel, m_Streamer->m_Priority, m_Streamer->m_Pids);
m_Streamer->m_Device->AttachReceiver(m_Streamer->m_Receiver);
INFOLOG("Currently unknown new streams found, receiver and demuxers reinited\n");
m_Streamer->RequestStreamChange();
}
else if (!m_Streamer->m_Receiver)
{
m_Streamer->m_Receiver = new cLiveReceiver(m_Streamer, m_Channel, m_Streamer->m_Priority, m_Streamer->m_Pids);
m_Streamer->m_Device->AttachReceiver(m_Streamer->m_Receiver);
}
}
}
// --- cLiveStreamer -------------------------------------------------
cLiveStreamer::cLiveStreamer(uint32_t timeout)
: cThread("cLiveStreamer stream processor")
, cRingBufferLinear(MEGABYTE(3), TS_SIZE, true)
, m_scanTimeout(timeout)
{
m_Channel = NULL;
m_Priority = 0;
m_Socket = NULL;
m_Device = NULL;
m_Receiver = NULL;
m_PatFilter = NULL;
m_Frontend = -1;
m_NumStreams = 0;
m_streamReady = false;
m_IsAudioOnly = false;
m_IsMPEGPS = false;
m_startup = true;
m_SignalLost = false;
m_IFrameSeen = false;
m_requestStreamChange = false;
m_packetEmpty = new cResponsePacket;
m_packetEmpty->initStream(VNSI_STREAM_MUXPKT, 0, 0, 0, 0);
memset(&m_FrontendInfo, 0, sizeof(m_FrontendInfo));
for (int idx = 0; idx < MAXRECEIVEPIDS; ++idx)
{
m_Streams[idx] = NULL;
m_Pids[idx] = 0;
}
if(m_scanTimeout == 0)
m_scanTimeout = VNSIServerConfig.stream_timeout;
SetTimeouts(0, 100);
}
cLiveStreamer::~cLiveStreamer()
{
DEBUGLOG("Started to delete live streamer");
Cancel(-1);
if (m_Device)
{
if (m_Receiver)
{
DEBUGLOG("Detaching Live Receiver");
m_Device->Detach(m_Receiver);
}
else
{
DEBUGLOG("No live receiver present");
}
if (m_PatFilter)
{
DEBUGLOG("Detaching Live Filter");
m_Device->Detach(m_PatFilter);
}
else
{
DEBUGLOG("No live filter present");
}
for (int idx = 0; idx < MAXRECEIVEPIDS; ++idx)
{
if (m_Streams[idx])
{
DEBUGLOG("Deleting stream demuxer %i for pid=%i and type=%i", m_Streams[idx]->GetStreamID(), m_Streams[idx]->GetPID(), m_Streams[idx]->Type());
DELETENULL(m_Streams[idx]);
m_Pids[idx] = 0;
}
}
if (m_Receiver)
{
DEBUGLOG("Deleting Live Receiver");
DELETENULL(m_Receiver);
}
if (m_PatFilter)
{
DEBUGLOG("Deleting Live Filter");
DELETENULL(m_PatFilter);
}
}
if (m_Frontend >= 0)
{
close(m_Frontend);
m_Frontend = -1;
}
delete m_packetEmpty;
DEBUGLOG("Finished to delete live streamer");
}
void cLiveStreamer::RequestStreamChange()
{
m_requestStreamChange = true;
}
void cLiveStreamer::Action(void)
{
int size = 0;
int used = 0;
unsigned char *buf = NULL;
m_startup = true;
cTimeMs last_info;
cTimeMs starttime;
m_last_tick.Set(0);
while (Running())
{
size = 0;
used = 0;
buf = Get(size);
if (!m_Receiver->IsAttached())
{
INFOLOG("returning from streamer thread, receiver is no more attached");
break;
}
// no data
if (buf == NULL || size <= TS_SIZE)
{
// keep client going
if(m_last_tick.Elapsed() >= (uint64_t)(m_scanTimeout*1000))
{
INFOLOG("No Signal");
sendStreamStatus();
m_last_tick.Set(0);
m_SignalLost = true;
}
continue;
}
/* Make sure we are looking at a TS packet */
while (size > TS_SIZE)
{
if (buf[0] == TS_SYNC_BYTE && buf[TS_SIZE] == TS_SYNC_BYTE)
break;
used++;
buf++;
size--;
}
// Send stream information as the first packet on startup
if (IsStarting() && IsReady())
{
INFOLOG("streaming of channel started");
last_info.Set(0);
m_last_tick.Set(0);
m_requestStreamChange = true;
m_startup = false;
}
while (size >= TS_SIZE)
{
if(!Running())
{
break;
}
unsigned int ts_pid = TsPid(buf);
cTSDemuxer *demuxer = FindStreamDemuxer(ts_pid);
if (demuxer)
{
demuxer->ProcessTSPacket(buf);
}
else
INFOLOG("no muxer found");
buf += TS_SIZE;
size -= TS_SIZE;
used += TS_SIZE;
}
Del(used);
if(last_info.Elapsed() >= 10*1000 && IsReady())
{
last_info.Set(0);
sendStreamInfo();
sendSignalInfo();
}
}
INFOLOG("exit streamer thread");
}
bool cLiveStreamer::StreamChannel(const cChannel *channel, int priority, cxSocket *Socket, cResponsePacket *resp)
{
if (channel == NULL)
{
ERRORLOG("Starting streaming of channel without valid channel");
return false;
}
m_Channel = channel;
m_Priority = priority;
m_Socket = Socket;
m_Device = cDevice::GetDevice(channel, m_Priority, true);
if (m_Device != NULL)
{
DEBUGLOG("Successfully found following device: %p (%d) for receiving", m_Device, m_Device ? m_Device->CardIndex() + 1 : 0);
if (m_Device->SwitchChannel(m_Channel, false))
{
if (m_Channel->Vpid())
{
#if APIVERSNUM >= 10701
if (m_Channel->Vtype() == 0x1B)
m_Streams[m_NumStreams] = new cTSDemuxer(this, m_NumStreams, stH264, m_Channel->Vpid());
else
#endif
m_Streams[m_NumStreams] = new cTSDemuxer(this, m_NumStreams, stMPEG2VIDEO, m_Channel->Vpid());
m_Pids[m_NumStreams] = m_Channel->Vpid();
m_NumStreams++;
}
else
{
/* m_streamReady is set by the Video demuxers, to have always valid stream informations
* like height and width. But if no Video PID is present like for radio channels
* VNSI will deadlock
*/
m_streamReady = true;
m_IsAudioOnly = true;
}
const int *APids = m_Channel->Apids();
for ( ; *APids && m_NumStreams < MAXRECEIVEPIDS; APids++)
{
int index = 0;
if (!FindStreamDemuxer(*APids))
{
m_Pids[m_NumStreams] = *APids;
m_Streams[m_NumStreams] = new cTSDemuxer(this, m_NumStreams, stMPEG2AUDIO, *APids);
m_Streams[m_NumStreams]->SetLanguage(m_Channel->Alang(index));
m_NumStreams++;
}
index++;
}
const int *DPids = m_Channel->Dpids();
for ( ; *DPids && m_NumStreams < MAXRECEIVEPIDS; DPids++)
{
int index = 0;
if (!FindStreamDemuxer(*DPids))
{
m_Pids[m_NumStreams] = *DPids;
m_Streams[m_NumStreams] = new cTSDemuxer(this, m_NumStreams, stAC3, *DPids);
m_Streams[m_NumStreams]->SetLanguage(m_Channel->Dlang(index));
m_NumStreams++;
}
index++;
}
const int *SPids = m_Channel->Spids();
if (SPids)
{
int index = 0;
for ( ; *SPids && m_NumStreams < MAXRECEIVEPIDS; SPids++)
{
if (!FindStreamDemuxer(*SPids))
{
m_Pids[m_NumStreams] = *SPids;
m_Streams[m_NumStreams] = new cTSDemuxer(this, m_NumStreams, stDVBSUB, *SPids);
m_Streams[m_NumStreams]->SetLanguage(m_Channel->Slang(index));
#if APIVERSNUM >= 10709
m_Streams[m_NumStreams]->SetSubtitlingDescriptor(m_Channel->SubtitlingType(index),
m_Channel->CompositionPageId(index),
m_Channel->AncillaryPageId(index));
#endif
m_NumStreams++;
}
index++;
}
}
if (m_Channel->Tpid())
{
m_Streams[m_NumStreams] = new cTSDemuxer(this, m_NumStreams, stTELETEXT, m_Channel->Tpid());
m_Pids[m_NumStreams] = m_Channel->Tpid();
m_NumStreams++;
}
m_Streams[m_NumStreams] = NULL;
m_Pids[m_NumStreams] = 0;
/* Send the OK response here, that it is before the Stream end message */
resp->add_U32(VNSI_RET_OK);
resp->finalise();
m_Socket->write(resp->getPtr(), resp->getLen());
if (m_Channel && ((m_Channel->Source() >> 24) == 'V')) m_IsMPEGPS = true;
if (m_NumStreams > 0 && m_Socket)
{
dsyslog("VNSI: Creating new live Receiver");
m_PatFilter = new cLivePatFilter(this, m_Channel);
m_Device->AttachFilter(m_PatFilter);
}
INFOLOG("Successfully switched to channel %i - %s", m_Channel->Number(), m_Channel->Name());
return true;
}
else
{
ERRORLOG("Can't switch to channel %i - %s", m_Channel->Number(), m_Channel->Name());
}
}
else
{
ERRORLOG("Can't get device for channel %i - %s", m_Channel->Number(), m_Channel->Name());
}
return false;
}
cTSDemuxer *cLiveStreamer::FindStreamDemuxer(int Pid)
{
int idx;
for (idx = 0; idx < m_NumStreams; ++idx)
if (m_Streams[idx] && m_Streams[idx]->GetPID() == Pid)
return m_Streams[idx];
return NULL;
}
int cLiveStreamer::HaveStreamDemuxer(int Pid, eStreamType streamType)
{
int idx;
for (idx = 0; idx < m_NumStreams; ++idx)
if (m_Streams[idx] && (Pid == 0 || m_Streams[idx]->GetPID() == Pid) && m_Streams[idx]->Type() == streamType)
return idx;
return -1;
}
inline void cLiveStreamer::Activate(bool On)
{
if (On)
{
DEBUGLOG("VDR active, sending stream start message");
Start();
}
else
{
DEBUGLOG("VDR inactive, sending stream end message");
Cancel(5);
}
}
void cLiveStreamer::Attach(void)
{
DEBUGLOG("%s", __FUNCTION__);
if (m_Device)
{
if (m_Receiver)
{
m_Device->Detach(m_Receiver);
m_Device->AttachReceiver(m_Receiver);
}
}
}
void cLiveStreamer::Detach(void)
{
DEBUGLOG("%s", __FUNCTION__);
if (m_Device)
{
if (m_Receiver)
m_Device->Detach(m_Receiver);
}
}
void cLiveStreamer::sendStreamPacket(sStreamPacket *pkt)
{
if(pkt == NULL)
return;
if(pkt->size == 0)
return;
if(!m_IsAudioOnly && !m_IFrameSeen && (pkt->frametype != PKT_I_FRAME))
return;
if(m_requestStreamChange)
sendStreamChange();
m_IFrameSeen = true;
if (!m_streamHeader.initStream(VNSI_STREAM_MUXPKT, pkt->id, pkt->duration, pkt->pts, pkt->dts))
{
ERRORLOG("stream response packet init fail");
return;
}
m_streamHeader.setLen(m_streamHeader.getStreamHeaderLength() + pkt->size);
m_streamHeader.finaliseStream();
m_Socket->write(m_streamHeader.getPtr(), m_streamHeader.getStreamHeaderLength(), -1, true);
m_Socket->write(pkt->data, pkt->size);
m_last_tick.Set(0);
m_SignalLost = false;
}
void cLiveStreamer::sendStreamChange()
{
cResponsePacket *resp = new cResponsePacket();
if (!resp->initStream(VNSI_STREAM_CHANGE, 0, 0, 0, 0))
{
ERRORLOG("stream response packet init fail");
delete resp;
return;
}
for (int idx = 0; idx < m_NumStreams; ++idx)
{
if (m_Streams[idx])
{
resp->add_U32(m_Streams[idx]->GetStreamID());
if (m_Streams[idx]->Type() == stMPEG2AUDIO)
{
resp->add_String("MPEG2AUDIO");
resp->add_String(m_Streams[idx]->GetLanguage());
}
else if (m_Streams[idx]->Type() == stMPEG2VIDEO)
{
resp->add_String("MPEG2VIDEO");
resp->add_U32(m_Streams[idx]->GetFpsScale());
resp->add_U32(m_Streams[idx]->GetFpsRate());
resp->add_U32(m_Streams[idx]->GetHeight());
resp->add_U32(m_Streams[idx]->GetWidth());
resp->add_double(m_Streams[idx]->GetAspect());
}
else if (m_Streams[idx]->Type() == stAC3)
{
resp->add_String("AC3");
resp->add_String(m_Streams[idx]->GetLanguage());
}
else if (m_Streams[idx]->Type() == stH264)
{
resp->add_String("H264");
resp->add_U32(m_Streams[idx]->GetFpsScale());
resp->add_U32(m_Streams[idx]->GetFpsRate());
resp->add_U32(m_Streams[idx]->GetHeight());
resp->add_U32(m_Streams[idx]->GetWidth());
resp->add_double(m_Streams[idx]->GetAspect());
}
else if (m_Streams[idx]->Type() == stDVBSUB)
{
resp->add_String("DVBSUB");
resp->add_String(m_Streams[idx]->GetLanguage());
resp->add_U32(m_Streams[idx]->CompositionPageId());
resp->add_U32(m_Streams[idx]->AncillaryPageId());
}
else if (m_Streams[idx]->Type() == stTELETEXT)
resp->add_String("TELETEXT");
else if (m_Streams[idx]->Type() == stAAC)
{
resp->add_String("AAC");
resp->add_String(m_Streams[idx]->GetLanguage());
}
else if (m_Streams[idx]->Type() == stEAC3)
{
resp->add_String("EAC3");
resp->add_String(m_Streams[idx]->GetLanguage());
}
else if (m_Streams[idx]->Type() == stDTS)
{
resp->add_String("DTS");
resp->add_String(m_Streams[idx]->GetLanguage());
}
}
}
resp->finaliseStream();
m_Socket->write(resp->getPtr(), resp->getLen(), -1, true);
delete resp;
m_requestStreamChange = false;
sendStreamInfo();
}
void cLiveStreamer::sendSignalInfo()
{
/* If no frontend is found m_Frontend is set to -2, in this case
return a empty signalinfo package */
if (m_Frontend == -2)
{
cResponsePacket *resp = new cResponsePacket();
if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0))
{
ERRORLOG("stream response packet init fail");
delete resp;
return;
}
resp->add_String(*cString::sprintf("Unknown"));
resp->add_String(*cString::sprintf("Unknown"));
resp->add_U32(0);
resp->add_U32(0);
resp->add_U32(0);
resp->add_U32(0);
resp->finaliseStream();
m_Socket->write(resp->getPtr(), resp->getLen(), -1, true);
delete resp;
return;
}
if (m_Channel && ((m_Channel->Source() >> 24) == 'V'))
{
if (m_Frontend < 0)
{
for (int i = 0; i < 8; i++)
{
m_DeviceString = cString::sprintf("/dev/video%d", i);
m_Frontend = open(m_DeviceString, O_RDONLY | O_NONBLOCK);
if (m_Frontend >= 0)
{
if (ioctl(m_Frontend, VIDIOC_QUERYCAP, &m_vcap) < 0)
{
ERRORLOG("cannot read analog frontend info.");
close(m_Frontend);
m_Frontend = -1;
memset(&m_vcap, 0, sizeof(m_vcap));
continue;
}
break;
}
}
if (m_Frontend < 0)
m_Frontend = -2;
}
if (m_Frontend >= 0)
{
cResponsePacket *resp = new cResponsePacket();
if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0))
{
ERRORLOG("stream response packet init fail");
delete resp;
return;
}
resp->add_String(*cString::sprintf("Analog #%s - %s (%s)", *m_DeviceString, (char *) m_vcap.card, m_vcap.driver));
resp->add_String("");
resp->add_U32(0);
resp->add_U32(0);
resp->add_U32(0);
resp->add_U32(0);
resp->finaliseStream();
m_Socket->write(resp->getPtr(), resp->getLen(), -1, true);
delete resp;
}
}
else
{
if (m_Frontend < 0)
{
m_DeviceString = cString::sprintf(FRONTEND_DEVICE, m_Device->CardIndex(), 0);
m_Frontend = open(m_DeviceString, O_RDONLY | O_NONBLOCK);
if (m_Frontend >= 0)
{
if (ioctl(m_Frontend, FE_GET_INFO, &m_FrontendInfo) < 0)
{
ERRORLOG("cannot read frontend info.");
close(m_Frontend);
m_Frontend = -2;
memset(&m_FrontendInfo, 0, sizeof(m_FrontendInfo));
return;
}
}
}
if (m_Frontend >= 0)
{
cResponsePacket *resp = new cResponsePacket();
if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0))
{
ERRORLOG("stream response packet init fail");
delete resp;
return;
}
fe_status_t status;
uint16_t fe_snr;
uint16_t fe_signal;
uint32_t fe_ber;
uint32_t fe_unc;
memset(&status, 0, sizeof(status));
ioctl(m_Frontend, FE_READ_STATUS, &status);
if (ioctl(m_Frontend, FE_READ_SIGNAL_STRENGTH, &fe_signal) == -1)
fe_signal = -2;
if (ioctl(m_Frontend, FE_READ_SNR, &fe_snr) == -1)
fe_snr = -2;
if (ioctl(m_Frontend, FE_READ_BER, &fe_ber) == -1)
fe_ber = -2;
if (ioctl(m_Frontend, FE_READ_UNCORRECTED_BLOCKS, &fe_unc) == -1)
fe_unc = -2;
switch (m_Channel->Source() & cSource::st_Mask)
{
case cSource::stSat:
resp->add_String(*cString::sprintf("DVB-S%s #%d - %s", (m_FrontendInfo.caps & 0x10000000) ? "2" : "", cDevice::ActualDevice()->CardIndex(), m_FrontendInfo.name));
break;
case cSource::stCable:
resp->add_String(*cString::sprintf("DVB-C #%d - %s", cDevice::ActualDevice()->CardIndex(), m_FrontendInfo.name));
break;
case cSource::stTerr:
resp->add_String(*cString::sprintf("DVB-T #%d - %s", cDevice::ActualDevice()->CardIndex(), m_FrontendInfo.name));
break;
}
resp->add_String(*cString::sprintf("%s:%s:%s:%s:%s", (status & FE_HAS_LOCK) ? "LOCKED" : "-", (status & FE_HAS_SIGNAL) ? "SIGNAL" : "-", (status & FE_HAS_CARRIER) ? "CARRIER" : "-", (status & FE_HAS_VITERBI) ? "VITERBI" : "-", (status & FE_HAS_SYNC) ? "SYNC" : "-"));
resp->add_U32(fe_snr);
resp->add_U32(fe_signal);
resp->add_U32(fe_ber);
resp->add_U32(fe_unc);
resp->finaliseStream();
m_Socket->write(resp->getPtr(), resp->getLen(), -1, true);
delete resp;
}
}
}
void cLiveStreamer::sendStreamInfo()
{
if(m_NumStreams == 0)
{
return;
}
cResponsePacket *resp = new cResponsePacket();
if (!resp->initStream(VNSI_STREAM_CONTENTINFO, 0, 0, 0, 0))
{
ERRORLOG("stream response packet init fail");
delete resp;
return;
}
for (int idx = 0; idx < m_NumStreams; ++idx)
{
if (m_Streams[idx])
{
if (m_Streams[idx]->Type() == stMPEG2AUDIO ||
m_Streams[idx]->Type() == stAC3 ||
m_Streams[idx]->Type() == stEAC3 ||
m_Streams[idx]->Type() == stDTS ||
m_Streams[idx]->Type() == stAAC)
{
resp->add_U32(m_Streams[idx]->GetStreamID());
resp->add_String(m_Streams[idx]->GetLanguage());
resp->add_U32(m_Streams[idx]->GetChannels());
resp->add_U32(m_Streams[idx]->GetSampleRate());
resp->add_U32(m_Streams[idx]->GetBlockAlign());
resp->add_U32(m_Streams[idx]->GetBitRate());
resp->add_U32(m_Streams[idx]->GetBitsPerSample());
}
else if (m_Streams[idx]->Type() == stMPEG2VIDEO || m_Streams[idx]->Type() == stH264)
{
resp->add_U32(m_Streams[idx]->GetStreamID());
resp->add_U32(m_Streams[idx]->GetFpsScale());
resp->add_U32(m_Streams[idx]->GetFpsRate());
resp->add_U32(m_Streams[idx]->GetHeight());
resp->add_U32(m_Streams[idx]->GetWidth());
resp->add_double(m_Streams[idx]->GetAspect());
}
else if (m_Streams[idx]->Type() == stDVBSUB)
{
resp->add_U32(m_Streams[idx]->GetStreamID());
resp->add_String(m_Streams[idx]->GetLanguage());
resp->add_U32(m_Streams[idx]->CompositionPageId());
resp->add_U32(m_Streams[idx]->AncillaryPageId());
}
}
}
resp->finaliseStream();
m_Socket->write(resp->getPtr(), resp->getLen());
delete resp;
}
void cLiveStreamer::sendStreamStatus()
{
cResponsePacket *resp = new cResponsePacket();
if (!resp->initStream(VNSI_STREAM_STATUS, 0, 0, 0, 0))
{
ERRORLOG("stream response packet init fail");
delete resp;
return;
}
resp->add_String("No Signal");
resp->finaliseStream();
m_Socket->write(resp->getPtr(), resp->getLen());
delete resp;
}
Jump to Line
Something went wrong with that request. Please try again.