diff options
author | randomdan <randomdan@localhost> | 2014-03-20 23:56:18 +0000 |
---|---|---|
committer | randomdan <randomdan@localhost> | 2014-03-20 23:56:18 +0000 |
commit | c6df1ea3d9f4ac3ffa85c3eefc3f30a84df6f8d7 (patch) | |
tree | f45b31a3ea600f6370200432256e1ba8147b4064 | |
parent | Remove the pointless maint functions for program map and program associations... (diff) | |
download | p2pvr-c6df1ea3d9f4ac3ffa85c3eefc3f30a84df6f8d7.tar.bz2 p2pvr-c6df1ea3d9f4ac3ffa85c3eefc3f30a84df6f8d7.tar.xz p2pvr-c6df1ea3d9f4ac3ffa85c3eefc3f30a84df6f8d7.zip |
Basic support for streaming services (slow, thanks ffmpeg) and recordings (assumes storage endpoint) through the web frontend - implemented as two project2 stream sources
-rw-r--r-- | p2pvr/.p2config | 2 | ||||
-rw-r--r-- | p2pvr/Jamfile.jam | 14 | ||||
-rw-r--r-- | p2pvr/daemon/recorder.cpp | 2 | ||||
-rw-r--r-- | p2pvr/daemon/storage.cpp | 44 | ||||
-rw-r--r-- | p2pvr/daemon/storage.h | 2 | ||||
-rw-r--r-- | p2pvr/ice/p2pvr.ice | 2 | ||||
-rw-r--r-- | p2pvr/lib/serviceStreamer.cpp | 12 | ||||
-rw-r--r-- | p2pvr/lib/serviceStreamer.h | 1 | ||||
-rw-r--r-- | p2pvr/p2comp/Jamfile.jam | 10 | ||||
-rw-r--r-- | p2pvr/p2comp/recordingStream.cpp | 80 | ||||
-rw-r--r-- | p2pvr/p2comp/serviceStream.cpp | 58 | ||||
-rw-r--r-- | p2pvr/p2comp/streamBase.cpp | 50 | ||||
-rw-r--r-- | p2pvr/p2comp/streamBase.h | 26 | ||||
-rw-r--r-- | p2pvr/p2comp/streamSinkWrapper.cpp | 28 | ||||
-rw-r--r-- | p2pvr/p2comp/streamSinkWrapper.h | 23 | ||||
-rw-r--r-- | p2pvr/streamer/Jamfile.jam | 7 | ||||
-rw-r--r-- | p2pvr/streamer/streamer.cpp | 52 | ||||
-rw-r--r-- | p2pvr/webfe/.htaccess | 12 | ||||
-rw-r--r-- | p2pvr/webfe/.p2config | 1 | ||||
l--------- | p2pvr/webfe/p2fcgi | 1 | ||||
-rw-r--r-- | p2pvr/webfe/present/stream/recording.xml | 8 | ||||
-rw-r--r-- | p2pvr/webfe/present/stream/service.xml | 8 |
22 files changed, 379 insertions, 64 deletions
diff --git a/p2pvr/.p2config b/p2pvr/.p2config index ddf998e..8e215ad 100644 --- a/p2pvr/.p2config +++ b/p2pvr/.p2config @@ -5,4 +5,4 @@ common.filelog.path = /tmp/p2daemon.log common.filelog.openmode = w common.consolelogLevel = 9 p2pvr.globaldevices.carddaemon = Devices:default -h defiant -p 10000 -p2pvr.storage.root = /tmp/p2pvr/recordings +p2pvr.storage.root = /var/store/p2pvr/recordings diff --git a/p2pvr/Jamfile.jam b/p2pvr/Jamfile.jam index 9a4baac..bee95cd 100644 --- a/p2pvr/Jamfile.jam +++ b/p2pvr/Jamfile.jam @@ -9,6 +9,10 @@ alias p2common : glibmm : : : <include>/usr/include/project2/lib <linkflags>"-lp2common" ; +alias p2cgi : glibmm : : : + <include>/usr/include/project2/cgi + <linkflags>"-lp2cgicommon" +; alias p2sql : glibmm : : : <include>/usr/include/project2/sql <linkflags>"-lp2sql" @@ -17,9 +21,13 @@ alias p2lib : glibmm : : : <include>/usr/include/project2/lib <linkflags>"-lp2lib" ; +alias p2streams : glibmm : : : + <include>/usr/include/project2/streams + <linkflags>"-lp2streams" +; alias p2ice : glibmm : : : <include>/usr/include/project2/ice - <linkflags>"-lp2ice" + <linkflags>"-lp2ice -lp2iceclient" ; alias p2daemonlib : glibmm : : : <cflags>"-I /usr/include/project2/daemon/lib" @@ -28,8 +36,8 @@ alias p2daemonlib : glibmm : : : build-project daemon ; build-project carddaemon ; -install debuginstall : dvb//p2pvrdvb devices//p2pvrdevices lib//p2pvrlib carddaemon//p2pvrcarddaemon daemonbase//p2pvrdaemonbase daemon//p2pvrdaemon ice//p2pvrice : <location>./testing ; -package.install install : : : carddaemon daemon ; +install debuginstall : p2comp//p2pvrp2comp dvb//p2pvrdvb devices//p2pvrdevices lib//p2pvrlib carddaemon//p2pvrcarddaemon daemonbase//p2pvrdaemonbase daemon//p2pvrdaemon ice//p2pvrice : <location>./testing ; +package.install install : : : p2comp carddaemon daemon ; import type ; import generators ; diff --git a/p2pvr/daemon/recorder.cpp b/p2pvr/daemon/recorder.cpp index bdd18c6..ccb975e 100644 --- a/p2pvr/daemon/recorder.cpp +++ b/p2pvr/daemon/recorder.cpp @@ -16,7 +16,7 @@ DECLARE_OPTIONS(Recorder, "P2PVR Recorder options") ("p2pvr.recorder.extension", Options::value(&extension, "mpg"), "File extension to save with (default: avi)") ("p2pvr.recorder.muxercommand", Options::value(&muxerCommand, "/usr/bin/ffmpeg -f mpegts -i - -f dvd -codec copy -"), - "File extension to save with (default: '/usr/bin/ffmpeg -f mpegts -i - -f dvd -codec copy -')") + "Command to perform TS->PS muxing (default: '/usr/bin/ffmpeg -f mpegts -i - -f dvd -codec copy -')") END_OPTIONS(Recorder); Recorder::Recorder(Ice::ObjectAdapterPtr a, IceUtil::TimerPtr t) : diff --git a/p2pvr/daemon/storage.cpp b/p2pvr/daemon/storage.cpp index 9d9345d..79280a1 100644 --- a/p2pvr/daemon/storage.cpp +++ b/p2pvr/daemon/storage.cpp @@ -273,3 +273,47 @@ Storage::DeleteFrom(const fs::path & path, const fs::path & from) } } +void +Storage::Send(const std::string & id, const P2PVR::RawDataClientPrx & target, Ice::Long start, Ice::Long len, const Ice::Current &) +{ + fs::path path = root / byAll / id; + path.replace_extension(FindExtension(id)); + auto fd = open(path.string().c_str(), O_RDONLY | O_LARGEFILE); + if (fd < 0) { + Logger()->messagebf(LOG_ERR, "%s: Failed to open file for reading at %s (%d:%s)", __PRETTY_FUNCTION__, + path, errno, strerror(errno)); + throw P2PVR::StorageException(path.string(), strerror(errno)); + } + lseek(fd, start, SEEK_SET); + auto end = std::min<off_t>(fs::file_size(path), start + len); + while (start < end) { + P2PVR::Data buf(16 * 1024); + auto r = read(fd, &buf.front(), std::min<size_t>(buf.size(), end - start)); + if (r < 0) { + Logger()->messagebf(LOG_ERR, "%s: Failed to read file %s (%d:%s)", __PRETTY_FUNCTION__, + path, errno, strerror(errno)); + close(fd); + throw P2PVR::StorageException(path.string(), strerror(errno)); + } + if (target->NewData(buf)) { + close(fd); + return; + } + start += r; + } + close(fd); +} + +Ice::Long +Storage::FileSize(const std::string & id, const Ice::Current &) +{ + fs::path path = root / byAll / id; + try { + path.replace_extension(FindExtension(id)); + return fs::file_size(path); + } + catch (...) { + throw P2PVR::StorageException(path.string(), "Couldn't get file size"); + } +} + diff --git a/p2pvr/daemon/storage.h b/p2pvr/daemon/storage.h index 093e00d..86bc76a 100644 --- a/p2pvr/daemon/storage.h +++ b/p2pvr/daemon/storage.h @@ -13,6 +13,8 @@ class Storage : public P2PVR::Storage { P2PVR::RawDataClientPrx OpenForWrite(const std::string &, const Ice::Current &); void Close(const P2PVR::RawDataClientPrx & file, const Ice::Current &); void Delete(const std::string &, const Ice::Current &); + Ice::Long FileSize(const std::string &, const Ice::Current &); + void Send(const std::string &, const P2PVR::RawDataClientPrx & target, Ice::Long start, Ice::Long len, const Ice::Current &); INITOPTIONS; diff --git a/p2pvr/ice/p2pvr.ice b/p2pvr/ice/p2pvr.ice index 08356ae..6b9d31b 100644 --- a/p2pvr/ice/p2pvr.ice +++ b/p2pvr/ice/p2pvr.ice @@ -66,6 +66,8 @@ module P2PVR { idempotent RawDataClient * OpenForWrite(string guid) throws StorageException; idempotent void Close(RawDataClient * file) throws StorageException; idempotent void Delete(string guid) throws StorageException; + idempotent long FileSize(string guid) throws StorageException; + void Send(string guid, RawDataClient * target, long start, long len) throws StorageException; }; interface Recordings { diff --git a/p2pvr/lib/serviceStreamer.cpp b/p2pvr/lib/serviceStreamer.cpp index f7a7dc2..b9de8fd 100644 --- a/p2pvr/lib/serviceStreamer.cpp +++ b/p2pvr/lib/serviceStreamer.cpp @@ -16,6 +16,18 @@ ServiceStreamer::ServiceStreamer(int sid, P2PVR::RawDataClientPrx t, const Ice:: { } +ServiceStreamer::ServiceStreamer(int sid, P2PVR::RawDataClientPrx t, const P2PVR::DevicesPrx & d, const P2PVR::SIPrx & s, const Ice::ObjectAdapterPtr & a) : + adapter(a), + devs(d), + si(s), + target(t), + patParser(adapter, new BindSiParserHandler<ProgramAssociationMapPtr, SiProgramAssociationParser>(boost::bind(&ServiceStreamer::HandlePAT, this, _1))), + pmtParser(adapter, new BindSiParserHandler<DVBSI::ProgramMapPtr, SiProgramMapParser>(boost::bind(&ServiceStreamer::HandlePMT, this, _1))), + serviceId(sid), + patHandle(0), pmtStream(0), pmtHandle(0), serviceHandle(0) +{ +} + ServiceStreamer::~ServiceStreamer() { } diff --git a/p2pvr/lib/serviceStreamer.h b/p2pvr/lib/serviceStreamer.h index bdabe7a..20ea8e2 100644 --- a/p2pvr/lib/serviceStreamer.h +++ b/p2pvr/lib/serviceStreamer.h @@ -12,6 +12,7 @@ class ServiceStreamer { public: ServiceStreamer(int sid, P2PVR::RawDataClientPrx, const Ice::CommunicatorPtr & ic, const Ice::ObjectAdapterPtr & a); + ServiceStreamer(int sid, P2PVR::RawDataClientPrx, const P2PVR::DevicesPrx & d, const P2PVR::SIPrx & s, const Ice::ObjectAdapterPtr & a); ~ServiceStreamer(); bool HandlePAT(ProgramAssociationMapPtr pam); diff --git a/p2pvr/p2comp/Jamfile.jam b/p2pvr/p2comp/Jamfile.jam new file mode 100644 index 0000000..6a847b7 --- /dev/null +++ b/p2pvr/p2comp/Jamfile.jam @@ -0,0 +1,10 @@ +lib p2pvrp2comp : + [ glob-tree *.cpp ] + : : + <library>../ice//p2pvrice + <library>../lib//p2pvrlib + <library>..//p2streams + <library>..//p2ice + <library>..//p2cgi + <implicit-dependency>../ice//p2pvrice + ; diff --git a/p2pvr/p2comp/recordingStream.cpp b/p2pvr/p2comp/recordingStream.cpp new file mode 100644 index 0000000..21e8684 --- /dev/null +++ b/p2pvr/p2comp/recordingStream.cpp @@ -0,0 +1,80 @@ +#include <p2pvr.h> +#include <options.h> +#include <misc.h> +#include <temporaryIceAdapterObject.h> +#include "streamSinkWrapper.h" +#include "streamBase.h" +#include <limits> +#include <cgiRequestContext.h> + +class RecordingStream : public StreamBase { + public: + RecordingStream(ScriptNodePtr p) : + StreamBase(p), + recording(p, "recording") + { + } + + void runStream(const Sink & sink, ExecContext * ec) const + { + auto storage = ice->GetProxy<P2PVR::StoragePrx>("Storage", ec); + assert(storage); + + std::promise<int> prom; + { + TemporarayIceAdapterObject<P2PVR::RawDataClient> output(adapter, new StreamSinkWrapper(sink, prom)); + + try { + auto range = getRange(ec); + if (!range) { + range = { std::string(), 0, (unsigned long long)(storage->FileSize(recording(ec)) - 1) }; + } + storage->Send(recording(ec), output, range->Start, range->End - range->Start + 1); + } + catch (const std::ios_base::failure &) { + } + } + } + + boost::optional<unsigned long long> getSizeInBytes(ExecContext * ec) const override + { + auto storage = ice->GetProxy<P2PVR::StoragePrx>("Storage", ec); + assert(storage); + return storage->FileSize(recording(ec)); + } + + bool isSeekable() const override + { + return true; + } + + boost::optional<RangeResponse> getRange(ExecContext * ec) const override + { + auto crc = dynamic_cast<CgiRequestContext *>(ec); + if (crc) { + auto range = crc->getRequestRange(); + if (range && range->Unit == "bytes") { + auto storage = ice->GetProxy<P2PVR::StoragePrx>("Storage", ec); + assert(storage); + auto size = storage->FileSize(recording(ec)); + if (!range->Start) { + range->Start = 0; + } + if (!range->End) { + range->End = size - 1; + } + else { + range->End = std::min<RangePos>(*range->End, size - 1); + } + return (RangeResponse){ std::string("bytes"), *range->Start, *range->End }; + } + } + return boost::optional<RangeResponse>(); + } + + private: + Variable recording; +}; + +DECLARE_LOADER("p2pvrrecordingstream", RecordingStream); + diff --git a/p2pvr/p2comp/serviceStream.cpp b/p2pvr/p2comp/serviceStream.cpp new file mode 100644 index 0000000..aedb4ad --- /dev/null +++ b/p2pvr/p2comp/serviceStream.cpp @@ -0,0 +1,58 @@ +#include <commonObjects.h> +#include <iceDataSource.h> +#include <serviceStreamer.h> +#include <options.h> +#include <misc.h> +#include <muxer.h> +#include <future> +#include <temporaryIceAdapterObject.h> +#include "streamSinkWrapper.h" +#include "streamBase.h" + +class ServiceStream : public StreamBase { + public: + ServiceStream(ScriptNodePtr p) : + StreamBase(p), + serviceId(p, "serviceId") + { + } + + void runStream(const Sink & sink, ExecContext * ec) const + { + auto devices = ice->GetProxy<P2PVR::DevicesPrx>("Devices", ec); + assert(devices); + auto si = ice->GetProxy<P2PVR::SIPrx>("SI", ec); + assert(si); + + std::promise<int> prom; + { + TemporarayIceAdapterObject<P2PVR::RawDataClient> output(adapter, new StreamSinkWrapper(sink, prom)); + { + TemporarayIceAdapterObject<P2PVR::RawDataClient> muxer(adapter, new Muxer(output, muxerCommand)); + + ServiceStreamerPtr ss(new ServiceStreamer(serviceId(ec), muxer, devices, si, adapter)); + + ss->Start(); + prom.get_future().get(); + ss->Stop(); + } + } + } + + INITOPTIONS; + + private: + Variable serviceId; + + static std::string muxerCommand; +}; + +DECLARE_OPTIONS(ServiceStream, "P2PVR Live Stream options") +("p2pvr.livestream.muxercommand", Options::value(&muxerCommand, "/usr/bin/ffmpeg -f mpegts -i - -f dvd -codec copy -"), + "Command to perform TS->PS muxing (default: '/usr/bin/ffmpeg -f mpegts -i - -f dvd -codec copy -')") +END_OPTIONS(ServiceStream); + +std::string ServiceStream::muxerCommand; + +DECLARE_LOADER("p2pvrservicestream", ServiceStream); + diff --git a/p2pvr/p2comp/streamBase.cpp b/p2pvr/p2comp/streamBase.cpp new file mode 100644 index 0000000..2ed4954 --- /dev/null +++ b/p2pvr/p2comp/streamBase.cpp @@ -0,0 +1,50 @@ +#include "streamBase.h" +#include <logger.h> +#include <Ice/ObjectAdapter.h> +#include <commonObjects.h> +#include <exception> + +StreamBase::StreamBase(ScriptNodePtr p) : + Stream(p), + dataSource(p, "datasource") +{ +} + +StreamBase::~StreamBase() +{ + adapter->deactivate(); + adapter->destroy(); +} + +void +StreamBase::loadComplete(const CommonObjects * co) +{ + ice = co->dataSource<IceDataSource>(dataSource(NULL)); + + Ice::CommunicatorPtr ic(ice->GetCommunicator()); + for (int port = startPort; !adapter && port <= endPort; ++port) { + try { + adapter = ic->createObjectAdapterWithEndpoints(std::string(), stringbf("default -p %d", port)); + adapter->activate(); + Logger()->messagebf(LOG_INFO, "%s: adapter activated on port %d", __PRETTY_FUNCTION__, port); + } + catch (...) { + adapter = NULL; + } + } + if (!adapter) { + Logger()->messagebf(LOG_ERR, "%s: Failed to bind to a free port in the configured range (%d - %d)", + __PRETTY_FUNCTION__, startPort, endPort); + throw std::runtime_error("Failed to bind to a free port"); + } +} + +DECLARE_OPTIONS(StreamBase, "P2PVR Stream Client options") +("p2pvr.stream.startport", Options::value(&startPort, 10001), + "Start of port range for ICE adapter when receiving streams") +("p2pvr.stream.endport", Options::value(&endPort, 11000), + "End of port range for ICE adapter when receiving streams") +END_OPTIONS(StreamBase); + +int StreamBase::startPort; +int StreamBase::endPort; diff --git a/p2pvr/p2comp/streamBase.h b/p2pvr/p2comp/streamBase.h new file mode 100644 index 0000000..279625b --- /dev/null +++ b/p2pvr/p2comp/streamBase.h @@ -0,0 +1,26 @@ +#ifndef STREAMBASE_H +#define STREAMBASE_H + +#include <iceDataSource.h> +#include <stream.h> +#include <options.h> + +class StreamBase : public Stream { + public: + StreamBase(ScriptNodePtr p); + ~StreamBase(); + + void loadComplete(const CommonObjects * co) override; + + INITOPTIONS; + protected: + Variable dataSource; + const IceDataSource * ice; + Ice::ObjectAdapterPtr adapter; + + static int startPort; + static int endPort; +}; + +#endif + diff --git a/p2pvr/p2comp/streamSinkWrapper.cpp b/p2pvr/p2comp/streamSinkWrapper.cpp new file mode 100644 index 0000000..3dd3d2b --- /dev/null +++ b/p2pvr/p2comp/streamSinkWrapper.cpp @@ -0,0 +1,28 @@ +#include "streamSinkWrapper.h" + +#define LOCK(lock) std::lock_guard<std::mutex> _lock##__LINE_NO__##lock(lock); + +StreamSinkWrapper::StreamSinkWrapper(const Stream::Sink & s, std::promise<int> & p) : + sink(s), + promise(p), + completed(false) +{ +} + +bool +StreamSinkWrapper::NewData(const P2PVR::Data & data, const Ice::Current &) +{ + try { + sink(reinterpret_cast<const char *>(&data.front()), data.size()); + return false; + } + catch (const std::ios_base::failure &) { + LOCK(lock) { + if (completed) return true; + completed = true; + promise.set_value(0); + return true; + } + } +} + diff --git a/p2pvr/p2comp/streamSinkWrapper.h b/p2pvr/p2comp/streamSinkWrapper.h new file mode 100644 index 0000000..0cbf915 --- /dev/null +++ b/p2pvr/p2comp/streamSinkWrapper.h @@ -0,0 +1,23 @@ +#ifndef STREAMSINKWRAPPER +#define STREAMSINKWRAPPER + +#include <stream.h> +#include <dvb.h> +#include <future> +#include <mutex> + +class StreamSinkWrapper : public P2PVR::RawDataClient { + public: + StreamSinkWrapper(const Stream::Sink & s, std::promise<int> & p); + + bool NewData(const P2PVR::Data & data, const Ice::Current &) override; + + private: + const Stream::Sink & sink; + std::promise<int> & promise; + bool completed; + std::mutex lock; +}; + +#endif + diff --git a/p2pvr/streamer/Jamfile.jam b/p2pvr/streamer/Jamfile.jam deleted file mode 100644 index d17e9b2..0000000 --- a/p2pvr/streamer/Jamfile.jam +++ /dev/null @@ -1,7 +0,0 @@ -lib streamer : - [ glob-tree *.cpp ] - : : - <library>../ice//p2pvrice - <library>../lib//p2pvrlib - <implicit-dependency>../ice//p2pvrice - ; diff --git a/p2pvr/streamer/streamer.cpp b/p2pvr/streamer/streamer.cpp deleted file mode 100644 index 2722266..0000000 --- a/p2pvr/streamer/streamer.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include <daemonBase.h> -#include <p2pvr.h> -#include "globalDevices.h" -#include "si.h" -#include <serviceStreamer.h> -#include <fileSink.h> -#include <muxer.h> - -class P2PvrStreamer : public DaemonBase { - public: - P2PvrStreamer(int argc, char ** argv) : - DaemonBase(argc, argv) - { - } - - void addServants(const Ice::ObjectAdapterPtr & adapter, const IceUtil::TimerPtr &) const - { - adapter->add(new GlobalDevices(), ic->stringToIdentity("GlobalDevices")); - adapter->add(new SI(), ic->stringToIdentity("SI")); - auto output = P2PVR::RawDataClientPrx::checkedCast(adapter->addWithUUID(new FileSink(1))); - assert(output); - auto muxer = P2PVR::RawDataClientPrx::checkedCast(adapter->addWithUUID(new Muxer(output, "/usr/bin/ffmpeg -f mpegts -i - -f dvd -codec copy -"))); - assert(muxer); - ss = ServiceStreamerPtr(new ServiceStreamer(4287, muxer, ic, adapter)); - assert(ss); - } - - void run() const - { - IceUtil::TimerPtr timer = new IceUtil::Timer(); - Logger()->messagebf(LOG_INFO, "Creating adapter (%s, %s)", Adapter, Endpoint); - auto adapter = ic->createObjectAdapterWithEndpoints(Adapter, Endpoint); - addServants(adapter, timer); - adapter->activate(); - - ss->Start(); - - ic->waitForShutdown(); - timer->destroy(); - } - - void shutdown() const - { - ss->Stop(); - DaemonBase::shutdown(); - } - private: - mutable ServiceStreamerPtr ss; -}; - -DECLARE_GENERIC_LOADER("p2pvrstreamer", DaemonLoader, P2PvrStreamer); - diff --git a/p2pvr/webfe/.htaccess b/p2pvr/webfe/.htaccess new file mode 100644 index 0000000..1588083 --- /dev/null +++ b/p2pvr/webfe/.htaccess @@ -0,0 +1,12 @@ +<Files "p2fcgi"> + sethandler fcgid-script +</Files> + +RewriteEngine on +RewriteCond %{REQUEST_URI} !^/css/ +RewriteCond %{REQUEST_URI} !^/js/ +RewriteCond %{REQUEST_URI} !^/img/ +RewriteCond %{REQUEST_URI} !^/[^/]+\.[^/]+$ +RewriteCond %{REQUEST_URI} !^/p2fcgi +RewriteRule ^(.*) /p2fcgi/\1 [L] + diff --git a/p2pvr/webfe/.p2config b/p2pvr/webfe/.p2config index f5ea06f..db4692d 100644 --- a/p2pvr/webfe/.p2config +++ b/p2pvr/webfe/.p2config @@ -1,5 +1,6 @@ ice.client.sliceclient = ice/common.ice library = libp2pvrice.so +library = libp2pvrp2comp.so ice.client.sliceclient = ice/dvbsi.ice ice.client.sliceclient = ice/dvb.ice ice.client.sliceclient = ice/p2pvr.ice diff --git a/p2pvr/webfe/p2fcgi b/p2pvr/webfe/p2fcgi new file mode 120000 index 0000000..b0e5628 --- /dev/null +++ b/p2pvr/webfe/p2fcgi @@ -0,0 +1 @@ +/usr/bin/p2fcgi
\ No newline at end of file diff --git a/p2pvr/webfe/present/stream/recording.xml b/p2pvr/webfe/present/stream/recording.xml new file mode 100644 index 0000000..fc7a1b0 --- /dev/null +++ b/p2pvr/webfe/present/stream/recording.xml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<view xmlns:project2="http://project2.randomdan.homeip.net"> + <project2:stream contenttype="video/mpeg"> + <project2:p2pvrrecordingstream datasource="p2pvr"> + <recording source="uri" index="2" /> + </project2:p2pvrrecordingstream> + </project2:stream> +</view> diff --git a/p2pvr/webfe/present/stream/service.xml b/p2pvr/webfe/present/stream/service.xml new file mode 100644 index 0000000..8587636 --- /dev/null +++ b/p2pvr/webfe/present/stream/service.xml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<view xmlns:project2="http://project2.randomdan.homeip.net"> + <project2:stream contenttype="video/mpeg"> + <project2:p2pvrservicestream datasource="p2pvr"> + <serviceId source="uri" index="2" /> + </project2:p2pvrservicestream> + </project2:stream> +</view> |