mirror of
https://projects.vdr-developer.org/git/vdr-plugin-streamdev.git
synced 2023-10-10 19:16:51 +02:00
Snapshot 2009-07-01
This commit is contained in:
parent
7254a67528
commit
008ea7f151
@ -1,6 +1,10 @@
|
|||||||
Special thanks go to the following persons (if you think your name is missing
|
Special thanks go to the following persons (if you think your name is missing
|
||||||
here, please send an email to vdrdev@schmirler.de):
|
here, please send an email to vdrdev@schmirler.de):
|
||||||
|
|
||||||
|
Klaus Schmidinger
|
||||||
|
for VDR as a whole
|
||||||
|
for permission to use VDR 1.6.0 cRemux code for PES remuxing
|
||||||
|
|
||||||
Sascha Volkenandt, the original author,
|
Sascha Volkenandt, the original author,
|
||||||
for this great plugin
|
for this great plugin
|
||||||
|
|
||||||
@ -30,6 +34,7 @@ Rolf Ahrenberg
|
|||||||
for replacing private members by cThread::Running()/Active()
|
for replacing private members by cThread::Running()/Active()
|
||||||
for improving externremux script termination
|
for improving externremux script termination
|
||||||
for fixing PAT repacker version field
|
for fixing PAT repacker version field
|
||||||
|
for correcting LIMIKUUTIO patch detection
|
||||||
|
|
||||||
Rantanen Teemu
|
Rantanen Teemu
|
||||||
for providing vdr-incompletesections.diff
|
for providing vdr-incompletesections.diff
|
||||||
@ -74,6 +79,7 @@ alexw
|
|||||||
|
|
||||||
Olli Lammi
|
Olli Lammi
|
||||||
for fixing a busy wait when client isn't accepting data fast enough
|
for fixing a busy wait when client isn't accepting data fast enough
|
||||||
|
for suggesting signaling instead of sleeping when writing to buffers
|
||||||
|
|
||||||
Joerg Pulz
|
Joerg Pulz
|
||||||
for his FreeBSD compatibility patch
|
for his FreeBSD compatibility patch
|
||||||
@ -111,3 +117,6 @@ Joachim K
|
|||||||
|
|
||||||
Artem Makhutov
|
Artem Makhutov
|
||||||
for suggesting and heavy testing IGMP based multicast streaming
|
for suggesting and heavy testing IGMP based multicast streaming
|
||||||
|
|
||||||
|
Alwin Esch
|
||||||
|
for adding XBMC support by extending VTP capabilities
|
||||||
|
17
HISTORY
17
HISTORY
@ -1,6 +1,23 @@
|
|||||||
VDR Plugin 'streamdev' Revision History
|
VDR Plugin 'streamdev' Revision History
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
|
- added XBMC support by extending VTP capabilities (thanks to Alwin Esch)
|
||||||
|
- now there's a common baseclass for all remuxers, make use of it
|
||||||
|
- added cDevice::NumProvidedSystems() which was introduced in VDR 1.7.0
|
||||||
|
- added namespace to remuxers
|
||||||
|
- increased WRITERBUFSIZE - buffer was too small for high bandwidth content
|
||||||
|
- removed cStreamdevStreamer::m_Running
|
||||||
|
- eliminated potential busy waits in remuxers
|
||||||
|
- updated cTSRemux static helpers to code of their VDR 1.6.0 counterparts
|
||||||
|
- re-enabled PES vor VDR 1.7.3+. Streamdev now uses a copy of VDR 1.6.0's
|
||||||
|
cRemux for TS to PES remuxing.
|
||||||
|
- make sure that only complete TS packets are written to ringbuffers
|
||||||
|
- use signaling instead of sleeps when writing to ringbuffers
|
||||||
|
- optimized cStreamdevPatFilter PAT packet initialization
|
||||||
|
- fixed cStreamdevPatFilter not processing PATs with length > TS_SIZE - 5
|
||||||
|
- use a small ringbuffer for cStreamdevPatFilter instead of writing to
|
||||||
|
cStreamdevStreamers SendBuffer as two threads mustn't write to the same
|
||||||
|
ringbuffer
|
||||||
- added missing call to StopSectionHandler which could cause crashes when
|
- added missing call to StopSectionHandler which could cause crashes when
|
||||||
shutting down VDR
|
shutting down VDR
|
||||||
- added IGMP based multicast streaming
|
- added IGMP based multicast streaming
|
||||||
|
6
Makefile
6
Makefile
@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# Makefile for a Video Disk Recorder plugin
|
# Makefile for a Video Disk Recorder plugin
|
||||||
#
|
#
|
||||||
# $Id: Makefile,v 1.17 2009/02/13 10:39:20 schmirl Exp $
|
# $Id: Makefile,v 1.19 2009/07/01 10:46:15 schmirl Exp $
|
||||||
|
|
||||||
# The official name of this plugin.
|
# The official name of this plugin.
|
||||||
# This name will be used in the '-P...' option of VDR to load the plugin.
|
# This name will be used in the '-P...' option of VDR to load the plugin.
|
||||||
@ -61,8 +61,8 @@ SERVEROBJS = $(PLUGIN)-server.o \
|
|||||||
server/componentVTP.o server/componentHTTP.o server/componentIGMP.o \
|
server/componentVTP.o server/componentHTTP.o server/componentIGMP.o \
|
||||||
server/connectionVTP.o server/connectionHTTP.o server/connectionIGMP.o \
|
server/connectionVTP.o server/connectionHTTP.o server/connectionIGMP.o \
|
||||||
server/streamer.o server/livestreamer.o server/livefilter.o \
|
server/streamer.o server/livestreamer.o server/livefilter.o \
|
||||||
server/suspend.o server/setup.o server/menuHTTP.o \
|
server/suspend.o server/setup.o server/menuHTTP.o server/recplayer.o \
|
||||||
remux/tsremux.o remux/ts2ps.o remux/ts2es.o remux/extern.o
|
remux/tsremux.o remux/ts2pes.o remux/ts2ps.o remux/ts2es.o remux/extern.o
|
||||||
|
|
||||||
ifdef DEBUG
|
ifdef DEBUG
|
||||||
DEFINES += -DDEBUG
|
DEFINES += -DDEBUG
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* $Id: device.h,v 1.8 2008/10/02 07:14:47 schmirl Exp $
|
* $Id: device.h,v 1.9 2009/06/23 10:26:54 schmirl Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef VDR_STREAMDEV_DEVICE_H
|
#ifndef VDR_STREAMDEV_DEVICE_H
|
||||||
@ -54,6 +54,9 @@ public:
|
|||||||
virtual bool ProvidesTransponder(const cChannel *Channel) const;
|
virtual bool ProvidesTransponder(const cChannel *Channel) const;
|
||||||
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1,
|
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1,
|
||||||
bool *NeedsDetachReceivers = NULL) const;
|
bool *NeedsDetachReceivers = NULL) const;
|
||||||
|
#if APIVERSNUM >= 10700
|
||||||
|
virtual int NumProvidedSystems(void) const { return 1; }
|
||||||
|
#endif
|
||||||
virtual bool IsTunedToTransponder(const cChannel *Channel);
|
virtual bool IsTunedToTransponder(const cChannel *Channel);
|
||||||
|
|
||||||
static bool Init(void);
|
static bool Init(void);
|
||||||
|
6
common.c
6
common.c
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* $Id: common.c,v 1.9 2009/01/16 11:35:43 schmirl Exp $
|
* $Id: common.c,v 1.10 2009/06/19 06:32:38 schmirl Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <vdr/channels.h>
|
#include <vdr/channels.h>
|
||||||
@ -10,13 +10,11 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
const char *VERSION = "0.5.0-pre-20090611";
|
const char *VERSION = "0.5.0-pre-20090701";
|
||||||
|
|
||||||
const char *StreamTypes[st_Count] = {
|
const char *StreamTypes[st_Count] = {
|
||||||
"TS",
|
"TS",
|
||||||
#if APIVERSNUM < 10703
|
|
||||||
"PES",
|
"PES",
|
||||||
#endif
|
|
||||||
"PS",
|
"PS",
|
||||||
"ES",
|
"ES",
|
||||||
"Extern",
|
"Extern",
|
||||||
|
5
common.h
5
common.h
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* $Id: common.h,v 1.12 2009/01/16 11:35:43 schmirl Exp $
|
* $Id: common.h,v 1.14 2009/07/01 10:46:16 schmirl Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef VDR_STREAMDEV_COMMON_H
|
#ifndef VDR_STREAMDEV_COMMON_H
|
||||||
@ -51,9 +51,7 @@ const cChannel *ChannelFromString(const char *String, int *Apid = NULL);
|
|||||||
|
|
||||||
enum eStreamType {
|
enum eStreamType {
|
||||||
stTS,
|
stTS,
|
||||||
#if APIVERSNUM < 10703
|
|
||||||
stPES,
|
stPES,
|
||||||
#endif
|
|
||||||
stPS,
|
stPS,
|
||||||
stES,
|
stES,
|
||||||
stExtern,
|
stExtern,
|
||||||
@ -74,6 +72,7 @@ enum eSocketId {
|
|||||||
siLive,
|
siLive,
|
||||||
siReplay,
|
siReplay,
|
||||||
siLiveFilter,
|
siLiveFilter,
|
||||||
|
siDataRespond,
|
||||||
si_Count
|
si_Count
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#define MAX_PLENGTH 0xFFFF
|
#define MAX_PLENGTH 0xFFFF
|
||||||
#define MMAX_PLENGTH (8*MAX_PLENGTH)
|
#define MMAX_PLENGTH (64*MAX_PLENGTH)
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace Streamdev {
|
||||||
|
|
||||||
class cTSExt: public cThread {
|
class cTSExt: public cThread {
|
||||||
private:
|
private:
|
||||||
cRingBufferLinear *m_ResultBuffer;
|
cRingBufferLinear *m_ResultBuffer;
|
||||||
@ -24,6 +26,9 @@ public:
|
|||||||
void Put(const uchar *Data, int Count);
|
void Put(const uchar *Data, int Count);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace Streamdev
|
||||||
|
using namespace Streamdev;
|
||||||
|
|
||||||
cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, std::string Parameter):
|
cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, std::string Parameter):
|
||||||
m_ResultBuffer(ResultBuffer),
|
m_ResultBuffer(ResultBuffer),
|
||||||
m_Active(false),
|
m_Active(false),
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include <vdr/ringbuffer.h>
|
#include <vdr/ringbuffer.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
namespace Streamdev {
|
||||||
|
|
||||||
class cTSExt;
|
class cTSExt;
|
||||||
|
|
||||||
class cExternRemux: public cTSRemux {
|
class cExternRemux: public cTSRemux {
|
||||||
@ -21,4 +23,6 @@ public:
|
|||||||
void Del(int Count) { m_ResultBuffer->Del(Count); }
|
void Del(int Count) { m_ResultBuffer->Del(Count); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace Streamdev
|
||||||
|
|
||||||
#endif // VDR_STREAMDEV_EXTERNREMUX_H
|
#endif // VDR_STREAMDEV_EXTERNREMUX_H
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
#include "remux/ts2es.h"
|
#include "remux/ts2es.h"
|
||||||
#include "server/streamer.h"
|
#include "server/streamer.h"
|
||||||
#include "libdvbmpeg/transform.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include <vdr/device.h>
|
#include <vdr/device.h>
|
||||||
|
|
||||||
// from VDR's remux.c
|
// from VDR's remux.c
|
||||||
#define MAXNONUSEFULDATA (10*1024*1024)
|
#define MAXNONUSEFULDATA (10*1024*1024)
|
||||||
|
|
||||||
|
namespace Streamdev {
|
||||||
|
|
||||||
class cTS2ES: public ipack {
|
class cTS2ES: public ipack {
|
||||||
friend void PutES(uint8_t *Buffer, int Size, void *Data);
|
friend void PutES(uint8_t *Buffer, int Size, void *Data);
|
||||||
|
|
||||||
@ -32,6 +33,9 @@ void PutES(uint8_t *Buffer, int Size, void *Data)
|
|||||||
This->start = 1;
|
This->start = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace Streamdev
|
||||||
|
using namespace Streamdev;
|
||||||
|
|
||||||
cTS2ES::cTS2ES(cRingBufferLinear *ResultBuffer)
|
cTS2ES::cTS2ES(cRingBufferLinear *ResultBuffer)
|
||||||
{
|
{
|
||||||
m_ResultBuffer = ResultBuffer;
|
m_ResultBuffer = ResultBuffer;
|
||||||
@ -75,10 +79,10 @@ void cTS2ES::PutTSPacket(const uint8_t *Buffer) {
|
|||||||
|
|
||||||
cTS2ESRemux::cTS2ESRemux(int Pid):
|
cTS2ESRemux::cTS2ESRemux(int Pid):
|
||||||
m_Pid(Pid),
|
m_Pid(Pid),
|
||||||
m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE, IPACKS)),
|
m_ResultBuffer(new cStreamdevBuffer(WRITERBUFSIZE, IPACKS)),
|
||||||
m_Remux(new cTS2ES(m_ResultBuffer))
|
m_Remux(new cTS2ES(m_ResultBuffer))
|
||||||
{
|
{
|
||||||
m_ResultBuffer->SetTimeouts(0, 100);
|
m_ResultBuffer->SetTimeouts(100, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
cTS2ESRemux::~cTS2ESRemux()
|
cTS2ESRemux::~cTS2ESRemux()
|
||||||
@ -111,8 +115,10 @@ int cTS2ESRemux::Put(const uchar *Data, int Count)
|
|||||||
break;
|
break;
|
||||||
if (Data[i] != TS_SYNC_BYTE)
|
if (Data[i] != TS_SYNC_BYTE)
|
||||||
break;
|
break;
|
||||||
if (m_ResultBuffer->Free() < 2 * IPACKS)
|
if (m_ResultBuffer->Free() < 2 * IPACKS) {
|
||||||
|
m_ResultBuffer->WaitForPut();
|
||||||
break; // A cTS2ES might write one full packet and also a small rest
|
break; // A cTS2ES might write one full packet and also a small rest
|
||||||
|
}
|
||||||
int pid = cTSRemux::GetPid(Data + i + 1);
|
int pid = cTSRemux::GetPid(Data + i + 1);
|
||||||
if (Data[i + 3] & 0x10) { // got payload
|
if (Data[i + 3] & 0x10) { // got payload
|
||||||
if (m_Pid == pid)
|
if (m_Pid == pid)
|
||||||
|
@ -2,15 +2,16 @@
|
|||||||
#define VDR_STREAMDEV_TS2ESREMUX_H
|
#define VDR_STREAMDEV_TS2ESREMUX_H
|
||||||
|
|
||||||
#include "remux/tsremux.h"
|
#include "remux/tsremux.h"
|
||||||
#include <vdr/ringbuffer.h>
|
#include "server/streamer.h"
|
||||||
|
|
||||||
|
namespace Streamdev {
|
||||||
|
|
||||||
class cTS2ES;
|
class cTS2ES;
|
||||||
class cRingBufferLinear;
|
|
||||||
|
|
||||||
class cTS2ESRemux: public cTSRemux {
|
class cTS2ESRemux: public cTSRemux {
|
||||||
private:
|
private:
|
||||||
int m_Pid;
|
int m_Pid;
|
||||||
cRingBufferLinear *m_ResultBuffer;
|
cStreamdevBuffer *m_ResultBuffer;
|
||||||
cTS2ES *m_Remux;
|
cTS2ES *m_Remux;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -22,4 +23,6 @@ public:
|
|||||||
void Del(int Count) { m_ResultBuffer->Del(Count); }
|
void Del(int Count) { m_ResultBuffer->Del(Count); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace Streamdev
|
||||||
|
|
||||||
#endif // VDR_STREAMDEV_TS2ESREMUX_H
|
#endif // VDR_STREAMDEV_TS2ESREMUX_H
|
||||||
|
2017
remux/ts2pes.c
Normal file
2017
remux/ts2pes.c
Normal file
File diff suppressed because it is too large
Load Diff
56
remux/ts2pes.h
Normal file
56
remux/ts2pes.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* ts2pes.h: A streaming MPEG2 remultiplexer
|
||||||
|
*
|
||||||
|
* This file is based on a copy of remux.h from Klaus Schmidinger's
|
||||||
|
* VDR, version 1.6.0.
|
||||||
|
*
|
||||||
|
* $Id: ts2pes.h,v 1.3 2009/06/30 06:04:33 schmirl Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef VDR_STREAMDEV_TS2PES_H
|
||||||
|
#define VDR_STREAMDEV_TS2PES_H
|
||||||
|
|
||||||
|
#include "remux/tsremux.h"
|
||||||
|
#include "server/streamer.h"
|
||||||
|
|
||||||
|
#define MAXTRACKS 64
|
||||||
|
|
||||||
|
namespace Streamdev {
|
||||||
|
|
||||||
|
class cTS2PES;
|
||||||
|
|
||||||
|
class cTS2PESRemux: public cTSRemux {
|
||||||
|
private:
|
||||||
|
bool noVideo;
|
||||||
|
bool synced;
|
||||||
|
int skipped;
|
||||||
|
cTS2PES *ts2pes[MAXTRACKS];
|
||||||
|
int numTracks;
|
||||||
|
cStreamdevBuffer *resultBuffer;
|
||||||
|
int resultSkipped;
|
||||||
|
public:
|
||||||
|
cTS2PESRemux(int VPid, const int *APids, const int *DPids, const int *SPids);
|
||||||
|
///< Creates a new remuxer for the given PIDs. VPid is the video PID, while
|
||||||
|
///< APids, DPids and SPids are pointers to zero terminated lists of audio,
|
||||||
|
///< dolby and subtitle PIDs (the pointers may be NULL if there is no such
|
||||||
|
///< PID).
|
||||||
|
~cTS2PESRemux();
|
||||||
|
int Put(const uchar *Data, int Count);
|
||||||
|
///< Puts at most Count bytes of Data into the remuxer.
|
||||||
|
///< \return Returns the number of bytes actually consumed from Data.
|
||||||
|
uchar *Get(int &Count);
|
||||||
|
///< Gets all currently available data from the remuxer.
|
||||||
|
///< \return Count contains the number of bytes the result points to, and
|
||||||
|
void Del(int Count);
|
||||||
|
///< Deletes Count bytes from the remuxer. Count must be the number returned
|
||||||
|
///< from a previous call to Get(). Several calls to Del() with fractions of
|
||||||
|
///< a previously returned Count may be made, but the total sum of all Count
|
||||||
|
///< values must be exactly what the previous Get() has returned.
|
||||||
|
void Clear(void);
|
||||||
|
///< Clears the remuxer of all data it might still contain, keeping the PID
|
||||||
|
///< settings as they are.
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Streamdev
|
||||||
|
|
||||||
|
#endif // VDR_STREAMDEV_TS2PES_H
|
@ -3,6 +3,8 @@
|
|||||||
#include <vdr/channels.h>
|
#include <vdr/channels.h>
|
||||||
#include <vdr/device.h>
|
#include <vdr/device.h>
|
||||||
|
|
||||||
|
namespace Streamdev {
|
||||||
|
|
||||||
class cTS2PS {
|
class cTS2PS {
|
||||||
friend void PutPES(uint8_t *Buffer, int Size, void *Data);
|
friend void PutPES(uint8_t *Buffer, int Size, void *Data);
|
||||||
|
|
||||||
@ -28,6 +30,9 @@ void PutPES(uint8_t *Buffer, int Size, void *Data)
|
|||||||
esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Size - n, Size);
|
esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Size - n, Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace Streamdev
|
||||||
|
using namespace Streamdev;
|
||||||
|
|
||||||
cTS2PS::cTS2PS(cRingBufferLinear *ResultBuffer, int Pid, uint8_t AudioCid)
|
cTS2PS::cTS2PS(cRingBufferLinear *ResultBuffer, int Pid, uint8_t AudioCid)
|
||||||
{
|
{
|
||||||
m_ResultBuffer = ResultBuffer;
|
m_ResultBuffer = ResultBuffer;
|
||||||
@ -74,13 +79,13 @@ void cTS2PS::PutTSPacket(const uint8_t *Buffer)
|
|||||||
|
|
||||||
cTS2PSRemux::cTS2PSRemux(int VPid, const int *APids, const int *DPids, const int *SPids):
|
cTS2PSRemux::cTS2PSRemux(int VPid, const int *APids, const int *DPids, const int *SPids):
|
||||||
m_NumTracks(0),
|
m_NumTracks(0),
|
||||||
m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE, IPACKS)),
|
m_ResultBuffer(new cStreamdevBuffer(WRITERBUFSIZE, IPACKS)),
|
||||||
m_ResultSkipped(0),
|
m_ResultSkipped(0),
|
||||||
m_Skipped(0),
|
m_Skipped(0),
|
||||||
m_Synced(false),
|
m_Synced(false),
|
||||||
m_IsRadio(VPid == 0 || VPid == 1 || VPid == 0x1FFF)
|
m_IsRadio(VPid == 0 || VPid == 1 || VPid == 0x1FFF)
|
||||||
{
|
{
|
||||||
m_ResultBuffer->SetTimeouts(0, 100);
|
m_ResultBuffer->SetTimeouts(100, 100);
|
||||||
|
|
||||||
if (VPid)
|
if (VPid)
|
||||||
m_Remux[m_NumTracks++] = new cTS2PS(m_ResultBuffer, VPid);
|
m_Remux[m_NumTracks++] = new cTS2PS(m_ResultBuffer, VPid);
|
||||||
@ -124,8 +129,10 @@ int cTS2PSRemux::Put(const uchar *Data, int Count)
|
|||||||
break;
|
break;
|
||||||
if (Data[i] != TS_SYNC_BYTE)
|
if (Data[i] != TS_SYNC_BYTE)
|
||||||
break;
|
break;
|
||||||
if (m_ResultBuffer->Free() < 2 * IPACKS)
|
if (m_ResultBuffer->Free() < 2 * IPACKS) {
|
||||||
|
m_ResultBuffer->WaitForPut();
|
||||||
break; // A cTS2PS might write one full packet and also a small rest
|
break; // A cTS2PS might write one full packet and also a small rest
|
||||||
|
}
|
||||||
int pid = GetPid(Data + i + 1);
|
int pid = GetPid(Data + i + 1);
|
||||||
if (Data[i + 3] & 0x10) { // got payload
|
if (Data[i + 3] & 0x10) { // got payload
|
||||||
for (int t = 0; t < m_NumTracks; t++) {
|
for (int t = 0; t < m_NumTracks; t++) {
|
||||||
|
@ -2,20 +2,21 @@
|
|||||||
#define VDR_STREAMDEV_TS2PESREMUX_H
|
#define VDR_STREAMDEV_TS2PESREMUX_H
|
||||||
|
|
||||||
#include "remux/tsremux.h"
|
#include "remux/tsremux.h"
|
||||||
#include <vdr/remux.h>
|
#include "server/streamer.h"
|
||||||
#include <vdr/ringbuffer.h>
|
|
||||||
|
|
||||||
#ifndef MAXTRACKS
|
#ifndef MAXTRACKS
|
||||||
#define MAXTRACKS 64
|
#define MAXTRACKS 64
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace Streamdev {
|
||||||
|
|
||||||
class cTS2PS;
|
class cTS2PS;
|
||||||
|
|
||||||
class cTS2PSRemux: public cTSRemux {
|
class cTS2PSRemux: public cTSRemux {
|
||||||
private:
|
private:
|
||||||
int m_NumTracks;
|
int m_NumTracks;
|
||||||
cTS2PS *m_Remux[MAXTRACKS];
|
cTS2PS *m_Remux[MAXTRACKS];
|
||||||
cRingBufferLinear *m_ResultBuffer;
|
cStreamdevBuffer *m_ResultBuffer;
|
||||||
int m_ResultSkipped;
|
int m_ResultSkipped;
|
||||||
int m_Skipped;
|
int m_Skipped;
|
||||||
bool m_Synced;
|
bool m_Synced;
|
||||||
@ -30,4 +31,6 @@ public:
|
|||||||
void Del(int Count) { m_ResultBuffer->Del(Count); }
|
void Del(int Count) { m_ResultBuffer->Del(Count); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace Streamdev
|
||||||
|
|
||||||
#endif // VDR_STREAMDEV_TS2PESREMUX_H
|
#endif // VDR_STREAMDEV_TS2PESREMUX_H
|
||||||
|
@ -2,11 +2,15 @@
|
|||||||
|
|
||||||
#define SC_PICTURE 0x00 // "picture header"
|
#define SC_PICTURE 0x00 // "picture header"
|
||||||
#define PID_MASK_HI 0x1F
|
#define PID_MASK_HI 0x1F
|
||||||
|
#define VIDEO_STREAM_S 0xE0
|
||||||
|
|
||||||
|
using namespace Streamdev;
|
||||||
|
|
||||||
void cTSRemux::SetBrokenLink(uchar *Data, int Length)
|
void cTSRemux::SetBrokenLink(uchar *Data, int Length)
|
||||||
{
|
{
|
||||||
if (Length > 9 && Data[0] == 0 && Data[1] == 0 && Data[2] == 1 && (Data[3] & 0xF0) == VIDEO_STREAM_S) {
|
int PesPayloadOffset = 0;
|
||||||
for (int i = Data[8] + 9; i < Length - 7; i++) { // +9 to skip video packet header
|
if (AnalyzePesHeader(Data, Length, PesPayloadOffset) >= phMPEG1 && (Data[3] & 0xF0) == VIDEO_STREAM_S) {
|
||||||
|
for (int i = PesPayloadOffset; i < Length - 7; i++) {
|
||||||
if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1 && Data[i + 3] == 0xB8) {
|
if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1 && Data[i + 3] == 0xB8) {
|
||||||
if (!(Data[i + 7] & 0x40)) // set flag only if GOP is not closed
|
if (!(Data[i + 7] & 0x40)) // set flag only if GOP is not closed
|
||||||
Data[i + 7] |= 0x20;
|
Data[i + 7] |= 0x20;
|
||||||
@ -40,17 +44,40 @@ int cTSRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &P
|
|||||||
// If the return value is -1 the packet was not completely in the buffer.
|
// If the return value is -1 the packet was not completely in the buffer.
|
||||||
int Length = GetPacketLength(Data, Count, Offset);
|
int Length = GetPacketLength(Data, Count, Offset);
|
||||||
if (Length > 0) {
|
if (Length > 0) {
|
||||||
if (Length >= 8) {
|
int PesPayloadOffset = 0;
|
||||||
int i = Offset + 8; // the minimum length of the video packet header
|
if (AnalyzePesHeader(Data + Offset, Length, PesPayloadOffset) >= phMPEG1) {
|
||||||
i += Data[i] + 1; // possible additional header bytes
|
const uchar *p = Data + Offset + PesPayloadOffset + 2;
|
||||||
for (; i < Offset + Length - 5; i++) {
|
const uchar *pLimit = Data + Offset + Length - 3;
|
||||||
if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
|
#ifdef TEST_cVideoRepacker
|
||||||
switch (Data[i + 3]) {
|
// cVideoRepacker ensures that a new PES packet is started for a new sequence,
|
||||||
case SC_PICTURE: PictureType = (Data[i + 5] >> 3) & 0x07;
|
// group or picture which allows us to easily skip scanning through a huge
|
||||||
return Length;
|
// amount of video data.
|
||||||
|
if (p < pLimit) {
|
||||||
|
if (p[-2] || p[-1] || p[0] != 0x01)
|
||||||
|
pLimit = 0; // skip scanning: packet doesn't start with 0x000001
|
||||||
|
else {
|
||||||
|
switch (p[1]) {
|
||||||
|
case SC_SEQUENCE:
|
||||||
|
case SC_GROUP:
|
||||||
|
case SC_PICTURE:
|
||||||
|
break;
|
||||||
|
default: // skip scanning: packet doesn't start a new sequence, group or picture
|
||||||
|
pLimit = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
while (p < pLimit && (p = (const uchar *)memchr(p, 0x01, pLimit - p))) {
|
||||||
|
if (!p[-2] && !p[-1]) { // found 0x000001
|
||||||
|
switch (p[1]) {
|
||||||
|
case SC_PICTURE: PictureType = (p[3] >> 3) & 0x07;
|
||||||
|
return Length;
|
||||||
|
}
|
||||||
|
p += 4; // continue scanning after 0x01ssxxyy
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
}
|
p += 3; // continue scanning after 0x01xxyy
|
||||||
|
}
|
||||||
}
|
}
|
||||||
PictureType = NO_PICTURE;
|
PictureType = NO_PICTURE;
|
||||||
return Length;
|
return Length;
|
||||||
|
@ -4,34 +4,26 @@
|
|||||||
#include "libdvbmpeg/transform.h"
|
#include "libdvbmpeg/transform.h"
|
||||||
#include <vdr/remux.h>
|
#include <vdr/remux.h>
|
||||||
|
|
||||||
#ifndef NO_PICTURE
|
// Picture types:
|
||||||
#define NO_PICTURE 0
|
#define NO_PICTURE 0
|
||||||
#endif
|
#define I_FRAME 1
|
||||||
|
#define P_FRAME 2
|
||||||
|
#define B_FRAME 3
|
||||||
|
|
||||||
#define RESULTBUFFERSIZE KILOBYTE(256)
|
namespace Streamdev {
|
||||||
|
|
||||||
class cTSRemux {
|
class cTSRemux {
|
||||||
protected:
|
|
||||||
/*uchar m_ResultBuffer[RESULTBUFFERSIZE];
|
|
||||||
int m_ResultCount;
|
|
||||||
int m_ResultDelivered;
|
|
||||||
int m_Synced;
|
|
||||||
int m_Skipped;
|
|
||||||
int m_Sync;
|
|
||||||
|
|
||||||
|
|
||||||
virtual void PutTSPacket(int Pid, const uint8_t *Data) = 0;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cTSRemux(bool Sync = true);
|
virtual int Put(const uchar *Data, int Count) = 0;
|
||||||
virtual ~cTSRemux();
|
virtual uchar *Get(int &Count) = 0;
|
||||||
|
virtual void Del(int Count) = 0;
|
||||||
virtual uchar *Process(const uchar *Data, int &Count, int &Result);*/
|
|
||||||
|
|
||||||
static void SetBrokenLink(uchar *Data, int Length);
|
static void SetBrokenLink(uchar *Data, int Length);
|
||||||
static int GetPid(const uchar *Data);
|
static int GetPid(const uchar *Data);
|
||||||
static int GetPacketLength(const uchar *Data, int Count, int Offset);
|
static int GetPacketLength(const uchar *Data, int Count, int Offset);
|
||||||
static int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
|
static int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace Streamdev
|
||||||
|
|
||||||
#endif // VDR_STREAMDEV_TSREMUX_H
|
#endif // VDR_STREAMDEV_TSREMUX_H
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* $Id: connectionHTTP.c,v 1.16 2009/02/13 07:02:19 schmirl Exp $
|
* $Id: connectionHTTP.c,v 1.17 2009/06/19 06:32:45 schmirl Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
@ -211,10 +211,8 @@ bool cConnectionHTTP::CmdGET(const std::string &Opts)
|
|||||||
const char* pType = type.c_str();
|
const char* pType = type.c_str();
|
||||||
if (strcasecmp(pType, "PS") == 0) {
|
if (strcasecmp(pType, "PS") == 0) {
|
||||||
m_StreamType = stPS;
|
m_StreamType = stPS;
|
||||||
#if APIVERSNUM < 10703
|
|
||||||
} else if (strcasecmp(pType, "PES") == 0) {
|
} else if (strcasecmp(pType, "PES") == 0) {
|
||||||
m_StreamType = stPES;
|
m_StreamType = stPES;
|
||||||
#endif
|
|
||||||
} else if (strcasecmp(pType, "TS") == 0) {
|
} else if (strcasecmp(pType, "TS") == 0) {
|
||||||
m_StreamType = stTS;
|
m_StreamType = stTS;
|
||||||
} else if (strcasecmp(pType, "ES") == 0) {
|
} else if (strcasecmp(pType, "ES") == 0) {
|
||||||
@ -266,9 +264,7 @@ bool cConnectionHTTP::CmdGET(const std::string &Opts)
|
|||||||
{
|
{
|
||||||
case stTS: base += "TS/"; break;
|
case stTS: base += "TS/"; break;
|
||||||
case stPS: base += "PS/"; break;
|
case stPS: base += "PS/"; break;
|
||||||
#if APIVERSNUM < 10703
|
|
||||||
case stPES: base += "PES/"; break;
|
case stPES: base += "PES/"; break;
|
||||||
#endif
|
|
||||||
case stES: base += "ES/"; break;
|
case stES: base += "ES/"; break;
|
||||||
case stExtern: base += "Extern/"; break;
|
case stExtern: base += "Extern/"; break;
|
||||||
default: break;
|
default: break;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -2,6 +2,7 @@
|
|||||||
#define VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
|
#define VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
|
||||||
|
|
||||||
#include "server/connection.h"
|
#include "server/connection.h"
|
||||||
|
#include "server/recplayer.h"
|
||||||
|
|
||||||
class cTBSocket;
|
class cTBSocket;
|
||||||
class cStreamdevLiveStreamer;
|
class cStreamdevLiveStreamer;
|
||||||
@ -9,6 +10,7 @@ class cStreamdevFilterStreamer;
|
|||||||
class cLSTEHandler;
|
class cLSTEHandler;
|
||||||
class cLSTCHandler;
|
class cLSTCHandler;
|
||||||
class cLSTTHandler;
|
class cLSTTHandler;
|
||||||
|
class cLSTRHandler;
|
||||||
|
|
||||||
class cConnectionVTP: public cServerConnection {
|
class cConnectionVTP: public cServerConnection {
|
||||||
friend class cLSTEHandler;
|
friend class cLSTEHandler;
|
||||||
@ -21,16 +23,19 @@ private:
|
|||||||
cStreamdevLiveStreamer *m_LiveStreamer;
|
cStreamdevLiveStreamer *m_LiveStreamer;
|
||||||
cTBSocket *m_FilterSocket;
|
cTBSocket *m_FilterSocket;
|
||||||
cStreamdevFilterStreamer *m_FilterStreamer;
|
cStreamdevFilterStreamer *m_FilterStreamer;
|
||||||
|
cTBSocket *m_RecSocket;
|
||||||
|
cTBSocket *m_DataSocket;
|
||||||
|
|
||||||
char *m_LastCommand;
|
char *m_LastCommand;
|
||||||
eStreamType m_StreamType;
|
eStreamType m_StreamType;
|
||||||
bool m_FiltersSupport;
|
bool m_FiltersSupport;
|
||||||
|
RecPlayer *m_RecPlayer;
|
||||||
|
|
||||||
// Members adopted for SVDRP
|
// Members adopted for SVDRP
|
||||||
cRecordings Recordings;
|
|
||||||
cLSTEHandler *m_LSTEHandler;
|
cLSTEHandler *m_LSTEHandler;
|
||||||
cLSTCHandler *m_LSTCHandler;
|
cLSTCHandler *m_LSTCHandler;
|
||||||
cLSTTHandler *m_LSTTHandler;
|
cLSTTHandler *m_LSTTHandler;
|
||||||
|
cLSTRHandler *m_LSTRHandler;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
template<class cHandler>
|
template<class cHandler>
|
||||||
@ -51,7 +56,9 @@ public:
|
|||||||
bool CmdCAPS(char *Opts);
|
bool CmdCAPS(char *Opts);
|
||||||
bool CmdPROV(char *Opts);
|
bool CmdPROV(char *Opts);
|
||||||
bool CmdPORT(char *Opts);
|
bool CmdPORT(char *Opts);
|
||||||
|
bool CmdREAD(char *Opts);
|
||||||
bool CmdTUNE(char *Opts);
|
bool CmdTUNE(char *Opts);
|
||||||
|
bool CmdPLAY(char *Opts);
|
||||||
bool CmdADDP(char *Opts);
|
bool CmdADDP(char *Opts);
|
||||||
bool CmdDELP(char *Opts);
|
bool CmdDELP(char *Opts);
|
||||||
bool CmdADDF(char *Opts);
|
bool CmdADDF(char *Opts);
|
||||||
@ -64,14 +71,20 @@ public:
|
|||||||
bool CmdLSTE(char *Opts);
|
bool CmdLSTE(char *Opts);
|
||||||
bool CmdLSTC(char *Opts);
|
bool CmdLSTC(char *Opts);
|
||||||
bool CmdLSTT(char *Opts);
|
bool CmdLSTT(char *Opts);
|
||||||
|
bool CmdLSTR(char *Opts);
|
||||||
|
|
||||||
// Commands adopted from SVDRP
|
// Commands adopted from SVDRP
|
||||||
|
bool CmdSTAT(const char *Option);
|
||||||
bool CmdMODT(const char *Option);
|
bool CmdMODT(const char *Option);
|
||||||
bool CmdNEWT(const char *Option);
|
bool CmdNEWT(const char *Option);
|
||||||
bool CmdDELT(const char *Option);
|
bool CmdDELT(const char *Option);
|
||||||
|
bool CmdNEXT(const char *Option);
|
||||||
//bool CmdLSTR(char *Opts);
|
bool CmdNEWC(const char *Option);
|
||||||
//bool CmdDELR(char *Opts);
|
bool CmdMODC(const char *Option);
|
||||||
|
bool CmdMOVC(const char *Option);
|
||||||
|
bool CmdDELC(const char *Option);
|
||||||
|
bool CmdDELR(const char *Option);
|
||||||
|
bool CmdRENR(const char *Option);
|
||||||
|
|
||||||
bool Respond(int Code, const char *Message, ...)
|
bool Respond(int Code, const char *Message, ...)
|
||||||
__attribute__ ((format (printf, 3, 4)));
|
__attribute__ ((format (printf, 3, 4)));
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <libsi/descriptor.h>
|
#include <libsi/descriptor.h>
|
||||||
|
|
||||||
#include "remux/ts2ps.h"
|
#include "remux/ts2ps.h"
|
||||||
|
#include "remux/ts2pes.h"
|
||||||
#include "remux/ts2es.h"
|
#include "remux/ts2es.h"
|
||||||
#include "remux/extern.h"
|
#include "remux/extern.h"
|
||||||
|
|
||||||
@ -13,7 +14,7 @@
|
|||||||
#include "server/livefilter.h"
|
#include "server/livefilter.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#define TSPATREPACKER
|
using namespace Streamdev;
|
||||||
|
|
||||||
// --- cStreamdevLiveReceiver -------------------------------------------------
|
// --- cStreamdevLiveReceiver -------------------------------------------------
|
||||||
|
|
||||||
@ -64,6 +65,8 @@ private:
|
|||||||
int pmtPid;
|
int pmtPid;
|
||||||
int pmtSid;
|
int pmtSid;
|
||||||
int pmtVersion;
|
int pmtVersion;
|
||||||
|
uchar tspat_buf[TS_SIZE];
|
||||||
|
cStreamdevBuffer siBuffer;
|
||||||
|
|
||||||
const cChannel *m_Channel;
|
const cChannel *m_Channel;
|
||||||
cStreamdevLiveStreamer *m_Streamer;
|
cStreamdevLiveStreamer *m_Streamer;
|
||||||
@ -73,9 +76,11 @@ private:
|
|||||||
int GetPid(SI::PMT::Stream& stream);
|
int GetPid(SI::PMT::Stream& stream);
|
||||||
public:
|
public:
|
||||||
cStreamdevPatFilter(cStreamdevLiveStreamer *Streamer, const cChannel *Channel);
|
cStreamdevPatFilter(cStreamdevLiveStreamer *Streamer, const cChannel *Channel);
|
||||||
|
uchar* Get(int &Count) { return siBuffer.Get(Count); }
|
||||||
|
void Del(int Count) { return siBuffer.Del(Count); }
|
||||||
};
|
};
|
||||||
|
|
||||||
cStreamdevPatFilter::cStreamdevPatFilter(cStreamdevLiveStreamer *Streamer, const cChannel *Channel)
|
cStreamdevPatFilter::cStreamdevPatFilter(cStreamdevLiveStreamer *Streamer, const cChannel *Channel): siBuffer(10 * TS_SIZE, TS_SIZE)
|
||||||
{
|
{
|
||||||
Dprintf("cStreamdevPatFilter(\"%s\")\n", Channel->Name());
|
Dprintf("cStreamdevPatFilter(\"%s\")\n", Channel->Name());
|
||||||
assert(Streamer);
|
assert(Streamer);
|
||||||
@ -85,6 +90,29 @@ cStreamdevPatFilter::cStreamdevPatFilter(cStreamdevLiveStreamer *Streamer, const
|
|||||||
pmtSid = 0;
|
pmtSid = 0;
|
||||||
pmtVersion = -1;
|
pmtVersion = -1;
|
||||||
Set(0x00, 0x00); // PAT
|
Set(0x00, 0x00); // PAT
|
||||||
|
// initialize PAT buffer. Only some values are dynamic (see comments)
|
||||||
|
memset(tspat_buf, 0xff, TS_SIZE);
|
||||||
|
tspat_buf[0] = TS_SYNC_BYTE; // Transport packet header sunchronization byte (1000011 = 0x47h)
|
||||||
|
tspat_buf[1] = 0x40; // Set payload unit start indicator bit
|
||||||
|
tspat_buf[2] = 0x0; // PID
|
||||||
|
tspat_buf[3] = 0x10; // Set payload flag, DYNAMIC: Continuity counter
|
||||||
|
tspat_buf[4] = 0x0; // SI pointer field
|
||||||
|
tspat_buf[5] = 0x0; // PAT table id
|
||||||
|
tspat_buf[6] = 0xb0; // Section syntax indicator bit and reserved bits set
|
||||||
|
tspat_buf[7] = 12 + 1; // Section length (12 bit): PAT_TABLE_LEN + 1
|
||||||
|
tspat_buf[8] = 0; // DYNAMIC: Transport stream ID (bits 8-15)
|
||||||
|
tspat_buf[9] = 0; // DYNAMIC: Transport stream ID (bits 0-7)
|
||||||
|
tspat_buf[10] = 0xc0; // Reserved, DYNAMIC: Version number, DYNAMIC: Current next indicator
|
||||||
|
tspat_buf[11] = 0x0; // Section number
|
||||||
|
tspat_buf[12] = 0x0; // Last section number
|
||||||
|
tspat_buf[13] = 0; // DYNAMIC: Program number (bits 8-15)
|
||||||
|
tspat_buf[14] = 0; // DYNAMIC: Program number (bits 0-7)
|
||||||
|
tspat_buf[15] = 0xe0; // Reserved, DYNAMIC: Network ID (bits 8-12)
|
||||||
|
tspat_buf[16] = 0; // DYNAMIC: Network ID (bits 0-7)
|
||||||
|
tspat_buf[17] = 0; // DYNAMIC: Checksum
|
||||||
|
tspat_buf[18] = 0; // DYNAMIC: Checksum
|
||||||
|
tspat_buf[19] = 0; // DYNAMIC: Checksum
|
||||||
|
tspat_buf[20] = 0; // DYNAMIC: Checksum
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char * const psStreamTypes[] = {
|
static const char * const psStreamTypes[] = {
|
||||||
@ -224,54 +252,37 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
|
|||||||
if (0 != (pmtPid = assoc.getPid())) {
|
if (0 != (pmtPid = assoc.getPid())) {
|
||||||
Dprintf("cStreamdevPatFilter: PMT pid for channel %s: %d\n", Channel->Name(), pmtPid);
|
Dprintf("cStreamdevPatFilter: PMT pid for channel %s: %d\n", Channel->Name(), pmtPid);
|
||||||
pmtSid = assoc.getServiceId();
|
pmtSid = assoc.getServiceId();
|
||||||
if (Length < TS_SIZE-5) {
|
// repack PAT to TS frame and send to client
|
||||||
// repack PAT to TS frame and send to client
|
int ts_id;
|
||||||
#ifndef TSPATREPACKER
|
unsigned int crc, i, len;
|
||||||
uint8_t pat_ts[TS_SIZE] = {TS_SYNC_BYTE, 0x40 /* pusi=1 */, 0 /* pid=0 */, 0x10 /* adaption=1 */, 0 /* pointer */};
|
uint8_t *tmp;
|
||||||
memcpy(pat_ts + 5, Data, Length);
|
static uint8_t ccounter = 0;
|
||||||
m_Streamer->Put(pat_ts, TS_SIZE);
|
ccounter = (ccounter + 1) % 16;
|
||||||
#else
|
ts_id = Channel->Tid(); // Get transport stream id of the channel
|
||||||
int ts_id;
|
tspat_buf[3] = 0x10 | ccounter; // Set payload flag, Continuity counter
|
||||||
unsigned int crc, i, len;
|
tspat_buf[8] = (ts_id >> 8); // Transport stream ID (bits 8-15)
|
||||||
uint8_t *tmp, tspat_buf[TS_SIZE];
|
tspat_buf[9] = (ts_id & 0xff); // Transport stream ID (bits 0-7)
|
||||||
static uint8_t ccounter = 0;
|
tspat_buf[10] = 0xc0 | ((pat.getVersionNumber() << 1) & 0x3e) |
|
||||||
ccounter = (ccounter + 1) % 16;
|
pat.getCurrentNextIndicator();// Version number, Current next indicator
|
||||||
memset(tspat_buf, 0xff, TS_SIZE);
|
tspat_buf[13] = (pmtSid >> 8); // Program number (bits 8-15)
|
||||||
ts_id = Channel->Tid(); // Get transport stream id of the channel
|
tspat_buf[14] = (pmtSid & 0xff); // Program number (bits 0-7)
|
||||||
tspat_buf[0] = TS_SYNC_BYTE; // Transport packet header sunchronization byte (1000011 = 0x47h)
|
tspat_buf[15] = 0xe0 | (pmtPid >> 8); // Network ID (bits 8-12)
|
||||||
tspat_buf[1] = 0x40; // Set payload unit start indicator bit
|
tspat_buf[16] = (pmtPid & 0xff); // Network ID (bits 0-7)
|
||||||
tspat_buf[2] = 0x0; // PID
|
crc = 0xffffffff;
|
||||||
tspat_buf[3] = 0x10 | ccounter; // Set payload flag, Continuity counter
|
len = 12; // PAT_TABLE_LEN
|
||||||
tspat_buf[4] = 0x0; // SI pointer field
|
tmp = &tspat_buf[4 + 1]; // TS_HDR_LEN + 1
|
||||||
tspat_buf[5] = 0x0; // PAT table id
|
while (len--) {
|
||||||
tspat_buf[6] = 0xb0; // Section syntax indicator bit and reserved bits set
|
crc ^= *tmp++ << 24;
|
||||||
tspat_buf[7] = 12 + 1; // Section length (12 bit): PAT_TABLE_LEN + 1
|
for (i = 0; i < 8; i++)
|
||||||
tspat_buf[8] = (ts_id >> 8); // Transport stream ID (bits 8-15)
|
crc = (crc << 1) ^ ((crc & 0x80000000) ? 0x04c11db7 : 0); // CRC32POLY
|
||||||
tspat_buf[9] = (ts_id & 0xff); // Transport stream ID (bits 0-7)
|
}
|
||||||
tspat_buf[10] = 0xc0 | ((pat.getVersionNumber() << 1) & 0x3e) |
|
tspat_buf[17] = crc >> 24 & 0xff; // Checksum
|
||||||
pat.getCurrentNextIndicator();// Version number, Current next indicator
|
tspat_buf[18] = crc >> 16 & 0xff; // Checksum
|
||||||
tspat_buf[11] = 0x0; // Section number
|
tspat_buf[19] = crc >> 8 & 0xff; // Checksum
|
||||||
tspat_buf[12] = 0x0; // Last section number
|
tspat_buf[20] = crc & 0xff; // Checksum
|
||||||
tspat_buf[13] = (pmtSid >> 8); // Program number (bits 8-15)
|
int written = siBuffer.PutTS(tspat_buf, TS_SIZE);
|
||||||
tspat_buf[14] = (pmtSid & 0xff); // Program number (bits 0-7)
|
if (written != TS_SIZE)
|
||||||
tspat_buf[15] = 0xe0 | (pmtPid >> 8); // Network ID (bits 8-12)
|
siBuffer.ReportOverflow(TS_SIZE - written);
|
||||||
tspat_buf[16] = (pmtPid & 0xff); // Network ID (bits 0-7)
|
|
||||||
crc = 0xffffffff;
|
|
||||||
len = 12; // PAT_TABLE_LEN
|
|
||||||
tmp = &tspat_buf[4 + 1]; // TS_HDR_LEN + 1
|
|
||||||
while (len--) {
|
|
||||||
crc ^= *tmp++ << 24;
|
|
||||||
for (i = 0; i < 8; i++)
|
|
||||||
crc = (crc << 1) ^ ((crc & 0x80000000) ? 0x04c11db7 : 0); // CRC32POLY
|
|
||||||
}
|
|
||||||
tspat_buf[17] = crc >> 24 & 0xff; // Checksum
|
|
||||||
tspat_buf[18] = crc >> 16 & 0xff; // Checksum
|
|
||||||
tspat_buf[19] = crc >> 8 & 0xff; // Checksum
|
|
||||||
tspat_buf[20] = crc & 0xff; // Checksum
|
|
||||||
m_Streamer->Put(tspat_buf, TS_SIZE);
|
|
||||||
#endif
|
|
||||||
} else
|
|
||||||
isyslog("cStreamdevPatFilter: PAT size %d too large to fit in one TS", Length);
|
|
||||||
if (pmtPid != prevPmtPid) {
|
if (pmtPid != prevPmtPid) {
|
||||||
m_Streamer->SetPids(pmtPid);
|
m_Streamer->SetPids(pmtPid);
|
||||||
Add(pmtPid, 0x02);
|
Add(pmtPid, 0x02);
|
||||||
@ -292,7 +303,7 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
|
|||||||
if (pmtVersion != -1) {
|
if (pmtVersion != -1) {
|
||||||
if (pmtVersion != pmt.getVersionNumber()) {
|
if (pmtVersion != pmt.getVersionNumber()) {
|
||||||
Dprintf("cStreamdevPatFilter: PMT version changed, detaching all pids\n");
|
Dprintf("cStreamdevPatFilter: PMT version changed, detaching all pids\n");
|
||||||
Del(pmtPid, 0x02);
|
cFilter::Del(pmtPid, 0x02);
|
||||||
pmtPid = 0; // this triggers PAT scan
|
pmtPid = 0; // this triggers PAT scan
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -329,12 +340,7 @@ cStreamdevLiveStreamer::cStreamdevLiveStreamer(int Priority, std::string Paramet
|
|||||||
m_Device(NULL),
|
m_Device(NULL),
|
||||||
m_Receiver(NULL),
|
m_Receiver(NULL),
|
||||||
m_PatFilter(NULL),
|
m_PatFilter(NULL),
|
||||||
#if APIVERSNUM < 10703
|
m_Remux(NULL)
|
||||||
m_PESRemux(NULL),
|
|
||||||
#endif
|
|
||||||
m_ESRemux(NULL),
|
|
||||||
m_PSRemux(NULL),
|
|
||||||
m_ExtRemux(NULL)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,12 +353,7 @@ cStreamdevLiveStreamer::~cStreamdevLiveStreamer()
|
|||||||
DELETENULL(m_PatFilter);
|
DELETENULL(m_PatFilter);
|
||||||
}
|
}
|
||||||
DELETENULL(m_Receiver);
|
DELETENULL(m_Receiver);
|
||||||
#if APIVERSNUM < 10703
|
delete m_Remux;
|
||||||
delete m_PESRemux;
|
|
||||||
#endif
|
|
||||||
delete m_ESRemux;
|
|
||||||
delete m_PSRemux;
|
|
||||||
delete m_ExtRemux;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cStreamdevLiveStreamer::HasPid(int Pid)
|
bool cStreamdevLiveStreamer::HasPid(int Pid)
|
||||||
@ -459,19 +460,17 @@ bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType Str
|
|||||||
int pid = ISRADIO(m_Channel) ? m_Channel->Apid(0) : m_Channel->Vpid();
|
int pid = ISRADIO(m_Channel) ? m_Channel->Apid(0) : m_Channel->Vpid();
|
||||||
if (Apid != 0)
|
if (Apid != 0)
|
||||||
pid = Apid;
|
pid = Apid;
|
||||||
m_ESRemux = new cTS2ESRemux(pid);
|
m_Remux = new cTS2ESRemux(pid);
|
||||||
return SetPids(pid);
|
return SetPids(pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if APIVERSNUM < 10703
|
|
||||||
case stPES:
|
case stPES:
|
||||||
m_PESRemux = new cRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
|
m_Remux = new cTS2PESRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
|
||||||
m_Channel->Spids(), false);
|
m_Channel->Spids());
|
||||||
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
|
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
|
||||||
#endif
|
|
||||||
|
|
||||||
case stPS:
|
case stPS:
|
||||||
m_PSRemux = new cTS2PSRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
|
m_Remux = new cTS2PSRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
|
||||||
m_Channel->Spids());
|
m_Channel->Spids());
|
||||||
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
|
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
|
||||||
|
|
||||||
@ -490,7 +489,7 @@ bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType Str
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case stExtern:
|
case stExtern:
|
||||||
m_ExtRemux = new cExternRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
|
m_Remux = new cExternRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
|
||||||
m_Channel->Spids(), m_Parameter);
|
m_Channel->Spids(), m_Parameter);
|
||||||
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
|
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
|
||||||
|
|
||||||
@ -503,82 +502,39 @@ bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType Str
|
|||||||
|
|
||||||
int cStreamdevLiveStreamer::Put(const uchar *Data, int Count)
|
int cStreamdevLiveStreamer::Put(const uchar *Data, int Count)
|
||||||
{
|
{
|
||||||
switch (m_StreamType) {
|
// insert si data
|
||||||
case stTS:
|
if (m_PatFilter) {
|
||||||
case stTSPIDS:
|
int siCount;
|
||||||
return cStreamdevStreamer::Put(Data, Count);
|
uchar *siData = m_PatFilter->Get(siCount);
|
||||||
|
if (siData) {
|
||||||
#if APIVERSNUM < 10703
|
if (m_Remux)
|
||||||
case stPES:
|
siCount = m_Remux->Put(siData, siCount);
|
||||||
return m_PESRemux->Put(Data, Count);
|
else
|
||||||
#endif
|
siCount = cStreamdevStreamer::Put(siData, siCount);
|
||||||
|
if (siCount)
|
||||||
case stES:
|
m_PatFilter->Del(siCount);
|
||||||
return m_ESRemux->Put(Data, Count);
|
}
|
||||||
|
|
||||||
case stPS:
|
|
||||||
return m_PSRemux->Put(Data, Count);
|
|
||||||
|
|
||||||
case stExtern:
|
|
||||||
return m_ExtRemux->Put(Data, Count);
|
|
||||||
|
|
||||||
default: // shouldn't happen???
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
if (m_Remux)
|
||||||
|
return m_Remux->Put(Data, Count);
|
||||||
|
else
|
||||||
|
return cStreamdevStreamer::Put(Data, Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
uchar *cStreamdevLiveStreamer::Get(int &Count)
|
uchar *cStreamdevLiveStreamer::Get(int &Count)
|
||||||
{
|
{
|
||||||
switch (m_StreamType) {
|
if (m_Remux)
|
||||||
case stTS:
|
return m_Remux->Get(Count);
|
||||||
case stTSPIDS:
|
else
|
||||||
return cStreamdevStreamer::Get(Count);
|
return cStreamdevStreamer::Get(Count);
|
||||||
|
|
||||||
#if APIVERSNUM < 10703
|
|
||||||
case stPES:
|
|
||||||
return m_PESRemux->Get(Count);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
case stES:
|
|
||||||
return m_ESRemux->Get(Count);
|
|
||||||
|
|
||||||
case stPS:
|
|
||||||
return m_PSRemux->Get(Count);
|
|
||||||
|
|
||||||
case stExtern:
|
|
||||||
return m_ExtRemux->Get(Count);
|
|
||||||
|
|
||||||
default: // shouldn't happen???
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cStreamdevLiveStreamer::Del(int Count)
|
void cStreamdevLiveStreamer::Del(int Count)
|
||||||
{
|
{
|
||||||
switch (m_StreamType) {
|
if (m_Remux)
|
||||||
case stTS:
|
m_Remux->Del(Count);
|
||||||
case stTSPIDS:
|
else
|
||||||
cStreamdevStreamer::Del(Count);
|
cStreamdevStreamer::Del(Count);
|
||||||
break;
|
|
||||||
|
|
||||||
#if APIVERSNUM < 10703
|
|
||||||
case stPES:
|
|
||||||
m_PESRemux->Del(Count);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
case stES:
|
|
||||||
m_ESRemux->Del(Count);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case stPS:
|
|
||||||
m_PSRemux->Del(Count);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case stExtern:
|
|
||||||
m_ExtRemux->Del(Count);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cStreamdevLiveStreamer::Attach(void)
|
void cStreamdevLiveStreamer::Attach(void)
|
||||||
|
@ -5,14 +5,12 @@
|
|||||||
#include <vdr/receiver.h>
|
#include <vdr/receiver.h>
|
||||||
|
|
||||||
#include "server/streamer.h"
|
#include "server/streamer.h"
|
||||||
|
#include "remux/tsremux.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
class cTS2PSRemux;
|
namespace Streamdev {
|
||||||
class cTS2ESRemux;
|
class cTSRemux;
|
||||||
class cExternRemux;
|
}
|
||||||
#if APIVERSNUM < 10703
|
|
||||||
class cRemux;
|
|
||||||
#endif
|
|
||||||
class cStreamdevPatFilter;
|
class cStreamdevPatFilter;
|
||||||
class cStreamdevLiveReceiver;
|
class cStreamdevLiveReceiver;
|
||||||
|
|
||||||
@ -29,12 +27,7 @@ private:
|
|||||||
cDevice *m_Device;
|
cDevice *m_Device;
|
||||||
cStreamdevLiveReceiver *m_Receiver;
|
cStreamdevLiveReceiver *m_Receiver;
|
||||||
cStreamdevPatFilter *m_PatFilter;
|
cStreamdevPatFilter *m_PatFilter;
|
||||||
#if APIVERSNUM < 10703
|
Streamdev::cTSRemux *m_Remux;
|
||||||
cRemux *m_PESRemux;
|
|
||||||
#endif
|
|
||||||
cTS2ESRemux *m_ESRemux;
|
|
||||||
cTS2PSRemux *m_PSRemux;
|
|
||||||
cExternRemux *m_ExtRemux;
|
|
||||||
|
|
||||||
void StartReceiver(void);
|
void StartReceiver(void);
|
||||||
bool HasPid(int Pid);
|
bool HasPid(int Pid);
|
||||||
|
@ -201,10 +201,8 @@ std::string cHtmlChannelList::StreamTypeMenu()
|
|||||||
(std::string) "[<a href=\"/TS/" + self + "\">TS</a>] ");
|
(std::string) "[<a href=\"/TS/" + self + "\">TS</a>] ");
|
||||||
typeMenu += (streamType == stPS ? (std::string) "[PS] " :
|
typeMenu += (streamType == stPS ? (std::string) "[PS] " :
|
||||||
(std::string) "[<a href=\"/PS/" + self + "\">PS</a>] ");
|
(std::string) "[<a href=\"/PS/" + self + "\">PS</a>] ");
|
||||||
#if APIVERSNUM < 10703
|
|
||||||
typeMenu += (streamType == stPES ? (std::string) "[PES] " :
|
typeMenu += (streamType == stPES ? (std::string) "[PES] " :
|
||||||
(std::string) "[<a href=\"/PES/" + self + "\">PES</a>] ");
|
(std::string) "[<a href=\"/PES/" + self + "\">PES</a>] ");
|
||||||
#endif
|
|
||||||
typeMenu += (streamType == stES ? (std::string) "[ES] " :
|
typeMenu += (streamType == stES ? (std::string) "[ES] " :
|
||||||
(std::string) "[<a href=\"/ES/" + self + "\">ES</a>] ");
|
(std::string) "[<a href=\"/ES/" + self + "\">ES</a>] ");
|
||||||
typeMenu += (streamType == stExtern ? (std::string) "[Extern] " :
|
typeMenu += (streamType == stExtern ? (std::string) "[Extern] " :
|
||||||
@ -343,10 +341,8 @@ std::string cHtmlChannelList::ItemText()
|
|||||||
switch (streamType) {
|
switch (streamType) {
|
||||||
case stTS: suffix = (std::string) ".ts"; break;
|
case stTS: suffix = (std::string) ".ts"; break;
|
||||||
case stPS: suffix = (std::string) ".vob"; break;
|
case stPS: suffix = (std::string) ".vob"; break;
|
||||||
#if APIVERSNUM < 10703
|
|
||||||
// for Network Media Tank
|
// for Network Media Tank
|
||||||
case stPES: suffix = (std::string) ".vdr"; break;
|
case stPES: suffix = (std::string) ".vdr"; break;
|
||||||
#endif
|
|
||||||
default: suffix = "";
|
default: suffix = "";
|
||||||
}
|
}
|
||||||
line += (std::string) "<li value=\"" + (const char*) itoa(current->Number()) + "\">";
|
line += (std::string) "<li value=\"" + (const char*) itoa(current->Number()) + "\">";
|
||||||
|
288
server/recplayer.c
Normal file
288
server/recplayer.c
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2004-2005 Chris Tallon
|
||||||
|
|
||||||
|
This file is part of VOMP.
|
||||||
|
and adopted for streamdev to play recordings
|
||||||
|
|
||||||
|
VOMP is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
VOMP is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with VOMP; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "recplayer.h"
|
||||||
|
|
||||||
|
#define _XOPEN_SOURCE 600
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
RecPlayer::RecPlayer(cRecording* rec)
|
||||||
|
{
|
||||||
|
file = NULL;
|
||||||
|
fileOpen = 0;
|
||||||
|
lastPosition = 0;
|
||||||
|
recording = rec;
|
||||||
|
for(int i = 1; i < 1000; i++) segments[i] = NULL;
|
||||||
|
|
||||||
|
// FIXME find out max file path / name lengths
|
||||||
|
|
||||||
|
#if VDRVERSNUM >= 10703
|
||||||
|
indexFile = new cIndexFile(recording->FileName(), false, rec->IsPesRecording());
|
||||||
|
#else
|
||||||
|
indexFile = new cIndexFile(recording->FileName(), false);
|
||||||
|
#endif
|
||||||
|
if (!indexFile) esyslog("ERROR: Streamdev: Failed to create indexfile!");
|
||||||
|
|
||||||
|
scan();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecPlayer::scan()
|
||||||
|
{
|
||||||
|
if (file) fclose(file);
|
||||||
|
totalLength = 0;
|
||||||
|
fileOpen = 0;
|
||||||
|
totalFrames = 0;
|
||||||
|
|
||||||
|
int i = 1;
|
||||||
|
while(segments[i++]) delete segments[i];
|
||||||
|
|
||||||
|
char fileName[2048];
|
||||||
|
for(i = 1; i < 1000; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
#if APIVERSNUM < 10703
|
||||||
|
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
|
||||||
|
//log->log("RecPlayer", Log::DEBUG, "FILENAME: %s", fileName);
|
||||||
|
file = fopen(fileName, "r");
|
||||||
|
#else
|
||||||
|
snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), i);
|
||||||
|
file = fopen(fileName, "r");
|
||||||
|
if (!file) {
|
||||||
|
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
|
||||||
|
file = fopen(fileName, "r");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (!file) break;
|
||||||
|
|
||||||
|
segments[i] = new Segment();
|
||||||
|
segments[i]->start = totalLength;
|
||||||
|
fseek(file, 0, SEEK_END);
|
||||||
|
totalLength += ftell(file);
|
||||||
|
totalFrames = indexFile->Last();
|
||||||
|
//log->log("RecPlayer", Log::DEBUG, "File %i found, totalLength now %llu, numFrames = %lu", i, totalLength, totalFrames);
|
||||||
|
segments[i]->end = totalLength;
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
file = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecPlayer::~RecPlayer()
|
||||||
|
{
|
||||||
|
//log->log("RecPlayer", Log::DEBUG, "destructor");
|
||||||
|
int i = 1;
|
||||||
|
while(segments[i++]) delete segments[i];
|
||||||
|
if (file) fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
int RecPlayer::openFile(int index)
|
||||||
|
{
|
||||||
|
if (file) fclose(file);
|
||||||
|
|
||||||
|
char fileName[2048];
|
||||||
|
|
||||||
|
#if APIVERSNUM >= 10703
|
||||||
|
snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), index);
|
||||||
|
isyslog("openFile called for index %i string:%s", index, fileName);
|
||||||
|
|
||||||
|
file = fopen(fileName, "r");
|
||||||
|
if (file)
|
||||||
|
{
|
||||||
|
fileOpen = index;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), index);
|
||||||
|
isyslog("openFile called for index %i string:%s", index, fileName);
|
||||||
|
//log->log("RecPlayer", Log::DEBUG, "openFile called for index %i string:%s", index, fileName);
|
||||||
|
|
||||||
|
file = fopen(fileName, "r");
|
||||||
|
if (file)
|
||||||
|
{
|
||||||
|
fileOpen = index;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//log->log("RecPlayer", Log::DEBUG, "file failed to open");
|
||||||
|
fileOpen = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t RecPlayer::getLengthBytes()
|
||||||
|
{
|
||||||
|
return totalLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t RecPlayer::getLengthFrames()
|
||||||
|
{
|
||||||
|
return totalFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long RecPlayer::getBlock(unsigned char* buffer, uint64_t position, unsigned long amount)
|
||||||
|
{
|
||||||
|
if ((amount > totalLength) || (amount > 500000))
|
||||||
|
{
|
||||||
|
//log->log("RecPlayer", Log::DEBUG, "Amount %lu requested and rejected", amount);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position >= totalLength)
|
||||||
|
{
|
||||||
|
//log->log("RecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((position + amount) > totalLength)
|
||||||
|
{
|
||||||
|
//log->log("RecPlayer", Log::DEBUG, "Client asked for some data past the end of recording, adjusting amount");
|
||||||
|
amount = totalLength - position;
|
||||||
|
}
|
||||||
|
|
||||||
|
// work out what block position is in
|
||||||
|
int segmentNumber;
|
||||||
|
for(segmentNumber = 1; segmentNumber < 1000; segmentNumber++)
|
||||||
|
{
|
||||||
|
if ((position >= segments[segmentNumber]->start) && (position < segments[segmentNumber]->end)) break;
|
||||||
|
// position is in this block
|
||||||
|
}
|
||||||
|
|
||||||
|
// we could be seeking around
|
||||||
|
if (segmentNumber != fileOpen)
|
||||||
|
{
|
||||||
|
if (!openFile(segmentNumber)) return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t currentPosition = position;
|
||||||
|
uint32_t yetToGet = amount;
|
||||||
|
uint32_t got = 0;
|
||||||
|
uint32_t getFromThisSegment = 0;
|
||||||
|
uint32_t filePosition;
|
||||||
|
|
||||||
|
while(got < amount)
|
||||||
|
{
|
||||||
|
if (got)
|
||||||
|
{
|
||||||
|
// if(got) then we have already got some and we are back around
|
||||||
|
// advance the file pointer to the next file
|
||||||
|
if (!openFile(++segmentNumber)) return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// is the request completely in this block?
|
||||||
|
if ((currentPosition + yetToGet) <= segments[segmentNumber]->end)
|
||||||
|
getFromThisSegment = yetToGet;
|
||||||
|
else
|
||||||
|
getFromThisSegment = segments[segmentNumber]->end - currentPosition;
|
||||||
|
|
||||||
|
filePosition = currentPosition - segments[segmentNumber]->start;
|
||||||
|
fseek(file, filePosition, SEEK_SET);
|
||||||
|
if (fread(&buffer[got], getFromThisSegment, 1, file) != 1) return 0; // umm, big problem.
|
||||||
|
|
||||||
|
// Tell linux not to bother keeping the data in the FS cache
|
||||||
|
posix_fadvise(file->_fileno, filePosition, getFromThisSegment, POSIX_FADV_DONTNEED);
|
||||||
|
|
||||||
|
got += getFromThisSegment;
|
||||||
|
currentPosition += getFromThisSegment;
|
||||||
|
yetToGet -= getFromThisSegment;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPosition = position;
|
||||||
|
return got;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t RecPlayer::getLastPosition()
|
||||||
|
{
|
||||||
|
return lastPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
cRecording* RecPlayer::getCurrentRecording()
|
||||||
|
{
|
||||||
|
return recording;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t RecPlayer::positionFromFrameNumber(uint32_t frameNumber)
|
||||||
|
{
|
||||||
|
if (!indexFile) return 0;
|
||||||
|
|
||||||
|
#if VDRVERSNUM >= 10703
|
||||||
|
uint16_t retFileNumber;
|
||||||
|
off_t retFileOffset;
|
||||||
|
#else
|
||||||
|
uchar retFileNumber;
|
||||||
|
int retFileOffset;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!indexFile->Get((int)frameNumber, &retFileNumber, &retFileOffset))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// log->log("RecPlayer", Log::DEBUG, "FN: %u FO: %i", retFileNumber, retFileOffset);
|
||||||
|
if (!segments[retFileNumber]) return 0;
|
||||||
|
uint64_t position = segments[retFileNumber]->start + retFileOffset;
|
||||||
|
// log->log("RecPlayer", Log::DEBUG, "Pos: %llu", position);
|
||||||
|
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t RecPlayer::frameNumberFromPosition(uint64_t position)
|
||||||
|
{
|
||||||
|
if (!indexFile) return 0;
|
||||||
|
|
||||||
|
if (position >= totalLength)
|
||||||
|
{
|
||||||
|
//log->log("RecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t segmentNumber;
|
||||||
|
for(segmentNumber = 1; segmentNumber < 255; segmentNumber++)
|
||||||
|
{
|
||||||
|
if ((position >= segments[segmentNumber]->start) && (position < segments[segmentNumber]->end)) break;
|
||||||
|
// position is in this block
|
||||||
|
}
|
||||||
|
uint32_t askposition = position - segments[segmentNumber]->start;
|
||||||
|
return indexFile->Get((int)segmentNumber, askposition);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RecPlayer::getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength)
|
||||||
|
{
|
||||||
|
// 0 = backwards
|
||||||
|
// 1 = forwards
|
||||||
|
|
||||||
|
if (!indexFile) return false;
|
||||||
|
|
||||||
|
int iframeLength;
|
||||||
|
int indexReturnFrameNumber;
|
||||||
|
|
||||||
|
indexReturnFrameNumber = (uint32_t)indexFile->GetNextIFrame(frameNumber, (direction==1 ? true : false), NULL, NULL, &iframeLength);
|
||||||
|
//log->log("RecPlayer", Log::DEBUG, "GNIF input framenumber:%lu, direction=%lu, output:framenumber=%i, framelength=%i", frameNumber, direction, indexReturnFrameNumber, iframeLength);
|
||||||
|
|
||||||
|
if (indexReturnFrameNumber == -1) return false;
|
||||||
|
|
||||||
|
*rfilePosition = positionFromFrameNumber(indexReturnFrameNumber);
|
||||||
|
*rframeNumber = (uint32_t)indexReturnFrameNumber;
|
||||||
|
*rframeLength = (uint32_t)iframeLength;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
63
server/recplayer.h
Normal file
63
server/recplayer.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2004-2005 Chris Tallon
|
||||||
|
|
||||||
|
This file is part of VOMP.
|
||||||
|
|
||||||
|
VOMP is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
VOMP is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with VOMP; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RECPLAYER_H
|
||||||
|
#define RECPLAYER_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <vdr/recording.h>
|
||||||
|
|
||||||
|
#include "server/streamer.h"
|
||||||
|
|
||||||
|
class Segment
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint64_t start;
|
||||||
|
uint64_t end;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RecPlayer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RecPlayer(cRecording* rec);
|
||||||
|
~RecPlayer();
|
||||||
|
uint64_t getLengthBytes();
|
||||||
|
uint32_t getLengthFrames();
|
||||||
|
unsigned long getBlock(unsigned char* buffer, uint64_t position, unsigned long amount);
|
||||||
|
int openFile(int index);
|
||||||
|
uint64_t getLastPosition();
|
||||||
|
cRecording* getCurrentRecording();
|
||||||
|
void scan();
|
||||||
|
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);
|
||||||
|
|
||||||
|
private:
|
||||||
|
cRecording* recording;
|
||||||
|
cIndexFile* indexFile;
|
||||||
|
FILE* file;
|
||||||
|
int fileOpen;
|
||||||
|
Segment* segments[1000];
|
||||||
|
uint64_t totalLength;
|
||||||
|
uint64_t lastPosition;
|
||||||
|
uint32_t totalFrames;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* $Id: streamer.c,v 1.18 2009/02/13 10:39:22 schmirl Exp $
|
* $Id: streamer.c,v 1.19 2009/06/19 06:32:45 schmirl Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <vdr/ringbuffer.h>
|
#include <vdr/ringbuffer.h>
|
||||||
@ -14,6 +14,13 @@
|
|||||||
#include "tools/select.h"
|
#include "tools/select.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
// --- cStreamdevBuffer -------------------------------------------------------
|
||||||
|
|
||||||
|
cStreamdevBuffer::cStreamdevBuffer(int Size, int Margin, bool Statistics, const char *Description):
|
||||||
|
cRingBufferLinear(Size, Margin, Statistics, Description)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
// --- cStreamdevWriter -------------------------------------------------------
|
// --- cStreamdevWriter -------------------------------------------------------
|
||||||
|
|
||||||
cStreamdevWriter::cStreamdevWriter(cTBSocket *Socket,
|
cStreamdevWriter::cStreamdevWriter(cTBSocket *Socket,
|
||||||
@ -95,14 +102,13 @@ void cStreamdevWriter::Action(void)
|
|||||||
|
|
||||||
cStreamdevStreamer::cStreamdevStreamer(const char *Name):
|
cStreamdevStreamer::cStreamdevStreamer(const char *Name):
|
||||||
cThread(Name),
|
cThread(Name),
|
||||||
m_Running(false),
|
|
||||||
m_Writer(NULL),
|
m_Writer(NULL),
|
||||||
m_RingBuffer(new cRingBufferLinear(STREAMERBUFSIZE, TS_SIZE * 2,
|
m_RingBuffer(new cStreamdevBuffer(STREAMERBUFSIZE, TS_SIZE * 2,
|
||||||
true, "streamdev-streamer")),
|
true, "streamdev-streamer")),
|
||||||
m_SendBuffer(new cRingBufferLinear(WRITERBUFSIZE, TS_SIZE * 2))
|
m_SendBuffer(new cStreamdevBuffer(WRITERBUFSIZE, TS_SIZE * 2))
|
||||||
{
|
{
|
||||||
m_RingBuffer->SetTimeouts(0, 100);
|
m_RingBuffer->SetTimeouts(0, 100);
|
||||||
m_SendBuffer->SetTimeouts(0, 100);
|
m_SendBuffer->SetTimeouts(100, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
cStreamdevStreamer::~cStreamdevStreamer()
|
cStreamdevStreamer::~cStreamdevStreamer()
|
||||||
@ -116,7 +122,6 @@ void cStreamdevStreamer::Start(cTBSocket *Socket)
|
|||||||
{
|
{
|
||||||
Dprintf("start streamer\n");
|
Dprintf("start streamer\n");
|
||||||
m_Writer = new cStreamdevWriter(Socket, this);
|
m_Writer = new cStreamdevWriter(Socket, this);
|
||||||
m_Running = true;
|
|
||||||
Attach();
|
Attach();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,9 +140,8 @@ void cStreamdevStreamer::Stop(void)
|
|||||||
Dprintf("stopping streamer\n");
|
Dprintf("stopping streamer\n");
|
||||||
Cancel(3);
|
Cancel(3);
|
||||||
}
|
}
|
||||||
if (m_Running) {
|
if (m_Writer) {
|
||||||
Detach();
|
Detach();
|
||||||
m_Running = false;
|
|
||||||
DELETENULL(m_Writer);
|
DELETENULL(m_Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,8 +156,6 @@ void cStreamdevStreamer::Action(void)
|
|||||||
int count = Put(block, got);
|
int count = Put(block, got);
|
||||||
if (count)
|
if (count)
|
||||||
m_RingBuffer->Del(count);
|
m_RingBuffer->Del(count);
|
||||||
else
|
|
||||||
cCondWait::SleepMs(100);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* $Id: streamer.h,v 1.10 2009/02/13 10:39:22 schmirl Exp $
|
* $Id: streamer.h,v 1.11 2009/06/19 06:32:45 schmirl Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef VDR_STREAMDEV_STREAMER_H
|
#ifndef VDR_STREAMDEV_STREAMER_H
|
||||||
@ -16,8 +16,34 @@ class cStreamdevStreamer;
|
|||||||
#define TS_SIZE 188
|
#define TS_SIZE 188
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define STREAMERBUFSIZE MEGABYTE(4)
|
#define STREAMERBUFSIZE (20000 * TS_SIZE)
|
||||||
#define WRITERBUFSIZE KILOBYTE(256)
|
#define WRITERBUFSIZE (5000 * TS_SIZE)
|
||||||
|
|
||||||
|
// --- cStreamdevBuffer -------------------------------------------------------
|
||||||
|
|
||||||
|
class cStreamdevBuffer: public cRingBufferLinear {
|
||||||
|
public:
|
||||||
|
// make public
|
||||||
|
void WaitForPut(void) { cRingBuffer::WaitForPut(); }
|
||||||
|
// Always write complete TS packets
|
||||||
|
// (assumes Count is a multiple of TS_SIZE)
|
||||||
|
int PutTS(const uchar *Data, int Count);
|
||||||
|
cStreamdevBuffer(int Size, int Margin = 0, bool Statistics = false, const char *Description = NULL);
|
||||||
|
};
|
||||||
|
|
||||||
|
inline int cStreamdevBuffer::PutTS(const uchar *Data, int Count)
|
||||||
|
{
|
||||||
|
int free = Free();
|
||||||
|
if (free < Count)
|
||||||
|
Count = free;
|
||||||
|
|
||||||
|
Count -= Count % TS_SIZE;
|
||||||
|
if (Count)
|
||||||
|
Count = Put(Data, Count);
|
||||||
|
else
|
||||||
|
WaitForPut();
|
||||||
|
return Count;
|
||||||
|
}
|
||||||
|
|
||||||
// --- cStreamdevWriter -------------------------------------------------------
|
// --- cStreamdevWriter -------------------------------------------------------
|
||||||
|
|
||||||
@ -38,15 +64,14 @@ public:
|
|||||||
|
|
||||||
class cStreamdevStreamer: public cThread {
|
class cStreamdevStreamer: public cThread {
|
||||||
private:
|
private:
|
||||||
bool m_Running;
|
|
||||||
cStreamdevWriter *m_Writer;
|
cStreamdevWriter *m_Writer;
|
||||||
cRingBufferLinear *m_RingBuffer;
|
cStreamdevBuffer *m_RingBuffer;
|
||||||
cRingBufferLinear *m_SendBuffer;
|
cStreamdevBuffer *m_SendBuffer;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void Action(void);
|
virtual void Action(void);
|
||||||
|
|
||||||
bool IsRunning(void) const { return m_Running; }
|
bool IsRunning(void) const { return m_Writer; }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cStreamdevStreamer(const char *Name);
|
cStreamdevStreamer(const char *Name);
|
||||||
@ -57,10 +82,10 @@ public:
|
|||||||
bool Abort(void);
|
bool Abort(void);
|
||||||
|
|
||||||
void Activate(bool On);
|
void Activate(bool On);
|
||||||
int Receive(uchar *Data, int Length) { return m_RingBuffer->Put(Data, Length); }
|
int Receive(uchar *Data, int Length) { return m_RingBuffer->PutTS(Data, Length); }
|
||||||
void ReportOverflow(int Bytes) { m_RingBuffer->ReportOverflow(Bytes); }
|
void ReportOverflow(int Bytes) { m_RingBuffer->ReportOverflow(Bytes); }
|
||||||
|
|
||||||
virtual int Put(const uchar *Data, int Count) { return m_SendBuffer->Put(Data, Count); }
|
virtual int Put(const uchar *Data, int Count) { return m_SendBuffer->PutTS(Data, Count); }
|
||||||
virtual uchar *Get(int &Count) { return m_SendBuffer->Get(Count); }
|
virtual uchar *Get(int &Count) { return m_SendBuffer->Get(Count); }
|
||||||
virtual void Del(int Count) { m_SendBuffer->Del(Count); }
|
virtual void Del(int Count) { m_SendBuffer->Del(Count); }
|
||||||
|
|
||||||
|
@ -3,12 +3,11 @@
|
|||||||
*
|
*
|
||||||
* See the README file for copyright information and how to reach the author.
|
* See the README file for copyright information and how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: streamdev-server.c,v 1.11 2008/10/14 11:05:47 schmirl Exp $
|
* $Id: streamdev-server.c,v 1.12 2009/06/19 06:32:38 schmirl Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <vdr/tools.h>
|
#include <vdr/tools.h>
|
||||||
#include "remux/extern.h"
|
|
||||||
#include "streamdev-server.h"
|
#include "streamdev-server.h"
|
||||||
#include "server/setup.h"
|
#include "server/setup.h"
|
||||||
#include "server/server.h"
|
#include "server/server.h"
|
||||||
|
Loading…
Reference in New Issue
Block a user