[vdr-plugin-vnsiserver] 01/03: Imported Upstream version 0.9.1+git20131103
Tobias Grimm
tiber-guest at alioth.debian.org
Sun Nov 3 17:58:08 UTC 2013
This is an automated email from the git hooks/post-receive script.
tiber-guest pushed a commit to branch master
in repository vdr-plugin-vnsiserver.
commit 7c4b33dd65b3662e69ebc2c1b65762fbc13338d0
Author: etobi <git at e-tobi.net>
Date: Sun Nov 3 18:56:13 2013 +0100
Imported Upstream version 0.9.1+git20131103
---
Makefile | 2 +-
config.h | 6 +
cxsocket.c | 12 +
cxsocket.h | 5 +
demuxer.c | 302 +++++++++++++++++--
demuxer.h | 12 +
parser.c | 380 ++++++++++++++++++------
parser.h | 120 ++------
parser_AAC.c | 15 +-
parser_AAC.h | 12 +-
parser_AC3.c | 14 +-
parser_AC3.h | 12 +-
parser_DTS.c | 14 +-
parser_DTS.h | 12 +-
parser_MPEGAudio.c | 15 +-
parser_MPEGAudio.h | 12 +-
parser_MPEGVideo.c | 54 ++--
parser_MPEGVideo.h | 16 +-
parser_Subtitle.c | 16 +-
parser_Subtitle.h | 12 +-
parser_Teletext.c | 14 +-
parser_Teletext.h | 12 +-
parser_h264.c | 42 +--
parser_h264.h | 13 +-
recplayer.c | 62 +++-
recplayer.h | 4 +-
requestpacket.c | 16 +
requestpacket.h | 1 +
responsepacket.c | 11 +-
responsepacket.h | 6 +-
setup.c | 72 +++--
setup.h | 7 +-
streamer.c | 204 ++++++++++---
streamer.h | 9 +-
videobuffer.c | 834 +++++++++++++++++++++++++++++++++++++++++++++++++++-
videobuffer.h | 24 +-
videoinput.c | 6 +
videoinput.h | 1 +
vnsi.c | 26 ++
vnsi.h | 9 +-
vnsiclient.c | 94 +++++-
vnsiclient.h | 5 +-
vnsicommand.h | 16 +-
vnsiserver.c | 50 +++-
44 files changed, 2115 insertions(+), 466 deletions(-)
diff --git a/Makefile b/Makefile
index ad09fba..8f37c92 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@
# This name will be used in the '-P...' option of VDR to load the plugin.
# By default the main source file also carries this name.
-PLUGIN = vnsiserver3
+PLUGIN = vnsiserver4
### The version number of this plugin (taken from the main source file):
diff --git a/config.h b/config.h
index d99deec..24657ad 100644
--- a/config.h
+++ b/config.h
@@ -64,6 +64,12 @@
#define FOLDERDELIMCHAR '~'
#endif
+// Error flags
+#define ERROR_PES_GENERAL 0x01
+#define ERROR_PES_SCRAMBLE 0x02
+#define ERROR_PES_STARTCODE 0x04
+#define ERROR_DEMUX_NODATA 0x10
+
class cVNSIServerConfig
{
public:
diff --git a/cxsocket.c b/cxsocket.c
index b9638d3..8f2fa5d 100644
--- a/cxsocket.c
+++ b/cxsocket.c
@@ -49,6 +49,10 @@
#include "config.h"
#include "cxsocket.h"
+#ifndef MSG_MORE
+#define MSG_MORE 0
+#endif
+
cxSocket::~cxSocket()
{
close();
@@ -63,6 +67,14 @@ void cxSocket::close() {
}
}
+void cxSocket::Shutdown()
+{
+ if(m_fd >= 0)
+ {
+ ::shutdown(m_fd, SHUT_RD);
+ }
+}
+
void cxSocket::LockWrite()
{
m_MutexWrite.Lock();
diff --git a/cxsocket.h b/cxsocket.h
index 9d0ecbd..4088152 100644
--- a/cxsocket.h
+++ b/cxsocket.h
@@ -27,6 +27,10 @@
#ifndef VNSI_CXSOCKET_H
#define VNSI_CXSOCKET_H
+#ifdef __FreeBSD__
+#include <netinet/in.h>
+#endif
+
#include <inttypes.h>
#include <sys/types.h>
#include <sys/socket.h>
@@ -46,6 +50,7 @@ class cxSocket
~cxSocket();
void SetHandle(int h);
void close(void);
+ void Shutdown(void);
void LockWrite();
void UnlockWrite();
ssize_t read(void *buffer, size_t size, int timeout_ms = -1);
diff --git a/demuxer.c b/demuxer.c
index e677168..1f3c9e8 100644
--- a/demuxer.c
+++ b/demuxer.c
@@ -39,6 +39,8 @@ cVNSIDemuxer::~cVNSIDemuxer()
void cVNSIDemuxer::Open(const cChannel &channel, cVideoBuffer *videoBuffer)
{
+ cMutexLock lock(&m_Mutex);
+
m_CurrentChannel = channel;
m_VideoBuffer = videoBuffer;
m_OldPmtVersion = -1;
@@ -47,10 +49,20 @@ void cVNSIDemuxer::Open(const cChannel &channel, cVideoBuffer *videoBuffer)
m_WaitIFrame = true;
else
m_WaitIFrame = false;
+
+ m_FirstFrameDTS = 0;
+
+ m_PtsWrap.m_Wrap = false;
+ m_PtsWrap.m_NoOfWraps = 0;
+ m_PtsWrap.m_ConfirmCount = 0;
+ m_MuxPacketSerial = 0;
+ m_Error = ERROR_DEMUX_NODATA;
}
void cVNSIDemuxer::Close()
{
+ cMutexLock lock(&m_Mutex);
+
for (std::list<cTSStream*>::iterator it = m_Streams.begin(); it != m_Streams.end(); ++it)
{
DEBUGLOG("Deleting stream parser for pid=%i and type=%i", (*it)->GetPID(), (*it)->Type());
@@ -66,6 +78,8 @@ int cVNSIDemuxer::Read(sStreamPacket *packet)
int len;
cTSStream *stream;
+ cMutexLock lock(&m_Mutex);
+
// clear packet
if (!packet)
return -1;
@@ -75,9 +89,14 @@ int cVNSIDemuxer::Read(sStreamPacket *packet)
// read TS Packet from buffer
len = m_VideoBuffer->Read(&buf, TS_SIZE);
- if (len != TS_SIZE)
+ // eof
+ if (len == -2)
+ return -2;
+ else if (len != TS_SIZE)
return -1;
+ m_Error &= ~ERROR_DEMUX_NODATA;
+
int ts_pid = TsPid(buf);
// parse PAT/PMT
@@ -112,18 +131,196 @@ int cVNSIDemuxer::Read(sStreamPacket *packet)
}
else if (stream = FindStream(ts_pid))
{
- if (stream->ProcessTSPacket(buf, packet, m_WaitIFrame))
+ int error = stream->ProcessTSPacket(buf, packet, m_WaitIFrame);
+ if (error == 0)
{
- m_WaitIFrame = false;
+ if (m_WaitIFrame)
+ {
+ m_FirstFrameDTS = packet->dts;
+ m_WaitIFrame = false;
+ }
+
+ if (packet->dts < m_FirstFrameDTS)
+ return 0;
+
+ packet->serial = m_MuxPacketSerial;
return 1;
}
+ else if (error < 0)
+ {
+ m_Error |= abs(error);
+ }
}
- else
- return -1;
return 0;
}
+bool cVNSIDemuxer::SeekTime(int64_t time)
+{
+ off_t pos, pos_min, pos_max, pos_limit, start_pos;
+ int64_t ts, ts_min, ts_max, last_ts;
+ int no_change;
+
+ if (!m_VideoBuffer->HasBuffer())
+ return false;
+
+ cMutexLock lock(&m_Mutex);
+
+// INFOLOG("----- seek to time: %ld", time);
+
+ // rescale to 90khz
+ time = cTSStream::Rescale(time, 90000, DVD_TIME_BASE);
+
+ m_VideoBuffer->GetPositions(&pos, &pos_min, &pos_max);
+
+// INFOLOG("----- seek to time: %ld", time);
+// INFOLOG("------pos: %ld, pos min: %ld, pos max: %ld", pos, pos_min, pos_max);
+
+ if (!GetTimeAtPos(&pos_min, &ts_min))
+ {
+ ResetParsers();
+ m_WaitIFrame = true;
+ return false;
+ }
+
+// INFOLOG("----time at min: %ld", ts_min);
+
+ if (ts_min >= time)
+ {
+ m_VideoBuffer->SetPos(pos_min);
+ ResetParsers();
+ m_WaitIFrame = true;
+ m_MuxPacketSerial++;
+ return true;
+ }
+
+ int64_t timecur;
+ GetTimeAtPos(&pos, &timecur);
+
+ // get time at end of buffer
+ unsigned int step= 1024;
+ bool gotTime;
+ do
+ {
+ pos_max -= step;
+ gotTime = GetTimeAtPos(&pos_max, &ts_max);
+ step += step;
+ } while (!gotTime && pos_max >= step);
+
+ if (!gotTime)
+ {
+ ResetParsers();
+ m_WaitIFrame = true;
+ return false;
+ }
+
+ if (ts_max <= time)
+ {
+ ResetParsers();
+ m_WaitIFrame = true;
+ m_MuxPacketSerial++;
+ return true;
+ }
+
+// INFOLOG(" - time in buffer: %ld", cTSStream::Rescale(ts_max-ts_min, DVD_TIME_BASE, 90000)/1000000);
+
+ // bisect seek
+ if(ts_min > ts_max)
+ {
+ ResetParsers();
+ m_WaitIFrame = true;
+ return false;
+ }
+ else if (ts_min == ts_max)
+ {
+ pos_limit = pos_min;
+ }
+ else
+ pos_limit = pos_max;
+
+ no_change = 0;
+ ts = time;
+ last_ts = 0;
+ while (pos_min < pos_limit)
+ {
+ if (no_change==0)
+ {
+ // interpolate position
+ pos = cTSStream::Rescale(time - ts_min, pos_max - pos_min, ts_max - ts_min)
+ + pos_min - (pos_max - pos_limit);
+ }
+ else if (no_change==1)
+ {
+ // bisection, if interpolation failed to change min or max pos last time
+ pos = (pos_min + pos_limit) >> 1;
+ }
+ else
+ {
+ // linear search if bisection failed
+ pos = pos_min;
+ }
+
+ // clamp calculated pos into boundaries
+ if( pos <= pos_min)
+ pos = pos_min + 1;
+ else if (pos > pos_limit)
+ pos = pos_limit;
+ start_pos = pos;
+
+ // get time stamp at pos
+ if (!GetTimeAtPos(&pos, &ts))
+ {
+ ResetParsers();
+ m_WaitIFrame = true;
+ return false;
+ }
+ pos = m_VideoBuffer->GetPosCur();
+
+ // determine method for next calculation of pos
+ if ((last_ts == ts) || (pos >= pos_max))
+ no_change++;
+ else
+ no_change=0;
+
+// INFOLOG("--- pos: %ld, \t time: %ld, diff time: %ld", pos, ts, time-ts);
+
+ // 0.4 sec is close enough
+ if (abs(time - ts) <= 36000)
+ {
+ break;
+ }
+ // target is to the left
+ else if (time <= ts)
+ {
+ pos_limit = start_pos - 1;
+ pos_max = pos;
+ ts_max = ts;
+ }
+ // target is to the right
+ if (time >= ts)
+ {
+ pos_min = pos;
+ ts_min = ts;
+ }
+ last_ts = ts;
+ }
+
+// INFOLOG("----pos found: %ld", pos);
+// INFOLOG("----time at pos: %ld, diff time: %ld", ts, cTSStream::Rescale(timecur-ts, DVD_TIME_BASE, 90000));
+
+ m_VideoBuffer->SetPos(pos);
+
+ ResetParsers();
+ m_WaitIFrame = true;
+ m_MuxPacketSerial++;
+ return true;
+}
+
+void cVNSIDemuxer::BufferStatus(bool ×hift, int &start, int ¤t, int &end)
+{
+ timeshift = m_VideoBuffer->HasBuffer();
+}
+
cTSStream *cVNSIDemuxer::GetFirstStream()
{
m_StreamsIterator = m_Streams.begin();
@@ -152,6 +349,14 @@ cTSStream *cVNSIDemuxer::FindStream(int Pid)
return NULL;
}
+void cVNSIDemuxer::ResetParsers()
+{
+ for (std::list<cTSStream*>::iterator it = m_Streams.begin(); it != m_Streams.end(); ++it)
+ {
+ (*it)->ResetParser();
+ }
+}
+
void cVNSIDemuxer::AddStreamInfo(sStreamInfo &stream)
{
m_StreamInfos.push_back(stream);
@@ -195,40 +400,40 @@ bool cVNSIDemuxer::EnsureParsers()
if (it->type == stH264)
{
- stream = new cTSStream(stH264, it->pID);
+ stream = new cTSStream(stH264, it->pID, &m_PtsWrap);
}
else if (it->type == stMPEG2VIDEO)
{
- stream = new cTSStream(stMPEG2VIDEO, it->pID);
+ stream = new cTSStream(stMPEG2VIDEO, it->pID, &m_PtsWrap);
}
else if (it->type == stMPEG2AUDIO)
{
- stream = new cTSStream(stMPEG2AUDIO, it->pID);
+ stream = new cTSStream(stMPEG2AUDIO, it->pID, &m_PtsWrap);
stream->SetLanguage(it->language);
}
else if (it->type == stAACADTS)
{
- stream = new cTSStream(stAACADTS, it->pID);
+ stream = new cTSStream(stAACADTS, it->pID, &m_PtsWrap);
stream->SetLanguage(it->language);
}
else if (it->type == stAACLATM)
{
- stream = new cTSStream(stAACLATM, it->pID);
+ stream = new cTSStream(stAACLATM, it->pID, &m_PtsWrap);
stream->SetLanguage(it->language);
}
else if (it->type == stAC3)
{
- stream = new cTSStream(stAC3, it->pID);
+ stream = new cTSStream(stAC3, it->pID, &m_PtsWrap);
stream->SetLanguage(it->language);
}
else if (it->type == stEAC3)
{
- stream = new cTSStream(stEAC3, it->pID);
+ stream = new cTSStream(stEAC3, it->pID, &m_PtsWrap);
stream->SetLanguage(it->language);
}
else if (it->type == stDVBSUB)
{
- stream = new cTSStream(stDVBSUB, it->pID);
+ stream = new cTSStream(stDVBSUB, it->pID, &m_PtsWrap);
stream->SetLanguage(it->language);
#if APIVERSNUM >= 10709
stream->SetSubtitlingDescriptor(it->subtitlingType, it->compositionPageId, it->ancillaryPageId);
@@ -236,7 +441,7 @@ bool cVNSIDemuxer::EnsureParsers()
}
else if (it->type == stTELETEXT)
{
- stream = new cTSStream(stTELETEXT, it->pID);
+ stream = new cTSStream(stTELETEXT, it->pID, &m_PtsWrap);
}
else
continue;
@@ -266,39 +471,39 @@ void cVNSIDemuxer::SetChannelStreams(const cChannel *channel)
AddStreamInfo(newStream);
}
- const int *APids = channel->Apids();
- for ( ; *APids; APids++)
+ const int *DPids = channel->Dpids();
+ for ( ; *DPids; DPids++)
{
int index = 0;
- if (!FindStream(*APids))
+ if (!FindStream(*DPids))
{
- newStream.pID = *APids;
- newStream.type = stMPEG2AUDIO;
+ newStream.pID = *DPids;
+ newStream.type = stAC3;
#if APIVERSNUM >= 10715
- if (channel->Atype(index) == 0x0F)
- newStream.type = stAACADTS;
- else if (channel->Atype(index) == 0x11)
- newStream.type = stAACLATM;
+ if (channel->Dtype(index) == SI::EnhancedAC3DescriptorTag)
+ newStream.type = stEAC3;
#endif
- newStream.SetLanguage(channel->Alang(index));
+ newStream.SetLanguage(channel->Dlang(index));
AddStreamInfo(newStream);
}
index++;
}
- const int *DPids = channel->Dpids();
- for ( ; *DPids; DPids++)
+ const int *APids = channel->Apids();
+ for ( ; *APids; APids++)
{
int index = 0;
- if (!FindStream(*DPids))
+ if (!FindStream(*APids))
{
- newStream.pID = *DPids;
- newStream.type = stAC3;
+ newStream.pID = *APids;
+ newStream.type = stMPEG2AUDIO;
#if APIVERSNUM >= 10715
- if (channel->Dtype(index) == SI::EnhancedAC3DescriptorTag)
- newStream.type = stEAC3;
+ if (channel->Atype(index) == 0x0F)
+ newStream.type = stAACADTS;
+ else if (channel->Atype(index) == 0x11)
+ newStream.type = stAACLATM;
#endif
- newStream.SetLanguage(channel->Dlang(index));
+ newStream.SetLanguage(channel->Alang(index));
AddStreamInfo(newStream);
}
index++;
@@ -385,3 +590,36 @@ void cVNSIDemuxer::SetChannelPids(cChannel *channel, cPatPmtParser *patPmtParser
Spids, SLangs,
Tpid);
}
+
+bool cVNSIDemuxer::GetTimeAtPos(off_t *pos, int64_t *time)
+{
+ uint8_t *buf;
+ int len;
+ cTSStream *stream;
+ int ts_pid;
+
+ m_VideoBuffer->SetPos(*pos);
+ ResetParsers();
+ while (len = m_VideoBuffer->Read(&buf, TS_SIZE) == TS_SIZE)
+ {
+ ts_pid = TsPid(buf);
+ if (stream = FindStream(ts_pid))
+ {
+ // only consider video or audio streams
+ if ((stream->Content() == scVIDEO || stream->Content() == scAUDIO) &&
+ stream->ReadTime(buf, time))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+uint16_t cVNSIDemuxer::GetError()
+{
+ uint16_t ret = m_Error;
+ m_Error = ERROR_DEMUX_NODATA;
+ return ret;
+}
+
diff --git a/demuxer.h b/demuxer.h
index 8efd0ba..019595f 100644
--- a/demuxer.h
+++ b/demuxer.h
@@ -57,13 +57,20 @@ public:
cTSStream *GetNextStream();
void Open(const cChannel &channel, cVideoBuffer *videoBuffer);
void Close();
+ bool SeekTime(int64_t time);
+ uint32_t GetSerial() { return m_MuxPacketSerial; }
+ void SetSerial(uint32_t serial) { m_MuxPacketSerial = serial; }
+ void BufferStatus(bool ×hift, int &start, int ¤t, int &end);
+ uint16_t GetError();
protected:
bool EnsureParsers();
+ void ResetParsers();
void SetChannelStreams(const cChannel *channel);
void SetChannelPids(cChannel *channel, cPatPmtParser *patPmtParser);
cTSStream *FindStream(int Pid);
void AddStreamInfo(sStreamInfo &stream);
+ bool GetTimeAtPos(off_t *pos, int64_t *time);
std::list<cTSStream*> m_Streams;
std::list<cTSStream*>::iterator m_StreamsIterator;
std::list<sStreamInfo> m_StreamInfos;
@@ -71,5 +78,10 @@ protected:
cPatPmtParser m_PatPmtParser;
int m_OldPmtVersion;
bool m_WaitIFrame;
+ int64_t m_FirstFrameDTS;
cVideoBuffer *m_VideoBuffer;
+ cMutex m_Mutex;
+ uint32_t m_MuxPacketSerial;
+ sPtsWrap m_PtsWrap;
+ uint16_t m_Error;
};
diff --git a/parser.c b/parser.c
index 55f815e..d325fae 100644
--- a/parser.c
+++ b/parser.c
@@ -1,8 +1,5 @@
/*
- * vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- * Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ * Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
* 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
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -46,14 +42,10 @@
// --- cParser -------------------------------------------------
-cParser::cParser(int pID, cTSStream *stream)
- : m_pID(pID)
+cParser::cParser(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+ : m_pID(pID), m_PtsWrap(ptsWrap), m_ObservePtsWraps(observePtsWraps)
{
- m_curPTS = DVD_NOPTS_VALUE;
- m_curDTS = DVD_NOPTS_VALUE;
- m_prevDTS = DVD_NOPTS_VALUE;
m_PesBuffer = NULL;
- m_IsError = false;
m_Stream = stream;
m_IsVideo = false;
m_PesBufferInitialSize = 1024;
@@ -66,23 +58,29 @@ cParser::~cParser()
free(m_PesBuffer);
}
-
+void cParser::Reset()
+{
+ m_curPTS = DVD_NOPTS_VALUE;
+ m_curDTS = DVD_NOPTS_VALUE;
+ m_prevDTS = DVD_NOPTS_VALUE;
+ m_PesBufferPtr = 0;
+ m_PesParserPtr = 0;
+ m_PesNextFramePtr = 0;
+ m_FoundFrame = false;
+ m_PesPacketLength = 0;
+ m_PesHeaderPtr = 0;
+ m_Error = ERROR_PES_GENERAL;
+}
/*
* Extract DTS and PTS and update current values in stream
*/
int cParser::ParsePESHeader(uint8_t *buf, size_t len)
{
- if (len < 6)
- return -1;
-
m_PesPacketLength = buf[4] << 8 | buf[5];
if (!PesIsVideoPacket(buf) && !PesIsAudioPacket(buf))
return 6;
- if (len < 9)
- return -1;
-
unsigned int hdr_len = PesHeaderLength(buf);
if (m_PesPacketLength > 0)
@@ -103,7 +101,42 @@ int cParser::ParsePESHeader(uint8_t *buf, size_t len)
pts |= ((int64_t)(buf[11] & 0xFE)) << 14 ;
pts |= ((int64_t) buf[12]) << 7 ;
pts |= ((int64_t)(buf[13] & 0xFE)) >> 1 ;
+
+ int64_t bit32and31 = pts >> 31;
+ if (m_ObservePtsWraps)
+ {
+ if ((bit32and31 == 3) && !m_PtsWrap->m_Wrap)
+ {
+ m_PtsWrap->m_ConfirmCount++;
+ if (m_PtsWrap->m_ConfirmCount >= 2)
+ {
+ m_PtsWrap->m_Wrap = true;
+ }
+ }
+ else if ((bit32and31 == 1) && m_PtsWrap->m_Wrap)
+ {
+ m_PtsWrap->m_ConfirmCount++;
+ if (m_PtsWrap->m_ConfirmCount >= 2)
+ {
+ m_PtsWrap->m_Wrap = false;
+ m_PtsWrap->m_NoOfWraps++;
+ }
+ }
+ else
+ m_PtsWrap->m_ConfirmCount = 0;
+ }
+
+ m_prevPTS = m_curPTS;
m_curPTS = pts;
+ m_PesTimePos = m_PesBufferPtr;
+ if (m_PtsWrap->m_Wrap && !(bit32and31))
+ {
+ m_curPTS += 1LL<<33;
+ }
+ if (m_PtsWrap->m_NoOfWraps)
+ {
+ m_curPTS += ((int64_t)m_PtsWrap->m_NoOfWraps<<33);
+ }
}
else
return hdr_len;
@@ -121,6 +154,14 @@ int cParser::ParsePESHeader(uint8_t *buf, size_t len)
dts |= (int64_t)( buf[17] << 7 );
dts |= (int64_t)((buf[18] & 0xFE) >> 1 );
m_curDTS = dts;
+ if (m_PtsWrap->m_Wrap && !(m_curDTS >> 31))
+ {
+ m_curDTS += 1LL<<33;
+ }
+ if (m_PtsWrap->m_NoOfWraps)
+ {
+ m_curDTS += ((int64_t)m_PtsWrap->m_NoOfWraps<<33);
+ }
}
else
m_curDTS = m_curPTS;
@@ -130,15 +171,29 @@ int cParser::ParsePESHeader(uint8_t *buf, size_t len)
int cParser::ParsePacketHeader(uint8_t *data)
{
- m_IsPusi = TsPayloadStart(data);
+ if (TsIsScrambled(data))
+ {
+ m_Error = ERROR_PES_SCRAMBLE;
+ return -1;
+ }
+
+ if (TsPayloadStart(data))
+ {
+ m_IsPusi = true;
+ m_Error = 0;
+ }
+
int bytes = TS_SIZE - TsPayloadOffset(data);
if(bytes < 0 || bytes > TS_SIZE)
+ {
+ m_Error = ERROR_PES_GENERAL;
return -1;
+ }
if (TsError(data))
{
- ERRORLOG("transport error");
+ m_Error = ERROR_PES_GENERAL;
return -1;
}
@@ -149,55 +204,146 @@ int cParser::ParsePacketHeader(uint8_t *data)
}
/* drop broken PES packets */
- if (m_IsError && !m_IsPusi)
+ if (m_Error)
{
return -1;
}
- /* handle new payload unit */
- if (m_IsPusi)
- {
- if (!PesIsHeader(data+TS_SIZE-bytes))
- {
- m_IsError = true;
-// ERRORLOG("--------------- no header");
- Reset();
- return -1;
- }
- m_IsError = false;
- }
-
return bytes;
}
bool cParser::AddPESPacket(uint8_t *data, int size)
{
// check for beginning of a PES packet
+ if (m_IsPusi && m_IsVideo && !IsValidStartCode(data, 4))
+ {
+ m_IsPusi = false;
+ }
if (m_IsPusi)
{
- if (IsValidStartCode(data, size))
+ int hdr_len = 6;
+ if (m_PesHeaderPtr + size < hdr_len)
{
- m_firstPUSIseen = true;
- int hlen = ParsePESHeader(data, size);
- if (hlen <= 0)
+ memcpy(m_PesHeader+m_PesHeaderPtr, data, size);
+ m_PesHeaderPtr += size;
+ return false;
+ }
+ else if (m_PesHeaderPtr)
+ {
+ int bytesNeeded = hdr_len-m_PesHeaderPtr;
+ if (bytesNeeded > 0)
+ {
+ memcpy(m_PesHeader+m_PesHeaderPtr, data, bytesNeeded);
+ m_PesHeaderPtr += bytesNeeded;
+ data += bytesNeeded;
+ size -= bytesNeeded;
+ }
+ if (!IsValidStartCode(m_PesHeader, hdr_len))
+ {
+ Reset();
+ m_Error |= ERROR_PES_STARTCODE;
+ return false;
+ }
+ if (PesIsVideoPacket(m_PesHeader) || PesIsAudioPacket(m_PesHeader))
+ {
+ hdr_len = 9;
+ bytesNeeded = hdr_len-m_PesHeaderPtr;
+ if (size < bytesNeeded)
+ {
+ memcpy(m_PesHeader+m_PesHeaderPtr, data, size);
+ m_PesHeaderPtr += size;
+ return false;
+ }
+ else if (bytesNeeded > 0)
+ {
+ memcpy(m_PesHeader+m_PesHeaderPtr, data, bytesNeeded);
+ m_PesHeaderPtr += bytesNeeded;
+ data += bytesNeeded;
+ size -= bytesNeeded;
+ }
+ if ((m_PesHeader[6] & 0x30))
+ {
+ Reset();
+ m_Error |= ERROR_PES_SCRAMBLE;
+ return false;
+ }
+ hdr_len = PesHeaderLength(m_PesHeader);
+ if (hdr_len > PES_HEADER_LENGTH)
+ {
+ Reset();
+ return false;
+ }
+ }
+ bytesNeeded = hdr_len-m_PesHeaderPtr;
+ if (size < bytesNeeded)
{
- INFOLOG("--------- reset");
+ memcpy(m_PesHeader+m_PesHeaderPtr, data, size);
+ m_PesHeaderPtr += size;
+ return false;
+ }
+ else if (bytesNeeded > 0)
+ {
+ memcpy(m_PesHeader+m_PesHeaderPtr, data, bytesNeeded);
+ m_PesHeaderPtr += bytesNeeded;
+ data += bytesNeeded;
+ size -= bytesNeeded;
+ }
+ if (ParsePESHeader(m_PesHeader, hdr_len) < 0)
+ {
+ INFOLOG("error parsing pes packet error ");
Reset();
return false;
}
- data += hlen;
- size -= hlen;
+ m_PesHeaderPtr = 0;
+ m_IsPusi = false;
}
- else
+ else if (!IsValidStartCode(data, size))
{
- INFOLOG("--------- reset 2");
Reset();
+ m_Error |= ERROR_PES_STARTCODE;
+ return false;
+ }
+ else
+ {
+ if (PesIsVideoPacket(data) || PesIsAudioPacket(data))
+ {
+ if (size < 9)
+ {
+ memcpy(m_PesHeader+m_PesHeaderPtr, data, size);
+ m_PesHeaderPtr += size;
+ return false;
+ }
+ if ((data[6] & 0x30))
+ {
+ Reset();
+ m_Error |= ERROR_PES_STARTCODE;
+ return false;
+ }
+ hdr_len = PesHeaderLength(data);
+ if (hdr_len > PES_HEADER_LENGTH)
+ {
+ Reset();
+ return false;
+ }
+ }
+ if (size < hdr_len)
+ {
+ memcpy(m_PesHeader+m_PesHeaderPtr, data, size);
+ m_PesHeaderPtr += size;
+ return false;
+ }
+ if (ParsePESHeader(data, hdr_len) < 0)
+ {
+ INFOLOG("error parsing pes packet error 2");
+ Reset();
+ return false;
+ }
+ data += hdr_len;
+ size -= hdr_len;
+ m_IsPusi = false;
}
}
- if (!m_firstPUSIseen)
- return false;
-
if (m_PesBuffer == NULL)
{
m_PesBufferSize = m_PesBufferInitialSize;
@@ -212,9 +358,9 @@ bool cParser::AddPESPacket(uint8_t *data, int size)
if (m_PesBufferPtr + size >= m_PesBufferSize)
{
- if (m_PesBufferPtr + size >= 500000)
+ if (m_PesBufferPtr + size >= 1000000)
{
- ERRORLOG("cParser::AddPESPacket - max buffer size reached");
+ ERRORLOG("cParser::AddPESPacket - max buffer size reached, pid: %d", m_pID);
Reset();
return false;
}
@@ -233,6 +379,7 @@ bool cParser::AddPESPacket(uint8_t *data, int size)
{
memmove(m_PesBuffer, m_PesBuffer+m_PesNextFramePtr, m_PesBufferPtr-m_PesNextFramePtr);
m_PesBufferPtr = m_PesBufferPtr-m_PesNextFramePtr;
+ m_PesTimePos -= m_PesNextFramePtr;
m_PesNextFramePtr = 0;
}
@@ -240,7 +387,7 @@ bool cParser::AddPESPacket(uint8_t *data, int size)
memcpy(m_PesBuffer+m_PesBufferPtr, data, size);
m_PesBufferPtr += size;
- return false;
+ return true;
}
inline bool cParser::IsValidStartCode(uint8_t *buf, int size)
@@ -284,19 +431,9 @@ inline bool cParser::IsValidStartCode(uint8_t *buf, int size)
return false;
}
-void cParser::Reset()
-{
- m_PesBufferPtr = 0;
- m_PesParserPtr = 0;
- m_firstPUSIseen = false;
- m_PesNextFramePtr = 0;
- m_FoundFrame = false;
- m_PesPacketLength = 0;
-}
-
// --- cTSStream ----------------------------------------------------
-cTSStream::cTSStream(eStreamType type, int pid)
+cTSStream::cTSStream(eStreamType type, int pid, sPtsWrap *ptsWrap)
: m_streamType(type)
, m_pID(pid)
{
@@ -316,25 +453,55 @@ cTSStream::cTSStream(eStreamType type, int pid)
m_IsStreamChange = false;
if (m_streamType == stMPEG2VIDEO)
- m_pesParser = new cParserMPEG2Video(m_pID, this);
+ {
+ m_pesParser = new cParserMPEG2Video(m_pID, this, ptsWrap, true);
+ m_streamContent = scVIDEO;
+ }
else if (m_streamType == stH264)
- m_pesParser = new cParserH264(m_pID, this);
+ {
+ m_pesParser = new cParserH264(m_pID, this, ptsWrap, true);
+ m_streamContent = scVIDEO;
+ }
else if (m_streamType == stMPEG2AUDIO)
- m_pesParser = new cParserMPEG2Audio(m_pID, this);
+ {
+ m_pesParser = new cParserMPEG2Audio(m_pID, this, ptsWrap, true);
+ m_streamContent = scAUDIO;
+ }
else if (m_streamType == stAACADTS)
- m_pesParser = new cParserAAC(m_pID, this);
+ {
+ m_pesParser = new cParserAAC(m_pID, this, ptsWrap, true);
+ m_streamContent = scAUDIO;
+ }
else if (m_streamType == stAACLATM)
- m_pesParser = new cParserAAC(m_pID, this);
+ {
+ m_pesParser = new cParserAAC(m_pID, this, ptsWrap, true);
+ m_streamContent = scAUDIO;
+ }
else if (m_streamType == stAC3)
- m_pesParser = new cParserAC3(m_pID, this);
+ {
+ m_pesParser = new cParserAC3(m_pID, this, ptsWrap, true);
+ m_streamContent = scAUDIO;
+ }
else if (m_streamType == stEAC3)
- m_pesParser = new cParserAC3(m_pID, this);
+ {
+ m_pesParser = new cParserAC3(m_pID, this, ptsWrap, true);
+ m_streamContent = scAUDIO;
+ }
else if (m_streamType == stDTS)
- m_pesParser = new cParserDTS(m_pID, this);
+ {
+ m_pesParser = new cParserDTS(m_pID, this, ptsWrap, true);
+ m_streamContent = scAUDIO;
+ }
else if (m_streamType == stTELETEXT)
- m_pesParser = new cParserTeletext(m_pID, this);
+ {
+ m_pesParser = new cParserTeletext(m_pID, this, ptsWrap, false);
+ m_streamContent = scTELETEXT;
+ }
else if (m_streamType == stDVBSUB)
- m_pesParser = new cParserSubtitle(m_pID, this);
+ {
+ m_pesParser = new cParserSubtitle(m_pID, this, ptsWrap, false);
+ m_streamContent = scSUBTITLE;
+ }
else
{
ERRORLOG("Unrecognised type %i inside stream %i", m_streamType, m_pID);
@@ -351,7 +518,7 @@ cTSStream::~cTSStream()
}
}
-bool cTSStream::ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, bool iframe)
+int cTSStream::ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, bool iframe)
{
if (!data)
return false;
@@ -360,13 +527,21 @@ bool cTSStream::ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, bool iframe)
return false;
int payloadSize = m_pesParser->ParsePacketHeader(data);
- if (payloadSize < 0)
- return false;
+ if (payloadSize == 0)
+ return 1;
+ else if (payloadSize < 0)
+ {
+ return -m_pesParser->GetError();
+ }
+
+ if (!m_pesParser->AddPESPacket(data+TS_SIZE-payloadSize, payloadSize))
+ {
+ return -m_pesParser->GetError();
+ }
- m_pesParser->AddPESPacket(data+TS_SIZE-payloadSize, payloadSize);
m_pesParser->Parse(pkt);
if (iframe && !m_pesParser->IsVideo())
- return false;
+ return 1;
if (pkt->data)
{
@@ -377,22 +552,57 @@ bool cTSStream::ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, bool iframe)
dts = pts;
// Rescale for XBMC
- pkt->dts = Rescale(dts);
- pkt->pts = Rescale(pts);
- pkt->duration = Rescale(pkt->duration);
- return true;
+ pkt->dts = Rescale(dts, DVD_TIME_BASE, 90000);
+ pkt->pts = Rescale(pts, DVD_TIME_BASE, 90000);
+ pkt->duration = Rescale(pkt->duration, DVD_TIME_BASE, 90000);
+ return 0;
}
+ return 1;
+}
+
+bool cTSStream::ReadTime(uint8_t *data, int64_t *dts)
+{
+ if (!data)
+ return false;
+
+ if (!m_pesParser)
+ return false;
+
+ int payloadSize = m_pesParser->ParsePacketHeader(data);
+ if (payloadSize < 0)
+ return false;
+
+ if (m_pesParser->m_IsPusi)
+ {
+ data += TS_SIZE-payloadSize;
+ if (payloadSize >= 6 && m_pesParser->IsValidStartCode(data, payloadSize))
+ {
+ m_pesParser->m_curDTS = DVD_NOPTS_VALUE;
+ m_pesParser->ParsePESHeader(data, payloadSize);
+ if (m_pesParser->m_curDTS != DVD_NOPTS_VALUE)
+ {
+ *dts = m_pesParser->m_curDTS;
+ return true;
+ }
+ }
+ m_pesParser->m_IsPusi = false;
+ }
return false;
}
-int64_t cTSStream::Rescale(int64_t a)
+void cTSStream::ResetParser()
+{
+ if (m_pesParser)
+ m_pesParser->Reset();
+}
+
+int64_t cTSStream::Rescale(int64_t a, int64_t b, int64_t c)
{
- uint64_t b = DVD_TIME_BASE;
- uint64_t c = 90000;
uint64_t r = c/2;
- if (b<=INT_MAX && c<=INT_MAX){
+ if (b<=INT_MAX && c<=INT_MAX)
+ {
if (a<=INT_MAX)
return (a * b + r)/c;
else
diff --git a/parser.h b/parser.h
index f42320f..f3e8db4 100644
--- a/parser.h
+++ b/parser.h
@@ -1,8 +1,5 @@
/*
- * vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- * Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ * Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
* 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
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -77,88 +73,11 @@ inline bool PesIsPS1Packet(const uchar *p)
return ((p)[3] == PRIVATE_STREAM1 || (p)[3] == PRIVATE_STREAM3 );
}
-inline bool PesIsPaddingPacket(const uchar *p)
-{
- return ((p)[3] == PADDING_STREAM);
-}
-
inline bool PesIsAudioPacket(const uchar *p)
{
return (PesIsMPEGAudioPacket(p) || PesIsPS1Packet(p));
}
-#if APIVERSNUM < 10701
-
-#define TS_ERROR 0x80
-#define TS_PAYLOAD_START 0x40
-#define TS_TRANSPORT_PRIORITY 0x20
-#define TS_PID_MASK_HI 0x1F
-#define TS_SCRAMBLING_CONTROL 0xC0
-#define TS_ADAPT_FIELD_EXISTS 0x20
-#define TS_PAYLOAD_EXISTS 0x10
-#define TS_CONT_CNT_MASK 0x0F
-#define TS_ADAPT_DISCONT 0x80
-#define TS_ADAPT_RANDOM_ACC 0x40 // would be perfect for detecting independent frames, but unfortunately not used by all broadcasters
-#define TS_ADAPT_ELEM_PRIO 0x20
-#define TS_ADAPT_PCR 0x10
-#define TS_ADAPT_OPCR 0x08
-#define TS_ADAPT_SPLICING 0x04
-#define TS_ADAPT_TP_PRIVATE 0x02
-#define TS_ADAPT_EXTENSION 0x01
-
-inline bool TsHasPayload(const uchar *p)
-{
- return p[3] & TS_PAYLOAD_EXISTS;
-}
-
-inline bool TsHasAdaptationField(const uchar *p)
-{
- return p[3] & TS_ADAPT_FIELD_EXISTS;
-}
-
-inline bool TsPayloadStart(const uchar *p)
-{
- return p[1] & TS_PAYLOAD_START;
-}
-
-inline bool TsError(const uchar *p)
-{
- return p[1] & TS_ERROR;
-}
-
-inline int TsPid(const uchar *p)
-{
- return (p[1] & TS_PID_MASK_HI) * 256 + p[2];
-}
-
-inline bool TsIsScrambled(const uchar *p)
-{
- return p[3] & TS_SCRAMBLING_CONTROL;
-}
-
-inline int TsPayloadOffset(const uchar *p)
-{
- return (p[3] & TS_ADAPT_FIELD_EXISTS) ? p[4] + 5 : 4;
-}
-
-inline int TsGetPayload(const uchar **p)
-{
- int o = TsPayloadOffset(*p);
- *p += o;
- return TS_SIZE - o;
-}
-
-inline int TsContinuityCounter(const uchar *p)
-{
- return p[3] & TS_CONT_CNT_MASK;
-}
-
-inline int TsGetAdaptationField(const uchar *p)
-{
- return TsHasAdaptationField(p) ? p[5] : 0x00;
-}
-#endif
-
enum eStreamContent
{
scVIDEO,
@@ -202,27 +121,41 @@ struct sStreamPacket
int size;
bool streamChange;
bool pmtChange;
+ uint32_t serial;
+};
+
+struct sPtsWrap
+{
+ bool m_Wrap;
+ int m_NoOfWraps;
+ int m_ConfirmCount;
};
class cTSStream;
+#define PES_HEADER_LENGTH 128
+
class cParser
{
+friend class cTSStream;
public:
- cParser(int pID, cTSStream *stream);
+ cParser(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
virtual ~cParser();
bool AddPESPacket(uint8_t *data, int size);
virtual void Parse(sStreamPacket *pkt) = 0;
- void ClearFrame() {m_PesBufferPtr = 0;}
+// void ClearFrame() {m_PesBufferPtr = 0;}
int ParsePacketHeader(uint8_t *data);
int ParsePESHeader(uint8_t *buf, size_t len);
virtual void Reset();
bool IsVideo() {return m_IsVideo; }
+ uint16_t GetError() { return m_Error; }
protected:
virtual bool IsValidStartCode(uint8_t *buf, int size);
+ uint8_t m_PesHeader[PES_HEADER_LENGTH];
+ int m_PesHeaderPtr;
int m_PesPacketLength;
uint8_t *m_PesBuffer;
int m_PesBufferSize;
@@ -230,20 +163,23 @@ protected:
size_t m_PesBufferInitialSize;
size_t m_PesParserPtr;
size_t m_PesNextFramePtr;
+ int m_PesTimePos;
bool m_FoundFrame;
int m_pID;
int64_t m_curPTS;
int64_t m_curDTS;
+ int64_t m_prevPTS;
int64_t m_prevDTS;
bool m_IsPusi;
- bool m_firstPUSIseen;
- bool m_IsError;
+ uint16_t m_Error;
cTSStream *m_Stream;
bool m_IsVideo;
+ sPtsWrap *m_PtsWrap;
+ bool m_ObservePtsWraps;
};
@@ -277,10 +213,12 @@ private:
uint16_t m_ancillaryPageId;
public:
- cTSStream(eStreamType type, int pid);
+ cTSStream(eStreamType type, int pid, sPtsWrap *ptsWrap);
virtual ~cTSStream();
- bool ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, bool iframe);
+ int ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, bool iframe);
+ bool ReadTime(uint8_t *data, int64_t *dts);
+ void ResetParser();
void SetLanguage(const char *language);
const char *GetLanguage() { return m_language; }
@@ -302,7 +240,7 @@ public:
uint16_t CompositionPageId() const { return m_compositionPageId; }
uint16_t AncillaryPageId() const { return m_ancillaryPageId; }
- int64_t Rescale(int64_t a);
+ static int64_t Rescale(int64_t a, int64_t b, int64_t c);
};
#endif // VNSI_DEMUXER_H
diff --git a/parser_AAC.c b/parser_AAC.c
index 2a3ff5d..2ef3cb1 100644
--- a/parser_AAC.c
+++ b/parser_AAC.c
@@ -1,8 +1,5 @@
/*
- * vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- * Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ * Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
* 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
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -35,10 +31,9 @@ static int aac_sample_rates[16] =
};
-cParserAAC::cParserAAC(int pID, cTSStream *stream)
- : cParser(pID, stream)
+cParserAAC::cParserAAC(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+ : cParser(pID, stream, ptsWrap, observePtsWraps)
{
- m_firstPUSIseen = false;
m_Configured = false;
m_FrameLengthType = 0;
m_PTS = 0;
diff --git a/parser_AAC.h b/parser_AAC.h
index defb1d4..15c2122 100644
--- a/parser_AAC.h
+++ b/parser_AAC.h
@@ -1,8 +1,5 @@
/*
- * vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- * Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ * Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
* 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
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -52,7 +48,7 @@ private:
uint32_t LATMGetValue(cBitstream *bs) { return bs->readBits(bs->readBits(2) * 8); }
public:
- cParserAAC(int pID, cTSStream *stream);
+ cParserAAC(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
virtual ~cParserAAC();
virtual void Parse(sStreamPacket *pkt);
virtual void Reset();
diff --git a/parser_AC3.c b/parser_AC3.c
index 4cb88d5..a1645a2 100644
--- a/parser_AC3.c
+++ b/parser_AC3.c
@@ -1,8 +1,5 @@
/*
- * vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- * Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ * Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
* 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
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -109,8 +105,8 @@ typedef enum {
EAC3_FRAME_TYPE_RESERVED
} EAC3FrameType;
-cParserAC3::cParserAC3(int pID, cTSStream *stream)
- : cParser(pID, stream)
+cParserAC3::cParserAC3(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+ : cParser(pID, stream, ptsWrap, observePtsWraps)
{
m_PTS = 0;
m_DTS = 0;
diff --git a/parser_AC3.h b/parser_AC3.h
index 5844437..b789423 100644
--- a/parser_AC3.h
+++ b/parser_AC3.h
@@ -1,8 +1,5 @@
/*
- * vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- * Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ * Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
* 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
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -43,7 +39,7 @@ private:
int FindHeaders(uint8_t *buf, int buf_size);
public:
- cParserAC3(int pID, cTSStream *stream);
+ cParserAC3(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
virtual ~cParserAC3();
virtual void Parse(sStreamPacket *pkt);
diff --git a/parser_DTS.c b/parser_DTS.c
index 042a1e5..2ad2385 100644
--- a/parser_DTS.c
+++ b/parser_DTS.c
@@ -1,8 +1,5 @@
/*
- * vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- * Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ * Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
* 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
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -29,8 +25,8 @@
#include "parser_DTS.h"
#include "bitstream.h"
-cParserDTS::cParserDTS(int pID, cTSStream *stream)
- : cParser(pID, stream)
+cParserDTS::cParserDTS(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+ : cParser(pID, stream, ptsWrap, observePtsWraps)
{
}
diff --git a/parser_DTS.h b/parser_DTS.h
index c388345..c28b4ba 100644
--- a/parser_DTS.h
+++ b/parser_DTS.h
@@ -1,8 +1,5 @@
/*
- * vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- * Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ * Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
* 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
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -34,7 +30,7 @@ class cParserDTS : public cParser
private:
public:
- cParserDTS(int pID, cTSStream *stream);
+ cParserDTS(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
virtual ~cParserDTS();
virtual void Parse(sStreamPacket *pkt);
diff --git a/parser_MPEGAudio.c b/parser_MPEGAudio.c
index 2f2cc5e..2c62030 100644
--- a/parser_MPEGAudio.c
+++ b/parser_MPEGAudio.c
@@ -1,8 +1,5 @@
/*
- * vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- * Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ * Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
* 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
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -44,8 +40,8 @@ const uint16_t BitrateTable[2][3][15] =
}
};
-cParserMPEG2Audio::cParserMPEG2Audio(int pID, cTSStream *stream)
- : cParser(pID, stream)
+cParserMPEG2Audio::cParserMPEG2Audio(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+ : cParser(pID, stream, ptsWrap, observePtsWraps)
{
m_PTS = 0;
m_DTS = 0;
@@ -113,6 +109,7 @@ int cParserMPEG2Audio::FindHeaders(uint8_t *buf, int buf_size)
int layer = bs.readBits(2);
if (layer == 0)
return 0;
+ layer = 4 - layer;
bs.skipBits(1); // protetion bit
int bitrate_index = bs.readBits(4);
diff --git a/parser_MPEGAudio.h b/parser_MPEGAudio.h
index b08689b..32a3989 100644
--- a/parser_MPEGAudio.h
+++ b/parser_MPEGAudio.h
@@ -1,8 +1,5 @@
/*
- * vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- * Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ * Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
* 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
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -43,7 +39,7 @@ private:
int FindHeaders(uint8_t *buf, int buf_size);
public:
- cParserMPEG2Audio(int pID, cTSStream *stream);
+ cParserMPEG2Audio(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
virtual ~cParserMPEG2Audio();
virtual void Parse(sStreamPacket *pkt);
diff --git a/parser_MPEGVideo.c b/parser_MPEGVideo.c
index 59620f1..5aa911a 100644
--- a/parser_MPEGVideo.c
+++ b/parser_MPEGVideo.c
@@ -1,8 +1,5 @@
/*
- * vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- * Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ * Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
* 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
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -52,8 +48,8 @@ const unsigned int mpeg2video_framedurations[16] = {
1500,
};
-cParserMPEG2Video::cParserMPEG2Video(int pID, cTSStream *stream)
- : cParser(pID, stream)
+cParserMPEG2Video::cParserMPEG2Video(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+ : cParser(pID, stream, ptsWrap, observePtsWraps)
{
m_FrameDuration = 0;
m_vbvDelay = -1;
@@ -97,7 +93,7 @@ void cParserMPEG2Video::Parse(sStreamPacket *pkt)
{
if (!m_NeedSPS && !m_NeedIFrame)
{
- int fpsScale = m_Stream->Rescale(m_FrameDuration);
+ int fpsScale = m_Stream->Rescale(m_FrameDuration, DVD_TIME_BASE, 90000);
bool streamChange = m_Stream->SetVideoInformation(fpsScale,DVD_TIME_BASE, m_Height, m_Width, m_Dar);
pkt->id = m_pID;
@@ -105,14 +101,12 @@ void cParserMPEG2Video::Parse(sStreamPacket *pkt)
pkt->data = m_PesBuffer;
pkt->dts = m_DTS;
pkt->pts = m_PTS;
- pkt->duration = m_curDTS - m_prevDTS;
+ pkt->duration = m_FrameDuration;
pkt->streamChange = streamChange;
}
m_StartCode = 0xffffffff;
m_PesParserPtr = 0;
m_FoundFrame = false;
- m_PTS = m_curPTS;
- m_DTS = m_curDTS;
}
}
@@ -122,7 +116,6 @@ void cParserMPEG2Video::Reset()
m_StartCode = 0xffffffff;
m_NeedIFrame = true;
m_NeedSPS = true;
- m_DTS = DVD_NOPTS_VALUE;
}
int cParserMPEG2Video::Parse_MPEG2Video(uint32_t startcode, int buf_ptr, bool &complete)
@@ -150,12 +143,35 @@ int cParserMPEG2Video::Parse_MPEG2Video(uint32_t startcode, int buf_ptr, bool &c
if (!Parse_MPEG2Video_PicStart(buf))
return 0;
- // if this is the first frame we see, set timestamp
- if (m_DTS == DVD_NOPTS_VALUE)
+ if (!m_FoundFrame)
+ {
+ m_AuPrevDTS = m_AuDTS;
+ if (buf_ptr - 4 >= m_PesTimePos)
+ {
+
+ m_AuDTS = m_curDTS;
+ m_AuPTS = m_curPTS;
+ }
+ else
+ {
+ m_AuDTS = m_prevDTS;
+ m_AuPTS = m_prevPTS;
+ }
+ }
+ if (m_AuPrevDTS == m_AuDTS)
{
- m_PTS = m_curPTS;
- m_DTS = m_curDTS;
+ m_DTS = m_AuDTS + m_PicNumber*m_FrameDuration;
+ m_PTS = m_AuPTS + (m_TemporalReference-m_TrLastTime)*m_FrameDuration;
}
+ else
+ {
+ m_PTS = m_AuPTS;
+ m_DTS = m_AuDTS;
+ m_PicNumber = 0;
+ m_TrLastTime = m_TemporalReference;
+ }
+
+ m_PicNumber++;
m_FoundFrame = true;
break;
}
@@ -237,7 +253,7 @@ bool cParserMPEG2Video::Parse_MPEG2Video_PicStart(uint8_t *buf)
{
cBitstream bs(buf, 4 * 8);
- bs.skipBits(10); /* temporal reference */
+ m_TemporalReference = bs.readBits(10); /* temporal reference */
int pct = bs.readBits(3);
if (pct < PKT_I_FRAME || pct > PKT_B_FRAME)
diff --git a/parser_MPEGVideo.h b/parser_MPEGVideo.h
index 2c728c0..18da858 100644
--- a/parser_MPEGVideo.h
+++ b/parser_MPEGVideo.h
@@ -1,8 +1,5 @@
/*
- * vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- * Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ * Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
* 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
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -46,13 +42,17 @@ private:
float m_Dar;
int64_t m_DTS;
int64_t m_PTS;
+ int64_t m_AuDTS, m_AuPTS, m_AuPrevDTS;
+ int m_TemporalReference;
+ int m_TrLastTime;
+ int m_PicNumber;
int Parse_MPEG2Video(uint32_t startcode, int buf_ptr, bool &complete);
bool Parse_MPEG2Video_SeqStart(uint8_t *buf);
bool Parse_MPEG2Video_PicStart(uint8_t *buf);
public:
- cParserMPEG2Video(int pID, cTSStream *stream);
+ cParserMPEG2Video(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
virtual ~cParserMPEG2Video();
virtual void Parse(sStreamPacket *pkt);
diff --git a/parser_Subtitle.c b/parser_Subtitle.c
index 2e539ed..6408fbd 100644
--- a/parser_Subtitle.c
+++ b/parser_Subtitle.c
@@ -1,8 +1,5 @@
/*
- * vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- * Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ * Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
* 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
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -28,8 +24,8 @@
#include "parser_Subtitle.h"
-cParserSubtitle::cParserSubtitle(int pID, cTSStream *stream)
- : cParser(pID, stream)
+cParserSubtitle::cParserSubtitle(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+ : cParser(pID, stream, ptsWrap, observePtsWraps)
{
m_PesBufferInitialSize = 4000;
}
@@ -55,7 +51,7 @@ void cParserSubtitle::Parse(sStreamPacket *pkt)
{
pkt->id = m_pID;
pkt->data = m_PesBuffer+2;
- pkt->size = m_PesPacketLength-2;
+ pkt->size = m_PesPacketLength-3;
pkt->duration = 0;
pkt->dts = m_curDTS;
pkt->pts = m_curPTS;
diff --git a/parser_Subtitle.h b/parser_Subtitle.h
index 88dfdfc..296ac7c 100644
--- a/parser_Subtitle.h
+++ b/parser_Subtitle.h
@@ -1,8 +1,5 @@
/*
- * vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- * Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ * Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
* 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
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -32,7 +28,7 @@
class cParserSubtitle : public cParser
{
public:
- cParserSubtitle(int pID, cTSStream *stream);
+ cParserSubtitle(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
virtual ~cParserSubtitle();
virtual void Parse(sStreamPacket *pkt);
diff --git a/parser_Teletext.c b/parser_Teletext.c
index 6db09b0..b2ecaee 100644
--- a/parser_Teletext.c
+++ b/parser_Teletext.c
@@ -1,8 +1,5 @@
/*
- * vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- * Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ * Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
* 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
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -27,8 +23,8 @@
#include "parser_Teletext.h"
-cParserTeletext::cParserTeletext(int pID, cTSStream *stream)
- : cParser(pID, stream)
+cParserTeletext::cParserTeletext(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+ : cParser(pID, stream, ptsWrap, observePtsWraps)
{
m_PesBufferInitialSize = 4000;
}
diff --git a/parser_Teletext.h b/parser_Teletext.h
index e35fd79..831e7f5 100644
--- a/parser_Teletext.h
+++ b/parser_Teletext.h
@@ -1,8 +1,5 @@
/*
- * vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- * Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ * Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
* 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
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -36,7 +32,7 @@ private:
int64_t m_lastPTS;
public:
- cParserTeletext(int pID, cTSStream *stream);
+ cParserTeletext(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
virtual ~cParserTeletext();
virtual void Parse(sStreamPacket *pkt);
diff --git a/parser_h264.c b/parser_h264.c
index cf96be9..6d45e30 100644
--- a/parser_h264.c
+++ b/parser_h264.c
@@ -1,8 +1,5 @@
/*
- * vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- * Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ * Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
* 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
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -49,8 +45,8 @@ static const int h264_lev2cpbsize[][2] =
{-1, -1},
};
-cParserH264::cParserH264(int pID, cTSStream *stream)
- : cParser(pID, stream)
+cParserH264::cParserH264(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+ : cParser(pID, stream, ptsWrap, observePtsWraps)
{
m_Height = 0;
m_Width = 0;
@@ -62,7 +58,7 @@ cParserH264::cParserH264(int pID, cTSStream *stream)
m_PixelAspect.den = 1;
m_PixelAspect.num = 0;
memset(&m_streamData, 0, sizeof(m_streamData));
- m_PesBufferInitialSize = 80000;
+ m_PesBufferInitialSize = 240000;
m_IsVideo = true;
Reset();
@@ -106,7 +102,7 @@ void cParserH264::Parse(sStreamPacket *pkt)
// int fpsScale = DVD_TIME_BASE / m_FPS;
if (m_FpsScale == 0)
{
- m_FpsScale = m_Stream->Rescale(m_curDTS - m_prevDTS);
+ m_FpsScale = m_Stream->Rescale(m_curDTS - m_prevDTS, DVD_TIME_BASE, 90000);
}
bool streamChange = m_Stream->SetVideoInformation(m_FpsScale,DVD_TIME_BASE, m_Height, m_Width, DAR);
@@ -121,8 +117,6 @@ void cParserH264::Parse(sStreamPacket *pkt)
m_StartCode = 0xffffffff;
m_PesParserPtr = 0;
m_FoundFrame = false;
- m_PTS = m_curPTS;
- m_DTS = m_curDTS;
}
}
@@ -133,7 +127,7 @@ void cParserH264::Reset()
m_NeedIFrame = true;
m_NeedSPS = true;
m_NeedPPS = true;
- m_DTS = DVD_NOPTS_VALUE;
+ memset(&m_streamData, 0, sizeof(m_streamData));
}
int cParserH264::Parse_H264(uint32_t startcode, int buf_ptr, bool &complete)
@@ -168,16 +162,22 @@ int cParserH264::Parse_H264(uint32_t startcode, int buf_ptr, bool &complete)
return -1;
}
- m_streamData.vcl_nal = vcl;
-
- // if this is the first frame we see, set timestamp
- if (m_DTS == DVD_NOPTS_VALUE)
+ if (!m_FoundFrame)
{
- m_PTS = m_curPTS;
- m_DTS = m_curDTS;
+ if (buf_ptr - 4 >= m_PesTimePos)
+ {
+ m_DTS = m_curDTS;
+ m_PTS = m_curPTS;
+ }
+ else
+ {
+ m_DTS = m_prevDTS;
+ m_PTS = m_prevPTS;
+ }
}
- m_FoundFrame = true;
+ m_streamData.vcl_nal = vcl;
+ m_FoundFrame = true;
break;
}
diff --git a/parser_h264.h b/parser_h264.h
index 174e2f1..609ce0a 100644
--- a/parser_h264.h
+++ b/parser_h264.h
@@ -1,8 +1,5 @@
/*
- * vdr-plugin-vnsi - XBMC server plugin for VDR
- *
- * Copyright (C) 2010 Alwin Esch (Team XBMC)
- *
+ * Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -16,9 +13,8 @@
* 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
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -102,7 +98,6 @@ private:
int64_t m_DTS;
int64_t m_PTS;
-
int Parse_H264(uint32_t startcode, int buf_ptr, bool &complete);
bool Parse_PPS(uint8_t *buf, int len);
bool Parse_SLH(uint8_t *buf, int len, h264_private::VCL_NAL &vcl);
@@ -110,7 +105,7 @@ private:
bool IsFirstVclNal(h264_private::VCL_NAL &vcl);
public:
- cParserH264(int pID, cTSStream *stream);
+ cParserH264(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
virtual ~cParserH264();
virtual void Parse(sStreamPacket *pkt);
diff --git a/recplayer.c b/recplayer.c
index 9af82f7..f5af3e4 100644
--- a/recplayer.c
+++ b/recplayer.c
@@ -34,11 +34,16 @@
#include <sys/stat.h>
#include <unistd.h>
-cRecPlayer::cRecPlayer(cRecording* rec)
+#ifndef O_NOATIME
+#define O_NOATIME 0
+#endif
+
+cRecPlayer::cRecPlayer(cRecording* rec, bool inProgress)
{
m_file = -1;
m_fileOpen = -1;
m_recordingFilename = strdup(rec->FileName());
+ m_inProgress = inProgress;
// FIXME find out max file path / name lengths
#if VDRVERSNUM < 10703
@@ -94,6 +99,39 @@ void cRecPlayer::scan()
INFOLOG("total frames: %u", m_totalFrames);
}
+void cRecPlayer::reScan()
+{
+ struct stat s;
+
+ m_totalLength = 0;
+
+ for(int i = 0; ; i++) // i think we only need one possible loop
+ {
+ fileNameFromIndex(i);
+
+ if(stat(m_fileName, &s) == -1) {
+ break;
+ }
+
+ cSegment* segment;
+ if (m_segments.Size() < i+1)
+ {
+ cSegment* segment = new cSegment();
+ m_segments.Append(segment);
+ segment->start = m_totalLength;
+ }
+ else
+ segment = m_segments[i];
+
+ segment->end = segment->start + s.st_size;
+
+ m_totalLength += s.st_size;
+ }
+
+ m_totalFrames = m_indexFile->Last();
+}
+
+
cRecPlayer::~cRecPlayer()
{
cleanup();
@@ -118,7 +156,7 @@ bool cRecPlayer::openFile(int index)
fileNameFromIndex(index);
INFOLOG("openFile called for index %i string:%s", index, m_fileName);
- m_file = open(m_fileName, O_RDONLY | O_NOATIME);
+ m_file = open(m_fileName, O_RDONLY);
if (m_file == -1)
{
INFOLOG("file failed to open");
@@ -155,8 +193,8 @@ uint32_t cRecPlayer::getLengthFrames()
int cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, int amount)
{
// dont let the block be larger than 256 kb
- if (amount > 256*1024)
- amount = 256*1024;
+ if (amount > 512*1024)
+ amount = 512*1024;
if ((uint64_t)amount > m_totalLength)
amount = m_totalLength;
@@ -195,16 +233,20 @@ int cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, int amount)
// try to read the block
int bytes_read = read(m_file, buffer, amount);
+ // we may got stuck at end of segment
+ if ((bytes_read == 0) && (position < m_totalLength))
+ bytes_read += getBlock(buffer, position+1 , amount);
+
if(bytes_read <= 0) {
return 0;
}
- // Tell linux not to bother keeping the data in the FS cache
- posix_fadvise(m_file, filePosition, bytes_read, POSIX_FADV_DONTNEED);
-
- // divide and conquer
- if(bytes_read < amount) {
- bytes_read += getBlock(&buffer[bytes_read], position + bytes_read, amount - bytes_read);
+ if (!m_inProgress)
+ {
+#ifndef __FreeBSD__
+ // Tell linux not to bother keeping the data in the FS cache
+ posix_fadvise(m_file, filePosition, bytes_read, POSIX_FADV_DONTNEED);
+#endif
}
return bytes_read;
diff --git a/recplayer.h b/recplayer.h
index a94e0d7..784489c 100644
--- a/recplayer.h
+++ b/recplayer.h
@@ -47,7 +47,7 @@ class cSegment
class cRecPlayer
{
public:
- cRecPlayer(cRecording* rec);
+ cRecPlayer(cRecording* rec, bool inProgress = false);
~cRecPlayer();
uint64_t getLengthBytes();
uint32_t getLengthFrames();
@@ -57,6 +57,7 @@ public:
void closeFile();
void scan();
+ void reScan();
uint64_t positionFromFrameNumber(uint32_t frameNumber);
uint32_t frameNumberFromPosition(uint64_t position);
bool getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength);
@@ -75,6 +76,7 @@ private:
uint32_t m_totalFrames;
char *m_recordingFilename;
bool m_pesrecording;
+ bool m_inProgress;
};
#endif // VNSI_RECPLAYER_H
diff --git a/requestpacket.c b/requestpacket.c
index cb61e6b..51245ca 100644
--- a/requestpacket.c
+++ b/requestpacket.c
@@ -28,7 +28,13 @@
#include <stdint.h>
#include <string.h>
+#ifndef __FreeBSD__
#include <asm/byteorder.h>
+#else
+#include <sys/endian.h>
+#define __be64_to_cpu be64toh
+#define __cpu_to_be64 htobe64
+#endif
#include "config.h"
#include "requestpacket.h"
@@ -102,6 +108,16 @@ uint64_t cRequestPacket::extract_U64()
return ull;
}
+int64_t cRequestPacket::extract_S64()
+{
+ if ((packetPos + sizeof(int64_t)) > userDataLength) return 0;
+ int64_t ll;
+ memcpy(&ll, &userData[packetPos], sizeof(int64_t));
+ ll = __be64_to_cpu(ll);
+ packetPos += sizeof(int64_t);
+ return ll;
+}
+
double cRequestPacket::extract_Double()
{
if ((packetPos + sizeof(uint64_t)) > userDataLength) return 0;
diff --git a/requestpacket.h b/requestpacket.h
index 4e3704d..44fab53 100644
--- a/requestpacket.h
+++ b/requestpacket.h
@@ -46,6 +46,7 @@ public:
uint8_t extract_U8();
uint32_t extract_U32();
uint64_t extract_U64();
+ int64_t extract_S64();
int32_t extract_S32();
double extract_Double();
diff --git a/responsepacket.c b/responsepacket.c
index 646e708..33ea5d0 100644
--- a/responsepacket.c
+++ b/responsepacket.c
@@ -31,8 +31,15 @@
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
+#include <inttypes.h>
+#ifndef __FreeBSD__
#include <asm/byteorder.h>
+#else
+#include <sys/endian.h>
+#define __be64_to_cpu be64toh
+#define __cpu_to_be64 htobe64
+#endif
#include "responsepacket.h"
#include "vnsicommand.h"
@@ -120,7 +127,7 @@ bool cResponsePacket::initStatus(uint32_t opCode)
return true;
}
-bool cResponsePacket::initStream(uint32_t opCode, uint32_t streamID, uint32_t duration, int64_t pts, int64_t dts)
+bool cResponsePacket::initStream(uint32_t opCode, uint32_t streamID, uint32_t duration, int64_t pts, int64_t dts, uint32_t serial)
{
initBuffers();
@@ -139,6 +146,8 @@ bool cResponsePacket::initStream(uint32_t opCode, uint32_t streamID, uint32_t du
memcpy(&buffer[16], &ull, sizeof(uint64_t));
ull = __cpu_to_be64(dts); // DTS
memcpy(&buffer[24], &ull, sizeof(uint64_t));
+ ul = htonl(serial);
+ memcpy(&buffer[32], &ul, sizeof(uint32_t));
ul = 0;
memcpy(&buffer[userDataLenPosStream], &ul, sizeof(uint32_t));
diff --git a/responsepacket.h b/responsepacket.h
index 936703c..0e0c9a0 100644
--- a/responsepacket.h
+++ b/responsepacket.h
@@ -40,7 +40,7 @@ public:
bool init(uint32_t requestID);
bool initScan(uint32_t opCode);
bool initStatus(uint32_t opCode);
- bool initStream(uint32_t opCode, uint32_t streamID, uint32_t duration, int64_t pts, int64_t dts);
+ bool initStream(uint32_t opCode, uint32_t streamID, uint32_t duration, int64_t pts, int64_t dts, uint32_t serial);
bool initOsd(uint32_t opCode, int32_t wnd, int32_t color, int32_t x0, int32_t y0, int32_t x1, int32_t y1);
void finalise();
void finaliseStream();
@@ -72,8 +72,8 @@ private:
const static uint32_t headerLength = 12;
const static uint32_t userDataLenPos = 8;
- const static uint32_t headerLengthStream = 36;
- const static uint32_t userDataLenPosStream = 32;
+ const static uint32_t headerLengthStream = 40;
+ const static uint32_t userDataLenPosStream = 36;
const static uint32_t headerLengthOSD = 36;
const static uint32_t userDataLenPosOSD = 32;
};
diff --git a/setup.c b/setup.c
index c49d300..61df5be 100644
--- a/setup.c
+++ b/setup.c
@@ -1,31 +1,51 @@
/*
-* Copyright (C) 2005-2012 Team XBMC
-* 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, see
-* <http://www.gnu.org/licenses/>.
-*
-*/
+ * Copyright (C) 2005-2012 Team XBMC
+ * 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
#include "setup.h"
+#include "vnsicommand.h"
int PmtTimeout = 5;
+int TimeshiftMode = 0;
+int TimeshiftBufferSize = 5;
+int TimeshiftBufferFileSize = 6;
+char TimeshiftBufferDir[PATH_MAX] = "\0";
cMenuSetupVNSI::cMenuSetupVNSI(void)
{
newPmtTimeout = PmtTimeout;
Add(new cMenuEditIntItem( tr("PMT Timeout (0-10)"), &newPmtTimeout));
+
+ timeshiftModesTexts[0] = tr("Off");
+ timeshiftModesTexts[1] = tr("RAM");
+ timeshiftModesTexts[2] = tr("File");
+ newTimeshiftMode = TimeshiftMode;
+ Add(new cMenuEditStraItem( tr("Time Shift Mode"), &newTimeshiftMode, 3, timeshiftModesTexts));
+
+ newTimeshiftBufferSize = TimeshiftBufferSize;
+ Add(new cMenuEditIntItem( tr("TS Buffersize (RAM) (1-20) x 100MB"), &newTimeshiftBufferSize));
+
+ newTimeshiftBufferFileSize = TimeshiftBufferFileSize;
+ Add(new cMenuEditIntItem( tr("TS Buffersize (File) (1-10) x 1GB"), &newTimeshiftBufferFileSize));
+
+ strn0cpy(newTimeshiftBufferDir, TimeshiftBufferDir, sizeof(newTimeshiftBufferDir));
+ Add(new cMenuEditStrItem(tr("TS Buffer Directory"), newTimeshiftBufferDir, sizeof(newTimeshiftBufferDir)));
}
void cMenuSetupVNSI::Store(void)
@@ -33,4 +53,20 @@ void cMenuSetupVNSI::Store(void)
if (newPmtTimeout > 10 || newPmtTimeout < 0)
newPmtTimeout = 2;
SetupStore(CONFNAME_PMTTIMEOUT, PmtTimeout = newPmtTimeout);
+
+ SetupStore(CONFNAME_TIMESHIFT, TimeshiftMode = newTimeshiftMode);
+
+ if (newTimeshiftBufferSize > 40)
+ newTimeshiftBufferSize = 40;
+ else if (newTimeshiftBufferSize < 1)
+ newTimeshiftBufferSize = 1;
+ SetupStore(CONFNAME_TIMESHIFTBUFFERSIZE, TimeshiftBufferSize = newTimeshiftBufferSize);
+
+ if (newTimeshiftBufferFileSize > 20)
+ newTimeshiftBufferFileSize = 20;
+ else if (newTimeshiftBufferFileSize < 1)
+ newTimeshiftBufferFileSize = 1;
+ SetupStore(CONFNAME_TIMESHIFTBUFFERFILESIZE, TimeshiftBufferFileSize = newTimeshiftBufferFileSize);
+
+ SetupStore(CONFNAME_TIMESHIFTBUFFERDIR, strn0cpy(TimeshiftBufferDir, newTimeshiftBufferDir, sizeof(TimeshiftBufferDir)));
}
diff --git a/setup.h b/setup.h
index 36d0f7c..acd584f 100644
--- a/setup.h
+++ b/setup.h
@@ -23,12 +23,15 @@
#include <vdr/plugin.h>
-#define CONFNAME_PMTTIMEOUT "PmtTimeout"
-
class cMenuSetupVNSI : public cMenuSetupPage
{
private:
int newPmtTimeout;
+ int newTimeshiftMode;
+ const char *timeshiftModesTexts[3];
+ int newTimeshiftBufferSize;
+ int newTimeshiftBufferFileSize;
+ char newTimeshiftBufferDir[PATH_MAX];
protected:
virtual void Store(void);
public:
diff --git a/streamer.c b/streamer.c
index 66ceba0..b9b81a3 100644
--- a/streamer.c
+++ b/streamer.c
@@ -40,8 +40,9 @@
// --- cLiveStreamer -------------------------------------------------
-cLiveStreamer::cLiveStreamer(uint32_t timeout)
+cLiveStreamer::cLiveStreamer(int clientID, uint8_t timeshift, uint32_t timeout)
: cThread("cLiveStreamer stream processor")
+ , m_ClientID(clientID)
, m_scanTimeout(timeout)
{
m_Channel = NULL;
@@ -52,6 +53,8 @@ cLiveStreamer::cLiveStreamer(uint32_t timeout)
m_startup = true;
m_SignalLost = false;
m_IFrameSeen = false;
+ m_VideoBuffer = NULL;
+ m_Timeshift = timeshift;
memset(&m_FrontendInfo, 0, sizeof(m_FrontendInfo));
@@ -64,18 +67,98 @@ cLiveStreamer::~cLiveStreamer()
DEBUGLOG("Started to delete live streamer");
Cancel(5);
+ Close();
+ DEBUGLOG("Finished to delete live streamer");
+}
+
+bool cLiveStreamer::Open(int serial)
+{
+ Close();
+
+#if APIVERSNUM >= 10725
+ m_Device = cDevice::GetDevice(m_Channel, m_Priority, true, true);
+#else
+ m_Device = cDevice::GetDevice(m_Channel, m_Priority, true);
+#endif
+
+ if (!m_Device)
+ return false;
+
+ bool recording = false;
+ if (0) // test harness
+ {
+ recording = true;
+ m_VideoBuffer = cVideoBuffer::Create("/home/xbmc/test.ts");
+ }
+ else if (serial == -1)
+ {
+ for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer))
+ {
+ if (timer &&
+ timer->Recording() &&
+ timer->Channel() == m_Channel)
+ {
+ Recordings.Load();
+ cRecording matchRec(timer, timer->Event());
+ cRecording *rec;
+ {
+ cThreadLock RecordingsLock(&Recordings);
+ rec = Recordings.GetByName(matchRec.FileName());
+ if (!rec)
+ {
+ return false;
+ }
+ }
+ m_VideoBuffer = cVideoBuffer::Create(rec);
+ recording = true;
+ break;
+ }
+ }
+ }
+ if (!recording)
+ {
+ m_VideoBuffer = cVideoBuffer::Create(m_ClientID, m_Timeshift);
+ }
+
+ if (!m_VideoBuffer)
+ return false;
+
+ if (!recording)
+ {
+ if (m_Channel && ((m_Channel->Source() >> 24) == 'V'))
+ m_IsMPEGPS = true;
+
+ if (!m_VideoInput.Open(m_Channel, m_Priority, m_VideoBuffer))
+ {
+ ERRORLOG("Can't switch to channel %i - %s", m_Channel->Number(), m_Channel->Name());
+ return false;
+ }
+ }
+
+ m_Demuxer.Open(*m_Channel, m_VideoBuffer);
+ if (serial >= 0)
+ m_Demuxer.SetSerial(serial);
+
+ return true;
+}
+
+void cLiveStreamer::Close(void)
+{
+ INFOLOG("LiveStreamer::Close - close");
m_VideoInput.Close();
m_Demuxer.Close();
- delete m_VideoBuffer;
+ if (m_VideoBuffer)
+ {
+ delete m_VideoBuffer;
+ m_VideoBuffer = NULL;
+ }
if (m_Frontend >= 0)
{
close(m_Frontend);
m_Frontend = -1;
}
-
- DEBUGLOG("Finished to delete live streamer");
}
void cLiveStreamer::Action(void)
@@ -84,6 +167,7 @@ void cLiveStreamer::Action(void)
sStreamPacket pkt;
bool requestStreamChange = false;
cTimeMs last_info(1000);
+ cTimeMs bufferStatsTimer(1000);
while (Running())
{
@@ -108,18 +192,33 @@ void cLiveStreamer::Action(void)
last_info.Set(0);
sendSignalInfo();
}
+
+ // send buffer stats
+ if(bufferStatsTimer.TimedOut())
+ {
+ sendBufferStatus();
+ bufferStatsTimer.Set(1000);
+ }
}
- else
+ else if (ret == -1)
{
// no data
+ usleep(10000);
if(m_last_tick.Elapsed() >= (uint64_t)(m_scanTimeout*1000))
{
- INFOLOG("No Signal");
sendStreamStatus();
m_last_tick.Set(0);
m_SignalLost = true;
}
}
+ else if (ret == -2)
+ {
+ if (!Open(m_Demuxer.GetSerial()))
+ {
+ m_Socket->Shutdown();
+ break;
+ }
+ }
}
INFOLOG("exit streamer thread");
}
@@ -132,32 +231,22 @@ bool cLiveStreamer::StreamChannel(const cChannel *channel, int priority, cxSocke
return false;
}
- m_VideoBuffer = cVideoBuffer::Create();
m_Channel = channel;
+ m_Priority = priority;
m_Socket = Socket;
- m_Device = cDevice::GetDevice(m_Channel, priority, true);
-
- if (m_Channel && ((m_Channel->Source() >> 24) == 'V')) m_IsMPEGPS = true;
- if (m_VideoInput.Open(channel, priority, m_VideoBuffer))
- {
- m_Demuxer.Open(*m_Channel, m_VideoBuffer);
+ if (!Open())
+ return false;
- // 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());
+ // 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());
- Activate(true);
+ Activate(true);
- 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());
- }
- return false;
+ INFOLOG("Successfully switched to channel %i - %s", m_Channel->Number(), m_Channel->Name());
+ return true;
}
inline void cLiveStreamer::Activate(bool On)
@@ -182,7 +271,7 @@ void cLiveStreamer::sendStreamPacket(sStreamPacket *pkt)
if(pkt->size == 0)
return;
- if (!m_streamHeader.initStream(VNSI_STREAM_MUXPKT, pkt->id, pkt->duration, pkt->pts, pkt->dts))
+ if (!m_streamHeader.initStream(VNSI_STREAM_MUXPKT, pkt->id, pkt->duration, pkt->pts, pkt->dts, pkt->serial))
{
ERRORLOG("stream response packet init fail");
return;
@@ -202,7 +291,7 @@ void cLiveStreamer::sendStreamPacket(sStreamPacket *pkt)
void cLiveStreamer::sendStreamChange()
{
cResponsePacket *resp = new cResponsePacket();
- if (!resp->initStream(VNSI_STREAM_CHANGE, 0, 0, 0, 0))
+ if (!resp->initStream(VNSI_STREAM_CHANGE, 0, 0, 0, 0, 0))
{
ERRORLOG("stream response packet init fail");
delete resp;
@@ -329,7 +418,7 @@ void cLiveStreamer::sendSignalInfo()
if (m_Frontend == -2)
{
cResponsePacket *resp = new cResponsePacket();
- if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0))
+ if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0, 0))
{
ERRORLOG("stream response packet init fail");
delete resp;
@@ -377,7 +466,7 @@ void cLiveStreamer::sendSignalInfo()
if (m_Frontend >= 0)
{
cResponsePacket *resp = new cResponsePacket();
- if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0))
+ if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0, 0))
{
ERRORLOG("stream response packet init fail");
delete resp;
@@ -417,7 +506,7 @@ void cLiveStreamer::sendSignalInfo()
if (m_Frontend >= 0)
{
cResponsePacket *resp = new cResponsePacket();
- if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0))
+ if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0, 0))
{
ERRORLOG("stream response packet init fail");
delete resp;
@@ -470,14 +559,63 @@ void cLiveStreamer::sendSignalInfo()
void cLiveStreamer::sendStreamStatus()
{
cResponsePacket *resp = new cResponsePacket();
- if (!resp->initStream(VNSI_STREAM_STATUS, 0, 0, 0, 0))
+ if (!resp->initStream(VNSI_STREAM_STATUS, 0, 0, 0, 0, 0))
+ {
+ ERRORLOG("stream response packet init fail");
+ delete resp;
+ return;
+ }
+ uint16_t error = m_Demuxer.GetError();
+ if (error & ERROR_PES_SCRAMBLE)
+ {
+ INFOLOG("Channel: scrambled %d", error);
+ resp->add_String(cString::sprintf("Channel: scrambled (%d)", error));
+ }
+ else if (error & ERROR_PES_STARTCODE)
+ {
+ INFOLOG("Channel: startcode %d", error);
+ resp->add_String(cString::sprintf("Channel: encrypted? (%d)", error));
+ }
+ else if (error & ERROR_DEMUX_NODATA)
+ {
+ INFOLOG("Channel: no data %d", error);
+ resp->add_String(cString::sprintf("Channel: no data"));
+ }
+ else
+ {
+ INFOLOG("Channel: unknown error %d", error);
+ resp->add_String(cString::sprintf("Channel: unknown error (%d)", error));
+ }
+
+ resp->finaliseStream();
+ m_Socket->write(resp->getPtr(), resp->getLen());
+ delete resp;
+}
+
+void cLiveStreamer::sendBufferStatus()
+{
+ cResponsePacket *resp = new cResponsePacket();
+ if (!resp->initStream(VNSI_STREAM_BUFFERSTATS, 0, 0, 0, 0, 0))
{
ERRORLOG("stream response packet init fail");
delete resp;
return;
}
- resp->add_String("No Signal");
+ int32_t start, current, end;
+ bool timeshift;
+ m_Demuxer.BufferStatus(timeshift, start, current, end);
+ resp->add_U8(timeshift);
+ resp->add_S32(start);
+ resp->add_S32(current);
+ resp->add_S32(end);
resp->finaliseStream();
m_Socket->write(resp->getPtr(), resp->getLen());
delete resp;
}
+
+bool cLiveStreamer::SeekTime(int64_t time, uint32_t &serial)
+{
+ bool ret = m_Demuxer.SeekTime(time);
+ serial = m_Demuxer.GetSerial();
+ return ret;
+}
diff --git a/streamer.h b/streamer.h
index afcd7c6..3c308e5 100644
--- a/streamer.h
+++ b/streamer.h
@@ -57,7 +57,9 @@ private:
void sendStreamChange();
void sendSignalInfo();
void sendStreamStatus();
+ void sendBufferStatus();
+ int m_ClientID;
const cChannel *m_Channel; /*!> Channel to stream */
cDevice *m_Device;
cxSocket *m_Socket; /*!> The socket class to communicate with client */
@@ -76,12 +78,16 @@ private:
cVNSIDemuxer m_Demuxer;
cVideoBuffer *m_VideoBuffer;
cVideoInput m_VideoInput;
+ int m_Priority;
+ uint8_t m_Timeshift;
protected:
virtual void Action(void);
+ bool Open(int serial = -1);
+ void Close();
public:
- cLiveStreamer(uint32_t timeout = 0);
+ cLiveStreamer(int clientID, uint8_t timeshift, uint32_t timeout = 0);
virtual ~cLiveStreamer();
void Activate(bool On);
@@ -90,6 +96,7 @@ public:
bool IsStarting() { return m_startup; }
bool IsAudioOnly() { return m_IsAudioOnly; }
bool IsMPEGPS() { return m_IsMPEGPS; }
+ bool SeekTime(int64_t time, uint32_t &serial);
};
#endif // VNSI_RECEIVER_H
diff --git a/videobuffer.c b/videobuffer.c
index 1ee9677..92c38f0 100644
--- a/videobuffer.c
+++ b/videobuffer.c
@@ -19,16 +19,25 @@
*/
#include "videobuffer.h"
+#include "config.h"
+#include "vnsi.h"
+#include "recplayer.h"
#include <vdr/ringbuffer.h>
#include <vdr/remux.h>
+#include <vdr/videodir.h>
+#include <vdr/recording.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
class cVideoBufferSimple : public cVideoBuffer
{
friend class cVideoBuffer;
public:
- virtual void Put(uint8_t *buf, int size);
- virtual int Read(uint8_t **buf, int size);
+ virtual void Put(uint8_t *buf, unsigned int size);
+ virtual int ReadBlock(uint8_t **buf, unsigned int size);
protected:
cVideoBufferSimple();
@@ -50,12 +59,12 @@ cVideoBufferSimple::~cVideoBufferSimple()
delete m_Buffer;
}
-void cVideoBufferSimple::Put(uint8_t *buf, int size)
+void cVideoBufferSimple::Put(uint8_t *buf, unsigned int size)
{
m_Buffer->Put(buf, size);
}
-int cVideoBufferSimple::Read(uint8_t **buf, int size)
+int cVideoBufferSimple::ReadBlock(uint8_t **buf, unsigned int size)
{
int readBytes;
if (m_BytesConsumed)
@@ -92,16 +101,827 @@ int cVideoBufferSimple::Read(uint8_t **buf, int size)
//-----------------------------------------------------------------------------
+#define MARGIN 40000
+
+class cVideoBufferTimeshift : public cVideoBuffer
+{
+friend class cVideoBuffer;
+public:
+ virtual off_t GetPosMin();
+ virtual off_t GetPosMax();
+ virtual off_t GetPosCur();
+ virtual void GetPositions(off_t *cur, off_t *min, off_t *max);
+ virtual bool HasBuffer() { return true; };
+
+protected:
+ cVideoBufferTimeshift();
+ virtual bool Init() = 0;
+ virtual off_t Available();
+ off_t m_BufferSize;
+ off_t m_WritePtr;
+ off_t m_ReadPtr;
+ bool m_BufferFull;
+ unsigned int m_Margin;
+ unsigned int m_BytesConsumed;
+ cMutex m_Mutex;
+};
+
+cVideoBufferTimeshift::cVideoBufferTimeshift()
+{
+ m_Margin = TS_SIZE*2;
+ m_BufferFull = false;
+ m_ReadPtr = 0;
+ m_WritePtr = 0;
+ m_BytesConsumed = 0;
+}
+
+off_t cVideoBufferTimeshift::GetPosMin()
+{
+ off_t ret;
+ if (!m_BufferFull)
+ return 0;
+
+ ret = m_WritePtr + MARGIN * 2;
+ if (ret >= m_BufferSize)
+ ret -= m_BufferSize;
+
+ return ret;
+}
+
+off_t cVideoBufferTimeshift::GetPosMax()
+{
+ off_t ret = m_WritePtr;
+ if (ret < GetPosMin())
+ ret += m_BufferSize;
+ return ret;
+}
+
+off_t cVideoBufferTimeshift::GetPosCur()
+{
+ off_t ret = m_ReadPtr;
+ if (ret < GetPosMin())
+ ret += m_BufferSize;
+ return ret;
+}
+
+void cVideoBufferTimeshift::GetPositions(off_t *cur, off_t *min, off_t *max)
+{
+ cMutexLock lock(&m_Mutex);
+
+ *cur = GetPosCur();
+ *min = GetPosMin();
+ *min = (*min > *cur) ? *cur : *min;
+ *max = GetPosMax();
+}
+
+off_t cVideoBufferTimeshift::Available()
+{
+ cMutexLock lock(&m_Mutex);
+
+ off_t ret;
+ if (m_ReadPtr <= m_WritePtr)
+ ret = m_WritePtr - m_ReadPtr;
+ else
+ ret = m_BufferSize - (m_ReadPtr - m_WritePtr);
+
+ return ret;
+}
+//-----------------------------------------------------------------------------
+
+class cVideoBufferRAM : public cVideoBufferTimeshift
+{
+friend class cVideoBuffer;
+public:
+ virtual void Put(uint8_t *buf, unsigned int size);
+ virtual int ReadBlock(uint8_t **buf, unsigned int size);
+ virtual void SetPos(off_t pos);
+
+protected:
+ cVideoBufferRAM();
+ virtual ~cVideoBufferRAM();
+ virtual bool Init();
+ uint8_t *m_Buffer;
+ uint8_t *m_BufferPtr;
+};
+
+cVideoBufferRAM::cVideoBufferRAM()
+{
+ m_Buffer = 0;
+}
+
+cVideoBufferRAM::~cVideoBufferRAM()
+{
+ if (m_Buffer)
+ free(m_Buffer);
+}
+
+bool cVideoBufferRAM::Init()
+{
+ m_BufferSize = (off_t)TimeshiftBufferSize*100*1000*1000;
+ INFOLOG("allocated timeshift buffer with size: %ld", m_BufferSize);
+ m_Buffer = (uint8_t*)malloc(m_BufferSize + m_Margin);
+ m_BufferPtr = m_Buffer + m_Margin;
+ if (!m_Buffer)
+ return false;
+ else
+ return true;
+}
+
+void cVideoBufferRAM::SetPos(off_t pos)
+{
+ cMutexLock lock(&m_Mutex);
+
+ m_ReadPtr = pos;
+ if (m_ReadPtr >= m_BufferSize)
+ m_ReadPtr -= m_BufferSize;
+ m_BytesConsumed = 0;
+}
+
+void cVideoBufferRAM::Put(uint8_t *buf, unsigned int size)
+{
+ if (Available() + MARGIN >= m_BufferSize)
+ {
+ ERRORLOG("------------- skipping data");
+ return;
+ }
+
+ if ((m_BufferSize - m_WritePtr) <= size)
+ {
+ int bytes = m_BufferSize - m_WritePtr;
+ memcpy(m_BufferPtr+m_WritePtr, buf, bytes);
+ size -= bytes;
+ buf += bytes;
+ cMutexLock lock(&m_Mutex);
+ m_WritePtr = 0;
+ }
+
+ memcpy(m_BufferPtr+m_WritePtr, buf, size);
+
+ cMutexLock lock(&m_Mutex);
+
+ m_WritePtr += size;
+ if (!m_BufferFull)
+ {
+ if ((m_WritePtr + 2*MARGIN) > m_BufferSize)
+ m_BufferFull = true;
+ }
+}
+
+int cVideoBufferRAM::ReadBlock(uint8_t **buf, unsigned int size)
+{
+ // move read pointer
+ if (m_BytesConsumed)
+ {
+ cMutexLock lock(&m_Mutex);
+ m_ReadPtr += m_BytesConsumed;
+ if (m_ReadPtr >= m_BufferSize)
+ m_ReadPtr -= m_BufferSize;
+ }
+ m_BytesConsumed = 0;
+
+ // check if we have anything to read
+ off_t readBytes = Available();
+ if (readBytes < m_Margin)
+ {
+ return 0;
+ }
+
+ // if we are close to end, copy margin to front
+ if (m_ReadPtr > (m_BufferSize - m_Margin))
+ {
+ int bytesToCopy = m_BufferSize - m_ReadPtr;
+ memmove(m_Buffer + (m_Margin - bytesToCopy), m_Buffer + m_ReadPtr, bytesToCopy);
+ *buf = m_Buffer + (m_Margin - bytesToCopy);
+ }
+ else
+ *buf = m_BufferPtr + m_ReadPtr;
+
+ // Make sure we are looking at a TS packet
+ while (readBytes > TS_SIZE)
+ {
+ if ((*buf)[0] == TS_SYNC_BYTE && (*buf)[TS_SIZE] == TS_SYNC_BYTE)
+ break;
+ m_BytesConsumed++;
+ (*buf)++;
+ readBytes--;
+ }
+
+ if ((*buf)[0] != TS_SYNC_BYTE)
+ {
+ return 0;
+ }
+
+ m_BytesConsumed += TS_SIZE;
+ return TS_SIZE;
+}
+
+//-----------------------------------------------------------------------------
+
+class cVideoBufferFile : public cVideoBufferTimeshift
+{
+friend class cVideoBuffer;
+public:
+ virtual off_t GetPosMax();
+ virtual void Put(uint8_t *buf, unsigned int size);
+ virtual int ReadBlock(uint8_t **buf, unsigned int size);
+ virtual void SetPos(off_t pos);
+
+protected:
+ cVideoBufferFile();
+ cVideoBufferFile(int clientID);
+ virtual ~cVideoBufferFile();
+ virtual bool Init();
+ virtual int ReadBytes(uint8_t *buf, off_t pos, unsigned int size);
+ int m_ClientID;
+ cString m_Filename;
+ int m_Fd;
+ uint8_t *m_ReadCache;
+ unsigned int m_ReadCachePtr;
+ unsigned int m_ReadCacheSize;
+ unsigned int m_ReadCacheMaxSize;
+};
+
+cVideoBufferFile::cVideoBufferFile()
+{
+
+}
+
+cVideoBufferFile::cVideoBufferFile(int clientID)
+{
+ m_ClientID = clientID;
+ m_Fd = 0;
+ m_ReadCacheSize = 0;
+ m_ReadCache = 0;
+}
+
+cVideoBufferFile::~cVideoBufferFile()
+{
+ if (m_Fd)
+ {
+ close(m_Fd);
+ unlink(m_Filename);
+ m_Fd = 0;
+ }
+ if (m_ReadCache)
+ free(m_ReadCache);
+}
+
+bool cVideoBufferFile::Init()
+{
+ m_ReadCache = 0;
+ m_ReadCacheMaxSize = 32000;
+
+ m_ReadCache = (uint8_t*)malloc(m_ReadCacheMaxSize);
+ if (!m_ReadCache)
+ return false;
+
+ m_BufferSize = (off_t)TimeshiftBufferFileSize*1000*1000*1000;
+
+ struct stat sb;
+ if ((*TimeshiftBufferDir) && stat(TimeshiftBufferDir, &sb) == 0 && S_ISDIR(sb.st_mode))
+ {
+ if (TimeshiftBufferDir[strlen(TimeshiftBufferDir)-1] == '/')
+ m_Filename = cString::sprintf("%sTimeshift-%d.vnsi", TimeshiftBufferDir, m_ClientID);
+ else
+ m_Filename = cString::sprintf("%s/Timeshift-%d.vnsi", TimeshiftBufferDir, m_ClientID);
+ }
+ else
+#if VDRVERSNUM >= 20102
+ m_Filename = cString::sprintf("%s/Timeshift-%d.vnsi", cVideoDirectory::Name(), m_ClientID);
+#else
+ m_Filename = cString::sprintf("%s/Timeshift-%d.vnsi", VideoDirectory, m_ClientID);
+#endif
+
+ m_Fd = open(m_Filename, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);
+ if (m_Fd == -1)
+ {
+ ERRORLOG("Could not open file: %s", (const char*)m_Filename);
+ return false;
+ }
+ m_WritePtr = lseek(m_Fd, m_BufferSize - 1, SEEK_SET);
+ if (m_WritePtr == -1)
+ {
+ ERRORLOG("(Init) Could not seek file: %s", (const char*)m_Filename);
+ return false;
+ }
+ char tmp = '0';
+ if (safe_write(m_Fd, &tmp, 1) < 0)
+ {
+ ERRORLOG("(Init) Could not write to file: %s", (const char*)m_Filename);
+ return false;
+ }
+
+ m_WritePtr = 0;
+ m_ReadPtr = 0;
+ m_ReadCacheSize = 0;
+ return true;
+}
+
+void cVideoBufferFile::SetPos(off_t pos)
+{
+ cMutexLock lock(&m_Mutex);
+
+ m_ReadPtr = pos;
+ if (m_ReadPtr >= m_BufferSize)
+ m_ReadPtr -= m_BufferSize;
+ m_BytesConsumed = 0;
+ m_ReadCacheSize = 0;
+}
+
+off_t cVideoBufferFile::GetPosMax()
+{
+ off_t posMax = cVideoBufferTimeshift::GetPosMax();
+ if (posMax >= m_ReadCacheMaxSize)
+ posMax -= m_ReadCacheMaxSize;
+ else
+ posMax = 0;
+ return posMax;
+}
+
+void cVideoBufferFile::Put(uint8_t *buf, unsigned int size)
+{
+ if (Available() + MARGIN >= m_BufferSize)
+ {
+ ERRORLOG("------------- skipping data");
+ return;
+ }
+
+ if ((m_BufferSize - m_WritePtr) <= size)
+ {
+ int bytes = m_BufferSize - m_WritePtr;
+
+ int p = 0;
+ off_t ptr = m_WritePtr;
+ while(bytes > 0)
+ {
+ p = pwrite(m_Fd, buf, bytes, ptr);
+ if (p < 0)
+ {
+ ERRORLOG("Could not write to file: %s", (const char*)m_Filename);
+ return;
+ }
+ size -= p;
+ bytes -= p;
+ buf += p;
+ ptr += p;
+ }
+ cMutexLock lock(&m_Mutex);
+ m_WritePtr = 0;
+ }
+
+ off_t ptr = m_WritePtr;
+ int bytes = size;
+ int p;
+ while(bytes > 0)
+ {
+ p = pwrite(m_Fd, buf, bytes, ptr);
+ if (p < 0)
+ {
+ ERRORLOG("Could not write to file: %s", (const char*)m_Filename);
+ return;
+ }
+ bytes -= p;
+ buf += p;
+ ptr += p;
+ }
+
+ cMutexLock lock(&m_Mutex);
+
+ m_WritePtr += size;
+ if (!m_BufferFull)
+ {
+ if ((m_WritePtr + 2*MARGIN) > m_BufferSize)
+ m_BufferFull = true;
+ }
+}
+
+int cVideoBufferFile::ReadBytes(uint8_t *buf, off_t pos, unsigned int size)
+{
+ int p;
+ for (;;)
+ {
+ p = pread(m_Fd, buf, size, pos);
+ if (p < 0 && errno == EINTR)
+ {
+ continue;
+ }
+ return p;
+ }
+}
+
+int cVideoBufferFile::ReadBlock(uint8_t **buf, unsigned int size)
+{
+ // move read pointer
+ if (m_BytesConsumed)
+ {
+ cMutexLock lock(&m_Mutex);
+ m_ReadPtr += m_BytesConsumed;
+ if (m_ReadPtr >= m_BufferSize)
+ m_ReadPtr -= m_BufferSize;
+ m_ReadCachePtr += m_BytesConsumed;
+ }
+ m_BytesConsumed = 0;
+
+ // check if we have anything to read
+ off_t readBytes;
+ if (m_ReadCacheSize && ((m_ReadCachePtr + m_Margin) <= m_ReadCacheSize))
+ {
+ readBytes = m_ReadCacheSize - m_ReadCachePtr;
+ *buf = m_ReadCache + m_ReadCachePtr;
+ }
+ else if ((readBytes = Available()) >= m_ReadCacheMaxSize)
+ {
+ if (m_ReadPtr + m_ReadCacheMaxSize <= m_BufferSize)
+ {
+ m_ReadCacheSize = ReadBytes(m_ReadCache, m_ReadPtr, m_ReadCacheMaxSize);
+ if (m_ReadCacheSize < 0)
+ {
+ ERRORLOG("Could not read file: %s", (const char*)m_Filename);
+ return 0;
+ }
+ if (m_ReadCacheSize < m_Margin)
+ {
+ ERRORLOG("Could not read file (margin): %s , read: %d", (const char*)m_Filename, m_ReadCacheSize);
+ m_ReadCacheSize = 0;
+ return 0;
+ }
+ readBytes = m_ReadCacheSize;
+ *buf = m_ReadCache;
+ m_ReadCachePtr = 0;
+ }
+ else
+ {
+ m_ReadCacheSize = ReadBytes(m_ReadCache, m_ReadPtr, m_BufferSize - m_ReadPtr);
+ if ((m_ReadCacheSize < m_Margin) && (m_ReadCacheSize != (m_BufferSize - m_ReadPtr)))
+ {
+ ERRORLOG("Could not read file (end): %s", (const char*)m_Filename);
+ m_ReadCacheSize = 0;
+ return 0;
+ }
+ readBytes = ReadBytes(m_ReadCache + m_ReadCacheSize, 0, m_ReadCacheMaxSize - m_ReadCacheSize);
+ if (readBytes < 0)
+ {
+ ERRORLOG("Could not read file (end): %s", (const char*)m_Filename);
+ m_ReadCacheSize = 0;
+ return 0;
+ }
+ m_ReadCacheSize += readBytes;
+ if (m_ReadCacheSize < m_Margin)
+ {
+ ERRORLOG("Could not read file (margin): %s", (const char*)m_Filename);
+ m_ReadCacheSize = 0;
+ return 0;
+ }
+ readBytes = m_ReadCacheSize;
+ *buf = m_ReadCache;
+ m_ReadCachePtr = 0;
+ }
+ }
+ else
+ return 0;
+
+ // Make sure we are looking at a TS packet
+ while (readBytes > TS_SIZE)
+ {
+ if ((*buf)[0] == TS_SYNC_BYTE && (*buf)[TS_SIZE] == TS_SYNC_BYTE)
+ break;
+ m_BytesConsumed++;
+ (*buf)++;
+ readBytes--;
+ }
+
+ if ((*buf)[0] != TS_SYNC_BYTE)
+ {
+ return 0;
+ }
+
+ m_BytesConsumed += TS_SIZE;
+ return TS_SIZE;
+}
+
+//-----------------------------------------------------------------------------
+
+class cVideoBufferRecording : public cVideoBufferFile
+{
+friend class cVideoBuffer;
+public:
+ virtual off_t GetPosMax();
+ virtual void Put(uint8_t *buf, unsigned int size);
+ virtual int ReadBlock(uint8_t **buf, unsigned int size);
+
+protected:
+ cVideoBufferRecording(cRecording *rec);
+ virtual ~cVideoBufferRecording();
+ virtual bool Init();
+ virtual off_t Available();
+ off_t GetPosEnd();
+ cRecPlayer *m_RecPlayer;
+ cRecording *m_Recording;
+ cTimeMs m_ScanTimer;
+};
+
+cVideoBufferRecording::cVideoBufferRecording(cRecording *rec)
+{
+ m_Recording = rec;
+ m_ReadCacheSize = 0;
+ m_ReadCache = 0;
+}
+
+cVideoBufferRecording::~cVideoBufferRecording()
+{
+ INFOLOG("delete cVideoBufferRecording");
+ if (m_RecPlayer)
+ delete m_RecPlayer;
+}
+
+off_t cVideoBufferRecording::GetPosMax()
+{
+ m_RecPlayer->reScan();
+ m_WritePtr = m_RecPlayer->getLengthBytes();
+ return cVideoBufferFile::GetPosMax();
+}
+
+void cVideoBufferRecording::Put(uint8_t *buf, unsigned int size)
+{
+
+}
+
+bool cVideoBufferRecording::Init()
+{
+ m_ReadCacheMaxSize = 32000;
+
+ m_ReadCache = (uint8_t*)malloc(m_ReadCacheMaxSize);
+ if (!m_ReadCache)
+ return false;
+
+ m_RecPlayer = new cRecPlayer(m_Recording, true);
+ if (!m_RecPlayer)
+ return false;
+
+ m_WritePtr = 0;
+ m_ReadPtr = 0;
+ m_ReadCacheSize = 0;
+ m_InputAttached = false;
+ m_ScanTimer.Set(0);
+ return true;
+}
+
+off_t cVideoBufferRecording::Available()
+{
+ if (m_ScanTimer.TimedOut())
+ {
+ m_RecPlayer->reScan();
+ m_ScanTimer.Set(1000);
+ }
+ m_BufferSize = m_WritePtr = m_RecPlayer->getLengthBytes();
+ return cVideoBufferTimeshift::Available();
+}
+
+int cVideoBufferRecording::ReadBlock(uint8_t **buf, unsigned int size)
+{
+ // move read pointer
+ if (m_BytesConsumed)
+ {
+ m_ReadPtr += m_BytesConsumed;
+ if (m_ReadPtr >= m_BufferSize)
+ {
+ m_ReadPtr -= m_BufferSize;
+ ERRORLOG("cVideoBufferRecording::ReadBlock - unknown error");
+ }
+ m_ReadCachePtr += m_BytesConsumed;
+ }
+ m_BytesConsumed = 0;
+
+ // check if we have anything to read
+ off_t readBytes;
+ if (m_ReadCacheSize && ((m_ReadCachePtr + m_Margin) <= m_ReadCacheSize))
+ {
+ readBytes = m_ReadCacheSize - m_ReadCachePtr;
+ *buf = m_ReadCache + m_ReadCachePtr;
+ }
+ else if ((readBytes = Available()) >= m_ReadCacheMaxSize)
+ {
+ if (m_ReadPtr + m_ReadCacheMaxSize <= m_BufferSize)
+ {
+ m_ReadCacheSize = m_RecPlayer->getBlock(m_ReadCache, m_ReadPtr, m_ReadCacheMaxSize);
+ if (m_ReadCacheSize < 0)
+ {
+ ERRORLOG("Could not read file, size: %d", m_ReadCacheSize);
+ m_ReadCacheSize = 0;
+ return 0;
+ }
+ readBytes = m_ReadCacheSize;
+ *buf = m_ReadCache;
+ m_ReadCachePtr = 0;
+ }
+ else
+ {
+ ERRORLOG("cVideoBufferRecording::ReadBlock - unknown error");
+ return 0;
+ }
+ }
+ else
+ return 0;
+
+ // Make sure we are looking at a TS packet
+ while (readBytes > TS_SIZE)
+ {
+ if ((*buf)[0] == TS_SYNC_BYTE && (*buf)[TS_SIZE] == TS_SYNC_BYTE)
+ break;
+ m_BytesConsumed++;
+ (*buf)++;
+ readBytes--;
+ }
+
+ if ((*buf)[0] != TS_SYNC_BYTE)
+ {
+ return 0;
+ }
+
+ m_BytesConsumed += TS_SIZE;
+ return TS_SIZE;
+}
+
+//-----------------------------------------------------------------------------
+
+class cVideoBufferTest : public cVideoBufferFile
+{
+friend class cVideoBuffer;
+public:
+ virtual off_t GetPosMax();
+ virtual void Put(uint8_t *buf, unsigned int size);
+
+protected:
+ cVideoBufferTest(cString filename);
+ virtual ~cVideoBufferTest();
+ virtual bool Init();
+ virtual off_t Available();
+ off_t GetPosEnd();
+};
+
+cVideoBufferTest::cVideoBufferTest(cString filename)
+{
+ m_Filename = filename;
+ m_Fd = 0;
+ m_ReadCacheSize = 0;
+}
+
+cVideoBufferTest::~cVideoBufferTest()
+{
+ if (m_Fd)
+ {
+ close(m_Fd);
+ m_Fd = 0;
+ }
+}
+
+off_t cVideoBufferTest::GetPosMax()
+{
+ m_WritePtr = GetPosEnd();
+ return cVideoBufferTimeshift::GetPosMax();
+}
+
+off_t cVideoBufferTest::GetPosEnd()
+{
+ off_t cur = lseek(m_Fd, 0, SEEK_CUR);
+ off_t end = lseek(m_Fd, 0, SEEK_END);
+ lseek(m_Fd, cur, SEEK_SET);
+ return end;
+}
+
+void cVideoBufferTest::Put(uint8_t *buf, unsigned int size)
+{
+
+}
+
+bool cVideoBufferTest::Init()
+{
+ m_ReadCache = 0;
+ m_ReadCacheMaxSize = 8000;
+
+ m_ReadCache = (uint8_t*)malloc(m_ReadCacheMaxSize);
+ if (!m_ReadCache)
+ return false;
+
+ m_Fd = open(m_Filename, O_RDONLY);
+ if (m_Fd == -1)
+ {
+ ERRORLOG("Could not open file: %s", (const char*)m_Filename);
+ return false;
+ }
+
+ m_WritePtr = 0;
+ m_ReadPtr = 0;
+ m_ReadCacheSize = 0;
+ m_InputAttached = false;
+ return true;
+}
+
+off_t cVideoBufferTest::Available()
+{
+ m_BufferSize = m_WritePtr = GetPosEnd();
+ return cVideoBufferTimeshift::Available();
+}
+
+//-----------------------------------------------------------------------------
+
cVideoBuffer::cVideoBuffer()
{
+ m_CheckEof = false;
+ m_InputAttached = true;
}
cVideoBuffer::~cVideoBuffer()
{
}
-cVideoBuffer* cVideoBuffer::Create()
+cVideoBuffer* cVideoBuffer::Create(int clientID, uint8_t timeshift)
+{
+ // no time shift
+ if (TimeshiftMode == 0 || timeshift == 0)
+ {
+ cVideoBufferSimple *buffer = new cVideoBufferSimple();
+ return buffer;
+ }
+ // buffer in ram
+ else if (TimeshiftMode == 1)
+ {
+ cVideoBufferRAM *buffer = new cVideoBufferRAM();
+ if (!buffer->Init())
+ {
+ delete buffer;
+ return NULL;
+ }
+ else
+ return buffer;
+ }
+ // buffer in file
+ else if (TimeshiftMode == 2)
+ {
+ cVideoBufferFile *buffer = new cVideoBufferFile(clientID);
+ if (!buffer->Init())
+ {
+ delete buffer;
+ return NULL;
+ }
+ else
+ return buffer;
+ }
+ else
+ return NULL;
+}
+
+cVideoBuffer* cVideoBuffer::Create(cString filename)
+{
+ INFOLOG("Open recording: %s", (const char*)filename);
+ cVideoBufferTest *buffer = new cVideoBufferTest(filename);
+ if (!buffer->Init())
+ {
+ delete buffer;
+ return NULL;
+ }
+ else
+ return buffer;
+}
+
+cVideoBuffer* cVideoBuffer::Create(cRecording *rec)
+{
+ INFOLOG("Open recording: %s", rec->FileName());
+ cVideoBufferRecording *buffer = new cVideoBufferRecording(rec);
+ if (!buffer->Init())
+ {
+ delete buffer;
+ return NULL;
+ }
+ else
+ return buffer;
+}
+
+int cVideoBuffer::Read(uint8_t **buf, unsigned int size)
+{
+ int count = ReadBlock(buf, size);
+
+ // check for end of file
+ if (!m_InputAttached && count != TS_SIZE)
+ {
+ if (m_CheckEof && m_Timer.TimedOut())
+ {
+ INFOLOG("Recoding - end of file");
+ return -2;
+ }
+ else if (!m_CheckEof)
+ {
+ m_CheckEof = true;
+ m_Timer.Set(3000);
+ }
+ }
+ else
+ m_CheckEof = false;
+
+ return count;
+}
+
+void cVideoBuffer::AttachInput(bool attach)
{
- cVideoBuffer *buffer = new cVideoBufferSimple();
- return buffer;
+ m_InputAttached = attach;
}
diff --git a/videobuffer.h b/videobuffer.h
index 881c3fc..8ea74fc 100644
--- a/videobuffer.h
+++ b/videobuffer.h
@@ -21,14 +21,32 @@
#pragma once
#include <stdint.h>
+#include <stdlib.h>
+#include <vdr/tools.h>
+
+class cRecording;
class cVideoBuffer
{
public:
virtual ~cVideoBuffer();
- static cVideoBuffer* Create();
- virtual void Put(uint8_t *buf, int size) = 0;
- virtual int Read(uint8_t **buf, int size) = 0;
+ static cVideoBuffer* Create(int clientID, uint8_t timeshift);
+ static cVideoBuffer* Create(cString filename);
+ static cVideoBuffer* Create(cRecording *rec);
+ virtual void Put(uint8_t *buf, unsigned int size) = 0;
+ virtual int ReadBlock(uint8_t **buf, unsigned int size) = 0;
+ virtual off_t GetPosMin() { return 0; };
+ virtual off_t GetPosMax() { return 0; };
+ virtual off_t GetPosCur() { return 0; };
+ virtual void GetPositions(off_t *cur, off_t *min, off_t *max) {};
+ virtual void SetPos(off_t pos) {};
+ virtual void SetCache(bool on) {};
+ virtual bool HasBuffer() { return false; };
+ int Read(uint8_t **buf, unsigned int size);
+ void AttachInput(bool attach);
protected:
cVideoBuffer();
+ cTimeMs m_Timer;
+ bool m_CheckEof;
+ bool m_InputAttached;
};
diff --git a/videoinput.c b/videoinput.c
index 7646454..45769ae 100644
--- a/videoinput.c
+++ b/videoinput.c
@@ -67,6 +67,7 @@ void cLiveReceiver::Receive(uchar *Data, int Length)
inline void cLiveReceiver::Activate(bool On)
{
+ m_VideoInput->Attach(On);
DEBUGLOG("activate live receiver: %d", On);
}
@@ -520,6 +521,11 @@ inline void cVideoInput::Receive(uchar *data, int length)
m_VideoBuffer->Put(data, length);
}
+inline void cVideoInput::Attach(bool on)
+{
+ m_VideoBuffer->AttachInput(on);
+}
+
void cVideoInput::Action()
{
cTimeMs starttime;
diff --git a/videoinput.h b/videoinput.h
index a78cf10..19a1d7d 100644
--- a/videoinput.h
+++ b/videoinput.h
@@ -43,6 +43,7 @@ protected:
void PmtChange(int pidChange);
cChannel *PmtChannel();
void Receive(uchar *data, int length);
+ void Attach(bool on);
cDevice *m_Device;
cLivePatFilter *m_PatFilter;
cLiveReceiver *m_Receiver;
diff --git a/vnsi.c b/vnsi.c
index 1a62f61..6ae4cf5 100644
--- a/vnsi.c
+++ b/vnsi.c
@@ -26,11 +26,15 @@
#include <getopt.h>
#include <vdr/plugin.h>
#include "vnsi.h"
+#include "vnsicommand.h"
#include "setup.h"
+cPluginVNSIServer* cPluginVNSIServer::VNSIServer = NULL;
+
cPluginVNSIServer::cPluginVNSIServer(void)
{
Server = NULL;
+ VNSIServer = NULL;
}
cPluginVNSIServer::~cPluginVNSIServer()
@@ -67,6 +71,8 @@ bool cPluginVNSIServer::Initialize(void)
{
// Initialize any background activities the plugin shall perform.
VNSIServerConfig.ConfigDirectory = ConfigDirectory(PLUGIN_NAME_I18N);
+
+ VNSIServer = this;
return true;
}
@@ -117,6 +123,14 @@ bool cPluginVNSIServer::SetupParse(const char *Name, const char *Value)
// Parse your own setup parameters and store their values.
if (!strcasecmp(Name, CONFNAME_PMTTIMEOUT))
PmtTimeout = atoi(Value);
+ else if (!strcasecmp(Name, CONFNAME_TIMESHIFT))
+ TimeshiftMode = atoi(Value);
+ else if (!strcasecmp(Name, CONFNAME_TIMESHIFTBUFFERSIZE))
+ TimeshiftBufferSize = atoi(Value);
+ else if (!strcasecmp(Name, CONFNAME_TIMESHIFTBUFFERFILESIZE))
+ TimeshiftBufferFileSize = atoi(Value);
+ else if (!strcasecmp(Name, CONFNAME_TIMESHIFTBUFFERDIR))
+ strn0cpy(TimeshiftBufferDir, Value, sizeof(TimeshiftBufferDir));
else
return false;
return true;
@@ -140,4 +154,16 @@ cString cPluginVNSIServer::SVDRPCommand(const char *Command, const char *Option,
return NULL;
}
+void cPluginVNSIServer::StoreSetup(const char *Name, int Value)
+{
+ if (VNSIServer)
+ {
+ if (VNSIServer->SetupParse(Name, itoa(Value)))
+ {
+ VNSIServer->SetupStore(Name, Value);
+ Setup.Save();
+ }
+ }
+}
+
VDRPLUGINCREATOR(cPluginVNSIServer); // Don't touch this!
diff --git a/vnsi.h b/vnsi.h
index 94461a4..eb7ed79 100644
--- a/vnsi.h
+++ b/vnsi.h
@@ -27,14 +27,19 @@
#include <vdr/plugin.h>
#include "vnsiserver.h"
-static const char *VERSION = "0.9.1";
+static const char *VERSION = "0.9.2";
static const char *DESCRIPTION = "VDR-Network-Streaming-Interface (VNSI) Server";
extern int PmtTimeout;
+extern int TimeshiftMode;
+extern int TimeshiftBufferSize;
+extern int TimeshiftBufferFileSize;
+extern char TimeshiftBufferDir[PATH_MAX];
class cPluginVNSIServer : public cPlugin {
private:
cVNSIServer *Server;
+ static cPluginVNSIServer *VNSIServer;
public:
cPluginVNSIServer(void);
@@ -57,5 +62,7 @@ public:
virtual bool Service(const char *Id, void *Data = NULL);
virtual const char **SVDRPHelpPages(void);
virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
+
+ static void StoreSetup(const char *Name, int Value);
};
diff --git a/vnsiclient.c b/vnsiclient.c
index b351ef0..f88d00b 100644
--- a/vnsiclient.c
+++ b/vnsiclient.c
@@ -35,6 +35,7 @@
#include <vdr/menu.h>
#include <vdr/device.h>
+#include "vnsi.h"
#include "config.h"
#include "vnsicommand.h"
#include "recordingscache.h"
@@ -176,10 +177,10 @@ void cVNSIClient::Action(void)
}
}
-bool cVNSIClient::StartChannelStreaming(const cChannel *channel, uint32_t timeout)
+bool cVNSIClient::StartChannelStreaming(const cChannel *channel, int32_t priority, uint8_t timeshift, uint32_t timeout)
{
- m_Streamer = new cLiveStreamer(timeout);
- m_isStreaming = m_Streamer->StreamChannel(channel, 50, &m_socket, m_resp);
+ m_Streamer = new cLiveStreamer(m_Id, timeshift, timeout);
+ m_isStreaming = m_Streamer->StreamChannel(channel, priority, &m_socket, m_resp);
return m_isStreaming;
}
@@ -411,6 +412,13 @@ bool cVNSIClient::processRequest(cRequestPacket* req)
result = process_Ping();
break;
+ case VNSI_GETSETUP:
+ result = process_GetSetup();
+ break;
+
+ case VNSI_STORESETUP:
+ result = process_StoreSetup();
+ break;
/** OPCODE 20 - 39: VNSI network functions for live streaming */
case VNSI_CHANNELSTREAM_OPEN:
@@ -421,6 +429,9 @@ bool cVNSIClient::processRequest(cRequestPacket* req)
result = processChannelStream_Close();
break;
+ case VNSI_CHANNELSTREAM_SEEK:
+ result = processChannelStream_Seek();
+ break;
/** OPCODE 40 - 59: VNSI network functions for recording streaming */
case VNSI_RECSTREAM_OPEN:
@@ -637,12 +648,61 @@ bool cVNSIClient::process_Ping() /* OPCODE 7 */
return true;
}
+bool cVNSIClient::process_GetSetup() /* OPCODE 8 */
+{
+ char* name = m_req->extract_String();
+ if (!strcasecmp(name, CONFNAME_PMTTIMEOUT))
+ m_resp->add_U32(PmtTimeout);
+ else if (!strcasecmp(name, CONFNAME_TIMESHIFT))
+ m_resp->add_U32(TimeshiftMode);
+ else if (!strcasecmp(name, CONFNAME_TIMESHIFTBUFFERSIZE))
+ m_resp->add_U32(TimeshiftBufferSize);
+ else if (!strcasecmp(name, CONFNAME_TIMESHIFTBUFFERFILESIZE))
+ m_resp->add_U32(TimeshiftBufferFileSize);
+
+ m_resp->finalise();
+ m_socket.write(m_resp->getPtr(), m_resp->getLen());
+ return true;
+}
+
+bool cVNSIClient::process_StoreSetup() /* OPCODE 9 */
+{
+ char* name = m_req->extract_String();
+
+ if (!strcasecmp(name, CONFNAME_PMTTIMEOUT))
+ {
+ int value = m_req->extract_U32();
+ cPluginVNSIServer::StoreSetup(CONFNAME_PMTTIMEOUT, value);
+ }
+ else if (!strcasecmp(name, CONFNAME_TIMESHIFT))
+ {
+ int value = m_req->extract_U32();
+ cPluginVNSIServer::StoreSetup(CONFNAME_TIMESHIFT, value);
+ }
+ else if (!strcasecmp(name, CONFNAME_TIMESHIFTBUFFERSIZE))
+ {
+ int value = m_req->extract_U32();
+ cPluginVNSIServer::StoreSetup(CONFNAME_TIMESHIFTBUFFERSIZE, value);
+ }
+ else if (!strcasecmp(name, CONFNAME_TIMESHIFTBUFFERFILESIZE))
+ {
+ int value = m_req->extract_U32();
+ cPluginVNSIServer::StoreSetup(CONFNAME_TIMESHIFTBUFFERFILESIZE, value);
+ }
+
+ m_resp->add_U32(VNSI_RET_OK);
+ m_resp->finalise();
+ m_socket.write(m_resp->getPtr(), m_resp->getLen());
+ return true;
+}
/** OPCODE 20 - 39: VNSI network functions for live streaming */
bool cVNSIClient::processChannelStream_Open() /* OPCODE 20 */
{
uint32_t uid = m_req->extract_U32();
+ int32_t priority = m_req->extract_S32();
+ uint8_t timeshift = m_req->extract_U8();
uint32_t timeout = m_req->extract_U32();
if(timeout == 0)
@@ -668,7 +728,7 @@ bool cVNSIClient::processChannelStream_Open() /* OPCODE 20 */
}
else
{
- if (StartChannelStreaming(channel, timeout))
+ if (StartChannelStreaming(channel, priority, timeshift, timeout))
{
INFOLOG("Started streaming of channel %s (timeout %i seconds)", channel->Name(), timeout);
// return here without sending the response
@@ -694,6 +754,25 @@ bool cVNSIClient::processChannelStream_Close() /* OPCODE 21 */
return true;
}
+bool cVNSIClient::processChannelStream_Seek() /* OPCODE 22 */
+{
+ uint32_t serial = 0;
+ if (m_isStreaming && m_Streamer)
+ {
+ int64_t time = m_req->extract_S64();
+ if (m_Streamer->SeekTime(time, serial))
+ m_resp->add_U32(VNSI_RET_OK);
+ else
+ m_resp->add_U32(VNSI_RET_ERROR);
+ }
+ else
+ m_resp->add_U32(VNSI_RET_ERROR);
+
+ m_resp->add_U32(serial);
+ m_resp->finalise();
+ m_socket.write(m_resp->getPtr(), m_resp->getLen());
+ return true;
+}
/** OPCODE 40 - 59: VNSI network functions for recording streaming */
@@ -1324,7 +1403,11 @@ bool cVNSIClient::processTIMER_Update() /* OPCODE 85 */
bool cVNSIClient::processRECORDINGS_GetDiskSpace() /* OPCODE 100 */
{
int FreeMB;
+#if VDRVERSNUM >= 20102
+ int Percent = cVideoDirectory::VideoDiskSpace(&FreeMB);
+#else
int Percent = VideoDiskSpace(&FreeMB);
+#endif
int Total = (FreeMB / (100 - Percent)) * 100;
m_resp->add_U32(Total);
@@ -1349,6 +1432,7 @@ bool cVNSIClient::processRECORDINGS_GetCount() /* OPCODE 101 */
bool cVNSIClient::processRECORDINGS_GetList() /* OPCODE 102 */
{
cMutexLock lock(&m_timerLock);
+ cThreadLock RecordingsLock(&Recordings);
for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
{
@@ -1477,7 +1561,7 @@ bool cVNSIClient::processRECORDINGS_Rename() /* OPCODE 103 */
// replace spaces in newtitle
strreplace(newtitle, ' ', '_');
- char* filename_new = new char[512];
+ char* filename_new = new char[1024];
strncpy(filename_new, filename_old, 512);
sep = strrchr(filename_new, '/');
if(sep != NULL) {
diff --git a/vnsiclient.h b/vnsiclient.h
index b23117f..948940a 100644
--- a/vnsiclient.h
+++ b/vnsiclient.h
@@ -94,7 +94,7 @@ protected:
void SetLoggedIn(bool yesNo) { m_loggedIn = yesNo; }
void SetStatusInterface(bool yesNo) { m_StatusInterfaceEnabled = yesNo; }
- bool StartChannelStreaming(const cChannel *channel, uint32_t timeout);
+ bool StartChannelStreaming(const cChannel *channel, int32_t priority, uint8_t timeshift, uint32_t timeout);
void StopChannelStreaming();
private:
@@ -111,9 +111,12 @@ private:
bool process_GetTime();
bool process_EnableStatusInterface();
bool process_Ping();
+ bool process_GetSetup();
+ bool process_StoreSetup();
bool processChannelStream_Open();
bool processChannelStream_Close();
+ bool processChannelStream_Seek();
bool processRecStream_Open();
bool processRecStream_Close();
diff --git a/vnsicommand.h b/vnsicommand.h
index a2b8000..17d3d08 100644
--- a/vnsicommand.h
+++ b/vnsicommand.h
@@ -27,7 +27,7 @@
#define VNSI_COMMAND_H
/** Current VNSI Protocol Version number */
-#define VNSI_PROTOCOLVERSION 3
+#define VNSI_PROTOCOLVERSION 4
/** Packet types */
#define VNSI_CHANNEL_REQUEST_RESPONSE 1
@@ -40,15 +40,24 @@
/** Response packets operation codes */
+#define CONFNAME_PMTTIMEOUT "PmtTimeout"
+#define CONFNAME_TIMESHIFT "Timeshift"
+#define CONFNAME_TIMESHIFTBUFFERSIZE "TimeshiftBufferSize"
+#define CONFNAME_TIMESHIFTBUFFERFILESIZE "TimeshiftBufferFileSize"
+#define CONFNAME_TIMESHIFTBUFFERDIR "TimeshiftBufferDir"
+
/* OPCODE 1 - 19: VNSI network functions for general purpose */
#define VNSI_LOGIN 1
#define VNSI_GETTIME 2
#define VNSI_ENABLESTATUSINTERFACE 3
#define VNSI_PING 7
+#define VNSI_GETSETUP 8
+#define VNSI_STORESETUP 9
/* OPCODE 20 - 39: VNSI network functions for live streaming */
-#define VNSI_CHANNELSTREAM_OPEN 20
-#define VNSI_CHANNELSTREAM_CLOSE 21
+#define VNSI_CHANNELSTREAM_OPEN 20
+#define VNSI_CHANNELSTREAM_CLOSE 21
+#define VNSI_CHANNELSTREAM_SEEK 22
/* OPCODE 40 - 59: VNSI network functions for recording streaming */
#define VNSI_RECSTREAM_OPEN 40
@@ -102,6 +111,7 @@
#define VNSI_STREAM_MUXPKT 4
#define VNSI_STREAM_SIGNALINFO 5
#define VNSI_STREAM_CONTENTINFO 6
+#define VNSI_STREAM_BUFFERSTATS 7
/** Scan packet types (server -> client) */
#define VNSI_SCANNER_PERCENTAGE 1
diff --git a/vnsiserver.c b/vnsiserver.c
index f116163..db2e033 100644
--- a/vnsiserver.c
+++ b/vnsiserver.c
@@ -36,10 +36,13 @@
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
+#include <sys/stat.h>
#include <vdr/plugin.h>
#include <vdr/shutdown.h>
+#include <vdr/videodir.h>
+#include "vnsi.h"
#include "vnsiserver.h"
#include "vnsiclient.h"
@@ -156,6 +159,7 @@ void cVNSIServer::NewClientConnected(int fd)
int val = 1;
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val));
+#ifdef SOL_TCP
val = 30;
setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &val, sizeof(val));
@@ -167,6 +171,7 @@ void cVNSIServer::NewClientConnected(int fd)
val = 1;
setsockopt(fd, SOL_TCP, TCP_NODELAY, &val, sizeof(val));
+#endif
INFOLOG("Client with ID %d connected: %s", m_IdCnt, cxSocket::ip2txt(sin.sin_addr.s_addr, sin.sin_port, buf));
cVNSIClient *connection = new cVNSIClient(fd, m_IdCnt, cxSocket::ip2txt(sin.sin_addr.s_addr, sin.sin_port, buf));
@@ -179,6 +184,12 @@ void cVNSIServer::Action(void)
fd_set fds;
struct timeval tv;
+ // initial time for channels change
+ struct timespec channelsUpdate;
+ channelsUpdate.tv_sec = 0;
+ channelsUpdate.tv_nsec = 0;
+ cTimeMs chanTimer(0);
+
// get initial state of the recordings
int recState = -1;
Recordings.StateChanged(recState);
@@ -190,6 +201,26 @@ void cVNSIServer::Action(void)
// last update of epg
time_t epgUpdate = cSchedules::Modified();
+ // delete old timeshift file
+ cString cmd;
+ struct stat sb;
+ if ((*TimeshiftBufferDir) && stat(TimeshiftBufferDir, &sb) == 0 && S_ISDIR(sb.st_mode))
+ {
+ if (TimeshiftBufferDir[strlen(TimeshiftBufferDir)-1] == '/')
+ cmd = cString::sprintf("rm -f %s*.vnsi", TimeshiftBufferDir);
+ else
+ cmd = cString::sprintf("rm -f %s/*.vnsi", TimeshiftBufferDir);
+ }
+ else
+ {
+#if VDRVERSNUM >= 20102
+ cmd = cString::sprintf("rm -f %s/*.vnsi", cVideoDirectory::Name());
+#else
+ cmd = cString::sprintf("rm -f %s/*.vnsi", VideoDirectory);
+#endif
+ }
+ int ret = system(cmd);
+
while (Running())
{
FD_ZERO(&fds);
@@ -221,16 +252,21 @@ void cVNSIServer::Action(void)
}
// trigger clients to reload the modified channel list
- if(m_clients.size() > 0)
+ if(m_clients.size() > 0 && chanTimer.TimedOut())
{
- Channels.Lock(false);
- if(Channels.Modified() != 0)
+ struct stat s;
+ if(stat(Channels.FileName(), &s) != -1)
{
- INFOLOG("Requesting clients to reload channel list");
- for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
- (*i)->ChannelChange();
+ if ((s.st_mtim.tv_sec != channelsUpdate.tv_sec) &&
+ (s.st_mtim.tv_nsec != channelsUpdate.tv_nsec))
+ {
+ INFOLOG("Requesting clients to reload channel list");
+ for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
+ (*i)->ChannelChange();
+ channelsUpdate = s.st_mtim;
+ }
}
- Channels.Unlock();
+ chanTimer.Set(5000);
}
// reset inactivity timeout as long as there are clients connected
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-vdr-dvb/vdr-plugin-vnsiserver.git
More information about the pkg-vdr-dvb-changes
mailing list