115 Commits

Author SHA1 Message Date
Frank Schmirler
7175d7de91 Updated README 2011-11-27 11:51:26 +01:00
Frank Schmirler
94aef85adc Moved "closing connection" log message to overload of cTBSocket::Close() in
cServerConnection.
2011-11-27 11:32:16 +01:00
Frank Schmirler
9b91301d94 Don't keep a pointer to the connection in components MulticastGroup
structure as the connection may now be deleted from outside via menu.
2011-11-25 00:24:37 +01:00
Frank Schmirler
7347e24123 Fixed missing Display() call after disconnecting a client. 2011-11-24 23:45:59 +01:00
Frank Schmirler
c652e8fa81 Added server menu with list of clients. Connections can be terminated
with the "red" key. The former main menu action of suspending live TV
moved to the "blue" key.
2011-11-22 01:16:42 +01:00
Frank Schmirler
ce1583a756 Code cleanup and optimization (thanks to Ville Skytt) 2011-11-21 13:03:06 +01:00
Frank Schmirler
20c931ac6d Optimized iterator. Preincrement saves a temporary copy of the object (thanks
to Ville Skytt)
2011-11-21 12:51:30 +01:00
Frank Schmirler
f9224d0618 properly shutdown IGMP timeout handler thread when the plugin is stopped.
Fixes occasional segfaults on VDR exit.
2011-11-13 23:27:46 +01:00
Frank Schmirler
30674fb6c8 fixed memory leak in libdvbmpeg read_pes (fixes #769) 2011-11-12 22:31:39 +01:00
Frank Schmirler
8850e63da5 dropped several unused functions in libdvbmpeg 2011-11-12 22:28:27 +01:00
Frank Schmirler
6c9c6ca77f restricted VTP command RENR to liemikuutio patch < 1.32 (fixes #786).
Build fails with newer versions of this patch. RENR was introduced for XBMC
which is no longer using streamdev at all, so the impact should be low.
2011-11-07 17:15:10 +01:00
Frank Schmirler
9cb72968f6 updated outdated COPYING file and FSF address (fixes #767, fixes #768) 2011-11-07 16:57:23 +01:00
Frank Schmirler
b9d2f66295 include SDT and TDT in TS streams 2011-10-25 17:18:23 +02:00
Frank Schmirler
e0e14bb322 Added notice that sysctl commands for max number of IGMP groups has to be
issued before starting VDR.
2011-10-25 12:30:05 +02:00
Frank Schmirler
077398fdef Revert "dropped cServerConnection::m_Pending"
Required by VTP

This reverts commit bdaea38b86.
2011-10-24 08:44:31 +02:00
Frank Schmirler
bdaea38b86 dropped cServerConnection::m_Pending 2011-10-20 23:45:44 +02:00
Frank Schmirler
6b633dbfa2 the icy-name HTTP header sent with radio streams makes VLC pick the wrong
demuxer. Send icy-name only for ES audio streams (fixes #746)
2011-10-20 23:37:27 +02:00
Frank Schmirler
615f101b9d fixed regression of "live TV must be switched in VDR main thread" change:
deadlock in IGMP streaming server when switching live TV.

Previously cComponentIGMP::Accept did all the work including the channel
switch with the new cConnectionIGMP waiting for MainThreadHook. But as the
new connection is stored *after* Accept, MainThreadHook didn't see it and
so wasn't able to switch. The streamdev main thread waited forever.

Moved the main work into cComponentIGMP::Welcome.
2011-10-18 08:50:54 +02:00
Frank Schmirler
2fae067cfe streamdev-client returns true in its AvoidRecording() method 2011-09-10 00:08:04 +02:00
Frank Schmirler
475e7bbd6a updated device selection to code of VDR 1.7.19 2011-09-10 00:04:08 +02:00
Frank Schmirler
4c0888d483 adaption to VDR 1.7.12 cReceiver API change 2011-09-09 23:26:45 +02:00
Frank Schmirler
24a1638ddd increased WRITERBUFSIZE. Has been reported to fix some ringbuffer overflows
(thanks to Lubo Doleel)
2011-09-02 13:18:01 +02:00
Frank Schmirler
cc74cba396 check availability of channel if VTP command TUNE is called without prior
PROV call (e.g. client side EPG scan)
2011-09-02 13:11:13 +02:00
Frank Schmirler
2c8377d42a added support for VDR 1.7.19 SignalStrength/SignalQuality 2011-07-06 09:13:50 +02:00
Frank Schmirler
673fbae2e2 analog video channels use the same transponder and pid for different
channels, so streamdev-client must always issue TUNE command.

Thanks to Martin Dauskardt for providing the technical background related
to pvrinput.
2011-06-27 13:07:05 +02:00
Frank Schmirler
f73665a0bd Server must close the VTP connection also if filter stream is broken
Otherwise, if a client which is not livestreaming (e.g. watching a recording)
doesn't properly shutdown the port 2004 connection, the filter stream remains
open until TCP keepalive takes care of it. In the meantime, the log will show
ringbuffer overflows.
2011-06-03 13:24:12 +02:00
Frank Schmirler
f6299b4584 fixed missing #ifdefs for new NumProvidedSystems setup option 2011-05-16 08:46:17 +02:00
frank
09e0a0176b - new externremux.sh mencoder config options: audio pid by language code
(-alang) and verbosity (-msglevel) (thanks to Pekko Tiitto)
2011-05-02 23:39:19 +02:00
Frank Schmirler
19e8cf2a28 writer must not spend too much time waiting in select() without checking
if the thread has been cancelled.

Delayed the streamdev main thread for up to 3 seconds. As the command timeou
of VTP connections is 1.5 seconds, one hanging connection could have caused
VTP connections to die.
2011-04-11 13:56:49 +02:00
Frank Schmirler
17bb6390f9 Merge branch 'master' of projects.vdr-developer.org:vdr-plugin-streamdev
Conflicts:
	HISTORY
2011-03-18 15:56:34 +01:00
Frank Schmirler
58a4f3dbe5 Added Spanish translation 2011-03-18 15:52:42 +01:00
Frank Schmirler
1a933d6cc9 Merge branch 'switch_livetv'
Conflicts:
	HISTORY
	server/connection.c
2011-03-07 23:06:27 +01:00
Frank Schmirler
c06970123c Live TV must be switched in VDR main thread (fixes #568). 2011-03-07 23:04:18 +01:00
Frank Schmirler
2cb0ba6cf1 Dropped compatibility with VDR < 1.5.16 2011-03-07 22:05:36 +01:00
Frank Schmirler
ee7929bd39 Deleted obsolete section about static priority 2011-02-16 08:59:12 +01:00
Frank Schmirler
dc41eecffd return value of streamdev-clients cDevice::NumProvidedSystems() now
configurable in plugin setup (fixes #552)
2011-02-16 08:57:05 +01:00
Frank Schmirler
30930d5c74 Release 0.5.1 2011-02-11 12:01:58 +01:00
Frank Schmirler
3a2936a140 updated copy of GetClippedNumProvidedSystems to the version used since VDR 1.7.15 2011-02-01 14:24:47 +01:00
Frank Schmirler
06d5418e42 Fixed the code deciding if a device is in use for live TV or not. It did
not work as expected for FF cards (fixes #536)
2011-01-28 12:42:33 +01:00
Frank Schmirler
0783799826 increased client side timeout for TUNE command 2011-01-27 16:15:25 +01:00
Frank Schmirler
316a1f884f connection runs as thread, so it must not use Skins.Message 2011-01-14 17:03:17 +01:00
Frank Schmirler
9fbc166848 Improved channel switch code
- Avoid device allows more efficient code when trying to move live TV to different card
- Added some more dsyslog messages to help debugging channel switch issues
2010-12-14 13:22:55 +01:00
Frank Schmirler
abf8a62fe9 make sure that a client doesn't interrupt replaying on server's FF card
http://www.vdr-portal.de/board/thread.php?postid=948311 (reported by wtor)
2010-12-10 18:06:39 +01:00
Frank Schmirler
a35675490d switching away live TV failed even when "always suspended"
The vdr main loop usually switches back to the previous channel at
"Make sure we have a visible programme in case device usage has changed"
(#472)
2010-12-10 17:03:04 +01:00
Frank Schmirler
0c07109974 fixed regression: no receiver created for ES/PS/PES
(reported by Gavin Hamill)
2010-12-10 16:38:54 +01:00
Frank Schmirler
6ea5efe939 Snapshot 2010-09-15 2011-03-22 21:16:18 +02:00
schmirl
0a860a1e3e don't use std::map.at(). It's not available in older libstdc++ version
Modified Files:
	CONTRIBUTORS HISTORY remux/extern.c server/connectionHTTP.c
2010-07-22 14:18:17 +00:00
schmirl
c7d19619db fixed extremux x264 using value of ABR for VBR 2010-07-22 06:30:46 +00:00
schmirl
c0a7f1610a fixed wrong URL path in m3u playlists 2010-07-20 12:26:29 +00:00
schmirl
edc3ad1c8e - set externremux.sh executable in distribution archive
- externremux quality value should be wlan54, not wlan45
2010-07-20 06:23:32 +00:00
schmirl
ebacf759ab dropped "Synchronize EPG" feature
Modified Files:
	HISTORY README streamdev-client.c streamdev-client.h
	client/device.c client/setup.c client/setup.h client/socket.c
	client/socket.h po/de_DE.po po/fi_FI.po po/fr_FR.po
	po/it_IT.po po/lt_LT.po po/ru_RU.po po/sk_SK.po
2010-06-08 05:55:16 +00:00
schmirl
1a1141e4a1 Switched to proper tsplay-0.2 patch detection
Modified Files:
	server/recplayer.c Makefile CONTRIBUTORS HISTORY
2010-06-04 18:32:34 +00:00
schmirl
7517b874d8 added compatibility with VDR 1.6 tsplay-0.1 patch 2010-05-14 06:23:59 +00:00
schmirl
6f984b87e3 Added support for EnhancedAC3 2010-04-19 10:17:33 +00:00
schmirl
8a128f7c57 fixed a memory leak in cStreamdevPatFilter::GetPid (thanks to lhanisch) 2010-02-20 23:02:10 +00:00
schmirl
2f027a6ffc - length -1 is the correct value for streams in M3U playlists 2010-02-20 22:18:14 +00:00
schmirl
dd7cc838ad switching between two encrypted channels on the same transponder didn't
always work (thanks to sk8ter@vdrportal)
2010-02-17 12:39:02 +00:00
schmirl
93c9aa9af0 added DELT FORCE option to delete running timers (#554) 2010-01-29 12:03:01 +00:00
schmirl
c882a991cc added VDR 1.7.11 parental rating support for VTP LSTE command (#555) 2010-01-29 11:44:52 +00:00
schmirl
b44c9ac3f5 added Lithuanian translation (thanks to Valdemaras Pipiras) 2009-12-15 13:38:29 +00:00
schmirl
b3c72deaed fixed missing virtual destructor for cTSRemux 2009-12-03 07:26:13 +00:00
schmirl
d794d83854 added defines for large file support to Makefile as required by VDR 1.7.4+ 2009-11-04 11:12:20 +00:00
schmirl
b5bd22e7bd Added Slovak translations (thanks to Milan Hrala) 2009-11-03 11:21:14 +00:00
schmirl
e99ea00348 fixed regression from fix for switching between encrypted channels. It was
no longer possible to receive multiple (FTA) streams from the same
transponder
2009-10-19 06:19:10 +00:00
schmirl
73e30fd5ca silenced warnings concerning asprintf (requested by Rolf Ahrenberg)
Modified Files:
	CONTRIBUTORS HISTORY server/connectionVTP.c server/setup.c
2009-10-13 06:38:47 +00:00
schmirl
ac40edfd24 don't update recordings list on CmdPLAY (reported by BBlack) 2009-09-30 10:10:40 +00:00
schmirl
6da7cf90d9 Added missing trNOOPs 2009-09-18 10:50:44 +00:00
schmirl
1cf41fb16f - cleaned up common.h / common.c
- dropped cStreamdevMenuSetupPage
2009-09-18 10:41:41 +00:00
schmirl
6fb88f8d99 report charset in HTTP replies (suggested by Rolf Ahrenberg) 2009-09-15 10:39:17 +00:00
schmirl
0e9e22c23d use SO_KEEPALIVE option on all sockets do detect dead sockets 2009-09-04 13:24:30 +00:00
schmirl
1474cdf799 Fixed sysctl command 2009-09-04 13:19:39 +00:00
schmirl
4ab256caea enable PatFilter for externremux, so VLC can be used as remuxer or client 2009-08-05 09:33:13 +00:00
schmirl
4f3a6aae88 attributed wrong patch name 2009-07-17 07:05:40 +00:00
schmirl
d5fa0a49f8 fixed insecure format strings in LSTX handlers 2009-07-17 06:21:23 +00:00
schmirl
8469a4d894 Updated Finish translations (thanks to Rolf Ahrenberg) 2009-07-07 10:47:35 +00:00
schmirl
41763e7710 removed redefinitions in includes - caused problems in older compilers 2009-07-06 06:23:28 +00:00
schmirl
c8cf217c01 fixed ts2ps.h defines 2009-07-06 06:14:20 +00:00
schmirl
5644d3c03f fixed missing virtual for cTS2PESRemux destructor 2009-07-06 06:11:11 +00:00
schmirl
fe77c5dccb silenced format mismatch warning on 64bit OS 2009-07-03 21:44:18 +00:00
schmirl
dd09bfbb45 improved PARENTALRATING patch detection (thanks to Rolf Ahrenberg) 2009-07-02 06:03:50 +00:00
schmirl
460d5f0689 Missing files from previous commit
Added Files:
	server/recplayer.c server/recplayer.h
2009-07-01 11:00:49 +00:00
schmirl
052a94db5a added XBMC support by extending VTP capabilities (thanks to Alwin Esch)
Modified Files:
	CONTRIBUTORS HISTORY Makefile common.h server/connectionVTP.c
	server/connectionVTP.h
2009-07-01 10:46:15 +00:00
schmirl
7b8e396f77 now there's a common baseclass for all remuxers, make use of it
Modified Files:
	HISTORY remux/ts2pes.c remux/ts2pes.h remux/tsremux.h
	server/livestreamer.c server/livestreamer.h
2009-06-30 06:04:33 +00:00
schmirl
64bf3e5ecf Removed unnecessary namespace qualifier 2009-06-29 06:23:33 +00:00
schmirl
44a71ffe8a added cDevice::NumProvidedSystems() which was introduced in VDR 1.7.0
Modified Files:
	HISTORY client/device.h
2009-06-23 10:26:54 +00:00
schmirl
008e7c8510 - 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

Modified Files:
	CONTRIBUTORS HISTORY Makefile common.c common.h
	streamdev-server.c libdvbmpeg/transform.h remux/extern.c
	remux/extern.h remux/ts2es.c remux/ts2es.h remux/ts2ps.c
	remux/ts2ps.h remux/tsremux.c remux/tsremux.h
	server/connectionHTTP.c server/connectionVTP.c
	server/livestreamer.c server/livestreamer.h server/menuHTTP.c
	server/streamer.c server/streamer.h
Added Files:
	remux/ts2pes.c remux/ts2pes.h
2009-06-19 06:32:38 +00:00
schmirl
64ff2c08be Added missing call to StopSectionHandler().
This method was introduced in VDR 1.5.15. Its absence could cause crashes
when shutting down VDR
2009-04-06 06:48:59 +00:00
schmirl
3d16ba0840 added comments to indicate that the VTP filter stream is proprietary format
Modified Files:
	client/filter.c server/livefilter.c
2009-02-13 13:02:39 +00:00
schmirl
78410ea576 Added IGMP multicast server
Modified Files:
	CONTRIBUTORS HISTORY Makefile README po/de_DE.po po/fi_FI.po
	po/fr_FR.po po/it_IT.po po/ru_RU.po server/component.c
	server/component.h server/connection.c server/connection.h
	server/livefilter.c server/server.c server/setup.c
	server/setup.h server/streamer.c server/streamer.h
	streamdev/streamdevhosts.conf tools/socket.c tools/socket.h
Added Files:
	patches/vdr-cap_net_raw.diff server/componentIGMP.c
	server/componentIGMP.h server/connectionIGMP.c
	server/connectionIGMP.h
2009-02-13 10:39:20 +00:00
schmirl
c26b89f9c2 ignore trailing blank lines in HTTP requests 2009-02-13 07:02:18 +00:00
schmirl
37f2adf02b Fixed parsing Min/MaxPriority from config 2009-02-03 10:26:21 +00:00
schmirl
479d5c4b48 - Updated Finnish translation
- Reordered entries in de and fr
2009-02-02 11:51:42 +00:00
schmirl
0e0b4b587d Added min/max priority (#508)
Modified Files:
	HISTORY README client/device.c client/setup.c client/setup.h
	po/de_DE.po po/fi_FI.po po/fr_FR.po po/it_IT.po po/ru_RU.po
2009-01-29 07:48:58 +00:00
schmirl
9af6ceb007 Commented PES for vdr 1.7.3+
Modified Files:
	HISTORY common.c common.h remux/ts2ps.h remux/tsremux.h
	server/connectionHTTP.c server/connectionVTP.c
	server/livestreamer.c server/livestreamer.h server/menuHTTP.c
	server/setup.c
2009-01-16 11:35:43 +00:00
schmirl
41cf7a5848 Credits for "fix to switch encrypted channel fix" 2009-01-14 07:46:40 +00:00
schmirl
30aa3b0610 Problem when switching between encrypted channels still exists if CA id
is set to FTA or streamdev device. Disabled transponder check.
2009-01-14 07:35:51 +00:00
schmirl
ca043780a7 added Network Media Tank browser support to HTML pages (#494) 2008-12-08 11:37:21 +00:00
schmirl
cd8d7fbd6c Compatiblity to Network Media Tank (#496)
- minor fixes of PAT repacker
- repack and send every PAT packet we receive
2008-11-24 12:10:27 +00:00
schmirl
459e41e810 - fixed null pointer in server.c when cConnection::Accept() failes 2008-10-31 12:19:57 +00:00
schmirl
7184adef83 consider Pids from channels.conf when HTTP TS streaming. Section filtering
is an optional feature for VDR devices, so we must not rely on the PMT
alone (#473)
2008-10-31 11:59:48 +00:00
schmirl
ac2e992305 Improved externremux script termination (#455) 2008-10-31 11:41:12 +00:00
schmirl
c364a3396d - use cThread::Running()/Active() instead of private members
- replaced the last usleep by cCondWait
thanks to Rolf Ahrenberg (#383)
Modified Files:
	CONTRIBUTORS HISTORY server/server.c server/server.h
	server/streamer.c server/streamer.h server/suspend.c
	server/suspend.h
2008-10-22 11:59:31 +00:00
schmirl
4a5af4f489 - fixed output format of some debug messages (thanks to Rolf Ahrenberg) 2008-10-22 11:17:43 +00:00
schmirl
86c82c1381 - added HTTP authentication (#475)
Modified Files:
	HISTORY README streamdev-server.c server/connection.h
	server/connectionHTTP.c server/connectionHTTP.h
	server/server.c server/server.h
2008-10-14 11:05:46 +00:00
schmirl
992444cb67 Compatibility to VDR 1.7.1 (#483) 2008-10-13 11:30:05 +00:00
schmirl
c5b5a4cbeb Update 2008-10-02 07:21:15 +00:00
schmirl
42474b8431 Added vdr-1.6.0-intcamdevices.patch (#429)
Modified Files:
	HISTORY README client/device.h
Added Files:
	patches/vdr-1.6.0-intcamdevices.patch
2008-10-02 07:14:47 +00:00
schmirl
924cf6e4d0 Workaround for problem when switching from one encrypted channel to an other
Yet it's not quite clear if this is really a proper fix. There have been
contradictory reports (#476).
2008-10-02 06:56:36 +00:00
schmirl
90bae5b011 - added preprocessor directive for ancient gcc 2008-07-16 06:00:48 +00:00
schmirl
9ecbab8882 - added Russian translation (thanks to Oleg Roitburd) 2008-06-26 14:16:56 +00:00
schmirl
685f43c6ec - Fixed assignment of externremux.sh's default location
cPlugin::ConfigDirectory() cannot be used directly after the plugin has
been loaded. The return value of AddDirectory() must be allocated.
2008-04-29 07:00:53 +00:00
schmirl
b2bea54b34 - added French translation (thanks to micky979) 2008-04-14 13:42:40 +00:00
schmirl
ecbde41090 Added Italian translation (thanks to Diego Pierotto) 2008-04-14 07:14:24 +00:00
schmirl
dc796dfe74 Fixed German translations 2008-04-14 07:14:08 +00:00
schmirl
34a62eb2ef - added gettext support (thanks to Rolf Ahrenberg)
- added vdr-1.6.0-ignore_missing_cam patch
- dropped obsolete respect_ca patch
- removed legacy code for < VDR 1.5.9 (thanks to Rolf Ahrenberg)
2008-04-08 14:18:15 +00:00
97 changed files with 8282 additions and 2771 deletions

View File

@@ -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
@@ -8,14 +12,14 @@ The Metzler Brothers
as a lot of code has been taken from their libdvbmpeg package as a lot of code has been taken from their libdvbmpeg package
Angelus (DOm) Angelus (DOm)
for providing italian language texts for providing Italian language texts
for reporting problems with the Elchi-Patch for reporting problems with the Elchi-Patch
Michal Michal
for sending a patch to select the HTTP streamtype via remote for sending a patch to select the HTTP streamtype via remote
Rolf Ahrenberg Rolf Ahrenberg
for providing finnish language texts for providing Finnish language texts
for adding externremux.sh commandline parameter for adding externremux.sh commandline parameter
for silencing compiler warnings for silencing compiler warnings
for adding PAT, PMT, PCR and EIT to HTTP TS streams for adding PAT, PMT, PCR and EIT to HTTP TS streams
@@ -25,6 +29,16 @@ Rolf Ahrenberg
for a TS PAT repacker based on Petri Laine's VDR TS recording patch for a TS PAT repacker based on Petri Laine's VDR TS recording patch
for making it possible to pass parameters to externremux.sh for making it possible to pass parameters to externremux.sh
for removing pre VDR 1.4 legacy code for removing pre VDR 1.4 legacy code
for adding gettext support
for fixing output format of some debug messages
for replacing private members by cThread::Running()/Active()
for improving externremux script termination
for fixing PAT repacker version field
for improving LIMIKUUTIO and PARENTALRATING patch detection
for suggesting to include the charset in HTTP replies
for requesting replacement of asprintf calls
for suggesting to change the URL path from EXTERN to EXT
for suggesting increased thread priorities for cStreamdevWriter/Streamer
Rantanen Teemu Rantanen Teemu
for providing vdr-incompletesections.diff for providing vdr-incompletesections.diff
@@ -43,6 +57,8 @@ Udo Richter
for fixing streamdev-server shutdown for fixing streamdev-server shutdown
for speeding up cPluginStreamdevServer::Active() for speeding up cPluginStreamdevServer::Active()
for adapting to VDR 1.5.0 API for adapting to VDR 1.5.0 API
for adapting to VDR 1.7.1
for proper tsplay-0.2 patch detection
greenman greenman
for reporting that the log could get flooded on connection failures. for reporting that the log could get flooded on connection failures.
@@ -68,9 +84,117 @@ 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
tobi tobi
for pointing to unused files in the libdvbmpeg directory for pointing to unused files in the libdvbmpeg directory
Diego Pierotto
for providing Italian language texts
micky979
for providing French language texts
Tiroler
for reporting a problem when switching between encrypted channels
Pixelpeter
for an initial fix to the "switching between ecncrypted channels" problem
Anssi Hannula
for the vdr-1.6.0-intcamdevices patch
for fixing insecure format strings in LSTX handlers
wirbel
for pointing out that section filtering is optional for VDR devices
for reporting a problem with Makefile defines in VDR 1.7.4+
Jori Hamalainen
for extensive testing while making stream compatible to Network Media Tank
for adding Network Media Tank browser support to HTML pages
owagner
for pointing out a problem with the encrypted channel switching fix
for suggesting use of SO_KEEPALIVE socket option to detect dead sockets
Joachim König-Baltes
for fixing Min/MaxPriority parsing
Artem Makhutov
for suggesting and heavy testing IGMP based multicast streaming
Alwin Esch
for adding XBMC support by extending VTP capabilities
for adding VDR 1.7.11 parental rating support for VTP LSTE command
for adding the DELT FORCE option to delete running timers
BBlack
for reporting that updating recordings list on CmdPLAY is a bad idea
Milan Hrala
for providing Slovak language texts
Valdemaras Pipiras
for providing Lithuanian language texts
sk8ter
for fixing failures when switching between two encrypted channels
lhanisch
for fixing a memory leak in cStreamdevPatFilter::GetPid
Eric Valette
for adding support for EnhancedAC3
carel
for reporting "plugin doesn't honor APIVERSION" error in new Makefile
for helping to find a way to cleanly shutdown externremux with mencoder
for reporting that GetClippedNumProvidedSystems is no longer up-to-date
wolfi.m
for reporting a typo in externremux quality parameter value
Norman Thiel
for reporting a wrong URL path in m3u playlists
vel_tins
for reporting that externremux x264 uses value of ABR for VBR
for various suggestions to improve externremux.sh
Matthias Prill
for reporting a compiler error with older libstdc++ versions
Timothy D. Lenz
for reporting missing support for invisible channel groups in HTTP menu
Rainer Blickle
for reporting that channel switches may interrupt live TV on the server
Gavin Hamill
for reporting that ES/PS/PES no longer works
Michal Novotny
for reporting that switching away live TV fails when "always suspended"
wtor
for reporting that a client may interrupt replaying on FF cards
for helping to debug channel switch issues on FF cards
Javier Bradineras
for providing Spanish language texts
Pekko Tiitto
for providing a git mirror of streamdev's lost CVS repository
for suggesting to use mencoder params -alang and -msglevel in externremux
Lubo¨ Dole¸el
for suggesting higher buffer sizes to fix some ringbuffer overflows
Ville Skyttä
for updating the outdated COPYING file and FSF address
for restricting VTP command RENR to liemikuutio patch < 1.32
for fixing memory and filedescriptor leaks in libdvbmpeg
for code cleanup and optimization

39
COPYING
View File

@@ -1,12 +1,12 @@
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
Version 2, June 1991 Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc. Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed. of this license document, but changing it is not allowed.
Preamble Preamble
The licenses for most software are designed to take away your The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public freedom to share and change it. By contrast, the GNU General Public
@@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to the GNU Lesser General Public License instead.) You can apply it to
your programs, too. your programs, too.
When we speak of free software, we are referring to freedom, not When we speak of free software, we are referring to freedom, not
@@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and The precise terms and conditions for copying, distribution and
modification follow. modification follow.
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains 0. This License applies to any program or other work which contains
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
License. (Exception: if the Program itself is interactive but License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on does not normally print such an announcement, your work based on
the Program is not required to print an announcement.) the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program, identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in and can be reasonably considered independent and separate works in
@@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not distribution of the source code, even though third parties are not
compelled to copy the source along with the object code. compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program 4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is otherwise to copy, modify, sublicense or distribute the Program is
@@ -225,7 +225,7 @@ impose that choice.
This section is intended to make thoroughly clear what is believed to This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License. be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in 8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License original copyright holder who places the Program under this License
@@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally. of promoting the sharing and reuse of software generally.
NO WARRANTY NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
@@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES. POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it possible use to the public, the best way to achieve this is to make it
@@ -303,10 +303,9 @@ the "copyright" line and a pointer to where the full notice is found.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License along
along with this program; if not, write to the Free Software with this program; if not, write to the Free Software Foundation, Inc.,
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail. Also add information on how to contact you by electronic and paper mail.
@@ -336,5 +335,5 @@ necessary. Here is a sample; alter the names:
This General Public License does not permit incorporating your program into This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. Public License instead of this License.

210
HISTORY
View File

@@ -1,6 +1,216 @@
VDR Plugin 'streamdev' Revision History VDR Plugin 'streamdev' Revision History
--------------------------------------- ---------------------------------------
- code cleanup and optimization (thanks to Ville Skyttä)
- properly shutdown IGMP timeout handler thread when the plugin is stopped.
Fixes occasional segfaults on VDR exit.
- fixed memory leak in libdvbmpeg read_pes (thanks to Ville Skyttä)
- dropped several unused functions in libdvbmpeg
- restricted VTP command RENR to liemikuutio patch < 1.32. Build fails with
newer versions of this patch (thanks to Ville Skyttä)
- updated outdated COPYING file and FSF address (thanks to Ville Skyttä)
- include SDT and TDT in TS streams
- the icy-name HTTP header sent with radio streams makes VLC pick the wrong
demuxer. Send icy-name only for ES audio streams.
- fixed regression of "live TV must be switched in VDR main thread" change:
deadlock in IGMP streaming server when switching live TV.
- streamdev-client returns true in its AvoidRecording() method introduced
with VDR 1.7.19. Note however that the impact of NumProvidedSystems is
higher.
- updated device selection to code of VDR 1.7.19
- adaption to VDR 1.7.12 cReceiver API change
- increased WRITERBUFSIZE. Has been reported to fix some ringbuffer
overflows (thanks to Lubo¨ Dole¸el)
- check availability of channel if VTP command TUNE is called without
prior PROV call (e.g. client side EPG scan)
- added support for VDR 1.7.19 SignalStrength/SignalQuality
- analog video channels use the same transponder and pid for different
channels, so streamdev-client must always issue TUNE command
- server must close the VTP connection also if filter stream is broken
- fixed missing #ifdefs for new NumProvidedSystems setup option
- new externremux.sh mencoder config options: audio pid by language code
(-alang) and verbosity (-msglevel) (thanks to Pekko Tiitto)
- writer must not spend too much time waiting in select() without checking
if the thread has been cancelled
- added Spanish translation (thanks to Javier Bradineras)
- live TV must be switched in VDR main thread
- dropped compatibility with VDR < 1.5.16
- return value of streamdev-clients cDevice::NumProvidedSystems() now
configurable in plugin setup
2011-02-11: Version 0.5.1
- updated copy of GetClippedNumProvidedSystems to the version used since
VDR 1.7.15 (reported by carel@vdrportal)
- fixed the code deciding if a device is in use for live TV or not. It did
not work as expected for FF cards (reported by wtor@vdrportal)
- increased client side timeout for TUNE command
- more dsyslog messages to help troubleshouting channel switch issues
- improved the channel switch code trying to move live TV to different card
- make sure that a client doesn't interrupt replaying on server's FF card
(reported by wtor@vdrportal)
- switching away live TV failed even when "always suspended" (reported by
Michal Novotny)
- fixed regression: no receiver created for ES/PS/PES (reported by Gavin
Hamill)
- VTP no longer uses a static priority value for its server-side receivers.
The server stores channel and priority requested with the PROV command and
re-uses these values in a subsequent TUNE for the same channel. The new
PRIO command is used to update the receiver's priority if necessary.
- added parameter HEIGHT to externremux.sh
- fixed syslog messages reporting local instead of remote IP and port
- fixed regression of the GetDevice(...) change. Filter streaming to clients
with a recent VDR version no longer worked.
- log an error if externremux.sh is missing or not executable
- since VDR 1.5.0 cDevice::GetDevice(...) is no longer a query only method.
It detaches all receivers of the device it returns. So it is no longer
suitable for testing the availability of a device. Added a copy of VDR's
cDevice::GetDevice(...) without the detach receivers part as a workaround
until a better solution is available
- added dsyslog messages to help troubleshouting channel switch issues
- VTP command SUSP didn't attach the player to the primary device
- fixed incompatibilities with older make versions
- replacing a connections receiver is now an atomic operation. Solves
stuttering audio/video due to lost TS packets when adding/removing PIDs
- disabled attribute warn_unused_result in libdvbmpeg
- slightly increased thread priorities of cStreamdevWriter/Streamer
(suggested by Rolf Ahrenberg)
- fixed missing support for invisible channel groups (groups without name)
in HTTP menu (reported by Timothy D. Lenz)
- don't quote actual program call in externremux.sh, so you can run the
program through e.g. nice or taskset just by extending the variable
which holds the program name
- in externremux.sh each mencoder audio and video codec has a dedicated
variable for a default option string now. Still you can override each
default option with an URL parameter
- externremux.sh mencoder now uses scale parameter with negative height
instead of -xy for scaling (suggested by vel_tins@vdrportal)
- added FPS (frames per second) parameter to externremux.sh (suggested by
vel_tins@vdrportal)
- don't use std::map.at(). It's not available in older libstdc++ version
(reported by Matthias Prill)
- fixed extremux x264 using value of ABR for VBR (thanks to vel_tins@vdrportal)
2010-07-20: Version 0.5.0b
- fixed wrong URL path in m3u playlists (reported by Norman Thiel)
2010-07-20: Version 0.5.0a
- set externremux.sh executable in distribution archive
- externremux quality value should be wlan54, not wlan45 (reported by
wolfi.m@vdrportal)
2010-07-19: Version 0.5.0
- using SIGINT in externremux to kill mencoder works better than SIGTERM;
especially x264 still needs a SIGKILL sometimes
- added --remove-destination to cp commands installing plugins
- fixed "plugin doesn't honor APIVERSION" (reported by carel@vdrportal)
- updated Italian translation (thanks to Diego Pierotto)
- config option "client may suspend" hidden if not applicable
- updated and enhanced README
- separated language resources of client and server
- restructured build process
- added support for HTTP method HEAD
- rewrite of externremux.sh, including support for various URL parameters,
logging and improved shutdown
- start externremux script in a separate process group
- changed HTTP URL path for externremux from EXTERN to EXT (suggested by
Rolf Ahrenberg)
- HTTP headers now have to be emitted by externremux script
- pass channel related information and URL parameters to externremux script
through environment
- implement CGI like interface for externremux script
- dropped "Synchronize EPG" feature. Please use epgsync-plugin instead
(available from http://vdr.schmirler.de)
- proper tsplay-0.2 patch detection. tsplay-0.1 is no longer recognized
(thanks to Udo Richter)
- added compatibility with VDR 1.6 tsplay-0.1 patch
- added support for EnhancedAC3 (thanks to Eric Valette)
- fixed a memory leak in cStreamdevPatFilter::GetPid (thanks to lhanisch)
- length -1 is the correct value for streams in M3U playlists
- switching between two encrypted channels on the same transponder didn't
always work (thanks to sk8ter@vdrportal)
- added DELT FORCE option to delete running timers (thanks to Alwin Esch)
- added VDR 1.7.11 parental rating support for VTP LSTE command (thanks to
Alwin Esch)
- added Lithuanian translation (thanks to Valdemaras Pipiras)
- fixed missing virtual destructor for cTSRemux
- added defines for large file support to Makefile as required by VDR 1.7.4+
(reported by wirbel@vdrportal)
- added Slovak translation (thanks to Milan Hrala)
- fixed regression from fix for switching between encrypted channels. It was
no longer possible to receive multiple (FTA) streams from the same
transponder
- silenced warnings concerning asprintf (requested by Rolf Ahrenberg)
- don't update recordings list on CmdPLAY (reported by BBlack)
- cleaned up common.h / common.c
- dropped cStreamdevMenuSetupPage
- report charset in HTTP replies (suggested by Rolf Ahrenberg)
- use SO_KEEPALIVE option on all sockets do detect dead sockets (thanks to
owagner)
- enable PatFilter for externremux, so VLC can be used as remuxer or client
- fixed insecure format strings in LSTX handlers (thanks to Anssi Hannula)
- updated Finish translation (thanks to Rolf Ahrenberg)
- removed redefinitions in includes - caused problems in older compilers
- fixed ts2ps.h defines
- fixed missing virtual for cTS2PESRemux destructor
- silenced format mismatch warning on 64bit OS
- 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
shutting down VDR
- added IGMP based multicast streaming
- ignore trailing blank lines in HTTP requests
- fixed parsing Min/MaxPriority from config (thanks to Joachim König-Baltes)
- updated Finnish translation (thanks to Rolf Ahrenberg)
- added Min/MaxPriority parameters. Can be used to keep client VDR from
using streamdev e.g. when recording
- disabled PES for VDR 1.7.3+
- added Network Media Tank browser support to HTML pages (thanks to Jori
Hamalainen)
- minor fixes of PAT repacker
- repack and send every PAT packet we receive
- fixed null pointer in server.c when cConnection::Accept() failes
- consider Pids from channels.conf when HTTP TS streaming. Section filtering
is an optional feature for VDR devices, so we must not rely on the PMT
alone (pointed out by wirbel@vdrportal)
- improved externremux script termination (thanks to Rolf Ahrenberg)
- use cThread::Running()/Active() instead of private members (thanks to
Rolf Ahrenberg)
- fixed output format of some debug messages (thanks to Rolf Ahrenberg)
- added HTTP authentication
- compatibility for VDR 1.7.1 (thanks to Udo Richter)
- added vdr-1.6.0-intcamdevices.patch (thanks to Anssi Hannula)
- fixed problem when switching from one encrypted channel to an other
(reported by Tiroler@vdrportal, initial bugfix by pixelpeter@vdrportal,
another fix by owagner@vdrportal)
- added preprocessor directive for ancient gcc
- added Russian translation (thanks to Oleg Roitburd)
- fixed assignment of externremux.sh's default location (reported by plautze)
- added French translation (thanks to micky979)
- added Italian translation (thanks to Diego Pierotto)
- added gettext support (thanks to Rolf Ahrenberg)
- added vdr-1.6.0-ignore_missing_cam patch
- dropped obsolete respect_ca patch
- removed legacy code for < VDR 1.5.9 (thanks to Rolf Ahrenberg)
2008-04-07: Branched v0_4 2008-04-07: Branched v0_4
- changed location of streamdevhosts.conf to VDRCONFDIR/plugins/streamdev - changed location of streamdevhosts.conf to VDRCONFDIR/plugins/streamdev

129
Makefile
View File

@@ -1,36 +1,59 @@
# #
# Makefile for a Video Disk Recorder plugin # Makefile for a Video Disk Recorder plugin
# #
# $Id: Makefile,v 1.15 2008/04/07 14:50:32 schmirl Exp $ # $Id: Makefile,v 1.23 2010/08/02 10:36:59 schmirl Exp $
# The official name of this plugin. # The main source file name.
# 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 = streamdev PLUGIN = streamdev
### The C/C++ compiler and options:
CC ?= gcc
CFLAGS ?= -g -O2 -Wall
CXX ?= g++
CXXFLAGS ?= -g -O2 -Wall -Woverloaded-virtual -Wno-parentheses
### The version number of this plugin (taken from the main source file): ### The version number of this plugin (taken from the main source file):
VERSION = $(shell grep 'const char \*VERSION *=' common.c | awk '{ print $$5 }' | sed -e 's/[";]//g') VERSION = $(shell grep 'const char \*VERSION *=' common.c | awk '{ print $$5 }' | sed -e 's/[";]//g')
### The C++ compiler and options:
CXX ?= g++
CXXFLAGS ?= -fPIC -g -O2 -Wall -Woverloaded-virtual -Wno-parentheses
### The directory environment: ### The directory environment:
VDRDIR = ../../.. VDRDIR = ../../..
LIBDIR = ../../lib LIBDIR = ../../lib
TMPDIR = /tmp TMPDIR = /tmp
### Allow user defined options to overwrite defaults:
-include $(VDRDIR)/Make.config
### The version number of VDR (taken from VDR's "config.h"): ### The version number of VDR (taken from VDR's "config.h"):
APIVERSION = $(shell grep 'define APIVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g') APIVERSION = $(shell grep 'define APIVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
APIVERSNUM = $(shell grep 'define APIVERSNUM ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
TSPLAYVERSNUM = $(shell grep 'define TSPLAY_PATCH_VERSION ' $(VDRDIR)/device.h | awk '{ print $$3 }')
### Allow user defined options to overwrite defaults:
ifeq ($(shell test $(APIVERSNUM) -ge 10713; echo $$?),0)
include $(VDRDIR)/Make.global
else
ifeq ($(shell test $(APIVERSNUM) -ge 10704 -o -n "$(TSPLAYVERSNUM)" ; echo $$?),0)
DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE
CFLAGS += -fPIC
CXXFLAGS += -fPIC
else
CFLAGS += -fPIC
CXXFLAGS += -fPIC
endif
endif
-include $(VDRDIR)/Make.config
### export all vars for sub-makes, using absolute paths
VDRDIR := $(shell cd $(VDRDIR) >/dev/null 2>&1 && pwd)
LIBDIR := $(shell cd $(LIBDIR) >/dev/null 2>&1 && pwd)
export
unexport PLUGIN
### The name of the distribution archive: ### The name of the distribution archive:
@@ -39,74 +62,35 @@ PACKAGE = vdr-$(ARCHIVE)
### Includes and Defines (add further entries here): ### Includes and Defines (add further entries here):
INCLUDES += -I$(VDRDIR)/include -I. INCLUDES += -I$(VDRDIR)/include -I..
DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' DEFINES += -D_GNU_SOURCE
### The object files (add further files here):
COMMONOBJS = common.o i18n.o \
\
tools/source.o tools/select.o tools/socket.o tools/tools.o
CLIENTOBJS = $(PLUGIN)-client.o \
\
client/socket.o client/device.o client/setup.o \
client/filter.o
SERVEROBJS = $(PLUGIN)-server.o \
\
server/server.o server/connectionVTP.o server/connectionHTTP.o \
server/componentHTTP.o server/componentVTP.o server/connection.o \
server/component.o server/suspend.o server/setup.o server/streamer.o \
server/livestreamer.o server/livefilter.o server/menuHTTP.o \
\
remux/tsremux.o remux/ts2ps.o remux/ts2es.o remux/extern.o
ifdef DEBUG ifdef DEBUG
DEFINES += -DDEBUG DEFINES += -DDEBUG
endif
ifdef STREAMDEV_DEBUG
DEFINES += -DDEBUG
endif endif
### The main target: ### The main target:
.PHONY: all dist clean .PHONY: all client server dist clean
all: libvdr-$(PLUGIN)-client.so libvdr-$(PLUGIN)-server.so all: client server
### Implicit rules:
%.o: %.c
$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
# Dependencies:
MAKEDEP = $(CXX) -MM -MG
DEPFILE = .dependencies
ifdef GCC3
$(DEPFILE): Makefile
@rm -f $@
@for i in $(CLIENTOBJS:%.o=%.c) $(SERVEROBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) ; do \
$(MAKEDEP) $(DEFINES) $(INCLUDES) -MT "`dirname $$i`/`basename $$i .c`.o" $$i >>$@ ; \
done
else
$(DEPFILE): Makefile
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(CLIENTOBJS:%.o=%.c) $(SERVEROBJS:%.o=%.c) \
$(COMMONOBJS:%.o=%.c) > $@
endif
-include $(DEPFILE)
### Targets: ### Targets:
libdvbmpeg/libdvbmpegtools.a: libdvbmpeg/*.c libdvbmpeg/*.h client:
$(MAKE) -C ./libdvbmpeg libdvbmpegtools.a $(MAKE) -C ./tools
$(MAKE) -C ./client
# installs to $(LIBDIR)/libvdr-streamdev-client.so.$(APIVERSION)
libvdr-$(PLUGIN)-client.so: $(CLIENTOBJS) $(COMMONOBJS) libdvbmpeg/libdvbmpegtools.a server:
libvdr-$(PLUGIN)-server.so: $(SERVEROBJS) $(COMMONOBJS) libdvbmpeg/libdvbmpegtools.a $(MAKE) -C ./tools
$(MAKE) -C ./libdvbmpeg
%.so: $(MAKE) -C ./remux
$(CXX) $(CXXFLAGS) -shared $^ -o $@ $(MAKE) -C ./server
@cp $@ $(LIBDIR)/$@.$(APIVERSION) # installs to $(LIBDIR)/libvdr-streamdev-server.so.$(APIVERSION)
dist: clean dist: clean
@-rm -rf $(TMPDIR)/$(ARCHIVE) @-rm -rf $(TMPDIR)/$(ARCHIVE)
@@ -117,5 +101,8 @@ dist: clean
@echo Distribution package created as $(PACKAGE).tgz @echo Distribution package created as $(PACKAGE).tgz
clean: clean:
@-rm -f $(COMMONOBJS) $(CLIENTOBJS) $(SERVEROBJS) $(DEPFILE) *.so *.tgz core* *~ $(MAKE) -C ./tools clean
$(MAKE) -C ./libdvbmpeg clean $(MAKE) -C ./libdvbmpeg clean
$(MAKE) -C ./remux clean
$(MAKE) -C ./client clean
$(MAKE) -C ./server clean

371
README
View File

@@ -15,18 +15,20 @@ Contents:
1. Description 1. Description
2. Installation 2. Installation
2.1 VDR 1.3.X and older 2.1 Compatibility
2.2 VDR 1.4.X and above 2.2 Compiling
2.3 Updating from streamdev 0.3.x 2.3 Updating
3. Usage 3. Usage
3.1 Usage HTTP server 3.1 Usage HTTP server
3.2 Usage VDR-to-VDR server 3.2 Usage IGMP multicast server
3.3 Usage VDR-to-VDR client 3.3 Usage VDR-to-VDR server
3.4 Usage VDR-to-VDR client
4. Other useful Plugins 4. Other useful Plugins
4.1 Plugins for VDR-to-VDR clients 4.1 Plugins for VDR-to-VDR clients
4.2 Plugins for Server 4.2 Plugins for Server
4.3 Alternatives 4.3 Alternatives
5. Known Problems 5. externremux.sh
6. Known Problems
1. Description: 1. Description:
@@ -61,7 +63,7 @@ the PROTOCOL file.
2. Installation: 2. Installation:
---------------- ----------------
Let's say streamdev's version is 0.4.0 and vdr's version is 1.X.X. If you Let's say streamdev's version is 0.5.0 and vdr's version is 1.X.X. If you
use anything else please exchange the version numbers appropriately (this use anything else please exchange the version numbers appropriately (this
way I don't have to update this section all the times;) ). way I don't have to update this section all the times;) ).
@@ -78,56 +80,93 @@ If you want to drive additional Input-Devices (with different sources) on the
client, you can merge the channels.conf files. VDR will detect if the local client, you can merge the channels.conf files. VDR will detect if the local
device or the network device can receive the channels. device or the network device can receive the channels.
Last, but not least you have to copy the streamdev folder into the Last, but not least you have to copy the streamdev-server folder into the
"plugins/streamdev" subfolder of VDR's config-directory (which is equal to your "plugins/streamdev-server" subfolder of VDR's config-directory (which is equal
video-directory if not specified otherwise). For example, if you didn't specify to your video-directory if not specified otherwise). For example, if you didn't
a separate config-directory, and specified your video directory as "/video0", specify a separate config-directory, and set your video directory to "/video0",
the directory has to be copied to /video0/plugins/streamdev. the directory has to be copied to /video0/plugins/streamdev-server.
The directory contains a file named streamdevhosts.conf which you must adjust The directory contains a file named streamdevhosts.conf which you must adjust
to your needs. The syntax is the same as for svdrphosts.conf, so please consult to your needs. The syntax is the same as for svdrphosts.conf, so please consult
VDR's documentation on how to fill that file, if you can't do it on-the-fly. VDR's documentation on how to fill that file, if you can't do it on-the-fly.
There's also a sample externremux.sh script in this directory. It is used by There's also a sample externremux.sh script in this directory. It is used by
streamdev's external remux feature. The sample script uses mencoder. Please streamdev's external remux feature. The sample script uses mencoder by default.
check the script for further information. You can specify a different script Please check the script for further information. You can specify a different
location with the -r parameter. The VDR commandline would then include a script location with the -r parameter. The VDR commandline would then include a
"-P 'streamdev-server -r /usr/local/bin/remux.sh'". Note the additional quotes, "-P 'streamdev-server -r /usr/local/bin/remux.sh'". Note the additional quotes,
as otherwise -r will be passed to VDR and not to streamdev. as otherwise -r will be passed to VDR and not to streamdev.
2.1 VDR 1.3.X and older: 2.1 Compatibility:
------------------------ ------------------
This version is not compatible to VDR releases older than 1.4.0. You will This version is not compatible to VDR releases older than 1.5.9. Take one of
probably need one of the streamdev-0.3.x releases. the streamdev-0.4.x releases if you are running at least VDR 1.4.x. For older
VDRs you will probably need one of the streamdev-0.3.x releases.
2.2 VDR 1.4.X and above: 2.2 Compiling:
------------------------ --------------
cd vdr-1.X.X/PLUGINS/src cd vdr-1.X.X/PLUGINS/src
tar xvfz vdr-streamdev-0.4.0.tgz tar xvfz vdr-streamdev-0.5.0.tgz
ln -s streamdev-0.4.0 streamdev ln -s streamdev-0.5.0 streamdev
cp -r streamdev/streamdev VDRCONFDIR/plugins/ cp -r streamdev/streamdev-server VDRCONFDIR/plugins/
cd ../.. cd ../..
make [options, if necessary] vdr make [options, if necessary] vdr
make [options, if necessary] plugins make [options, if necessary] plugins
2.3 Updating from streamdev 0.3.x To build only the plugin, change into the streamdev source folder and issue
---------------------------------- make
Starting with streamdev 0.4.0, all additional files are kept in a directory To build only streamdev-server or only streamdev-client, use
called "streamdev" inside VDR's plugin config directory. This affects in make server
particular the file "streamdevhosts.conf". You will have to move it to its make client
2.3 Updating:
--------------
If you are updating streamdev from an earlier release, you might have to
perform some additional steps. Check which version you've been running before,
then read below for the necessary changes.
* Location of files:
--------------------
(Affected: 0.3.x, 0.4.x, 0.4.0pre, 0.5.0pre)
Starting with streamdev 0.5.0, all additional files are kept in a directory
called "streamdev-server" inside VDR's plugin config directory. It is the new
default location of externremux.sh and the new place where streamdev-server
expects the file "streamdevhosts.conf". You will have to move this file to its
new location: new location:
mv VDRCONFDIR/plugins/streamdevhosts.conf VDRCONFDIR/plugins/streamdev/ streamdev 0.3.x:
mv VDRCONFDIR/plugins/streamdevhosts.conf VDRCONFDIR/plugins/streamdev-server/
(Directory VDRCONFDIR/plugins/streamdev already exists, as you copied the streamdev 0.4.x, 0.4.0pre and 0.5.0pre:
whole folder from the sources directory as suggested above, right?) mv VDRCONFDIR/plugins/streamdev VDRCONFDIR/plugins/streamdev-server/
The new default location for externremux.sh is also in this directory. Now check the contents of streamdevhosts.conf. Does it contain a "0.0.0.0/0"
entry? If your VDR machine is connected to the Internet, this line gives
*anyone* full access to streamdev, unless you took some other measures to
prevent this (e.g. firewall). You might want to remove this line and enable
HTTP authentication instead.
* Handling of externremux script:
---------------------------------
(Affected: 0.3.x, 0.4.0pre, 0.5.0pre)
Streamdev server's externremux script became responsible for emitting all HTTP
headers. A quick and dirty extension to your current script would be:
echo -ne 'Content-type: video/mpeg\r\n'
echo -ne '\r\n'
However I encourage you to try the new externremux.sh script shipped with the
streamdev source distribution.
To emphasize the required change in externremux, the URL path for passing the
stream through externremux has changed from EXTERN to EXT.
3. Usage: 3. Usage:
--------- ---------
@@ -136,10 +175,16 @@ Start the server core itself by specifying -Pstreamdev-server on your VDR
commandline. To use the client core, specify -Pstreamdev-client. Both parts commandline. To use the client core, specify -Pstreamdev-client. Both parts
can run in one VDR instance, if necessary. can run in one VDR instance, if necessary.
On the server, the main menu entry "Streamdev Connections" gives you a list
of currently connected clients. Use the "red" key to terminate a connection.
Note that depending on connection type and client, the client might re-connect
sooner or later. Depending on the server setup, the "blue" key might be enabled
as well. Please read below.
The parameter "Suspend behaviour" allows you to specify how the server should The parameter "Suspend behaviour" allows you to specify how the server should
react in case the client requests a channel that would require switching the react in case the client requests a channel that would require switching the
primary device (i.e. disrupt live-tv). If set to "Offer suspend mode", you will primary device (i.e. disrupt live-tv). If set to "Offer suspend mode", you
have a new entry in the main menu. Activating that will put the server into enable the "blue" key in the server's main menu. It will put the server into
"Suspend Mode" (a picture is displayed on TV). Then, a client may switch the "Suspend Mode" (a picture is displayed on TV). Then, a client may switch the
primary card to wherever it likes to. While watching TV (Suspend deactivated), primary card to wherever it likes to. While watching TV (Suspend deactivated),
the client may not switch the transponder on the primary device. If you set the client may not switch the transponder on the primary device. If you set
@@ -151,10 +196,10 @@ suspend" to yes, the client can suspend the server remotely (this only applies
if "Offer suspend mode" is selected). if "Offer suspend mode" is selected).
NOTE: This mainly applies to One-Card-Systems, since with multiple cards there NOTE: This mainly applies to One-Card-Systems, since with multiple cards there
is no need to switch transponders on the primary interface, if the secondary is no need to switch transponders on the primary interface, if on of the other
can stream a given channel (i.e. if it is not blocked by a recording). If both cards is idle (i.e. if it is not blocked by a recording). If all cards are in
cards are in use (i.e. when something is recorded, or by multiple clients), use (i.e. when something is recorded, or by multiple clients), this applies to
this applies to Multiple-Card-Systems as well. Multiple-Card-Systems as well.
3.1 Usage HTTP server: 3.1 Usage HTTP server:
---------------------- ----------------------
@@ -166,15 +211,15 @@ listen to with the parameter "HTTP Server Port". The parameter "HTTP Streamtype"
allows you to specify a default stream type, which is used if no specific type allows you to specify a default stream type, which is used if no specific type
has been requested in the URL (see below). The supported stream types are: has been requested in the URL (see below). The supported stream types are:
TS Transport Stream (i.e. a dump from the device) TS Transport Stream (i.e. a dump from the device)
PES Packetized Elemetary Stream (VDR's native recording format) PES Packetized Elemetary Stream (VDR's native recording format)
PS Program Stream (SVCD, DVD like stream) PS Program Stream (SVCD, DVD like stream)
ES Elementary Stream (only Video, if available, otherwise only Audio) ES Elementary Stream (only Video, if available, otherwise only Audio)
EXTERN Pass stream through external script (e.g. for converting with mencoder) EXT Pass stream through external script (e.g. for converting with mencoder)
Assuming that you leave the default port (3000), point your web browser to Assuming that you leave the default port (3000), point your web browser to
http://hostname:3000/ http://hostname:3000/
You will be presented a menu with links to various channel lists, including M3U You will be presented a menu with links to various channel lists, including M3U
playlist formats. playlist formats.
@@ -182,29 +227,106 @@ playlist formats.
If you don't want to use the HTML menu or the M3U playlists, you can access the If you don't want to use the HTML menu or the M3U playlists, you can access the
streams directly like this: streams directly like this:
http://hostname:3000/3 http://hostname:3000/3
http://hostname:3000/S19.2E-0-12480-898 http://hostname:3000/S19.2E-0-12480-898
The first one will deliver a channel by number on the server, the second one The first one will deliver a channel by number on the server, the second one
will request the channel by unique channel id. In addition, you can specify will request the channel by unique channel id. In addition, you can specify
the desired stream type as a path to the channel. the desired stream type as a path to the channel.
http://hostname:3000/TS/3 http://hostname:3000/TS/3
http://hostname:3000/PES/S19.2E-0-12480-898 http://hostname:3000/PES/S19.2E-0-12480-898
The first one would deliver the stream in TS, the second one in PES format. The first one would deliver the stream in TS, the second one in PES format.
Possible values are 'PES', 'TS', 'PS', 'ES' and 'EXTERN'. You need to specify Possible values are 'PES', 'TS', 'PS', 'ES' and 'EXT'. You need to specify
the ES format explicitly if you want to listen to radio channels. Play them the ES format explicitly if you want to listen to radio channels. Play them
back i.e. with mpg123. back i.e. with mpg123.
mpg123 http://hostname:3000/ES/200 mpg123 http://hostname:3000/ES/200
With 'EXTERN' you can also add a parameter which is passed as argument to the With 'EXT' you can also add parameters which are passed as arguments to the
externremux script. externremux script (e.g. http://hostname:3000/EXT;param1=value1;param2=value2/3)
Check your externremux.sh script for the parameters it understands. For details
on how to modify or write your own externremux.sh, please see the chapter upon
externremux.sh further down.
http://hostname:3000/EXTERN;some_parameter/3 If you want to access streamdev's HTTP server from the Internet, do *not* grant
access for anyone by allowing any IP in "streamdevhosts.conf". Instead, pass the
"-a" commandline option to streamdev-server. It takes a username and a password
as argument. Clients with an IP not accepted by "streamdevhosts.conf" will then
have to login. The VDR commandline will have to look like this:
3.2 Usage VDR-to-VDR server: vdr ... -P 'streamdev-server -a vdr:secret' ...
Note the single quotes, as otherwise "-a" will be passed to VDR and not to
streamdev-server. The login ("vdr" in the example above) doesn't have to exist
as a system account.
3.2 Usage IGMP multicast server:
--------------------------------
IGMP based multicast streaming is often used by settop boxes to receive IP TV.
Streamdev's multicast server allows you to feed live TV from VDR to such a
settop box. VLC is known to work well if you look for a software client.
The advantage of multicasting is that the actual stream is sent out only once,
regardless of how many clients want to receive it. The downside is, that you
cannot simply multicast across network boundaries. You need multicast routers.
For multicast streaming over the public Internet you would even need to register
for your own IP range. So don't even think of multicasting via Internet with
streamdev! Streamdev will send the stream only to one local ethernet segment and
all clients must be connected to this same segment. There must not be a router
inbetween. Also note that the client must not run on the streamdev-server
machine.
Each channel is offered on a different multicast IP. Channel 1 is available from
multicast IP 239.255.0.1, channel 2 from 239.255.0.2 and so on. The upper limit
is 239.255.254.255 which corresponds to channel 65279 (239.255.255.0/24 is
reserved according to RFC-2365).
Before you can use streamdev's multicast server, you might need to patch VDR.
Binding an IGMP socket is a privileged operation, so you must start VDR as root.
If you pass the -u option to VDR, it will drop almost all priviledges before
streamdev is even loaded. Apply vdr-cap_net_raw.diff to keep VDR from dropping
the CAP_NET_RAW capability required to bind the IGMP socket. The patch is part
of streamdev's source distribution. Check the patches subdirectory. There's no
need to patch VDR if it is kept running as root (not recommended).
The multicast server is disabled by default. Enter the streamdev-server setup
menu to enable it and - IMPORTANT - bind the multicast server to the IP of your
VDR server's LAN ethernet card. The multicast server will refuse to start with
the default bind adresse "0.0.0.0".
Now edit your streamdevhosts.conf. To allow streaming of all channels, it must
contain "239.255.0.0/16". Note that you cannot limit connections by client IP
here. You can however restrict which channels are allowed to be multicasted.
Enter individual multicast IPs instead of "239.255.0.0/16".
By default, the linux kernel will refuse to join more than 20 multicast groups.
You might want to increase this up to "number_of_channels + 1". Note that it's
"number_of_channels", not "maximum_channel_number".
#First 100 channels:
bash# sysctl -w net.ipv4.igmp_max_memberships=101
#All channels:
bash# COUNT=$(grep -c '^[^:]' PATH_TO_YOUR/channels.conf)
bash# sysctl -w net.ipv4.igmp_max_memberships=$((COUNT + 1))
You need to run the sysctl command *before* VDR is started. The setting is lost
after the next reboot. Check the documentation of your Linux distro on how to
make the setting persist (i.e. have your distro change the value for you as
part of the boot procedure). Most likely /etc/sysctl.conf is your friend.
A multicast server never knows how many clients are actually receiving a stream.
If a client signals that it leaves a multicast group, the server has to query
for other listeners before it can stop the stream. This may delay zapping from
one transponder to an other. The client will probably requests the new channel
before the previous stream has been stopped. If there's no free DVB card, VDR
won't be able to fulfill the request until a DVB card becomes available and the
client resends the request.
3.3 Usage VDR-to-VDR server:
---------------------------- ----------------------------
You can activate the VDR-to-VDR server part in the PlugIn's Setup Menu. It is You can activate the VDR-to-VDR server part in the PlugIn's Setup Menu. It is
@@ -213,7 +335,7 @@ port where you want the server to listen for incoming connections. The server
will be activated when you push the OK button inside the setup menu, so there's will be activated when you push the OK button inside the setup menu, so there's
no need to restart VDR. no need to restart VDR.
3.3 Usage VDR-to-VDR client: 3.4 Usage VDR-to-VDR client:
---------------------------- ----------------------------
Streamdev-client adds a "Suspend Server" item to VDR's mainmenu. With the Streamdev-client adds a "Suspend Server" item to VDR's mainmenu. With the
@@ -260,14 +382,46 @@ With "Filter Streaming" enabled, the client will receive meta information like
EPG data and service information, just as if the client had its own DVB card. EPG data and service information, just as if the client had its own DVB card.
Link channels and even a client-side EPG scan have been reported to work. Link channels and even a client-side EPG scan have been reported to work.
The last parameter, "Synchronize EPG", will have the client synchronize it's With maximum and minimum priority you can keep VDR from considering streamdev
program table with the server every now and then, but not regularly. This in certain cases. If for instance you have a streamdev client with its own DVB
happens when starting the client, and everytime VDR does its housekeeping card, VDR might use streamdev for recording. If this is not what you want, you
tasks. The only thing that's guaranteed is, that there will be a minimum could set the maximum priority to 0. As recordings usually have a much higher
interval of ten seconds between each EPG synchronization. With "Filter priority (default 50), streamdev is now no longer used for recordings. The two
Streaming" this option has been obsoleted. If you still need to synchronize parameters define the inclusive range of priorities for which streamdev will
EPG as additional information is available from the server, you should use the accept to tune. Setting the minimum priority to a higher value than the maximum,
epgsync-plugin instead (http://vdr.schmirler.de). you will get two ranges: "up to maximum" and "minimum and above".
If you are running at least VDR 1.7.0, you can also configure the "Broadcast
Systems / Cost" of the streamdev-client device. On a pure streamdev-client only
system it doesn't matter what you configure here. But if your client is equipped
with a DVB card, you should read on. VDR always prefers the cheapest device
in terms of supported broadcast systems and modulations. A DVB-S2 card supports
two broadcast systems (DVB-S and DVB-S2). From VDR 1.7.15 on, the supported
modulations are counted as well (QPSK, QAM32/64/128/256, VSB8/16, TURBO_FEC).
So for a DVB-S2 card which does QPSK you'll get a total cost of three. A DVB-C
card (one broadcast system) which can do QAM32,QAM64,QAM128,QAM256 would give
you a total of five. Check your log for "frontend ... provides ... with ..."
messages to find out the cost of your DVB cards. Then pick a suitable value for
streamdev-client. With equal costs, VDR will usually prefer the DVB card and
take streamdev for recordings. If streamdev's costs are higher, live TV will
use your DVB card until a recordings kicks in. Then the recording will take the
DVB card and live TV will be shifted to streamdev (you'll notice a short
interruption of live TV).
Note that streamdev-client acts similar to a DVB card. It is possible to receive
multiple channels simultaneously, but only from the same transponder. Just add
additional instances of streamdev-client and you will be able to receive as many
transponders at a time. The same trick allows a client to receive channels from
different servers. To create an additional instance, copy the streamdev-client
binary to a different name (e.g. streamdev-client2):
cd VDRPLUGINLIBDIR
cp libvdr-streamdev-client.so.1.X.X libvdr-streamdev-client2.so.1.X.X
Now add -Pstreamdev-client2 to the VDR commandline. In the VDR plugin setup
a second streamdev-client entry should show up. Both instances have to be
configured individually.
4. Other useful Plugins: 4. Other useful Plugins:
------------------------ ------------------------
@@ -311,11 +465,80 @@ With its networking option, xineliboutput provides an alternative to streamdev.
You will get the picture of the server VDR, including its OSD. However you You will get the picture of the server VDR, including its OSD. However you
won't get independent clients, as they all share the same output. won't get independent clients, as they all share the same output.
5. Known Problems: 5. externremux.sh:
------------------ ------------------
* In VDR-to-VDR setup, the availability of a channel is checked with a different When selecting streamtype "EXT", the TS stream from VDR is passed through an
priority than the actual channel switch. The later always uses priority 0. external program for further processing. By default a script installed at
Usually a channel switch for live TV has priority 0 anyway, so it is not a VDRCONFDIR/plugins/streamdev/externremux.sh is expected, however you may
problem here. However timers usually have a higher priority. Either avoid specify a different location as parameter -r to the streamdev-server plugin
client side recordings or set the priority of client side timers to 0. (see chapter upon Installation above).
The TS stream is passed to the script on stdin, the resulting stream is expected
on stdout. The following parameters are passed to the script in the environment:
* Information on the channel:
REMUX_CHANNEL_ID VDR channel ID
REMUX_CHANNEL_NAME Channel name
REMUX_VTYPE Video type (2 for MPEG-2)
REMUX_VPID Video PID (undefined if audio only)
REMUX_PPID PCR PID (undefined if equal to VPID)
REMUX_TPID Teletext PID (undefined if not available)
REMUX_APID Space separated list of audio pids
REMUX_ALANG Space separated list of audio languages
REMUX_DPID Space separated list of dolby pids
REMUX_DLANG Space separated list of dolby languages
REMUX_SPID Space separated list of subtitle pids
REMUX_SLANG Space separated list of subtitle languages
REMUX_PARAM_* All (user supplied) parameters (e.g. REMUX_PARAM_x)
* Information on the connection (CGI like)
REMOTE_ADDR Client IP
SERVER_NAME Local IP
SERVER_PORT Local port
SERVER_PROTOCOL Streamdev protocol (HTTP, VTP, IGMP)
SERVER_SOFTWARE Streamdev version
All HTTP headers converted to uppercase, '-' replaced by '_' (e.g. USER_AGENT)
The script should perform the following steps (pseudocode):
if (SERVER_PROTOCOL == HTTP)
write headers (including Content-Type) to STDOUT
write empty line to STDOUT
if (REQUEST_METHOD == HEAD)
exit
endif
endif
while (read STDIN)
remux to STDOUT
wend
onSIGINT/SIGKILL: cleanup and exit
6. Known Problems:
------------------
* There have been reports that channel switching with VDR 1.5.x/1.6.x clients
sometimes fails. Current version includes a workaround which seems to work, but
YMMV ;)
* Viewing encrypted channels became an issue with VDR's new CAM handling code.
Streamdev doesn't provide a (dummy) CAM, so out of the box, VDR won't ever try
to receive encrypted channels from streamdev. Pick one of the following
solutions to work around the problem:
1. Force VDR to use streamdev. Open the channels menu on the client (or edit its
channels.conf if you know how to do this) and set the CA field of all channels
that only the server can decrypt to streamdev's device index. Usually streamdev
will get number 9 or 10. Streamdev logs the actual device number when starting
up. So please consider the logs for the correct value. Remember to fill in
hexadecimal values if you are using an editor to modify your channels.conf
(number 10 becomes an "a", number 11 a "b", ...).
2. Apply either patch "patches/vdr-1.6.0-intcamdevices.patch" or patch
"patches/vdr-1.6.0-ignore_missing_cam.diff" to your client VDR. Intcamdevices
is the clean solution, but it modifies the VDR API. So you will need to
recompile all of your plugins. The ignore_missing_cam patch is trivial, no need
to recompile other plugins. However it is not suitable for clients with a DVB
card of their own.

84
client/Makefile Normal file
View File

@@ -0,0 +1,84 @@
#
# Makefile for a Video Disk Recorder plugin
#
# $Id: Makefile,v 1.2 2010/07/19 13:49:25 schmirl Exp $
# The official name of this plugin.
# 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 = streamdev-client
### Includes and Defines (add further entries here):
DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
### The object files (add further files here):
COMMONOBJS = ../common.o
CLIENTOBJS = $(PLUGIN).o \
device.o filter.o setup.o socket.o
### The main target:
.PHONY: all i18n dist clean
all: libvdr-$(PLUGIN).so i18n
### Implicit rules:
%.o: %.c
$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
### Dependencies:
MAKEDEP = $(CXX) -MM -MG
DEPFILE = .dependencies
$(DEPFILE): Makefile
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(CLIENTOBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) > $@
-include $(DEPFILE)
### Internationalization (I18N):
PODIR = po
LOCALEDIR = $(VDRDIR)/locale
I18Npo = $(wildcard $(PODIR)/*.po)
I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
I18Npot = $(PODIR)/$(PLUGIN).pot
%.mo: %.po
msgfmt -c -o $@ $<
$(I18Npot): $(CLIENTOBJS:%.o=%.c)
xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --msgid-bugs-address='<http://www.vdr-developer.org/mantisbt/>' -o $@ $^
%.po: $(I18Npot)
msgmerge -U --no-wrap --no-location --backup=none -q $@ $<
@touch $@
$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
@mkdir -p $(dir $@)
cp $< $@
i18n: $(I18Nmsgs)
### Targets:
libvdr-$(PLUGIN).so: $(CLIENTOBJS) $(COMMONOBJS) ../tools/sockettools.a
%.so:
$(CXX) $(CXXFLAGS) -shared $^ -o $@
@cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION)
dist: clean
@-rm -rf $(TMPDIR)/$(ARCHIVE)
@mkdir $(TMPDIR)/$(ARCHIVE)
@cp -a * $(TMPDIR)/$(ARCHIVE)
@tar czf $(PACKAGE).tgz --exclude CVS -C $(TMPDIR) $(ARCHIVE)
@-rm -rf $(TMPDIR)/$(ARCHIVE)
@echo Distribution package created as $(PACKAGE).tgz
clean:
@-rm -f $(COMMONOBJS) $(CLIENTOBJS) $(DEPFILE) $(PODIR)/*.mo $(PODIR)/*.pot *.so *.tgz core* *~

View File

@@ -1,5 +1,5 @@
/* /*
* $Id: device.c,v 1.18 2008/04/07 14:50:32 schmirl Exp $ * $Id: device.c,v 1.27 2010/08/18 10:26:55 schmirl Exp $
*/ */
#include "client/device.h" #include "client/device.h"
@@ -8,6 +8,7 @@
#include "tools/select.h" #include "tools/select.h"
#include <vdr/config.h>
#include <vdr/channels.h> #include <vdr/channels.h>
#include <vdr/ringbuffer.h> #include <vdr/ringbuffer.h>
#include <vdr/eit.h> #include <vdr/eit.h>
@@ -32,10 +33,8 @@ cStreamdevDevice::cStreamdevDevice(void) {
m_Device = this; m_Device = this;
m_Pids = 0; m_Pids = 0;
m_Priority = -1;
m_DvrClosed = true; m_DvrClosed = true;
if (StreamdevClientSetup.SyncEPG)
ClientSocket.SynchronizeEPG();
} }
cStreamdevDevice::~cStreamdevDevice() { cStreamdevDevice::~cStreamdevDevice() {
@@ -50,10 +49,16 @@ cStreamdevDevice::~cStreamdevDevice() {
Cancel(3); Cancel(3);
StopSectionHandler();
DELETENULL(m_Filters); DELETENULL(m_Filters);
DELETENULL(m_TSBuffer); DELETENULL(m_TSBuffer);
} }
#if APIVERSNUM >= 10700
int cStreamdevDevice::NumProvidedSystems(void) const
{ return StreamdevClientSetup.NumProvidedSystems; }
#endif
bool cStreamdevDevice::ProvidesSource(int Source) const { bool cStreamdevDevice::ProvidesSource(int Source) const {
Dprintf("ProvidesSource, Source=%d\n", Source); Dprintf("ProvidesSource, Source=%d\n", Source);
return true; return true;
@@ -69,7 +74,9 @@ bool cStreamdevDevice::IsTunedToTransponder(const cChannel *Channel)
{ {
bool res = false; bool res = false;
if (ClientSocket.DataSocket(siLive) != NULL if (ClientSocket.DataSocket(siLive) != NULL
&& TRANSPONDER(Channel, m_Channel)) && TRANSPONDER(Channel, m_Channel)
&& Channel->Ca() == CA_FTA
&& m_Channel->Ca() == CA_FTA)
res = true; res = true;
return res; return res;
} }
@@ -85,6 +92,19 @@ bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
Dprintf("ProvidesChannel, Channel=%s, Prio=%d\n", Channel->Name(), Priority); Dprintf("ProvidesChannel, Channel=%s, Prio=%d\n", Channel->Name(), Priority);
if (StreamdevClientSetup.MinPriority <= StreamdevClientSetup.MaxPriority)
{
if (Priority < StreamdevClientSetup.MinPriority ||
Priority > StreamdevClientSetup.MaxPriority)
return false;
}
else
{
if (Priority < StreamdevClientSetup.MinPriority &&
Priority > StreamdevClientSetup.MaxPriority)
return false;
}
if (ClientSocket.DataSocket(siLive) != NULL if (ClientSocket.DataSocket(siLive) != NULL
&& TRANSPONDER(Channel, m_Channel)) && TRANSPONDER(Channel, m_Channel))
res = true; res = true;
@@ -103,12 +123,17 @@ bool cStreamdevDevice::SetChannelDevice(const cChannel *Channel,
bool LiveView) { bool LiveView) {
Dprintf("SetChannelDevice Channel: %s, LiveView: %s\n", Channel->Name(), Dprintf("SetChannelDevice Channel: %s, LiveView: %s\n", Channel->Name(),
LiveView ? "true" : "false"); LiveView ? "true" : "false");
LOCK_THREAD;
m_UpdatePriority = ClientSocket.SupportsPrio();
if (LiveView) if (LiveView)
return false; return false;
if (ClientSocket.DataSocket(siLive) != NULL if (ClientSocket.DataSocket(siLive) != NULL
&& TRANSPONDER(Channel, m_Channel)) && TRANSPONDER(Channel, m_Channel)
&& Channel->Ca() == CA_FTA
&& m_Channel->Ca() == CA_FTA)
return true; return true;
DetachAllReceivers(); DetachAllReceivers();
@@ -123,6 +148,8 @@ bool cStreamdevDevice::SetPid(cPidHandle *Handle, int Type, bool On) {
Handle->used); Handle->used);
LOCK_THREAD; LOCK_THREAD;
m_UpdatePriority = ClientSocket.SupportsPrio();
if (On && !m_TSBuffer) { if (On && !m_TSBuffer) {
Dprintf("SetPid: no data connection -> OpenDvr()"); Dprintf("SetPid: no data connection -> OpenDvr()");
OpenDvrInt(); OpenDvrInt();
@@ -190,16 +217,11 @@ void cStreamdevDevice::CloseDvrInt(void) {
} }
Dprintf("cStreamdevDevice::CloseDvrInt(): Closing DVR connection\n"); Dprintf("cStreamdevDevice::CloseDvrInt(): Closing DVR connection\n");
#if VDRVERSNUM < 10500
DELETENULL(m_TSBuffer);
ClientSocket.CloseDvr();
#else
// Hack for VDR 1.5.x clients (sometimes sending ABRT after TUNE) // Hack for VDR 1.5.x clients (sometimes sending ABRT after TUNE)
// TODO: Find a clean solution to fix this // TODO: Find a clean solution to fix this
ClientSocket.SetChannelDevice(m_Channel); ClientSocket.SetChannelDevice(m_Channel);
ClientSocket.CloseDvr(); ClientSocket.CloseDvr();
DELETENULL(m_TSBuffer); DELETENULL(m_TSBuffer);
#endif
} }
void cStreamdevDevice::CloseDvr(void) { void cStreamdevDevice::CloseDvr(void) {
@@ -289,3 +311,31 @@ bool cStreamdevDevice::ReInit(void) {
return StreamdevClientSetup.StartClient ? Init() : true; return StreamdevClientSetup.StartClient ? Init() : true;
} }
void cStreamdevDevice::UpdatePriority(void) {
if (m_Device) {
m_Device->Lock();
if (m_Device->m_UpdatePriority && ClientSocket.DataSocket(siLive)) {
int Priority = m_Device->Priority();
if (m_Device == cDevice::ActualDevice() && Priority < Setup.PrimaryLimit)
Priority = Setup.PrimaryLimit;
if (m_Device->m_Priority != Priority && ClientSocket.SetPriority(Priority))
m_Device->m_Priority = Priority;
}
m_Device->Unlock();
}
}
int cStreamdevDevice::SignalStrength(void) const {
int strength = -1;
if (ClientSocket.DataSocket(siLive) != NULL)
ClientSocket.GetSignal(&strength, NULL);
return strength;
}
int cStreamdevDevice::SignalQuality(void) const {
int quality = -1;
if (ClientSocket.DataSocket(siLive) != NULL)
ClientSocket.GetSignal(NULL, &quality);
return quality;
}

View File

@@ -1,5 +1,5 @@
/* /*
* $Id: device.h,v 1.7 2008/04/07 14:40:39 schmirl Exp $ * $Id: device.h,v 1.10 2010/08/18 10:26:55 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEV_DEVICE_H #ifndef VDR_STREAMDEV_DEVICE_H
@@ -15,13 +15,14 @@ class cTBString;
#define CMD_LOCK_OBJ(x) cMutexLock CmdLock((cMutex*)&(x)->m_Mutex) #define CMD_LOCK_OBJ(x) cMutexLock CmdLock((cMutex*)&(x)->m_Mutex)
class cStreamdevDevice: public cDevice { class cStreamdevDevice: public cDevice {
friend class cRemoteRecordings;
private: private:
const cChannel *m_Channel; const cChannel *m_Channel;
cTSBuffer *m_TSBuffer; cTSBuffer *m_TSBuffer;
cStreamdevFilters *m_Filters; cStreamdevFilters *m_Filters;
int m_Pids; int m_Pids;
int m_Priority;
bool m_UpdatePriority;
bool m_DvrClosed; bool m_DvrClosed;
static cStreamdevDevice *m_Device; static cStreamdevDevice *m_Device;
@@ -49,12 +50,22 @@ public:
cStreamdevDevice(void); cStreamdevDevice(void);
virtual ~cStreamdevDevice(); virtual ~cStreamdevDevice();
virtual bool HasInternalCam(void) { return true; }
virtual bool ProvidesSource(int Source) const; virtual bool ProvidesSource(int Source) const;
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;
#endif
#if APIVERSNUM >= 10719
virtual bool AvoidRecording(void) const { return true; }
#endif
virtual bool IsTunedToTransponder(const cChannel *Channel); virtual bool IsTunedToTransponder(const cChannel *Channel);
virtual int SignalStrength(void) const;
virtual int SignalQuality(void) const;
static void UpdatePriority(void);
static bool Init(void); static bool Init(void);
static bool ReInit(void); static bool ReInit(void);

View File

@@ -1,5 +1,5 @@
/* /*
* $Id: filter.c,v 1.12 2008/04/07 14:27:28 schmirl Exp $ * $Id: filter.c,v 1.14 2009/02/13 13:02:39 schmirl Exp $
*/ */
#include "client/filter.h" #include "client/filter.h"
@@ -9,6 +9,7 @@
#include <vdr/device.h> #include <vdr/device.h>
#define PID_MASK_HI 0x1F
// --- cStreamdevFilter ------------------------------------------------------ // --- cStreamdevFilter ------------------------------------------------------
class cStreamdevFilter: public cListObject { class cStreamdevFilter: public cListObject {
@@ -226,6 +227,7 @@ void cStreamdevFilters::Action(void) {
u_short pid = (((u_short)block[1] & PID_MASK_HI) << 8) | block[2]; u_short pid = (((u_short)block[1] & PID_MASK_HI) << 8) | block[2];
u_char tid = block[3]; u_char tid = block[3];
bool Pusi = block[1] & 0x40; bool Pusi = block[1] & 0x40;
// proprietary extension
int len = block[4]; int len = block[4];
#if 0 #if 0
if (block[1] == 0xff && if (block[1] == 0xff &&

52
client/po/de_DE.po Normal file
View File

@@ -0,0 +1,52 @@
# VDR streamdev plugin language source file.
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
# This file is distributed under the same license as the VDR streamdev package.
# Frank Schmirler <vdrdev@schmirler.de>, 2008
#
msgid ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2011-02-16 08:49+0100\n"
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
"Last-Translator: Frank Schmirler <vdrdev@schmirler.de>\n"
"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "VTP Streaming Client"
msgstr "VTP Streaming Client"
msgid "Suspend Server"
msgstr "Server pausieren"
msgid "Server is suspended"
msgstr "Server ist pausiert"
msgid "Couldn't suspend Server!"
msgstr "Konnte Server nicht pausieren!"
msgid "Hide Mainmenu Entry"
msgstr "Hauptmenüeintrag verstecken"
msgid "Start Client"
msgstr "Client starten"
msgid "Remote IP"
msgstr "IP der Gegenseite"
msgid "Remote Port"
msgstr "Port der Gegenseite"
msgid "Filter Streaming"
msgstr "Filter-Daten streamen"
msgid "Minimum Priority"
msgstr "Minimale Priorität"
msgid "Maximum Priority"
msgstr "Maximale Priorität"
msgid "Broadcast Systems / Cost"
msgstr "Empfangssysteme / Kosten"

51
client/po/es_ES.po Normal file
View File

@@ -0,0 +1,51 @@
# VDR streamdev plugin language source file.
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
# This file is distributed under the same license as the VDR streamdev package.
# Javier Bradineras <jbradi@hotmail.com>, 2011
#
msgid ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
"Last-Translator: Javier Bradineras <jbradi@hotmail.com>\n"
"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "VTP Streaming Client"
msgstr "Cliente trasmisión VTP"
msgid "Suspend Server"
msgstr "Suspender servidor"
msgid "Server is suspended"
msgstr "Servidor en suspensión"
msgid "Couldn't suspend Server!"
msgstr "Imposible suspender el servidor!"
msgid "Hide Mainmenu Entry"
msgstr "Ocultar entrada en menú principal"
msgid "Start Client"
msgstr "Iniciar Cliente"
msgid "Remote IP"
msgstr "Indicar IP del Servidor"
msgid "Remote Port"
msgstr "Indicar puerto remoto del Servidor"
msgid "Filter Streaming"
msgstr "Filtrar transmisión"
msgid "Minimum Priority"
msgstr "Prioridad mínima"
msgid "Maximum Priority"
msgstr "Prioridad máxima"

50
client/po/fi_FI.po Normal file
View File

@@ -0,0 +1,50 @@
# VDR streamdev plugin language source file.
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
# This file is distributed under the same license as the VDR streamdev package.
# Rolf Ahrenberg <rahrenbe@cc.hut.fi>, 2008
#
msgid ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
"Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"
"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "VTP Streaming Client"
msgstr "VTP-suoratoistoasiakas"
msgid "Suspend Server"
msgstr "Pysäytä palvelin"
msgid "Server is suspended"
msgstr "Palvelin on pysäytetty"
msgid "Couldn't suspend Server!"
msgstr "Palvelinta ei onnistuttu pysäyttämään!"
msgid "Hide Mainmenu Entry"
msgstr "Piilota valinta päävalikosta"
msgid "Start Client"
msgstr "Käynnistä VDR-asiakas"
msgid "Remote IP"
msgstr "Etäkoneen IP-osoite"
msgid "Remote Port"
msgstr "Etäkoneen portti"
msgid "Filter Streaming"
msgstr "Suodatetun tiedon suoratoisto"
msgid "Minimum Priority"
msgstr "Pienin prioriteetti"
msgid "Maximum Priority"
msgstr "Suurin prioriteetti"

50
client/po/fr_FR.po Normal file
View File

@@ -0,0 +1,50 @@
# VDR streamdev plugin language source file.
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
# This file is distributed under the same license as the VDR streamdev package.
# Frank Schmirler <vdrdev@schmirler.de>, 2008
#
msgid ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
"Last-Translator: micky979 <micky979@free.fr>\n"
"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "VTP Streaming Client"
msgstr "Client de streaming VTP"
msgid "Suspend Server"
msgstr "Suspendre le serveur"
msgid "Server is suspended"
msgstr "Le serveur est suspendu"
msgid "Couldn't suspend Server!"
msgstr "Impossible de suspendre le serveur!"
msgid "Hide Mainmenu Entry"
msgstr "Masquer dans le menu principal"
msgid "Start Client"
msgstr "Démarrage du client"
msgid "Remote IP"
msgstr "Adresse IP du serveur"
msgid "Remote Port"
msgstr "Port du serveur"
msgid "Filter Streaming"
msgstr "Filtre streaming"
msgid "Minimum Priority"
msgstr ""
msgid "Maximum Priority"
msgstr ""

52
client/po/it_IT.po Normal file
View File

@@ -0,0 +1,52 @@
# VDR streamdev plugin language source file.
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
# This file is distributed under the same license as the VDR streamdev package.
# Alberto Carraro <bertocar@tin.it>, 2001
# Antonio Ospite <ospite@studenti.unina.it>, 2003
# Sean Carlos <seanc@libero.it>, 2005
#
msgid ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "VTP Streaming Client"
msgstr "Client trasmissione VTP"
msgid "Suspend Server"
msgstr "Sospendi Server"
msgid "Server is suspended"
msgstr "Server sospeso"
msgid "Couldn't suspend Server!"
msgstr "Impossibile sospendere il server!"
msgid "Hide Mainmenu Entry"
msgstr "Nascondi voce menu principale"
msgid "Start Client"
msgstr "Avvia Client"
msgid "Remote IP"
msgstr "Indirizzo IP del Server"
msgid "Remote Port"
msgstr "Porta Server Remoto"
msgid "Filter Streaming"
msgstr "Filtra trasmissione"
msgid "Minimum Priority"
msgstr "Priorità minima"
msgid "Maximum Priority"
msgstr "Priorità massima"

50
client/po/lt_LT.po Normal file
View File

@@ -0,0 +1,50 @@
# VDR streamdev plugin language source file.
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
# This file is distributed under the same license as the VDR streamdev package.
# Frank Schmirler <vdrdev@schmirler.de>, 2008
#
msgid ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
"PO-Revision-Date: 2009-11-26 21:57+0200\n"
"Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
"Language-Team: Lietuvių\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "VTP Streaming Client"
msgstr "VTP transliavimo standartas"
msgid "Suspend Server"
msgstr "Sustabdyti serverį"
msgid "Server is suspended"
msgstr "Serveris sustabdytas"
msgid "Couldn't suspend Server!"
msgstr "Negali sustabdyti serverio!"
msgid "Hide Mainmenu Entry"
msgstr "Paslėpti pagrindinio meniu įrašą"
msgid "Start Client"
msgstr "Paleisti klientą"
msgid "Remote IP"
msgstr "Nuotolinis IP adresas"
msgid "Remote Port"
msgstr "Nuotolinis portas"
msgid "Filter Streaming"
msgstr "Filtruoti transliavimą"
msgid "Minimum Priority"
msgstr "Minimalus prioritetas"
msgid "Maximum Priority"
msgstr "Maksimalus prioritetas"

50
client/po/ru_RU.po Normal file
View File

@@ -0,0 +1,50 @@
# VDR streamdev plugin language source file.
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
# This file is distributed under the same license as the VDR streamdev package.
# Frank Schmirler <vdrdev@schmirler.de>, 2008
#
msgid ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
"PO-Revision-Date: 2008-06-26 15:36+0100\n"
"Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n"
"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-5\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "VTP Streaming Client"
msgstr "VTP Streaming ÚÛØÕÝâ"
msgid "Suspend Server"
msgstr "¾áâÐÝÞÒØâì áÕàÒÕà"
msgid "Server is suspended"
msgstr "ÁÕàÒÕà ÞáâÐÝÞÒÛÕÝ"
msgid "Couldn't suspend Server!"
msgstr "ÝÕ ÜÞÓã ÞáâÐÝÞÒØâì áÕàÒÕà"
msgid "Hide Mainmenu Entry"
msgstr "ÁßàïâÐâì Ò ÓÛÐÒÝÞÜ ÜÕÝî"
msgid "Start Client"
msgstr "ÁâÐàâ ÚÛØÕÝâÐ"
msgid "Remote IP"
msgstr "ÃÔÐÛÕÝÝëÙ IP"
msgid "Remote Port"
msgstr "ÃÔÐÛÕÝÝëÙ ßÞàâ"
msgid "Filter Streaming"
msgstr "ÄØÛìâà ßÞâÞÚÐ"
msgid "Minimum Priority"
msgstr ""
msgid "Maximum Priority"
msgstr ""

52
client/po/sk_SK.po Normal file
View File

@@ -0,0 +1,52 @@
# VDR streamdev plugin language source file.
# Copyright (C) 2009 streamdev development team. See http://streamdev.vdr-developer.org
# This file is distributed under the same license as the VDR streamdev package.
# Milan Hrala <hrala.milan@gmail.com>, 2009
#
msgid ""
msgstr ""
"Project-Id-Version: streamdev_SK\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
"PO-Revision-Date: \n"
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
"Language-Team: Slovak <hrala.milan@gmail.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-2\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Slovak\n"
"X-Poedit-Country: SLOVAKIA\n"
msgid "VTP Streaming Client"
msgstr "VTP prúdový klient"
msgid "Suspend Server"
msgstr "Server pozastavený"
msgid "Server is suspended"
msgstr "Server je doèasne preru¹ený"
msgid "Couldn't suspend Server!"
msgstr "Nepodarilo sa pozastavi» Server!"
msgid "Hide Mainmenu Entry"
msgstr "Schova» polo¾ku v hlavnom menu"
msgid "Start Client"
msgstr "Spusti» Klienta"
msgid "Remote IP"
msgstr "Vzdialená IP"
msgid "Remote Port"
msgstr "Vzdialený port"
msgid "Filter Streaming"
msgstr "filtrova» prúdy"
msgid "Minimum Priority"
msgstr "minimálna priorita"
msgid "Maximum Priority"
msgstr "maximálna priorita"

View File

@@ -1,12 +1,11 @@
/* /*
* $Id: setup.c,v 1.5 2008/04/07 14:50:32 schmirl Exp $ * $Id: setup.c,v 1.10 2010/06/08 05:55:17 schmirl Exp $
*/ */
#include <vdr/menuitems.h> #include <vdr/menuitems.h>
#include "client/setup.h" #include "client/setup.h"
#include "client/device.h" #include "client/device.h"
#include "i18n.h"
cStreamdevClientSetup StreamdevClientSetup; cStreamdevClientSetup StreamdevClientSetup;
@@ -14,8 +13,12 @@ cStreamdevClientSetup::cStreamdevClientSetup(void) {
StartClient = false; StartClient = false;
RemotePort = 2004; RemotePort = 2004;
StreamFilters = false; StreamFilters = false;
SyncEPG = false;
HideMenuEntry = false; HideMenuEntry = false;
MinPriority = -1;
MaxPriority = MAXPRIORITY;
#if APIVERSNUM >= 10700
NumProvidedSystems = 1;
#endif
strcpy(RemoteIp, ""); strcpy(RemoteIp, "");
} }
@@ -29,8 +32,12 @@ bool cStreamdevClientSetup::SetupParse(const char *Name, const char *Value) {
} }
else if (strcmp(Name, "RemotePort") == 0) RemotePort = atoi(Value); else if (strcmp(Name, "RemotePort") == 0) RemotePort = atoi(Value);
else if (strcmp(Name, "StreamFilters") == 0) StreamFilters = atoi(Value); else if (strcmp(Name, "StreamFilters") == 0) StreamFilters = atoi(Value);
else if (strcmp(Name, "SyncEPG") == 0) SyncEPG = atoi(Value);
else if (strcmp(Name, "HideMenuEntry") == 0) HideMenuEntry = atoi(Value); else if (strcmp(Name, "HideMenuEntry") == 0) HideMenuEntry = atoi(Value);
else if (strcmp(Name, "MinPriority") == 0) MinPriority = atoi(Value);
else if (strcmp(Name, "MaxPriority") == 0) MaxPriority = atoi(Value);
#if APIVERSNUM >= 10700
else if (strcmp(Name, "NumProvidedSystems") == 0) NumProvidedSystems = atoi(Value);
#endif
else return false; else return false;
return true; return true;
} }
@@ -38,12 +45,18 @@ bool cStreamdevClientSetup::SetupParse(const char *Name, const char *Value) {
cStreamdevClientMenuSetupPage::cStreamdevClientMenuSetupPage(void) { cStreamdevClientMenuSetupPage::cStreamdevClientMenuSetupPage(void) {
m_NewSetup = StreamdevClientSetup; m_NewSetup = StreamdevClientSetup;
AddBoolEdit (tr("Hide Mainmenu Entry"),m_NewSetup.HideMenuEntry); Add(new cMenuEditBoolItem(tr("Hide Mainmenu Entry"), &m_NewSetup.HideMenuEntry));
AddBoolEdit (tr("Start Client"), m_NewSetup.StartClient); Add(new cMenuEditBoolItem(tr("Start Client"), &m_NewSetup.StartClient));
AddIpEdit (tr("Remote IP"), m_NewSetup.RemoteIp); Add(new cMenuEditIpItem (tr("Remote IP"), m_NewSetup.RemoteIp));
AddShortEdit(tr("Remote Port"), m_NewSetup.RemotePort); Add(new cMenuEditIntItem (tr("Remote Port"), &m_NewSetup.RemotePort, 0, 65535));
AddBoolEdit (tr("Filter Streaming"), m_NewSetup.StreamFilters); Add(new cMenuEditBoolItem(tr("Filter Streaming"), &m_NewSetup.StreamFilters));
AddBoolEdit (tr("Synchronize EPG"), m_NewSetup.SyncEPG); Add(new cMenuEditIntItem (tr("Minimum Priority"), &m_NewSetup.MinPriority, -1, MAXPRIORITY));
Add(new cMenuEditIntItem (tr("Maximum Priority"), &m_NewSetup.MaxPriority, -1, MAXPRIORITY));
#if APIVERSNUM >= 10715
Add(new cMenuEditIntItem (tr("Broadcast Systems / Cost"), &m_NewSetup.NumProvidedSystems, 1, 15));
#elif APIVERSNUM >= 10700
Add(new cMenuEditIntItem (tr("Broadcast Systems / Cost"), &m_NewSetup.NumProvidedSystems, 1, 4));
#endif
SetCurrent(Get(0)); SetCurrent(Get(0));
} }
@@ -63,8 +76,12 @@ void cStreamdevClientMenuSetupPage::Store(void) {
SetupStore("RemoteIp", m_NewSetup.RemoteIp); SetupStore("RemoteIp", m_NewSetup.RemoteIp);
SetupStore("RemotePort", m_NewSetup.RemotePort); SetupStore("RemotePort", m_NewSetup.RemotePort);
SetupStore("StreamFilters", m_NewSetup.StreamFilters); SetupStore("StreamFilters", m_NewSetup.StreamFilters);
SetupStore("SyncEPG", m_NewSetup.SyncEPG);
SetupStore("HideMenuEntry", m_NewSetup.HideMenuEntry); SetupStore("HideMenuEntry", m_NewSetup.HideMenuEntry);
SetupStore("MinPriority", m_NewSetup.MinPriority);
SetupStore("MaxPriority", m_NewSetup.MaxPriority);
#if APIVERSNUM >= 10700
SetupStore("NumProvidedSystems", m_NewSetup.NumProvidedSystems);
#endif
StreamdevClientSetup = m_NewSetup; StreamdevClientSetup = m_NewSetup;

View File

@@ -1,5 +1,5 @@
/* /*
* $Id: setup.h,v 1.4 2008/04/07 14:50:32 schmirl Exp $ * $Id: setup.h,v 1.7 2010/06/08 05:55:17 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEV_SETUPCLIENT_H #ifndef VDR_STREAMDEV_SETUPCLIENT_H
@@ -16,13 +16,17 @@ struct cStreamdevClientSetup {
char RemoteIp[20]; char RemoteIp[20];
int RemotePort; int RemotePort;
int StreamFilters; int StreamFilters;
int SyncEPG;
int HideMenuEntry; int HideMenuEntry;
int MinPriority;
int MaxPriority;
#if APIVERSNUM >= 10700
int NumProvidedSystems;
#endif
}; };
extern cStreamdevClientSetup StreamdevClientSetup; extern cStreamdevClientSetup StreamdevClientSetup;
class cStreamdevClientMenuSetupPage: public cStreamdevMenuSetupPage { class cStreamdevClientMenuSetupPage: public cMenuSetupPage {
private: private:
cStreamdevClientSetup m_NewSetup; cStreamdevClientSetup m_NewSetup;

View File

@@ -1,5 +1,5 @@
/* /*
* $Id: socket.c,v 1.11 2008/04/07 14:40:40 schmirl Exp $ * $Id: socket.c,v 1.15 2010/08/18 10:26:55 schmirl Exp $
*/ */
#include <tools/select.h> #include <tools/select.h>
@@ -14,13 +14,16 @@
#include "client/socket.h" #include "client/socket.h"
#include "client/setup.h" #include "client/setup.h"
#include "common.h" #include "common.h"
#include "i18n.h"
cClientSocket ClientSocket; cClientSocket ClientSocket;
cClientSocket::cClientSocket(void) cClientSocket::cClientSocket(void)
{ {
memset(m_DataSockets, 0, sizeof(cTBSocket*) * si_Count); memset(m_DataSockets, 0, sizeof(cTBSocket*) * si_Count);
m_Prio = false;
m_LastSignalUpdate = 0;
m_LastSignalStrength = -1;
m_LastSignalQuality = -1;
Reset(); Reset();
} }
@@ -143,8 +146,14 @@ bool cClientSocket::CheckConnection(void) {
if(Command("CAPS FILTERS", 220)) if(Command("CAPS FILTERS", 220))
Filters = ",FILTERS"; Filters = ",FILTERS";
isyslog("Streamdev: Connected to server %s:%d using capabilities TSPIDS%s", const char *Prio = "";
RemoteIp().c_str(), RemotePort(), Filters); if(Command("CAPS PRIO", 220)) {
Prio = ",PRIO";
m_Prio = true;
}
isyslog("Streamdev: Connected to server %s:%d using capabilities TSPIDS%s%s",
RemoteIp().c_str(), RemotePort(), Filters, Prio);
return true; return true;
} }
@@ -242,16 +251,54 @@ bool cClientSocket::SetChannelDevice(const cChannel *Channel) {
CMD_LOCK; CMD_LOCK;
std::string command = (std::string)"TUNE " std::string command = (std::string)"TUNE "
+ (const char*)Channel->GetChannelID().ToString(); + (const char*)Channel->GetChannelID().ToString();
if (!Command(command, 220)) { if (!Command(command, 220, 10000)) {
if (errno == 0) if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't tune %s:%d to channel %s", esyslog("ERROR: Streamdev: Couldn't tune %s:%d to channel %s",
RemoteIp().c_str(), RemotePort(), Channel->Name()); RemoteIp().c_str(), RemotePort(), Channel->Name());
return false; return false;
} }
m_LastSignalUpdate = 0;
return true; return true;
} }
bool cClientSocket::SetPriority(int Priority) {
if (!CheckConnection()) return false;
CMD_LOCK;
std::string command = (std::string)"PRIO " + (const char*)itoa(Priority);
if (!Command(command, 220)) {
if (errno == 0)
esyslog("Streamdev: Failed to update priority on %s:%d", RemoteIp().c_str(),
RemotePort());
return false;
}
return true;
}
bool cClientSocket::GetSignal(int *SignalStrength, int *SignalQuality) {
if (!CheckConnection()) return -1;
CMD_LOCK;
if (m_LastSignalUpdate != time(NULL)) {
std::string buffer;
if (!Command("SGNL") || !Expect(220, &buffer)
|| sscanf(buffer.c_str(), "%*d %*d %d:%d", &m_LastSignalStrength, &m_LastSignalQuality) != 2) {
m_LastSignalStrength = -1;
m_LastSignalQuality = -1;
}
m_LastSignalUpdate = time(NULL);
}
if (SignalStrength)
*SignalStrength = m_LastSignalStrength;
if (SignalQuality)
*SignalQuality = m_LastSignalQuality;
return 0;
}
bool cClientSocket::SetPid(int Pid, bool On) { bool cClientSocket::SetPid(int Pid, bool On) {
if (!CheckConnection()) return false; if (!CheckConnection()) return false;
@@ -260,8 +307,8 @@ bool cClientSocket::SetPid(int Pid, bool On) {
std::string command = (std::string)(On ? "ADDP " : "DELP ") + (const char*)itoa(Pid); std::string command = (std::string)(On ? "ADDP " : "DELP ") + (const char*)itoa(Pid);
if (!Command(command, 220)) { if (!Command(command, 220)) {
if (errno == 0) if (errno == 0)
esyslog("Streamdev: Pid %d not available from %s:%d", Pid, LocalIp().c_str(), esyslog("Streamdev: Pid %d not available from %s:%d", Pid, RemoteIp().c_str(),
LocalPort()); RemotePort());
return false; return false;
} }
return true; return true;
@@ -277,7 +324,7 @@ bool cClientSocket::SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On) {
if (!Command(command, 220)) { if (!Command(command, 220)) {
if (errno == 0) if (errno == 0)
esyslog("Streamdev: Filter %hu, %hhu, %hhu not available from %s:%d", esyslog("Streamdev: Filter %hu, %hhu, %hhu not available from %s:%d",
Pid, Tid, Mask, LocalIp().c_str(), LocalPort()); Pid, Tid, Mask, RemoteIp().c_str(), RemotePort());
return false; return false;
} }
return true; return true;
@@ -301,52 +348,6 @@ bool cClientSocket::CloseDvr(void) {
return true; return true;
} }
bool cClientSocket::SynchronizeEPG(void) {
std::string buffer;
bool result;
FILE *epgfd;
if (!CheckConnection()) return false;
isyslog("Streamdev: Synchronizing EPG from server\n");
CMD_LOCK;
if (!Command("LSTE"))
return false;
if ((epgfd = tmpfile()) == NULL) {
esyslog("ERROR: Streamdev: Error while processing EPG data: %s",
strerror(errno));
return false;
}
while ((result = Expect(215, &buffer))) {
if (buffer[3] == ' ') break;
fputs(buffer.c_str() + 4, epgfd);
fputc('\n', epgfd);
}
if (!result) {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't fetch EPG data from %s:%d",
RemoteIp().c_str(), RemotePort());
fclose(epgfd);
return false;
}
rewind(epgfd);
if (cSchedules::Read(epgfd))
cSchedules::Cleanup(true);
else {
esyslog("ERROR: Streamdev: Parsing EPG data failed");
fclose(epgfd);
return false;
}
fclose(epgfd);
return true;
}
bool cClientSocket::Quit(void) { bool cClientSocket::Quit(void) {
bool res; bool res;

View File

@@ -1,5 +1,5 @@
/* /*
* $Id: socket.h,v 1.6 2008/04/07 14:40:40 schmirl Exp $ * $Id: socket.h,v 1.8 2010/08/18 10:26:55 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEV_CLIENT_CONNECTION_H #ifndef VDR_STREAMDEV_CLIENT_CONNECTION_H
@@ -20,7 +20,11 @@ private:
cTBSocket *m_DataSockets[si_Count]; cTBSocket *m_DataSockets[si_Count];
cMutex m_Mutex; cMutex m_Mutex;
char m_Buffer[BUFSIZ + 1]; // various uses char m_Buffer[BUFSIZ + 1]; // various uses
bool m_Prio; // server supports command PRIO
time_t m_LastSignalUpdate;
int m_LastSignalStrength;
int m_LastSignalQuality;
protected: protected:
/* Send Command, and return true if the command results in Expected. /* Send Command, and return true if the command results in Expected.
Returns false on failure, setting errno appropriately if it has been Returns false on failure, setting errno appropriately if it has been
@@ -45,10 +49,12 @@ public:
bool CreateDataConnection(eSocketId Id); bool CreateDataConnection(eSocketId Id);
bool CloseDataConnection(eSocketId Id); bool CloseDataConnection(eSocketId Id);
bool SetChannelDevice(const cChannel *Channel); bool SetChannelDevice(const cChannel *Channel);
bool SupportsPrio() { return m_Prio; }
bool SetPriority(int Priority);
bool SetPid(int Pid, bool On); bool SetPid(int Pid, bool On);
bool SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On); bool SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On);
bool GetSignal(int *SignalStrength, int *SignalQuality);
bool CloseDvr(void); bool CloseDvr(void);
bool SynchronizeEPG(void);
bool SuspendServer(void); bool SuspendServer(void);
bool Quit(void); bool Quit(void);

View File

@@ -3,19 +3,18 @@
* *
* 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-client.c,v 1.5 2008/04/07 14:50:32 schmirl Exp $ * $Id: streamdev-client.c,v 1.3 2010/08/18 10:26:56 schmirl Exp $
*/ */
#include "streamdev-client.h" #include "streamdev-client.h"
#include "client/device.h" #include "client/device.h"
#include "client/setup.h" #include "client/setup.h"
#include "i18n.h"
#if VDRVERSNUM < 10400 #if !defined(APIVERSNUM) || APIVERSNUM < 10516
#error "VDR-1.4.0 or greater is required" #error "VDR-1.5.16 API version or greater is required!"
#endif #endif
const char *cPluginStreamdevClient::DESCRIPTION = "VTP Streaming Client"; const char *cPluginStreamdevClient::DESCRIPTION = trNOOP("VTP Streaming Client");
cPluginStreamdevClient::cPluginStreamdevClient(void) { cPluginStreamdevClient::cPluginStreamdevClient(void) {
} }
@@ -28,17 +27,9 @@ const char *cPluginStreamdevClient::Description(void) {
} }
bool cPluginStreamdevClient::Start(void) { bool cPluginStreamdevClient::Start(void) {
i18n_name = Name(); I18nRegister(PLUGIN_NAME_I18N);
RegisterI18n(Phrases);
cStreamdevDevice::Init(); cStreamdevDevice::Init();
return true;
return true;
}
void cPluginStreamdevClient::Housekeeping(void) {
if (StreamdevClientSetup.StartClient && StreamdevClientSetup.SyncEPG)
ClientSocket.SynchronizeEPG();
} }
const char *cPluginStreamdevClient::MainMenuEntry(void) { const char *cPluginStreamdevClient::MainMenuEntry(void) {
@@ -61,4 +52,8 @@ bool cPluginStreamdevClient::SetupParse(const char *Name, const char *Value) {
return StreamdevClientSetup.SetupParse(Name, Value); return StreamdevClientSetup.SetupParse(Name, Value);
} }
void cPluginStreamdevClient::MainThreadHook(void) {
cStreamdevDevice::UpdatePriority();
}
VDRPLUGINCREATOR(cPluginStreamdevClient); // Don't touch this! VDRPLUGINCREATOR(cPluginStreamdevClient); // Don't touch this!

View File

@@ -1,5 +1,5 @@
/* /*
* $Id: streamdev-client.h,v 1.1 2004/12/30 22:43:59 lordjaxom Exp $ * $Id: streamdev-client.h,v 1.3 2010/08/18 10:26:56 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEVCLIENT_H #ifndef VDR_STREAMDEVCLIENT_H
@@ -19,11 +19,11 @@ public:
virtual const char *Version(void) { return VERSION; } virtual const char *Version(void) { return VERSION; }
virtual const char *Description(void); virtual const char *Description(void);
virtual bool Start(void); virtual bool Start(void);
virtual void Housekeeping(void);
virtual const char *MainMenuEntry(void); virtual const char *MainMenuEntry(void);
virtual cOsdObject *MainMenuAction(void); virtual cOsdObject *MainMenuAction(void);
virtual cMenuSetupPage *SetupMenu(void); virtual cMenuSetupPage *SetupMenu(void);
virtual bool SetupParse(const char *Name, const char *Value); virtual bool SetupParse(const char *Name, const char *Value);
virtual void MainThreadHook(void);
}; };
#endif // VDR_STREAMDEVCLIENT_H #endif // VDR_STREAMDEVCLIENT_H

140
common.c
View File

@@ -1,5 +1,5 @@
/* /*
* $Id: common.c,v 1.7 2008/04/07 14:27:27 schmirl Exp $ * $Id: common.c,v 1.12 2010/07/19 13:49:24 schmirl Exp $
*/ */
#include <vdr/channels.h> #include <vdr/channels.h>
@@ -7,146 +7,12 @@
#include "common.h" #include "common.h"
#include "tools/select.h" #include "tools/select.h"
#include "i18n.h"
using namespace std; using namespace std;
const char *VERSION = "0.4.0-pre"; const char *VERSION = "0.5.1-git";
const char *StreamTypes[st_Count] = { const char cMenuEditIpItem::IpCharacters[] = "0123456789.";
"TS",
"PES",
"PS",
"ES",
"Extern",
"", // used internally only
};
const char *SuspendModes[sm_Count] = {
"Offer suspend mode",
"Always suspended",
"Never suspended"
};
const char IpCharacters[] = "0123456789.";
char *GetNextLine(char *String, uint Length, uint &Offset) {
char *last, *first;
first = String + Offset;
for (last = first; last < String + Length; ++last) {
if (*last == '\012') {
if (*(last - 1) == '\015')
*(last - 1) = '\0';
*last++ = '\0';
Dprintf("IN: |%s|\n", first);
Offset = last - String;
return first;
}
}
return NULL;
}
const cChannel *ChannelFromString(const char *String, int *Apid) {
const cChannel *channel = NULL;
char *string = strdup(String);
char *ptr, *end;
int apididx = 0;
if ((ptr = strrchr(string, '+')) != NULL) {
*(ptr++) = '\0';
apididx = strtoul(ptr, &end, 10);
Dprintf("found apididx: %d\n", apididx);
}
if (isnumber(string)) {
int temp = strtol(String, NULL, 10);
if (temp >= 1 && temp <= Channels.MaxNumber())
channel = Channels.GetByNumber(temp);
} else {
channel = Channels.GetByChannelID(tChannelID::FromString(string));
if (channel == NULL) {
int i = 1;
while ((channel = Channels.GetByNumber(i, 1)) != NULL) {
if (String == channel->Name())
break;
i = channel->Number() + 1;
}
}
}
if (channel != NULL && apididx > 0) {
int apid = 0, index = 1;
for (int i = 0; channel->Apid(i) != 0; ++i, ++index) {
if (index == apididx) {
apid = channel->Apid(i);
break;
}
}
if (apid == 0) {
for (int i = 0; channel->Dpid(i) != 0; ++i, ++index) {
if (index == apididx) {
apid = channel->Dpid(i);
break;
}
}
}
if (Apid != NULL)
*Apid = apid;
}
free(string);
return channel;
}
void cStreamdevMenuSetupPage::AddCategory(const char *Title) {
char *buffer = NULL;
asprintf(&buffer, "--- %s -------------------------------------------------"
"---------------", Title );
cOsdItem *item = new cOsdItem(buffer);
free(buffer);
item->SetSelectable(false);
Add(item);
}
void cStreamdevMenuSetupPage::AddBoolEdit(const char *Title, int &Value) {
Add(new cMenuEditBoolItem(Title, &Value));
}
void cStreamdevMenuSetupPage::AddIpEdit(const char *Title, char *Value) {
Add(new cMenuEditIpItem(Title, Value));
}
void cStreamdevMenuSetupPage::AddShortEdit(const char *Title, int &Value) {
AddRangeEdit(Title, Value, 0, 65535);
}
void cStreamdevMenuSetupPage::AddRangeEdit(const char *Title, int &Value,
int Min, int Max) {
Add(new cMenuEditIntItem(Title, &Value, Min, Max));
}
void cStreamdevMenuSetupPage::AddSuspEdit(const char *Title, int &Value) {
static const char *SuspendModesTR[sm_Count] = { NULL };
if (SuspendModesTR[0] == NULL) {
for (int i = 0; i < sm_Count; ++i)
SuspendModesTR[i] = tr(SuspendModes[i]);
}
Add(new cMenuEditStraItem(Title, &Value, sm_Count, SuspendModesTR));
}
void cStreamdevMenuSetupPage::AddTypeEdit(const char *Title, int &Value) {
Add(new cMenuEditStraItem(Title, &Value, st_CountSetup, StreamTypes));
}
cMenuEditIpItem::cMenuEditIpItem(const char *Name, char *Value): cMenuEditIpItem::cMenuEditIpItem(const char *Name, char *Value):
cMenuEditItem(Name) { cMenuEditItem(Name) {

View File

@@ -1,5 +1,5 @@
/* /*
* $Id: common.h,v 1.11 2008/04/07 14:40:39 schmirl Exp $ * $Id: common.h,v 1.16 2010/07/19 13:49:24 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEV_COMMON_H #ifndef VDR_STREAMDEV_COMMON_H
@@ -23,42 +23,27 @@
# define Dprintf(x...) # define Dprintf(x...)
#endif #endif
# define TRANSPONDER(c1, c2) (c1->Transponder() == c2->Transponder()) #if APIVERSNUM >= 10714
#define TRANSPONDER(c1, c2) (c1->Transponder() == c2->Transponder() && !c1->IsSourceType('V'))
#else
#define TRANSPONDER(c1, c2) (c1->Transponder() == c2->Transponder())
#endif
# define MAXPARSEBUFFER KILOBYTE(16) #define MAXPARSEBUFFER KILOBYTE(16)
/* Check if a channel is a radio station. */ /* Check if a channel is a radio station. */
#define ISRADIO(x) ((x)->Vpid()==0||(x)->Vpid()==1||(x)->Vpid()==0x1fff) #define ISRADIO(x) ((x)->Vpid()==0||(x)->Vpid()==1||(x)->Vpid()==0x1fff)
class cChannel; class cChannel;
char *GetNextLine(char *String, uint Length, uint &Offset);
const cChannel *ChannelFromString(const char *String, int *Apid = NULL);
/* Disable logging if BUFCOUNT buffer overflows occur within BUFOVERTIME
milliseconds. Enable logging again if there is no error within BUFOVERTIME
milliseconds. */
#define BUFOVERTIME 5000
#define BUFOVERCOUNT 100
#define POLLFAIL esyslog("Streamdev: Polling failed: %s", strerror(errno))
#define WRITEFAIL esyslog("Streamdev: Writing failed: %s", strerror(errno))
#define READFAIL esyslog("Streamdev: Reading failed: %s", strerror(errno))
#define CHECKPOLL(x) if ((x)<0){POLLFAIL; return false;}
#define CHECKWRITE(x) if ((x)<0) { WRITEFAIL; return false; }
#define CHECKREAD(x) if ((x)<0) { READFAIL; return false; }
enum eStreamType { enum eStreamType {
stTS, stTS,
stPES, stPES,
stPS, stPS,
stES, stES,
stExtern, stEXT,
stTSPIDS, stTSPIDS,
st_Count
#define st_CountSetup (stExtern+1)
#define st_Count (stTSPIDS+1)
}; };
enum eSuspendMode { enum eSuspendMode {
@@ -72,29 +57,15 @@ enum eSocketId {
siLive, siLive,
siReplay, siReplay,
siLiveFilter, siLiveFilter,
siDataRespond,
si_Count si_Count
}; };
extern const char *VERSION; extern const char *VERSION;
extern const char *StreamTypes[st_Count];
extern const char *SuspendModes[sm_Count];
extern const char IpCharacters[];
class cStreamdevMenuSetupPage: public cMenuSetupPage {
protected:
void AddCategory(const char *Title);
virtual void Store(void) = 0;
void AddBoolEdit(const char *Title, int &Value);
void AddIpEdit(const char *Title, char *Value);
void AddShortEdit(const char *Title, int &Value);
void AddRangeEdit(const char *Title, int &Value, int Min, int Max);
void AddSuspEdit(const char *Title, int &Value);
void AddTypeEdit(const char *Title, int &Value);
};
class cMenuEditIpItem: public cMenuEditItem { class cMenuEditIpItem: public cMenuEditItem {
private: private:
static const char IpCharacters[];
char *value; char *value;
int curNum; int curNum;
int pos; int pos;

786
i18n.c
View File

@@ -1,786 +0,0 @@
/*
* $Id: i18n.c,v 1.8 2008/04/07 14:50:32 schmirl Exp $
*/
#include "i18n.h"
const char *i18n_name = NULL;
const tI18nPhrase Phrases[] = {
{ "VDR Streaming Server", // English
"VDR Streaming Server", // Deutsch
"", // Slovenski
"", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"VDR-suoratoistopalvelin", // suomi
"", // Polski
"", // Español
"", // Ellinika / Greek
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "VTP Streaming Client", // English
"VTP Streaming Client", // Deutsch
"", // Slovenski
"", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"VTP-suoratoistoasiakas ", // suomi
"", // Polski
"", // Español
"", // Ellinika / Greek
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Start VDR-to-VDR Server", // English
"VDR-zu-VDR Server starten", // Deutsch
"", // Slovenski
"Avvia il Server VDR-toVDR", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Käynnistä VDR-palvelin", // suomi
"", // Polski
"", // Español
"", // Ellinika / Greek
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Start HTTP Server", // English
"HTTP Server starten", // Deutsch
"", // Slovenski
"Avvia il Server HTTP", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Käynnistä HTTP-palvelin", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "HTTP Streamtype", // English
"HTTP Streamtyp", // Deutsch
"", // Slovenski
"Tipo di Stream HTTP", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"HTTP-lähetysmuoto", // Suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Start Client", // English
"Client starten", // Deutsch
"", // Slovenski
"Avvia il Client", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Käynnistä VDR-asiakas", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "VDR-to-VDR Server Port", // English
"Port des VDR-zu-VDR Servers", // Deutsch
"", // Slovenski
"Porta del Server VDR-to-VDR", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"VDR-palvelimen portti", // Suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "HTTP Server Port", // English
"Port des HTTP Servers", // Deutsch
"", // Slovenski
"Porta del Server HTTP", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"HTTP-palvelimen portti", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Maximum Number of Clients", // English
"Maximalanzahl an Clients", // Deutsch
"", // Slovenski
"Numero Massimo di Client", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Suurin sallittu asiakkaiden määrä", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Remote IP", // English
"IP der Gegenseite", // Deutsch
"", // Slovenski
"Indirizzo IP del Server", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Etäkoneen IP-osoite", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Remote Port", // English
"Port der Gegenseite", // Deutsch
"", // Slovenski
"Porta del Server Remoto", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Etäkoneen portti", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Remote Streamtype", // English
"Streamtyp von Gegenseite", // Deutsch
"", // Slovenski
"Tipo di Stream", // Italiano (oppure Flusso ?)
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Etäkoneen lähetysmuoto", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Common Settings", // English
"Allgemeines", // Deutsch
"", // Slovenski
"Settaggi Comuni", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Yleiset asetukset", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "VDR-to-VDR Server", // English
"VDR-zu-VDR Server", // Deutsch
"", // Slovenski
"Server VDR-to-VDR", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"VDR-palvelin", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "HTTP Server", // English
"HTTP Server", // Deutsch
"", // Slovenski
"Server HTTP", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"HTTP-palvelin", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "VDR-to-VDR Client", // English
"VDR-zu-VDR Client", // Deutsch
"", // Slovenski
"Client VDR-to-VDR", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"VDR-asiakas", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Please restart VDR to activate changes", // English
"Bitte starten Sie für die Änderungen VDR neu", // Deutsch
"", // Slovenski
"Riavviare VDR per attivare i cambiamenti", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Aktivoi muutokset käynnistämällä VDR uudelleen", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Synchronize EPG", // English
"EPG synchronisieren", // Deutsch
"", // Slovenski
"", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Päivitä ohjelmaopas", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Suspend Live TV", // English
"Live-TV pausieren", // Deutsch
"", // Slovenski
"", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Pysäytä suora TV-lähetys", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Suspend behaviour", // English
"Pausierverhalten", // Deutsch
"", // Slovenski
"", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Pysäytystoiminto", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Offer suspend mode", // English
"Pausieren anbieten", // Deutsch
"", // Slovenski
"", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"tyrkytä", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Always suspended", // English
"Immer pausiert", // Deutsch
"", // Slovenski
"", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"aina", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Never suspended", // English
"Nie pausiert", // Deutsch
"", // Slovenski
"", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"ei koskaan", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Suspend Server", // English
"Server pausieren", // Deutsch
"", // Slovenski
"", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Pysäytä palvelin", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Server is suspended", // English
"Server ist pausiert", // Deutsch
"", // Slovenski
"", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Palvelin on pysäytetty", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Couldn't suspend Server!", // English
"Konnte Server nicht pausieren!", // Deutsch
"", // Slovenski
"", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Palvelinta ei onnistuttu pysäyttämään!", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Client may suspend", // English
"Client darf pausieren", // Deutsch
"", // Slovenski
"", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Asiakas saa pysäyttää palvelimen", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Bind to IP", // English
"", // Deutsch
"", // Slovenski
"", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Sido osoitteeseen", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Filter Streaming", // English
"", // Deutsch
"", // Slovenski
"", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Suodatetun tiedon suoratoisto", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Streaming active", // English
"Streamen im Gange", // Deutsch
"", // Slovenski
"", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Suoratoistopalvelin aktiivinen", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ "Hide Mainmenu Entry", // English
"Hauptmenüeintrag verstecken", // Deutsch
"", // Slovenski
"", // Italiano
"", // Nederlands
"", // Português
"", // Français
"", // Norsk
"Piilota valinta päävalikosta", // suomi
"", // Polski
"", // Español
"", // Ellinika
"", // Svenska
"", // Romaneste
"", // Magyar
"", // Catala
"", // Russian
"", // Hrvatski
"", // Eesti
"", // Dansk
"", // Czech
#if VDRVERSNUM >= 10502
"", // Türkçe
#endif
},
{ NULL }
};

16
i18n.h
View File

@@ -1,16 +0,0 @@
/*
* $Id: i18n.h,v 1.1 2004/12/30 22:43:58 lordjaxom Exp $
*/
#ifndef VDR_STREAMDEV_I18N_H
#define VDR_STREAMDEV_I18N_H
#include <vdr/i18n.h>
extern const char *i18n_name;
extern const tI18nPhrase Phrases[];
#undef tr
#define tr(s) I18nTranslate(s, i18n_name)
#endif // VDR_STREAMDEV_I18N_H

View File

@@ -1,25 +1,38 @@
INCS = -I. #
CFLAGS = -g -Wall -O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -fPIC # Makefile for a Video Disk Recorder plugin
MFLAG = -M #
OBJS = ctools.o ringbuffy.o remux.o transform.o # $Id: Makefile,v 1.5 2010/07/30 10:49:28 schmirl Exp $
SRC = $(wildcard *.c)
DESTDIR = /usr/local ### The object files (add further files here):
OBJS = ctools.o remux.o ringbuffy.o transform.o
### Disable attribute warn_unused_result
DEFINES += -U_FORTIFY_SOURCE
### The main target:
.PHONY: clean .PHONY: clean
clean:
- rm -f *.o *~ *.a .depend
libdvbmpegtools.a: $(OBJS) libdvbmpegtools.a: $(OBJS)
ar -rcs libdvbmpegtools.a $(OBJS) ar -rcs libdvbmpegtools.a $(OBJS)
%.o: %.c ### Implicit rules:
$(CC) -c $(CFLAGS) $(INCS) $(DEFINES) $<
.depend: %.o: %.c
$(CXX) $(DEFINES) $(MFLAG) $(SRC) $(INCS)> .depend $(CC) $(CFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
### Dependencies:
MAKEDEP = $(CC) -MM -MG
DEPFILE = .dependencies
-include .depend $(DEPFILE): Makefile
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
-include $(DEPFILE)
### Targets:
clean:
@-rm -f $(OBJS) $(DEPFILE) *.a core* *~

View File

@@ -19,7 +19,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
* *
@@ -596,7 +596,10 @@ int read_pes(int f, pes_packet *p){
if (p->length >0){ if (p->length >0){
buf = (uint8_t *) malloc(p->length); buf = (uint8_t *) malloc(p->length);
if((neof = save_read(f,buf,p->length))< p->length) return -1; if((neof = save_read(f,buf,p->length))< p->length){
free(buf);
return -1;
}
cread_pes((char *)buf,p); cread_pes((char *)buf,p);
free(buf); free(buf);
} else return 0; } else return 0;
@@ -1876,528 +1879,3 @@ int write_ps_header(uint8_t *buf,
} }
} }
#define MAX_BASE 80
#define MAX_PATH 256
#define MAX_EXT 10
int break_up_filename(char *name, char *base_name, char *path, char *ext)
{
int l,i,sstop,sstart;
l = strlen(name);
sstop = l;
sstart = -1;
for( i= l-1; i >= 0; i--){
if (sstop == l && name[i] == '.') sstop = i;
if (sstart<0 && name[i] == '/') sstart = i+1;
}
if (sstart < 0) sstart = 0;
if (sstop-sstart < MAX_BASE){
strncpy(base_name, name+sstart, sstop-sstart);
base_name[sstop-sstart]=0;
if(sstart > 0){
if( l - sstop + sstart < MAX_PATH){
strncpy(path, name, sstart);
path[sstart] = 0;
} else {
fprintf(stderr,"PATH too long\n");
return -1;
}
} else {
strcpy(path, "./");
}
if(sstop < l){
if( l - sstop -1 < MAX_EXT){
strncpy(ext, name+sstop+1, l-sstop-1);
ext[l-sstop-1]=0;
} else {
fprintf(stderr,"Extension too long\n");
return -1;
}
} else {
strcpy(ext, "");
}
} else {
fprintf(stderr,"Name too long\n");
return -1;
}
/*
printf("%d %d\n",sstart, sstop);
printf("%s %d\n",name, strlen(name));
printf("%s %d\n",base_name, strlen(base_name));
printf("%s %d\n",path,strlen(path));
printf("%s %d\n",ext,strlen(ext));
*/
return 0;
}
int seek_mpg_start(uint8_t *buf, int size)
{
int found = 0;
int c=0;
int seq = 0;
int mpeg = 0;
int mark = 0;
while ( !seq ){
while (found != 4){
switch (found) {
case 0:
if ( buf[c] == 0x00 ) found++;
c++;
break;
case 1:
if ( buf[c] == 0x00 ) found++;
else found = 0;
c++;
break;
case 2:
if ( buf[c] == 0x01 ) found++;
else found = 0;
if ( buf[c] == 0x00 ) found = 2;
c++;
break;
case 3:
if ( (buf[c] & 0xe0) == 0xe0 ) found++;
else found = 0;
c++;
break;
}
if (c >= size) return -1;
}
if (found == 4){
mark = c-4;
c+=2;
if (c >= size) return -1;
if ( (buf[c] & 0xC0) == 0x80 ){
mpeg = 2;
c += 2;
if (c >= size) return -1;
c += buf[c]+1;
if (c >= size) return -1;
} else {
mpeg = 1;
while( buf[c] == 0xFF ) {
c++;
if (c >= size) return -1;
}
if ( (buf[c] & 0xC0) == 0x40) c+=2;
if (c >= size) return -1;
if ( (buf[c] & 0x30) ){
if ( (buf[c] & 0x30) == 0x20) c+=5;
else c+=10;
} else c++;
if (c >= size) return -1;
}
if ( buf[c] == 0x00 &&
buf[c+1] == 0x00 &&
buf[c+2] == 0x01 &&
buf[c+3] == 0xB3 )
seq = 1;
}
found = 0;
}
return size-mark;
}
void write_mpg(int fstart, uint64_t length, int fdin, int fdout)
{
// uint8_t mpeg_end[4] = { 0x00, 0x00, 0x01, 0xB9 };
uint8_t *buf;
uint64_t l=0;
uint64_t count = 0;
struct stat sb;
int buf_size;
fstat (fdout, &sb);
buf_size = sb.st_blksize;
buf = (uint8_t *) alloca (buf_size + sizeof (int));
lseek(fdin, fstart, SEEK_SET);
while ( count < length && (l = read(fdin,buf,buf_size)) >= 0){
if (l > 0) count+=l;
write(fdout,buf,l);
printf("written %02.2f%%\r",(100.*count)/length);
}
printf("\n");
//write( fdout, mpeg_end, 4);
}
#define CHECKBUF (1024*1024)
#define ONE_GIG (1024UL*1024UL*1024UL)
void split_mpg(char *name, uint64_t size)
{
char base_name[MAX_BASE];
char path[MAX_PATH];
char ext[MAX_EXT];
char new_name[256];
uint8_t buf[CHECKBUF];
int fdin;
int fdout;
uint64_t length = 0;
uint64_t last;
int i;
int mark, csize;
struct stat sb;
if (break_up_filename(name,base_name,path,ext) < 0) exit(1);
#ifdef __FreeBSD__
if ( (fdin = open(name, O_RDONLY)) < 0){
#else
if ( (fdin = open(name, O_RDONLY|O_LARGEFILE)) < 0){
#endif
fprintf(stderr,"Can't open %s\n",name);
exit(1);
}
fstat (fdin, &sb);
length = sb.st_size;
if ( length < ONE_GIG )
printf("Filelength = %2.2f MB\n", length/1024./1024.);
else
printf("Filelength = %2.2f GB\n", length/1024./1024./1024.);
if ( length < size ) length = size;
printf("Splitting %s into Files with size <= %2.2f MB\n",name,
size/1024./1024.);
csize = CHECKBUF;
read(fdin, buf, csize);
if ( (mark = seek_mpg_start(buf,csize)) < 0){
fprintf(stderr,"Couldn't find sequence header\n");
exit(1);
}
last = csize-mark;
for ( i = 0 ; i < length/size; i++){
csize = CHECKBUF;
if (csize > length-last) csize = length-last;
lseek(fdin, last+size-csize, SEEK_SET);
read(fdin, buf, csize);
if ( (mark = seek_mpg_start(buf,csize)) < 0){
fprintf(stderr,"Couldn't find sequence header\n");
exit(1);
}
sprintf(new_name,"%s-%03d.%s",base_name,i,ext);
printf("writing %s\n",new_name);
#ifdef __FreeBSD__
if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC,
#else
if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC
|O_LARGEFILE,
#endif
S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|
S_IROTH|S_IWOTH)) < 0){
fprintf(stderr,"Can't open %s\n",new_name);
exit(1);
}
write_mpg(last, size-mark, fdin, fdout);
last = last + size - mark;
}
sprintf(new_name,"%s-%03d.%s",base_name,i,ext);
printf("writing %s\n",new_name);
#ifdef __FreeBSD__
if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC,
#else
if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC
|O_LARGEFILE,
#endif
S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|
S_IROTH|S_IWOTH)) < 0){
fprintf(stderr,"Can't open %s\n",new_name);
exit(1);
}
write_mpg(last, length-last, fdin, fdout);
}
void cut_mpg(char *name, uint64_t size)
{
char base_name[MAX_BASE];
char path[MAX_PATH];
char ext[MAX_EXT];
char new_name[256];
uint8_t buf[CHECKBUF];
int fdin;
int fdout;
uint64_t length = 0;
uint64_t last;
int mark, csize;
struct stat sb;
if (break_up_filename(name,base_name,path,ext) < 0) exit(1);
#ifdef __FreeBSD__
if ( (fdin = open(name, O_RDONLY)) < 0){
#else
if ( (fdin = open(name, O_RDONLY|O_LARGEFILE)) < 0){
#endif
fprintf(stderr,"Can't open %s\n",name);
exit(1);
}
fstat (fdin, &sb);
length = sb.st_size;
if ( length < ONE_GIG )
printf("Filelength = %2.2f MB\n", length/1024./1024.);
else
printf("Filelength = %2.2f GB\n", length/1024./1024./1024.);
if ( length < size ) length = size;
printf("Splitting %s into 2 Files with length %.2f MB and %.2f MB\n",
name, size/1024./1024., (length-size)/1024./1024.);
csize = CHECKBUF;
read(fdin, buf, csize);
if ( (mark = seek_mpg_start(buf,csize)) < 0){
fprintf(stderr,"Couldn't find sequence header\n");
exit(1);
}
last = csize-mark;
if (csize > length-last) csize = length-last;
lseek(fdin, last+size-csize, SEEK_SET);
read(fdin, buf, csize);
if ( (mark = seek_mpg_start(buf,csize)) < 0){
fprintf(stderr,"Couldn't find sequence header\n");
exit(1);
}
sprintf(new_name,"%s-1.%s",base_name,ext);
printf("writing %s\n",new_name);
#ifdef __FreeBSD__
if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC,
#else
if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC
|O_LARGEFILE,
#endif
S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|
S_IROTH|S_IWOTH)) < 0){
fprintf(stderr,"Can't open %s\n",new_name);
exit(1);
}
write_mpg(last, size-mark, fdin, fdout);
last = last + size - mark;
sprintf(new_name,"%s-2.%s",base_name,ext);
printf("writing %s\n",new_name);
#ifdef __FreeBSD__
if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC,
#else
if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC
|O_LARGEFILE,
#endif
S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|
S_IROTH|S_IWOTH)) < 0){
fprintf(stderr,"Can't open %s\n",new_name);
exit(1);
}
write_mpg(last, length-last, fdin, fdout);
}
void write_all (int fd, const char *data, int length)
{
int r;
while (length) {
if ((r = write(fd, data, length)) > 0) {
data += r;
length -= r;
}
}
}
void read_all (int fd, char *data, int length)
{
int c = 0;
while(1) {
if( read(fd, data+c, 1) == 1) {
c++;
if(data[c-1] == '\n') {
data[c] = 0;
break;
}
}
else {
fprintf (stderr, "Error reading socket\n");
exit(1);
}
}
}
char *url2host (char *url, char **name, uint32_t *ip, uint32_t *port)
{
char *murl;
struct hostent *hoste;
struct in_addr haddr;
int found_ip = 1;
if (!(strncmp(url, "http://", 7)))
url += 7;
*name = strdup(url);
if (!(*name)) {
*name = NULL;
return (NULL);
}
murl = url;
while (*murl && *murl != ':' && *murl != '/') {
if ((*murl < '0' || *murl > '9') && *murl != '.')
found_ip = 0;
murl++;
}
(*name)[murl - url] = 0;
if (found_ip) {
if ((*ip = inet_addr(*name)) == INADDR_NONE)
return (NULL);
} else {
if (!(hoste = gethostbyname(*name)))
return (NULL);
memcpy (&haddr, hoste->h_addr, sizeof(haddr));
*ip = haddr.s_addr;
}
if (!*murl || *murl == '/') {
*port = 80;
return (murl);
}
*port = atoi(++murl);
while (*murl && *murl != '/')
murl++;
return (murl);
}
#define ACCEPT "Accept: video/mpeg, video/x-mpegurl, */*\r\n"
int http_open (char *url)
{
char purl[1024], *host, req[1024], *sptr;
uint32_t ip;
uint32_t port;
int sock;
int reloc, relocnum = 0;
struct sockaddr_in server;
int mfd;
strncpy (purl, url, 1023);
purl[1023] = '\0';
do {
host = NULL;
strcpy (req, "GET ");
if (!(sptr = url2host(purl, &host, &ip, &port))) {
fprintf (stderr, "Unknown host\n");
exit (1);
}
strcat (req, sptr);
sprintf (req + strlen(req),
" HTTP/1.0\r\nUser-Agent: %s/%s\r\n",
"whatever", "you want");
if (host) {
sprintf(req + strlen(req),
"Host: %s:%u\r\n", host, port);
free (host);
}
strcat (req, ACCEPT);
strcat (req, "\r\n");
server.sin_port = htons(port);
server.sin_family = AF_INET;
server.sin_addr.s_addr = ip;
if ((sock = socket(PF_INET, SOCK_STREAM, 6)) < 0) {
perror ("socket");
exit (1);
}
if (connect(sock, (struct sockaddr *)&server,
sizeof(server))) {
perror ("connect");
exit (1);
}
write_all (sock, req, strlen(req));
if (!(mfd = fileno(fdopen(sock, "rb")))) {
perror ("open");
exit (1);
}
reloc = 0;
purl[0] = '\0';
read_all (mfd, req, 1023);
if ((sptr = strchr(req, ' '))) {
switch (sptr[1]) {
case '2':
break;
case '3':
reloc = 1;
default:
fprintf (stderr, "HTTP req failed:%s",
sptr+1);
exit (1);
}
}
do {
read_all (mfd,req, 1023);
if (!strncmp(req, "Location:", 9))
strncpy (purl, req+10, 1023);
} while (req[0] != '\r' && req[0] != '\n');
} while (reloc && purl[0] && relocnum++ < 3);
if (reloc) {
fprintf (stderr, "Too many HTTP relocations.\n");
exit (1);
}
return sock;
}
extern int errno;
const char * strerrno (void)
{
return strerror(errno);
}

View File

@@ -19,7 +19,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
* *
@@ -387,16 +387,7 @@ extern "C" {
uint8_t buffer2_scale, uint8_t buffer2_scale,
uint32_t buffer2_size); uint32_t buffer2_size);
int seek_mpg_start(uint8_t *buf, int size);
void split_mpg(char *name, uint64_t size);
void cut_mpg(char *name, uint64_t size);
int http_open (char *url);
ssize_t save_read(int fd, void *buf, size_t count); ssize_t save_read(int fd, void *buf, size_t count);
const char * strerrno(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /* __cplusplus */

View File

@@ -19,7 +19,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
* *
@@ -388,10 +388,9 @@ int refill_buffy(Remux *rem)
while ( acount > MAX_PLENGTH && vcount > MAX_PLENGTH && count < 10){ while ( acount > MAX_PLENGTH && vcount > MAX_PLENGTH && count < 10){
int neof;
count++; count++;
init_pes(&pes); init_pes(&pes);
if ((neof = read_pes(fin,&pes)) <= 0) return -1; if (read_pes(fin,&pes) <= 0) return -1;
switch(pes.stream_id){ switch(pes.stream_id){
case AUDIO_STREAM_S ... AUDIO_STREAM_E: case AUDIO_STREAM_S ... AUDIO_STREAM_E:
rem->apes++; rem->apes++;
@@ -702,22 +701,6 @@ void init_remux(Remux *rem, int fin, int fout, int mult)
rem->time_off = 0; rem->time_off = 0;
} }
uint32_t bytes2pts(int bytes, int rate)
{
if (bytes < 0xFFFFFFFFUL/720000UL)
return (uint32_t)(bytes*720000UL/rate);
else
return (uint32_t)(bytes/rate*720000UL);
}
long pts2bytes( uint32_t pts, int rate)
{
if (pts < 0xEFFFFFFFUL/rate)
return (pts*rate/720000);
else
return (pts* (rate/720000));
}
int write_audio_pes( Remux *rem, uint8_t *buf, int *alength) int write_audio_pes( Remux *rem, uint8_t *buf, int *alength)
{ {
int add; int add;
@@ -1088,12 +1071,6 @@ struct remux_s{
} REMUX; } REMUX;
void init_REMUX(REMUX *rem)
{
rem->num_pbuf = 0;
}
#define REPACK 2048 #define REPACK 2048
#define ABUF_SIZE REPACK*1024 #define ABUF_SIZE REPACK*1024

View File

@@ -20,7 +20,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
* *

View File

@@ -15,7 +15,8 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301 USA.
*/ */
#include "ringbuffy.h" #include "ringbuffy.h"
@@ -191,9 +192,8 @@ int ring_read_file(ringbuffy *rbuf, int fd, int count)
} }
int ring_rest(ringbuffy *rbuf){ int ring_rest(ringbuffy *rbuf){
int diff, free, pos, rest; int diff, free, pos;
pos = rbuf->read_pos; pos = rbuf->read_pos;
rest = rbuf->size - pos;
diff = rbuf->write_pos - pos; diff = rbuf->write_pos - pos;
free = (diff >= 0) ? diff : rbuf->size+diff; free = (diff >= 0) ? diff : rbuf->size+diff;

View File

@@ -15,7 +15,8 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301 USA.
*/ */
#ifndef RINGBUFFY_H #ifndef RINGBUFFY_H

View File

@@ -19,7 +19,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
* *

View File

@@ -19,7 +19,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
* *
@@ -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" {

View File

@@ -1,43 +0,0 @@
# The cannels.conf ca field can be used to bind a channel to a specific
# device. The streamdev-client does not consider this information, so
# there's no way to keep VDR from using streamdev for a specific
# channel. Apply this patch if you need this feature.
#
# This fix should probably become part of streamdev. However as it
# changes the behaviour of streamdev, I decided to keep it as a separate
# patch until there is something like a new official streamdev release.
#
--- client/device.h.bak 2006-11-09 12:25:21.000000000 +0100
+++ client/device.h 2006-11-09 12:26:57.000000000 +0100
@@ -50,6 +50,7 @@
cStreamdevDevice(void);
virtual ~cStreamdevDevice();
+ virtual int ProvidesCa(const cChannel *Channel) const;
virtual bool ProvidesSource(int Source) const;
virtual bool ProvidesTransponder(const cChannel *Channel) const;
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1,
--- client/device.c.bak 2006-11-09 12:23:24.000000000 +0100
+++ client/device.c 2006-11-09 12:35:48.000000000 +0100
@@ -57,6 +57,12 @@
#endif
}
+int cStreamdevDevice::ProvidesCa(const cChannel *Channel) const
+{
+ // Encrypted is acceptable for now. Will ask the server later.
+ return Channel->Ca() <= CA_DVB_MAX ? cDevice::ProvidesCa(Channel) : 1;
+}
+
bool cStreamdevDevice::ProvidesSource(int Source) const {
Dprintf("ProvidesSource, Source=%d\n", Source);
return false;
@@ -78,7 +84,7 @@
if (ClientSocket.DataSocket(siLive) != NULL
&& TRANSPONDER(Channel, m_Channel))
res = true;
- else {
+ else if (ProvidesCa(Channel)) {
res = prio && ClientSocket.ProvidesChannel(Channel, Priority);
ndr = true;
}

View File

@@ -0,0 +1,13 @@
--- device.c.orig 2008-03-28 11:47:25.000000000 +0100
+++ device.c 2008-03-28 11:47:09.000000000 +0100
@@ -375,8 +375,8 @@
}
}
}
- if (!NumUsableSlots)
- return NULL; // no CAM is able to decrypt this channel
+// if (!NumUsableSlots)
+// return NULL; // no CAM is able to decrypt this channel
}
bool NeedsDetachReceivers = false;

View File

@@ -0,0 +1,78 @@
Index: vdr-1.6.0-nocamdevices/device.c
===================================================================
--- vdr-1.6.0-nocamdevices/device.c
+++ vdr-1.6.0-nocamdevices/device.c 2008-04-27 18:55:37.000000000 +0300
@@ -363,6 +363,7 @@
int NumCamSlots = CamSlots.Count();
int SlotPriority[NumCamSlots];
int NumUsableSlots = 0;
+ bool InternalCamNeeded = false;
if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
SlotPriority[CamSlot->Index()] = MAXPRIORITY + 1; // assumes it can't be used
@@ -376,7 +377,7 @@
}
}
if (!NumUsableSlots)
- return NULL; // no CAM is able to decrypt this channel
+ InternalCamNeeded = true; // no CAM is able to decrypt this channel
}
bool NeedsDetachReceivers = false;
@@ -392,11 +393,13 @@
continue; // this device shall be temporarily avoided
if (Channel->Ca() && Channel->Ca() <= CA_DVB_MAX && Channel->Ca() != device[i]->CardIndex() + 1)
continue; // a specific card was requested, but not this one
- if (NumUsableSlots && !CamSlots.Get(j)->Assign(device[i], true))
+ if (InternalCamNeeded && !device[i]->HasInternalCam())
+ continue; // no CAM is able to decrypt this channel and the device uses vdr handled CAMs
+ if (NumUsableSlots && !device[i]->HasInternalCam() && !CamSlots.Get(j)->Assign(device[i], true))
continue; // CAM slot can't be used with this device
bool ndr;
if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
- if (NumUsableSlots && device[i]->CamSlot() && device[i]->CamSlot() != CamSlots.Get(j))
+ if (NumUsableSlots && !device[i]->HasInternalCam() && device[i]->CamSlot() && device[i]->CamSlot() != CamSlots.Get(j))
ndr = true; // using a different CAM slot requires detaching receivers
// Put together an integer number that reflects the "impact" using
// this device would have on the overall system. Each condition is represented
@@ -410,18 +413,18 @@
imp <<= 1; imp |= device[i]->Receiving(); // avoid devices that are receiving
imp <<= 1; imp |= device[i] == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device
imp <<= 8; imp |= min(max(device[i]->Priority() + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
- imp <<= 8; imp |= min(max((NumUsableSlots ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
+ imp <<= 8; imp |= min(max(((NumUsableSlots && !device[i]->HasInternalCam()) ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
imp <<= 1; imp |= ndr; // avoid devices if we need to detach existing receivers
imp <<= 1; imp |= device[i]->IsPrimaryDevice(); // avoid the primary device
- imp <<= 1; imp |= NumUsableSlots ? 0 : device[i]->HasCi(); // avoid cards with Common Interface for FTA channels
+ imp <<= 1; imp |= (NumUsableSlots || InternalCamNeeded) ? 0 : device[i]->HasCi(); // avoid cards with Common Interface for FTA channels
imp <<= 1; imp |= device[i]->HasDecoder(); // avoid full featured cards
- imp <<= 1; imp |= NumUsableSlots ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
+ imp <<= 1; imp |= (NumUsableSlots && !device[i]->HasInternalCam()) ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
if (imp < Impact) {
// This device has less impact than any previous one, so we take it.
Impact = imp;
d = device[i];
NeedsDetachReceivers = ndr;
- if (NumUsableSlots)
+ if (NumUsableSlots && !device[i]->HasInternalCam())
s = CamSlots.Get(j);
}
}
Index: vdr-1.6.0-nocamdevices/device.h
===================================================================
--- vdr-1.6.0-nocamdevices/device.h
+++ vdr-1.6.0-nocamdevices/device.h 2008-04-27 18:55:49.000000000 +0300
@@ -335,6 +335,12 @@
public:
virtual bool HasCi(void);
///< Returns true if this device has a Common Interface.
+ virtual bool HasInternalCam(void) { return false; }
+ ///< Returns true if this device handles encrypted channels itself
+ ///< without VDR assistance. This can be e.g. when the device is a
+ ///< client that gets the stream from another VDR instance that has
+ ///< already decrypted the stream. In this case ProvidesChannel()
+ ///< shall check whether the channel can be decrypted.
void SetCamSlot(cCamSlot *CamSlot);
///< Sets the given CamSlot to be used with this device.
cCamSlot *CamSlot(void) const { return camSlot; }

View File

@@ -0,0 +1,11 @@
--- vdr.c.orig 2009-02-13 09:45:55.000000000 +0100
+++ vdr.c 2009-02-13 09:46:24.000000000 +0100
@@ -115,7 +115,7 @@
static bool SetCapSysTime(void)
{
// drop all capabilities except cap_sys_time
- cap_t caps = cap_from_text("= cap_sys_time=ep");
+ cap_t caps = cap_from_text("= cap_sys_time,cap_net_raw=ep");
if (!caps) {
fprintf(stderr, "vdr: cap_from_text failed: %s\n", strerror(errno));
return false;

34
remux/Makefile Normal file
View File

@@ -0,0 +1,34 @@
#
# Makefile for a Video Disk Recorder plugin
#
# $Id: Makefile,v 1.2 2010/07/19 13:49:28 schmirl Exp $
### The object files (add further files here):
OBJS = tsremux.o ts2es.o ts2pes.o ts2ps.o extern.o
### The main target:
.PHONY: clean
remux.a: $(OBJS)
ar -rcs remux.a $^
### Implicit rules:
%.o: %.c
$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
### Dependencies:
MAKEDEP = $(CXX) -MM -MG
DEPFILE = .dependencies
$(DEPFILE): Makefile
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
-include $(DEPFILE)
### Targets:
clean:
@-rm -f $(OBJS) $(DEPFILE) *.a core* *~

View File

@@ -1,13 +1,18 @@
#include "remux/extern.h" #include "remux/extern.h"
#include "server/server.h" #include "server/server.h"
#include "server/connection.h"
#include "server/streamer.h" #include "server/streamer.h"
#include <vdr/channels.h>
#include <vdr/tools.h> #include <vdr/tools.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h>
const char *g_ExternRemux = EXTERNREMUXPATH; namespace Streamdev {
#define MAXENV 63
class cTSExt: public cThread { class cTSExt: public cThread {
private: private:
@@ -20,16 +25,19 @@ protected:
virtual void Action(void); virtual void Action(void);
public: public:
cTSExt(cRingBufferLinear *ResultBuffer, std::string Parameter); cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connection, const cChannel *Channel, const int *Apids, const int *Dpids);
virtual ~cTSExt(); virtual ~cTSExt();
void Put(const uchar *Data, int Count); void Put(const uchar *Data, int Count);
}; };
cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, std::string Parameter): } // namespace Streamdev
using namespace Streamdev;
cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connection, const cChannel *Channel, const int *Apids, const int *Dpids):
m_ResultBuffer(ResultBuffer), m_ResultBuffer(ResultBuffer),
m_Active(false), m_Active(false),
m_Process(0), m_Process(-1),
m_Inpipe(0), m_Inpipe(0),
m_Outpipe(0) m_Outpipe(0)
{ {
@@ -59,6 +67,118 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, std::string Parameter):
if (m_Process == 0) { if (m_Process == 0) {
// child process // child process
char *env[MAXENV + 1];
int i = 0;
#define ADDENV(x...) if (asprintf(&env[i++], x) < 0) i--
// add channel ID, name and pids to environment
ADDENV("REMUX_CHANNEL_ID=%s", *Channel->GetChannelID().ToString());
ADDENV("REMUX_CHANNEL_NAME=%s", Channel->Name());
#if APIVERSNUM >= 10701
ADDENV("REMUX_VTYPE=%d", Channel->Vtype());
#endif
if (Channel->Vpid())
ADDENV("REMUX_VPID=%d", Channel->Vpid());
if (Channel->Ppid() != Channel->Vpid())
ADDENV("REMUX_PPID=%d", Channel->Ppid());
if (Channel->Tpid())
ADDENV("REMUX_TPID=%d", Channel->Tpid());
std::string buffer;
if (Apids && *Apids) {
for (const int *pid = Apids; *pid; pid++)
(buffer += (const char *) itoa(*pid)) += (*(pid + 1) ? " " : "");
ADDENV("REMUX_APID=%s", buffer.c_str());
buffer.clear();
for (const int *pid = Apids; *pid; pid++) {
int j;
for (j = 0; Channel->Apid(j) && Channel->Apid(j) != *pid; j++)
;
(buffer += Channel->Alang(j)) += (*(pid + 1) ? " " : "");
}
ADDENV("REMUX_ALANG=%s", buffer.c_str());
}
if (Dpids && *Dpids) {
buffer.clear();
for (const int *pid = Dpids; *pid; pid++)
(buffer += (const char *) itoa(*pid)) += (*(pid + 1) ? " " : "");
ADDENV("REMUX_DPID=%s", buffer.c_str());
buffer.clear();
for (const int *pid = Dpids; *pid; pid++) {
int j;
for (j = 0; Channel->Dpid(j) && Channel->Dpid(j) != *pid; j++)
;
(buffer += Channel->Dlang(j)) += (*(pid + 1) ? " " : "");
}
ADDENV("REMUX_DLANG=%s", buffer.c_str());
}
if (Channel->Spid(0)) {
buffer.clear();
for (const int *pid = Channel->Spids(); *pid; pid++)
(buffer += (const char *) itoa(*pid)) += (*(pid + 1) ? " " : "");
ADDENV("REMUX_SPID=%s", buffer.c_str());
buffer.clear();
for (int j = 0; Channel->Spid(j); j++)
(buffer += Channel->Slang(j)) += (Channel->Spid(j + 1) ? " " : "");
ADDENV("REMUX_SLANG=%s", buffer.c_str());
}
if (Connection) {
// add vars for a CGI like interface
// the following vars are not implemented:
// REMOTE_HOST, REMOTE_IDENT, REMOTE_USER
// CONTENT_TYPE, CONTENT_LENGTH,
// SCRIPT_NAME, PATH_TRANSLATED, GATEWAY_INTERFACE
ADDENV("REMOTE_ADDR=%s", Connection->RemoteIp().c_str());
ADDENV("SERVER_NAME=%s", Connection->LocalIp().c_str());
ADDENV("SERVER_PORT=%d", Connection->LocalPort());
ADDENV("SERVER_PROTOCOL=%s", Connection->Protocol());
ADDENV("SERVER_SOFTWARE=%s", VERSION);
for (tStrStrMap::const_iterator it = Connection->Headers().begin(); it != Connection->Headers().end(); ++it) {
if (i >= MAXENV) {
esyslog("streamdev-server: Too many headers for externremux.sh");
break;
}
ADDENV("%s=%s", it->first.c_str(), it->second.c_str());
}
// look for section parameters: /path;param1=value1;param2=value2/
std::string::size_type begin, end;
const static std::string PATH_INFO("PATH_INFO");
tStrStrMap::const_iterator it_pathinfo = Connection->Headers().find(PATH_INFO);
const std::string& path = it_pathinfo == Connection->Headers().end() ? "/" : it_pathinfo->second;
begin = path.find(';', 0);
begin = path.find_first_not_of(';', begin);
end = path.find_first_of(";/", begin);
while (begin != std::string::npos && path[begin] != '/') {
std::string param = path.substr(begin, end - begin);
std::string::size_type e = param.find('=');
if (i >= MAXENV) {
esyslog("streamdev-server: Too many parameters for externremux.sh");
break;
}
else if (e > 0 && e != std::string::npos) {
ADDENV("REMUX_PARAM_%s", param.c_str());
}
else
esyslog("streamdev-server: Invalid externremux.sh parameter %s", param.c_str());
begin = path.find_first_not_of(';', end);
end = path.find_first_of(";/", begin);
}
}
env[i] = NULL;
dup2(inpipe[0], STDIN_FILENO); dup2(inpipe[0], STDIN_FILENO);
close(inpipe[1]); close(inpipe[1]);
dup2(outpipe[1], STDOUT_FILENO); dup2(outpipe[1], STDOUT_FILENO);
@@ -68,9 +188,20 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, std::string Parameter):
for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++) for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
close(i); //close all dup'ed filedescriptors close(i); //close all dup'ed filedescriptors
std::string cmd = std::string(g_ExternRemux) + " " + Parameter; if (setpgid(0, 0) == -1)
execl("/bin/sh", "sh", "-c", cmd.c_str(), NULL); esyslog("streamdev-server: externremux setpgid failed: %m");
_exit(-1);
if (access(opt_remux, X_OK) == -1) {
esyslog("streamdev-server %s: %m", opt_remux);
_exit(-1);
}
if (execle("/bin/sh", "sh", "-c", opt_remux, NULL, env) == -1) {
esyslog("streamdev-server: externremux script '%s' execution failed: %m", opt_remux);
_exit(-1);
}
// should never be reached
_exit(0);
} }
close(inpipe[0]); close(inpipe[0]);
@@ -85,16 +216,31 @@ cTSExt::~cTSExt()
m_Active = false; m_Active = false;
Cancel(3); Cancel(3);
if (m_Process > 0) { if (m_Process > 0) {
// close pipes
close(m_Outpipe); close(m_Outpipe);
close(m_Inpipe); close(m_Inpipe);
kill(m_Process, SIGTERM); // signal and wait for termination
for (int i = 0; waitpid(m_Process, NULL, WNOHANG) == 0; i++) { if (kill(m_Process, SIGINT) < 0) {
if (i == 20) { esyslog("streamdev-server: externremux SIGINT failed: %m");
esyslog("streamdev-server: externremux process won't stop - killing it");
kill(m_Process, SIGKILL);
}
cCondWait::SleepMs(100);
} }
else {
int i = 0;
int retval;
while ((retval = waitpid(m_Process, NULL, WNOHANG)) == 0) {
if ((++i % 20) == 0) {
esyslog("streamdev-server: externremux process won't stop - killing it");
kill(m_Process, SIGKILL);
}
cCondWait::SleepMs(100);
}
if (retval < 0)
esyslog("streamdev-server: externremux process waitpid failed: %m");
else
Dprintf("streamdev-server: externremux child (%d) exited as expected\n", m_Process);
}
m_Process = -1;
} }
} }
@@ -150,9 +296,9 @@ void cTSExt::Put(const uchar *Data, int Count)
} }
} }
cExternRemux::cExternRemux(int VPid, const int *APids, const int *Dpids, const int *SPids, std::string Parameter): cExternRemux::cExternRemux(const cServerConnection *Connection, const cChannel *Channel, const int *Apids, const int *Dpids):
m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE, TS_SIZE * 2)), m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE, TS_SIZE * 2)),
m_Remux(new cTSExt(m_ResultBuffer, Parameter)) m_Remux(new cTSExt(m_ResultBuffer, Connection, Channel, Apids, Dpids))
{ {
m_ResultBuffer->SetTimeouts(500, 100); m_ResultBuffer->SetTimeouts(500, 100);
} }

View File

@@ -5,7 +5,10 @@
#include <vdr/ringbuffer.h> #include <vdr/ringbuffer.h>
#include <string> #include <string>
extern const char *g_ExternRemux; class cChannel;
class cServerConnection;
namespace Streamdev {
class cTSExt; class cTSExt;
@@ -15,7 +18,7 @@ private:
cTSExt *m_Remux; cTSExt *m_Remux;
public: public:
cExternRemux(int VPid, const int *APids, const int *Dpids, const int *SPids, std::string Parameter); cExternRemux(const cServerConnection *Connection, const cChannel *Channel, const int *APids, const int *Dpids);
virtual ~cExternRemux(); virtual ~cExternRemux();
int Put(const uchar *Data, int Count); int Put(const uchar *Data, int Count);
@@ -23,4 +26,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

View File

@@ -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)

View File

@@ -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

File diff suppressed because it is too large Load Diff

56
remux/ts2pes.h Normal file
View 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.4 2009/07/06 06:11:11 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).
virtual ~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

View File

@@ -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++) {

View File

@@ -1,9 +1,14 @@
#ifndef VDR_STREAMDEV_TS2PESREMUX_H #ifndef VDR_STREAMDEV_TS2PSREMUX_H
#define VDR_STREAMDEV_TS2PESREMUX_H #define VDR_STREAMDEV_TS2PSREMUX_H
#include "remux/tsremux.h" #include "remux/tsremux.h"
#include <vdr/remux.h> #include "server/streamer.h"
#include <vdr/ringbuffer.h>
#ifndef MAXTRACKS
#define MAXTRACKS 64
#endif
namespace Streamdev {
class cTS2PS; class cTS2PS;
@@ -11,7 +16,7 @@ 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;
@@ -26,4 +31,6 @@ public:
void Del(int Count) { m_ResultBuffer->Del(Count); } void Del(int Count) { m_ResultBuffer->Del(Count); }
}; };
#endif // VDR_STREAMDEV_TS2PESREMUX_H } // namespace Streamdev
#endif // VDR_STREAMDEV_TS2PSREMUX_H

View File

@@ -1,11 +1,16 @@
#include "remux/tsremux.h" #include "remux/tsremux.h"
#define SC_PICTURE 0x00 // "picture header" #define SC_PICTURE 0x00 // "picture header"
#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;
@@ -39,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;

View File

@@ -4,30 +4,25 @@
#include "libdvbmpeg/transform.h" #include "libdvbmpeg/transform.h"
#include <vdr/remux.h> #include <vdr/remux.h>
#define RESULTBUFFERSIZE KILOBYTE(256) // Picture types:
#define NO_PICTURE 0
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 ~cTSRemux() {};
virtual ~cTSRemux();
virtual uchar *Process(const uchar *Data, int &Count, int &Result);*/ virtual int Put(const uchar *Data, int Count) = 0;
virtual uchar *Get(int &Count) = 0;
virtual void Del(int Count) = 0;
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

82
server/Makefile Normal file
View File

@@ -0,0 +1,82 @@
#
# Makefile for a Video Disk Recorder plugin
#
# $Id: Makefile,v 1.2 2010/07/19 13:49:31 schmirl Exp $
# The official name of this plugin.
# 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 = streamdev-server
### Includes and Defines (add further entries here):
DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
### The object files (add further files here):
COMMONOBJS = ../common.o
SERVEROBJS = $(PLUGIN).o \
server.o component.o connection.o \
componentVTP.o connectionVTP.o \
componentHTTP.o connectionHTTP.o menuHTTP.o \
componentIGMP.o connectionIGMP.o \
streamer.o livestreamer.o livefilter.o recplayer.o \
menu.o suspend.o setup.o
### The main target:
.PHONY: all i18n clean
all: libvdr-$(PLUGIN).so i18n
### Implicit rules:
%.o: %.c
$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
### Dependencies:
MAKEDEP = $(CXX) -MM -MG
DEPFILE = .dependencies
$(DEPFILE): Makefile
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(SERVEROBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) > $@
-include $(DEPFILE)
### Internationalization (I18N):
PODIR = po
LOCALEDIR = $(VDRDIR)/locale
I18Npo = $(wildcard $(PODIR)/*.po)
I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
I18Npot = $(PODIR)/$(PLUGIN).pot
%.mo: %.po
msgfmt -c -o $@ $<
$(I18Npot): $(SERVEROBJS:%.o=%.c)
xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --msgid-bugs-address='<http://www.vdr-developer.org/mantisbt/>' -o $@ $^
%.po: $(I18Npot)
msgmerge -U --no-wrap --no-location --backup=none -q $@ $<
@touch $@
$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
@mkdir -p $(dir $@)
cp $< $@
i18n: $(I18Nmsgs)
### Targets:
libvdr-$(PLUGIN).so: $(SERVEROBJS) $(COMMONOBJS) \
../tools/sockettools.a ../remux/remux.a ../libdvbmpeg/libdvbmpegtools.a
%.so:
$(CXX) $(CXXFLAGS) -shared $^ -o $@
@cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION)
clean:
@-rm -f $(COMMONOBJS) $(SERVEROBJS) $(DEPFILE) $(PODIR)/*.mo $(PODIR)/*.pot *.so *.tgz core* *~

View File

@@ -1,13 +1,14 @@
/* /*
* $Id: component.c,v 1.3 2005/05/09 20:22:29 lordjaxom Exp $ * $Id: component.c,v 1.4 2009/02/13 10:39:22 schmirl Exp $
*/ */
#include "server/component.h" #include "server/component.h"
#include "server/connection.h" #include "server/connection.h"
cServerComponent::cServerComponent(const char *Protocol, const char *ListenIp, cServerComponent::cServerComponent(const char *Protocol, const char *ListenIp,
uint ListenPort): uint ListenPort, int Type, int IpProto):
m_Protocol(Protocol), m_Protocol(Protocol),
m_Listen(Type, IpProto),
m_ListenIp(ListenIp), m_ListenIp(ListenIp),
m_ListenPort(ListenPort) m_ListenPort(ListenPort)
{ {

View File

@@ -1,5 +1,5 @@
/* /*
* $Id: component.h,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $ * $Id: component.h,v 1.3 2009/02/13 10:39:22 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEV_SERVERS_COMPONENT_H #ifndef VDR_STREAMDEV_SERVERS_COMPONENT_H
@@ -17,8 +17,8 @@ class cServerConnection;
class cServerComponent: public cListObject { class cServerComponent: public cListObject {
private: private:
cTBSocket m_Listen;
const char *m_Protocol; const char *m_Protocol;
cTBSocket m_Listen;
const char *m_ListenIp; const char *m_ListenIp;
uint m_ListenPort; uint m_ListenPort;
@@ -27,7 +27,7 @@ protected:
virtual cServerConnection *NewClient(void) = 0; virtual cServerConnection *NewClient(void) = 0;
public: public:
cServerComponent(const char *Protocol, const char *ListenIp, uint ListenPort); cServerComponent(const char *Protocol, const char *ListenIp, uint ListenPort, int Type = SOCK_STREAM, int IpProto = 0);
virtual ~cServerComponent(); virtual ~cServerComponent();
/* Starts listening on the specified Port, override if you want to do things /* Starts listening on the specified Port, override if you want to do things

461
server/componentIGMP.c Normal file
View File

@@ -0,0 +1,461 @@
/*
* $Id: componentIGMP.c,v 1.2 2009/07/03 21:44:19 schmirl Exp $
*/
#include <netinet/ip.h>
#include <netinet/igmp.h>
#include "server/componentIGMP.h"
#include "server/connectionIGMP.h"
#include "server/server.h"
#include "server/setup.h"
#ifndef IGMP_ALL_HOSTS
#define IGMP_ALL_HOSTS htonl(0xE0000001L)
#endif
#ifndef IGMP_ALL_ROUTER
#define IGMP_ALL_ROUTER htonl(0xE0000002L)
#endif
// IGMP parameters according to RFC2236. All time values in seconds.
#define IGMP_ROBUSTNESS 2
#define IGMP_QUERY_INTERVAL 125
#define IGMP_QUERY_RESPONSE_INTERVAL 10
#define IGMP_GROUP_MEMBERSHIP_INTERVAL (2 * IGMP_QUERY_INTERVAL + IGMP_QUERY_RESPONSE_INTERVAL)
#define IGMP_OTHER_QUERIER_PRESENT_INTERVAL (2 * IGMP_QUERY_INTERVAL + IGMP_QUERY_RESPONSE_INTERVAL / 2)
#define IGMP_STARTUP_QUERY_INTERVAL (IGMP_QUERY_INTERVAL / 4)
#define IGMP_STARTUP_QUERY_COUNT IGMP_ROBUSTNESS
// This value is 1/10 sec. RFC default is 10. Reduced to minimum to free unused channels ASAP
#define IGMP_LAST_MEMBER_QUERY_INTERVAL_TS 1
#define IGMP_LAST_MEMBER_QUERY_COUNT IGMP_ROBUSTNESS
// operations on struct timeval
#define TV_CMP(a, cmp, b) (a.tv_sec == b.tv_sec ? a.tv_usec cmp b.tv_usec : a.tv_sec cmp b.tv_sec)
#define TV_SET(tv) (tv.tv_sec || tv.tv_usec)
#define TV_CLR(tv) memset(&tv, 0, sizeof(tv))
#define TV_CPY(dst, src) memcpy(&dst, &src, sizeof(dst))
#define TV_ADD(dst, ts) dst.tv_sec += ts / 10; dst.tv_usec += (ts % 10) * 100000; if (dst.tv_usec >= 1000000) { dst.tv_usec -= 1000000; dst.tv_sec++; }
class cMulticastGroup: public cListObject
{
public:
in_addr_t group;
in_addr_t reporter;
struct timeval timeout;
struct timeval v1timer;
struct timeval retransmit;
cMulticastGroup(in_addr_t Group);
};
cMulticastGroup::cMulticastGroup(in_addr_t Group) :
group(Group),
reporter(0)
{
TV_CLR(timeout);
TV_CLR(v1timer);
TV_CLR(retransmit);
}
void logIGMP(uint8_t type, struct in_addr Src, struct in_addr Dst, struct in_addr Grp)
{
const char* msg;
switch (type) {
case IGMP_MEMBERSHIP_QUERY: msg = "membership query"; break;
case IGMP_V1_MEMBERSHIP_REPORT: msg = "V1 membership report"; break;
case IGMP_V2_MEMBERSHIP_REPORT: msg = "V2 membership report"; break;
case IGMP_V2_LEAVE_GROUP: msg = "leave group"; break;
default: msg = "unknown"; break;
}
char* s = strdup(inet_ntoa(Src));
char* d = strdup(inet_ntoa(Dst));
dsyslog("streamdev-server IGMP: Received %s from %s (dst %s) for %s", msg, s, d, inet_ntoa(Grp));
free(s);
free(d);
}
/* Taken from http://tools.ietf.org/html/rfc1071 */
uint16_t inetChecksum(uint16_t *addr, int count)
{
uint32_t sum = 0;
while (count > 1) {
sum += *addr++;
count -= 2;
}
if( count > 0 )
sum += * (uint8_t *) addr;
while (sum>>16)
sum = (sum & 0xffff) + (sum >> 16);
return ~sum;
}
cComponentIGMP::cComponentIGMP(void):
cServerComponent("IGMP", "0.0.0.0", 0, SOCK_RAW, IPPROTO_IGMP),
cThread("IGMP timeout handler"),
m_BindIp(inet_addr(StreamdevServerSetup.IGMPBindIP)),
m_MaxChannelNumber(0),
m_StartupQueryCount(IGMP_STARTUP_QUERY_COUNT),
m_Querier(true)
{
}
cComponentIGMP::~cComponentIGMP(void)
{
}
cMulticastGroup* cComponentIGMP::FindGroup(in_addr_t Group) const
{
cMulticastGroup *group = m_Groups.First();
while (group && group->group != Group)
group = m_Groups.Next(group);
return group;
}
bool cComponentIGMP::Initialize(void)
{
if (cServerComponent::Initialize() && IGMPMembership(IGMP_ALL_ROUTER))
{
for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
{
if (channel->GroupSep())
continue;
int num = channel->Number();
if (!IGMPMembership(htonl(MULTICAST_PRIV_MIN + num)))
break;
m_MaxChannelNumber = num;
}
if (m_MaxChannelNumber == 0)
{
IGMPMembership(IGMP_ALL_ROUTER, false);
esyslog("streamdev-server IGMP: no multicast group joined");
}
else
{
Start();
}
}
return m_MaxChannelNumber > 0;
}
void cComponentIGMP::Destruct(void)
{
if (m_MaxChannelNumber > 0)
{
Cancel(-1);
m_CondWait.Signal();
Cancel(2);
for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
{
if (channel->GroupSep())
continue;
int num = channel->Number();
if (num > m_MaxChannelNumber)
break;
IGMPMembership(htonl(MULTICAST_PRIV_MIN + num), false);
}
IGMPMembership(IGMP_ALL_ROUTER, false);
}
m_MaxChannelNumber = 0;
cServerComponent::Destruct();
}
cServerConnection *cComponentIGMP::NewClient(void)
{
return new cConnectionIGMP("IGMP", StreamdevServerSetup.IGMPClientPort, (eStreamType) StreamdevServerSetup.IGMPStreamType);
}
cServerConnection* cComponentIGMP::Accept(void)
{
ssize_t recv_len;
int ip_hdrlen, ip_datalen;
struct ip *ip;
struct igmp *igmp;
while ((recv_len = ::recvfrom(Socket(), m_ReadBuffer, sizeof(m_ReadBuffer), 0, NULL, NULL)) < 0 && errno == EINTR)
errno = 0;
if (recv_len < 0) {
esyslog("streamdev-server IGMP: read failed: %m");
return NULL;
}
else if (recv_len < (ssize_t) sizeof(struct ip)) {
esyslog("streamdev-server IGMP: IP packet too short");
return NULL;
}
ip = (struct ip*) m_ReadBuffer;
// filter out my own packets
if (ip->ip_src.s_addr == m_BindIp)
return NULL;
ip_hdrlen = ip->ip_hl << 2;
#ifdef __FreeBSD__
ip_datalen = ip->ip_len;
#else
ip_datalen = ntohs(ip->ip_len) - ip_hdrlen;
#endif
if (ip->ip_p != IPPROTO_IGMP) {
esyslog("streamdev-server IGMP: Unexpected protocol %hhu", ip->ip_p);
return NULL;
}
if (recv_len < ip_hdrlen + IGMP_MINLEN) {
esyslog("streamdev-server IGMP: packet too short");
return NULL;
}
igmp = (struct igmp*) (m_ReadBuffer + ip_hdrlen);
uint16_t chksum = igmp->igmp_cksum;
igmp->igmp_cksum = 0;
if (chksum != inetChecksum((uint16_t *)igmp, ip_datalen))
{
esyslog("INVALID CHECKSUM %d %d %d %lu 0x%x 0x%x", (int) ntohs(ip->ip_len), ip_hdrlen, ip_datalen, (unsigned long int) recv_len, chksum, inetChecksum((uint16_t *)igmp, ip_datalen));
return NULL;
}
logIGMP(igmp->igmp_type, ip->ip_src, ip->ip_dst, igmp->igmp_group);
return ProcessMessage(igmp, igmp->igmp_group.s_addr, ip->ip_src.s_addr);
}
cServerConnection* cComponentIGMP::ProcessMessage(struct igmp *Igmp, in_addr_t Group, in_addr_t Sender)
{
cServerConnection* conn = NULL;
cMulticastGroup* group;
LOCK_THREAD;
switch (Igmp->igmp_type) {
case IGMP_MEMBERSHIP_QUERY:
if (ntohl(Sender) < ntohl(m_BindIp))
IGMPStartOtherQuerierPresentTimer();
break;
case IGMP_V1_MEMBERSHIP_REPORT:
case IGMP_V2_MEMBERSHIP_REPORT:
group = FindGroup(Group);
if (!group) {
group = new cMulticastGroup(Group);
m_Groups.Add(group);
}
conn = IGMPStartMulticast(group);
IGMPStartTimer(group, Sender);
if (Igmp->igmp_type == IGMP_V1_MEMBERSHIP_REPORT)
IGMPStartV1HostTimer(group);
break;
case IGMP_V2_LEAVE_GROUP:
group = FindGroup(Group);
if (group && !TV_SET(group->v1timer)) {
if (group->reporter == Sender) {
IGMPStartTimerAfterLeave(group, m_Querier ? IGMP_LAST_MEMBER_QUERY_INTERVAL_TS : Igmp->igmp_code);
if (m_Querier)
IGMPSendGroupQuery(group);
IGMPStartRetransmitTimer(group);
}
m_CondWait.Signal();
}
break;
default:
break;
}
return conn;
}
void cComponentIGMP::Action()
{
while (Running()) {
struct timeval now;
struct timeval next;
gettimeofday(&now, NULL);
TV_CPY(next, now);
next.tv_sec += IGMP_QUERY_INTERVAL;
cMulticastGroup *del = NULL;
{
LOCK_THREAD;
if (TV_CMP(m_GeneralQueryTimer, <, now)) {
dsyslog("General Query");
IGMPSendGeneralQuery();
IGMPStartGeneralQueryTimer();
}
if (TV_CMP(next, >, m_GeneralQueryTimer))
TV_CPY(next, m_GeneralQueryTimer);
for (cMulticastGroup *group = m_Groups.First(); group; group = m_Groups.Next(group)) {
if (TV_CMP(group->timeout, <, now)) {
IGMPStopMulticast(group);
IGMPClearRetransmitTimer(group);
if (del)
m_Groups.Del(del);
del = group;
}
else if (m_Querier && TV_SET(group->retransmit) && TV_CMP(group->retransmit, <, now)) {
IGMPSendGroupQuery(group);
IGMPStartRetransmitTimer(group);
if (TV_CMP(next, >, group->retransmit))
TV_CPY(next, group->retransmit);
}
else if (TV_SET(group->v1timer) && TV_CMP(group->v1timer, <, now)) {
TV_CLR(group->v1timer);
}
else {
if (TV_CMP(next, >, group->timeout))
TV_CPY(next, group->timeout);
if (TV_SET(group->retransmit) && TV_CMP(next, >, group->retransmit))
TV_CPY(next, group->retransmit);
if (TV_SET(group->v1timer) && TV_CMP(next, >, group->v1timer))
TV_CPY(next, group->v1timer);
}
}
if (del)
m_Groups.Del(del);
}
int sleep = (next.tv_sec - now.tv_sec) * 1000;
sleep += (next.tv_usec - now.tv_usec) / 1000;
if (next.tv_usec < now.tv_usec)
sleep += 1000;
dsyslog("Sleeping %d ms", sleep);
m_CondWait.Wait(sleep);
}
}
bool cComponentIGMP::IGMPMembership(in_addr_t Group, bool Add)
{
struct ip_mreqn mreq;
mreq.imr_multiaddr.s_addr = Group;
mreq.imr_address.s_addr = INADDR_ANY;
mreq.imr_ifindex = 0;
if (setsockopt(Socket(), IPPROTO_IP, Add ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
{
esyslog("streamdev-server IGMP: unable to %s %s: %m", Add ? "join" : "leave", inet_ntoa(mreq.imr_multiaddr));
if (errno == ENOBUFS)
esyslog("consider increasing sys.net.ipv4.igmp_max_memberships");
return false;
}
return true;
}
void cComponentIGMP::IGMPSendQuery(in_addr_t Group, int Timeout)
{
struct sockaddr_in dst;
struct igmp query;
dst.sin_family = AF_INET;
dst.sin_port = IPPROTO_IGMP;
dst.sin_addr.s_addr = Group;
query.igmp_type = IGMP_MEMBERSHIP_QUERY;
query.igmp_code = Timeout * 10;
query.igmp_cksum = 0;
query.igmp_group.s_addr = (Group == IGMP_ALL_HOSTS) ? 0 : Group;
query.igmp_cksum = inetChecksum((uint16_t *) &query, sizeof(query));
for (int i = 0; i < 5 && ::sendto(Socket(), &query, sizeof(query), 0, (sockaddr*)&dst, sizeof(dst)) == -1; i++) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
esyslog("streamdev-server IGMP: unable to query group %s: %m", inet_ntoa(dst.sin_addr));
break;
}
cCondWait::SleepMs(10);
}
}
// Querier state actions
void cComponentIGMP::IGMPStartGeneralQueryTimer()
{
m_Querier = true;
if (m_StartupQueryCount) {
gettimeofday(&m_GeneralQueryTimer, NULL);
m_GeneralQueryTimer.tv_sec += IGMP_STARTUP_QUERY_INTERVAL;
m_StartupQueryCount--;
}
else {
gettimeofday(&m_GeneralQueryTimer, NULL);
m_GeneralQueryTimer.tv_sec += IGMP_QUERY_INTERVAL;
}
}
void cComponentIGMP::IGMPStartOtherQuerierPresentTimer()
{
m_Querier = false;
m_StartupQueryCount = 0;
gettimeofday(&m_GeneralQueryTimer, NULL);
m_GeneralQueryTimer.tv_sec += IGMP_OTHER_QUERIER_PRESENT_INTERVAL;
}
void cComponentIGMP::IGMPSendGeneralQuery()
{
IGMPSendQuery(IGMP_ALL_HOSTS, IGMP_QUERY_RESPONSE_INTERVAL);
}
// Group state actions
void cComponentIGMP::IGMPStartTimer(cMulticastGroup* Group, in_addr_t Member)
{
gettimeofday(&Group->timeout, NULL);
Group->timeout.tv_sec += IGMP_GROUP_MEMBERSHIP_INTERVAL;
TV_CLR(Group->retransmit);
Group->reporter = Member;
}
void cComponentIGMP::IGMPStartV1HostTimer(cMulticastGroup* Group)
{
gettimeofday(&Group->v1timer, NULL);
Group->v1timer.tv_sec += IGMP_GROUP_MEMBERSHIP_INTERVAL;
}
void cComponentIGMP::IGMPStartTimerAfterLeave(cMulticastGroup* Group, unsigned int MaxResponseTimeTs)
{
//Group->Update(time(NULL) + MaxResponseTime * IGMP_LAST_MEMBER_QUERY_COUNT / 10);
MaxResponseTimeTs *= IGMP_LAST_MEMBER_QUERY_COUNT;
gettimeofday(&Group->timeout, NULL);
TV_ADD(Group->timeout, MaxResponseTimeTs);
TV_CLR(Group->retransmit);
Group->reporter = 0;
}
void cComponentIGMP::IGMPStartRetransmitTimer(cMulticastGroup* Group)
{
gettimeofday(&Group->retransmit, NULL);
TV_ADD(Group->retransmit, IGMP_LAST_MEMBER_QUERY_INTERVAL_TS);
}
void cComponentIGMP::IGMPClearRetransmitTimer(cMulticastGroup* Group)
{
TV_CLR(Group->retransmit);
}
void cComponentIGMP::IGMPSendGroupQuery(cMulticastGroup* Group)
{
IGMPSendQuery(Group->group, IGMP_LAST_MEMBER_QUERY_INTERVAL_TS);
}
cServerConnection* cComponentIGMP::IGMPStartMulticast(cMulticastGroup* Group)
{
cServerConnection *conn = NULL;
in_addr_t g = ntohl(Group->group);
if (g > MULTICAST_PRIV_MIN && g <= MULTICAST_PRIV_MAX) {
cThreadLock lock;
cChannel *channel = Channels.GetByNumber(g - MULTICAST_PRIV_MIN);
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
cServerConnection *s = clients.First();
while (s) {
if (s->RemoteIpAddr() == Group->group)
break;
s = clients.Next(s);
}
if (!s) {
conn = NewClient();
if (!((cConnectionIGMP *)conn)->SetChannel(channel, Group->group)) {
DELETENULL(conn);
}
}
}
return conn;
}
void cComponentIGMP::IGMPStopMulticast(cMulticastGroup* Group)
{
cThreadLock lock;
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
for (cServerConnection *s = clients.First(); s; s = clients.Next(s)) {
if (s->RemoteIpAddr() == Group->group)
s->Close();
}
}

61
server/componentIGMP.h Normal file
View File

@@ -0,0 +1,61 @@
/*
* $Id: componentIGMP.h,v 1.1 2009/02/13 10:39:22 schmirl Exp $
*/
#ifndef VDR_STREAMDEV_IGMPSERVER_H
#define VDR_STREAMDEV_IGMPSERVER_H
#include <sys/time.h>
#include <time.h>
#include <vdr/thread.h>
#include "server/component.h"
class cMulticastGroup;
class cComponentIGMP: public cServerComponent, public cThread {
private:
char m_ReadBuffer[2048];
cList<cMulticastGroup> m_Groups;
in_addr_t m_BindIp;
int m_MaxChannelNumber;
struct timeval m_GeneralQueryTimer;
int m_StartupQueryCount;
bool m_Querier;
cCondWait m_CondWait;
cMulticastGroup* FindGroup(in_addr_t Group) const;
/* Add or remove local host to multicast group */
bool IGMPMembership(in_addr_t Group, bool Add = true);
void IGMPSendQuery(in_addr_t Group, int Timeout);
cServerConnection* ProcessMessage(struct igmp *Igmp, in_addr_t Group, in_addr_t Sender);
void IGMPStartGeneralQueryTimer();
void IGMPStartOtherQuerierPresentTimer();
void IGMPSendGeneralQuery();
void IGMPStartTimer(cMulticastGroup* Group, in_addr_t Member);
void IGMPStartV1HostTimer(cMulticastGroup* Group);
void IGMPStartTimerAfterLeave(cMulticastGroup* Group, unsigned int MaxResponseTime);
void IGMPStartRetransmitTimer(cMulticastGroup* Group);
void IGMPClearRetransmitTimer(cMulticastGroup* Group);
void IGMPSendGroupQuery(cMulticastGroup* Group);
cServerConnection* IGMPStartMulticast(cMulticastGroup* Group);
void IGMPStopMulticast(cMulticastGroup* Group);
virtual void Action();
protected:
virtual cServerConnection *NewClient(void);
public:
virtual bool Initialize(void);
virtual void Destruct(void);
virtual cServerConnection* Accept(void);
cComponentIGMP(void);
~cComponentIGMP(void);
};
#endif // VDR_STREAMDEV_IGMPSERVER_H

View File

@@ -1,5 +1,5 @@
/* /*
* $Id: connection.c,v 1.10 2007/05/07 12:25:11 schmirl Exp $ * $Id: connection.c,v 1.16 2010/08/03 10:51:53 schmirl Exp $
*/ */
#include "server/connection.h" #include "server/connection.h"
@@ -8,11 +8,62 @@
#include "common.h" #include "common.h"
#include <vdr/tools.h> #include <vdr/tools.h>
#include <vdr/thread.h>
#include <vdr/transfer.h>
#include <string.h> #include <string.h>
#include <stdarg.h> #include <stdarg.h>
#include <errno.h> #include <errno.h>
cServerConnection::cServerConnection(const char *Protocol): class cSwitchLive {
private:
cMutex mutex;
cCondWait switched;
cDevice *device;
const cChannel *channel;
public:
cDevice* Switch(cDevice *Device, const cChannel *Channel);
void Switch(void);
cSwitchLive(void);
};
cSwitchLive::cSwitchLive(): device(NULL), channel(NULL)
{
}
cDevice* cSwitchLive::Switch(cDevice *Device, const cChannel *Channel)
{
mutex.Lock();
device = Device;
channel = Channel;
mutex.Unlock();
switched.Wait();
return device;
}
void cSwitchLive::Switch(void)
{
mutex.Lock();
if (channel && device) {
cDevice::SetAvoidDevice(device);
if (!Channels.SwitchTo(cDevice::CurrentChannel())) {
if (StreamdevServerSetup.SuspendMode == smAlways) {
Channels.SwitchTo(channel->Number());
Skins.Message(mtInfo, tr("Streaming active"));
}
else {
esyslog("streamdev: Can't receive channel %d (%s) from device %d. Moving live TV to other device failed (PrimaryDevice=%d, ActualDevice=%d)", channel->Number(), channel->Name(), device->CardIndex(), cDevice::PrimaryDevice()->CardIndex(), cDevice::ActualDevice()->CardIndex());
device = NULL;
}
}
// make sure we don't come in here next time
channel = NULL;
switched.Signal();
}
mutex.Unlock();
}
cServerConnection::cServerConnection(const char *Protocol, int Type):
cTBSocket(Type),
m_Protocol(Protocol), m_Protocol(Protocol),
m_DeferClose(false), m_DeferClose(false),
m_Pending(false), m_Pending(false),
@@ -20,10 +71,72 @@ cServerConnection::cServerConnection(const char *Protocol):
m_WriteBytes(0), m_WriteBytes(0),
m_WriteIndex(0) m_WriteIndex(0)
{ {
m_SwitchLive = new cSwitchLive();
} }
cServerConnection::~cServerConnection() cServerConnection::~cServerConnection()
{ {
delete m_SwitchLive;
}
const cChannel* cServerConnection::ChannelFromString(const char *String, int *Apid, int *Dpid) {
const cChannel *channel = NULL;
char *string = strdup(String);
char *ptr, *end;
int apididx = 0;
if ((ptr = strrchr(string, '+')) != NULL) {
*(ptr++) = '\0';
apididx = strtoul(ptr, &end, 10);
Dprintf("found apididx: %d\n", apididx);
}
if (isnumber(string)) {
int temp = strtol(String, NULL, 10);
if (temp >= 1 && temp <= Channels.MaxNumber())
channel = Channels.GetByNumber(temp);
} else {
channel = Channels.GetByChannelID(tChannelID::FromString(string));
if (channel == NULL) {
int i = 1;
while ((channel = Channels.GetByNumber(i, 1)) != NULL) {
if (String == channel->Name())
break;
i = channel->Number() + 1;
}
}
}
if (channel != NULL && apididx > 0) {
int apid = 0, dpid = 0;
int index = 1;
for (int i = 0; channel->Apid(i) != 0; ++i, ++index) {
if (index == apididx) {
apid = channel->Apid(i);
break;
}
}
if (apid == 0) {
for (int i = 0; channel->Dpid(i) != 0; ++i, ++index) {
if (index == apididx) {
dpid = channel->Dpid(i);
break;
}
}
}
if (Apid != NULL)
*Apid = apid;
if (Dpid != NULL)
*Dpid = dpid;
}
free(string);
return channel;
} }
bool cServerConnection::Read(void) bool cServerConnection::Read(void)
@@ -124,82 +237,183 @@ bool cServerConnection::Respond(const char *Message, bool Last, ...)
return true; return true;
} }
bool cServerConnection::Close()
{
if (IsOpen())
isyslog("streamdev-server: closing %s connection to %s:%d", Protocol(), RemoteIp().c_str(), RemotePort());
return cTBSocket::Close();
}
#if APIVERSNUM >= 10700
static int GetClippedNumProvidedSystems(int AvailableBits, cDevice *Device)
{
int MaxNumProvidedSystems = (1 << AvailableBits) - 1;
int NumProvidedSystems = Device->NumProvidedSystems();
if (NumProvidedSystems > MaxNumProvidedSystems) {
esyslog("ERROR: device %d supports %d modulation systems but cDevice::GetDevice() currently only supports %d delivery systems which should be fixed", Device->CardIndex() + 1, NumProvidedSystems, MaxNumProvidedSystems);
NumProvidedSystems = MaxNumProvidedSystems;
}
else if (NumProvidedSystems <= 0) {
esyslog("ERROR: device %d reported an invalid number (%d) of supported delivery systems - assuming 1", Device->CardIndex() + 1, NumProvidedSystems);
NumProvidedSystems = 1;
}
return NumProvidedSystems;
}
#endif
/*
* copy of cDevice::GetDevice(...) but without side effects (not detaching receivers)
*/
cDevice* cServerConnection::CheckDevice(const cChannel *Channel, int Priority, bool LiveView, const cDevice *AvoidDevice)
{
//cDevice *AvoidDevice = avoidDevice;
//avoidDevice = NULL;
// Collect the current priorities of all CAM slots that can decrypt the channel:
int NumCamSlots = CamSlots.Count();
int SlotPriority[NumCamSlots];
int NumUsableSlots = 0;
if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
SlotPriority[CamSlot->Index()] = MAXPRIORITY + 1; // assumes it can't be used
if (CamSlot->ModuleStatus() == msReady) {
if (CamSlot->ProvidesCa(Channel->Caids())) {
if (!ChannelCamRelations.CamChecked(Channel->GetChannelID(), CamSlot->SlotNumber())) {
SlotPriority[CamSlot->Index()] = CamSlot->Priority();
NumUsableSlots++;
}
}
}
}
if (!NumUsableSlots)
return NULL; // no CAM is able to decrypt this channel
}
cDevice *d = NULL;
//cCamSlot *s = NULL;
uint32_t Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
for (int j = 0; j < NumCamSlots || !NumUsableSlots; j++) {
if (NumUsableSlots && SlotPriority[j] > MAXPRIORITY)
continue; // there is no CAM available in this slot
for (int i = 0; i < cDevice::NumDevices(); i++) {
cDevice *device = cDevice::GetDevice(i);
if (device == AvoidDevice)
continue; // we've been asked to skip this device
if (Channel->Ca() && Channel->Ca() <= CA_DVB_MAX && Channel->Ca() != device->CardIndex() + 1)
continue; // a specific card was requested, but not this one
if (NumUsableSlots && !CamSlots.Get(j)->Assign(device, true))
continue; // CAM slot can't be used with this device
bool ndr;
if (device->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
if (NumUsableSlots && device->CamSlot() && device->CamSlot() != CamSlots.Get(j))
ndr = true; // using a different CAM slot requires detaching receivers
// Put together an integer number that reflects the "impact" using
// this device would have on the overall system. Each condition is represented
// by one bit in the number (or several bits, if the condition is actually
// a numeric value). The sequence in which the conditions are listed corresponds
// to their individual severity, where the one listed first will make the most
// difference, because it results in the most significant bit of the result.
uint32_t imp = 0;
imp <<= 1; imp |= LiveView ? !device->IsPrimaryDevice() || ndr : 0; // prefer the primary device for live viewing if we don't need to detach existing receivers
imp <<= 1; imp |= !device->Receiving() && (device != cTransferControl::ReceiverDevice() || device->IsPrimaryDevice()) || ndr; // use receiving devices if we don't need to detach existing receivers, but avoid primary device in local transfer mode
imp <<= 1; imp |= device->Receiving(); // avoid devices that are receiving
#if APIVERSNUM >= 10700
imp <<= 4; imp |= GetClippedNumProvidedSystems(4, device) - 1; // avoid cards which support multiple delivery systems
#endif
imp <<= 1; imp |= device == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device
imp <<= 8; imp |= min(max(device->Priority() + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
imp <<= 8; imp |= min(max((NumUsableSlots ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
imp <<= 1; imp |= ndr; // avoid devices if we need to detach existing receivers
#if VDRVERSNUM < 10719
imp <<= 1; imp |= device->IsPrimaryDevice(); // avoid the primary device
#endif
imp <<= 1; imp |= NumUsableSlots ? 0 : device->HasCi(); // avoid cards with Common Interface for FTA channels
#if VDRVERSNUM < 10719
imp <<= 1; imp |= device->HasDecoder(); // avoid full featured cards
#else
imp <<= 1; imp |= device->AvoidRecording(); // avoid SD full featured cards
#endif
imp <<= 1; imp |= NumUsableSlots ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
#if VDRVERSNUM >= 10719
imp <<= 1; imp |= device->IsPrimaryDevice(); // avoid the primary device
#endif
if (imp < Impact) {
// This device has less impact than any previous one, so we take it.
Impact = imp;
d = device;
}
}
}
if (!NumUsableSlots)
break; // no CAM necessary, so just one loop over the devices
}
return d;
}
bool cServerConnection::UsedByLiveTV(cDevice *device)
{
return device == cTransferControl::ReceiverDevice() ||
(device->IsPrimaryDevice() && device->HasDecoder() && !device->Replaying());
}
cDevice *cServerConnection::GetDevice(const cChannel *Channel, int Priority) cDevice *cServerConnection::GetDevice(const cChannel *Channel, int Priority)
{ {
cDevice *device = NULL; // turn off the streams of this connection
Detach();
// This call may detach receivers of the device it returns
cDevice *device = cDevice::GetDevice(Channel, Priority, false);
/*Dprintf("+ Statistics:\n"); if (device && !device->IsTunedToTransponder(Channel)
Dprintf("+ Current Channel: %d\n", cDevice::CurrentChannel()); && UsedByLiveTV(device)) {
Dprintf("+ Current Device: %d\n", cDevice::ActualDevice()->CardIndex()); // now we would have to switch away live tv...let's see if live tv
Dprintf("+ Transfer Mode: %s\n", cDevice::ActualDevice() // can be handled by another device
== cDevice::PrimaryDevice() ? "false" : "true"); device = m_SwitchLive->Switch(device, Channel);
Dprintf("+ Replaying: %s\n", cDevice::PrimaryDevice()->Replaying() ? "true"
: "false");*/
Dprintf(" * GetDevice(const cChannel*, int)\n");
Dprintf(" * -------------------------------\n");
#if VDRVERSNUM < 10500
device = cDevice::GetDevice(Channel, Priority);
#else
device = cDevice::GetDevice(Channel, Priority, false);
#endif
Dprintf(" * Found following device: %p (%d)\n", device,
device ? device->CardIndex() + 1 : 0);
if (device == cDevice::ActualDevice())
Dprintf(" * is actual device\n");
if (!cSuspendCtl::IsActive() && StreamdevServerSetup.SuspendMode != smAlways)
Dprintf(" * NOT suspended\n");
if (!device || (device == cDevice::ActualDevice()
&& !cSuspendCtl::IsActive()
&& StreamdevServerSetup.SuspendMode != smAlways)) {
// mustn't switch actual device
// maybe a device would be free if THIS connection did turn off its streams?
Dprintf(" * trying again...\n");
const cChannel *current = Channels.GetByNumber(cDevice::CurrentChannel());
isyslog("streamdev-server: Detaching current receiver");
Detach();
#if VDRVERSNUM < 10500
device = cDevice::GetDevice(Channel, Priority);
#else
device = cDevice::GetDevice(Channel, Priority, false);
#endif
Attach();
Dprintf(" * Found following device: %p (%d)\n", device,
device ? device->CardIndex() + 1 : 0);
if (device == cDevice::ActualDevice())
Dprintf(" * is actual device\n");
if (!cSuspendCtl::IsActive()
&& StreamdevServerSetup.SuspendMode != smAlways)
Dprintf(" * NOT suspended\n");
if (current && !TRANSPONDER(Channel, current))
Dprintf(" * NOT same transponder\n");
if (device && (device == cDevice::ActualDevice()
&& !cSuspendCtl::IsActive()
&& StreamdevServerSetup.SuspendMode != smAlways
&& current != NULL
&& !TRANSPONDER(Channel, current))) {
// now we would have to switch away live tv...let's see if live tv
// can be handled by another device
cDevice *newdev = NULL;
for (int i = 0; i < cDevice::NumDevices(); ++i) {
cDevice *dev = cDevice::GetDevice(i);
if (dev->ProvidesChannel(current, 0) && dev != device) {
newdev = dev;
break;
}
}
Dprintf(" * Found device for live tv: %p (%d)\n", newdev,
newdev ? newdev->CardIndex() + 1 : 0);
if (newdev == NULL || newdev == device)
// no suitable device to continue live TV, giving up...
device = NULL;
else
newdev->SwitchChannel(current, true);
}
} }
if (!device) {
// can't switch - continue the current stream
Attach();
dsyslog("streamdev: GetDevice failed for channel %d (%s) at priority %d (PrimaryDevice=%d, ActualDevice=%d)", Channel->Number(), Channel->Name(), Priority, cDevice::PrimaryDevice()->CardIndex(), cDevice::ActualDevice()->CardIndex());
}
return device; return device;
} }
bool cServerConnection::ProvidesChannel(const cChannel *Channel, int Priority)
{
cDevice *device = CheckDevice(Channel, Priority, false);
if (!device || (StreamdevServerSetup.SuspendMode != smAlways
&& !device->IsTunedToTransponder(Channel)
&& UsedByLiveTV(device))) {
// no device available or the device is in use for live TV and suspend mode doesn't allow us to switch it:
// maybe a device would be free if THIS connection did turn off its streams?
Detach();
device = CheckDevice(Channel, Priority, false);
Attach();
if (device && StreamdevServerSetup.SuspendMode != smAlways
&& !device->IsTunedToTransponder(Channel)
&& UsedByLiveTV(device)) {
// now we would have to switch away live tv...let's see if live tv
// can be handled by another device
const cChannel *current = Channels.GetByNumber(cDevice::CurrentChannel());
cDevice *newdev = current ? CheckDevice(current, 0, true, device) : NULL;
if (newdev) {
dsyslog("streamdev: Providing channel %d (%s) at priority %d requires moving live TV to device %d (PrimaryDevice=%d, ActualDevice=%d)", Channel->Number(), Channel->Name(), Priority, newdev->CardIndex(), cDevice::PrimaryDevice()->CardIndex(), cDevice::ActualDevice()->CardIndex());
}
else {
device = NULL;
dsyslog("streamdev: Not providing channel %d (%s) at priority %d - live TV not suspended", Channel->Number(), Channel->Name(), Priority);
}
}
else if (!device)
dsyslog("streamdev: No device provides channel %d (%s) at priority %d", Channel->Number(), Channel->Name(), Priority);
}
return device;
}
void cServerConnection::MainThreadHook()
{
m_SwitchLive->Switch();
}
cString cServerConnection::ToText() const
{ return cString::sprintf("%s\t%s:%d", Protocol(), RemoteIp().c_str(), RemotePort()); }

View File

@@ -1,5 +1,5 @@
/* /*
* $Id: connection.h,v 1.5 2007/04/16 11:01:02 schmirl Exp $ * $Id: connection.h,v 1.10 2010/08/03 10:46:41 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEV_SERVER_CONNECTION_H #ifndef VDR_STREAMDEV_SERVER_CONNECTION_H
@@ -8,8 +8,14 @@
#include "tools/socket.h" #include "tools/socket.h"
#include "common.h" #include "common.h"
#include <map>
typedef std::map<std::string,std::string> tStrStrMap;
typedef std::pair<std::string,std::string> tStrStr;
class cChannel; class cChannel;
class cDevice; class cDevice;
class cSwitchLive;
/* Basic capabilities of a straight text-based protocol, most functions /* Basic capabilities of a straight text-based protocol, most functions
virtual to support more complicated protocols */ virtual to support more complicated protocols */
@@ -28,6 +34,19 @@ private:
uint m_WriteBytes; uint m_WriteBytes;
uint m_WriteIndex; uint m_WriteIndex;
cSwitchLive *m_SwitchLive;
tStrStrMap m_Headers;
/* Check if a device would be available for transfering the given
channel. This call has no side effects except for temporarily
detaching this connection's receivers. */
cDevice *CheckDevice(const cChannel *Channel, int Priority, bool LiveView, const cDevice *AvoidDevice = NULL);
/* Test if device is in use as the transfer mode receiver device
or a FF card, displaying live TV from internal tuner */
static bool UsedByLiveTV(cDevice *device);
protected: protected:
/* Will be called when a command terminated by a newline has been /* Will be called when a command terminated by a newline has been
received */ received */
@@ -41,12 +60,20 @@ protected:
virtual bool Respond(const char *Message, bool Last = true, ...); virtual bool Respond(const char *Message, bool Last = true, ...);
//__attribute__ ((format (printf, 2, 4))); //__attribute__ ((format (printf, 2, 4)));
/* Add a request header */
void SetHeader(const char *Name, const char *Value, const char *Prefix = "") { m_Headers.insert(tStrStr(std::string(Prefix) + Name, Value)); }
static const cChannel *ChannelFromString(const char *String, int *Apid = NULL, int *Dpid = NULL);
public: public:
/* If you derive, specify a short string such as HTTP for Protocol, which /* If you derive, specify a short string such as HTTP for Protocol, which
will be displayed in error messages */ will be displayed in error messages */
cServerConnection(const char *Protocol); cServerConnection(const char *Protocol, int Type = SOCK_STREAM);
virtual ~cServerConnection(); virtual ~cServerConnection();
/* If true, any client IP will be accepted */
virtual bool CanAuthenticate(void) { return false; }
/* Gets called if the client has been accepted by the core */ /* Gets called if the client has been accepted by the core */
virtual void Welcome(void) { } virtual void Welcome(void) { }
@@ -76,14 +103,33 @@ public:
/* Will make the socket close after sending all queued output data */ /* Will make the socket close after sending all queued output data */
void DeferClose(void) { m_DeferClose = true; } void DeferClose(void) { m_DeferClose = true; }
/* Will retrieve an unused device for transmitting data. Use the returned /* Close the socket */
virtual bool Close(void);
/* Will retrieve an unused device for transmitting data. Receivers have
already been attached from the device if necessary. Use the returned
cDevice in a following call to StartTransfer */ cDevice in a following call to StartTransfer */
cDevice *GetDevice(const cChannel *Channel, int Priority); cDevice *GetDevice(const cChannel *Channel, int Priority);
/* Test if a call to GetDevice would return a usable device. */
bool ProvidesChannel(const cChannel *Channel, int Priority);
/* Do things which must be done in VDR's main loop */
void MainThreadHook();
virtual void Flushed(void) {} virtual void Flushed(void) {}
virtual void Detach(void) = 0; virtual void Detach(void) = 0;
virtual void Attach(void) = 0; virtual void Attach(void) = 0;
/* This connections protocol name */
virtual const char* Protocol(void) const { return m_Protocol; }
/* Representation in menu */
virtual cString ToText(void) const;
/* std::map with additional information */
const tStrStrMap& Headers(void) const { return m_Headers; }
}; };
inline bool cServerConnection::HasData(void) const inline bool cServerConnection::HasData(void) const

View File

@@ -1,24 +1,25 @@
/* /*
* $Id: connectionHTTP.c,v 1.13 2008/03/28 15:11:40 schmirl Exp $ * $Id: connectionHTTP.c,v 1.21 2010/08/03 10:46:41 schmirl Exp $
*/ */
#include <ctype.h> #include <ctype.h>
#include "server/connectionHTTP.h" #include "server/connectionHTTP.h"
#include "server/menuHTTP.h" #include "server/menuHTTP.h"
#include "server/server.h"
#include "server/setup.h" #include "server/setup.h"
cConnectionHTTP::cConnectionHTTP(void): cConnectionHTTP::cConnectionHTTP(void):
cServerConnection("HTTP"), cServerConnection("HTTP"),
m_Status(hsRequest), m_Status(hsRequest),
m_LiveStreamer(NULL), m_LiveStreamer(NULL),
m_StreamerParameter(""),
m_Channel(NULL), m_Channel(NULL),
m_Apid(0),
m_StreamType((eStreamType)StreamdevServerSetup.HTTPStreamType), m_StreamType((eStreamType)StreamdevServerSetup.HTTPStreamType),
m_ChannelList(NULL) m_ChannelList(NULL)
{ {
Dprintf("constructor hsRequest\n"); Dprintf("constructor hsRequest\n");
m_Apid[0] = m_Apid[1] = 0;
m_Dpid[0] = m_Dpid[1] = 0;
} }
cConnectionHTTP::~cConnectionHTTP() cConnectionHTTP::~cConnectionHTTP()
@@ -26,28 +27,79 @@ cConnectionHTTP::~cConnectionHTTP()
delete m_LiveStreamer; delete m_LiveStreamer;
} }
bool cConnectionHTTP::CanAuthenticate(void)
{
return opt_auth != NULL;
}
bool cConnectionHTTP::Command(char *Cmd) bool cConnectionHTTP::Command(char *Cmd)
{ {
Dprintf("command %s\n", Cmd); Dprintf("command %s\n", Cmd);
switch (m_Status) { switch (m_Status) {
case hsRequest: case hsRequest:
Dprintf("Request\n"); // parse METHOD PATH[?QUERY] VERSION
m_Request = Cmd; {
m_Status = hsHeaders; char *p, *q, *v;
return true; p = strchr(Cmd, ' ');
if (p) {
*p = 0;
v = strchr(++p, ' ');
if (v) {
*v = 0;
SetHeader("REQUEST_METHOD", Cmd);
q = strchr(p, '?');
if (q)
*q = 0;
SetHeader("QUERY_STRING", q ? ++q : "");
SetHeader("PATH_INFO", p);
m_Status = hsHeaders;
return true;
}
}
}
return false;
case hsHeaders: case hsHeaders:
if (*Cmd == '\0') { if (*Cmd == '\0') {
m_Status = hsBody; m_Status = hsBody;
return ProcessRequest(); return ProcessRequest();
} }
if (strncasecmp(Cmd, "Host:", 5) == 0) { else if (isspace(*Cmd)) {
Dprintf("Host-Header\n"); ; //TODO: multi-line header
m_Host = (std::string) skipspace(Cmd + 5); }
else {
// convert header name to CGI conventions:
// uppercase, '-' replaced with '_', prefix "HTTP_"
char *p;
for (p = Cmd; *p != 0 && *p != ':'; p++) {
if (*p == '-')
*p = '_';
else
*p = toupper(*p);
}
if (*p == ':') {
*p = 0;
p = skipspace(++p);
// don't disclose Authorization header
if (strcmp(Cmd, "AUTHORIZATION") == 0) {
char *q;
for (q = p; *q != 0 && *q != ' '; q++)
*q = toupper(*q);
if (p != q) {
*q = 0;
SetHeader("AUTH_TYPE", p);
m_Authorization = (std::string) skipspace(++q);
}
}
else
SetHeader(Cmd, p, "HTTP_");
}
} }
Dprintf("header\n");
return true; return true;
default: default:
// skip additional blank lines
if (*Cmd == '\0')
return true;
break; break;
} }
return false; // ??? shouldn't happen return false; // ??? shouldn't happen
@@ -55,45 +107,118 @@ bool cConnectionHTTP::Command(char *Cmd)
bool cConnectionHTTP::ProcessRequest(void) bool cConnectionHTTP::ProcessRequest(void)
{ {
// keys for Headers() hash
const static std::string AUTH_TYPE("AUTH_TYPE");
const static std::string REQUEST_METHOD("REQUEST_METHOD");
const static std::string PATH_INFO("PATH_INFO");
Dprintf("process\n"); Dprintf("process\n");
if (m_Request.substr(0, 4) == "GET " && CmdGET(m_Request.substr(4))) { if (!StreamdevHosts.Acceptable(RemoteIpAddr())) {
switch (m_Job) { bool authOk = opt_auth && !m_Authorization.empty();
case hjListing: if (authOk) {
if (m_ChannelList) tStrStrMap::const_iterator it = Headers().find(AUTH_TYPE);
return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
break;
case hjTransfer: if (it == Headers().end()) {
if (m_Channel == NULL) { // no authorization header present
DeferClose(); authOk = false;
return Respond("HTTP/1.0 404 not found");
} }
else if (it->second.compare("BASIC") == 0) {
// basic auth
authOk &= m_Authorization.compare(opt_auth) == 0;
}
else {
// unsupported auth type
authOk = false;
}
}
if (!authOk) {
isyslog("streamdev-server: HTTP authorization required");
DeferClose();
return Respond("HTTP/1.0 401 Authorization Required")
&& Respond("WWW-authenticate: basic Realm=\"Streamdev-Server\")")
&& Respond("");
}
}
m_LiveStreamer = new cStreamdevLiveStreamer(0, m_StreamerParameter); tStrStrMap::const_iterator it_method = Headers().find(REQUEST_METHOD);
cDevice *device = GetDevice(m_Channel, 0); tStrStrMap::const_iterator it_pathinfo = Headers().find(PATH_INFO);
if (it_method == Headers().end() || it_pathinfo == Headers().end()) {
// should never happen
esyslog("streamdev-server connectionHTTP: Missing method or pathinfo");
} else if (it_method->second.compare("GET") == 0 && ProcessURI(it_pathinfo->second)) {
if (m_ChannelList)
return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
else if (m_Channel != NULL) {
cDevice *device = NULL;
if (ProvidesChannel(m_Channel, 0))
device = GetDevice(m_Channel, 0);
if (device != NULL) { if (device != NULL) {
device->SwitchChannel(m_Channel, false); device->SwitchChannel(m_Channel, false);
if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid)) { m_LiveStreamer = new cStreamdevLiveStreamer(0, this);
if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL)) {
m_LiveStreamer->SetDevice(device); m_LiveStreamer->SetDevice(device);
if (!SetDSCP()) if (!SetDSCP())
LOG_ERROR_STR("unable to set DSCP sockopt"); LOG_ERROR_STR("unable to set DSCP sockopt");
if (m_StreamType == stES && (m_Apid != 0 || ISRADIO(m_Channel))) { if (m_StreamType == stEXT) {
return Respond("HTTP/1.0 200 OK");
} else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) {
return Respond("HTTP/1.0 200 OK") return Respond("HTTP/1.0 200 OK")
&& Respond("Content-Type: audio/mpeg") && Respond("Content-Type: audio/mpeg")
&& Respond("icy-name: %s", true, m_Channel->Name()) && Respond("icy-name: %s", true, m_Channel->Name())
&& Respond(""); && Respond("");
} else if (ISRADIO(m_Channel)) {
return Respond("HTTP/1.0 200 OK")
&& Respond("Content-Type: audio/mpeg")
&& Respond("");
} else { } else {
return Respond("HTTP/1.0 200 OK") return Respond("HTTP/1.0 200 OK")
&& Respond("Content-Type: video/mpeg") && Respond("Content-Type: video/mpeg")
&& Respond(""); && Respond("");
} }
} }
DELETENULL(m_LiveStreamer);
} }
DELETENULL(m_LiveStreamer);
DeferClose(); DeferClose();
return Respond("HTTP/1.0 409 Channel not available") return Respond("HTTP/1.0 409 Channel not available")
&& Respond(""); && Respond("");
} }
else {
DeferClose();
return Respond("HTTP/1.0 404 not found")
&& Respond("");
}
} else if (it_method->second.compare("HEAD") == 0 && ProcessURI(it_pathinfo->second)) {
DeferClose();
if (m_ChannelList)
return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
else if (m_Channel != NULL) {
if (ProvidesChannel(m_Channel, 0)) {
if (m_StreamType == stEXT) {
// TODO
return Respond("HTTP/1.0 200 OK")
&& Respond("");
} else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) {
return Respond("HTTP/1.0 200 OK")
&& Respond("Content-Type: audio/mpeg")
&& Respond("icy-name: %s", true, m_Channel->Name())
&& Respond("");
} else if (ISRADIO(m_Channel)) {
return Respond("HTTP/1.0 200 OK")
&& Respond("Content-Type: audio/mpeg")
&& Respond("");
} else {
return Respond("HTTP/1.0 200 OK")
&& Respond("Content-Type: video/mpeg")
&& Respond("");
}
}
return Respond("HTTP/1.0 409 Channel not available")
&& Respond("");
}
else {
return Respond("HTTP/1.0 404 not found")
&& Respond("");
}
} }
DeferClose(); DeferClose();
@@ -103,83 +228,112 @@ bool cConnectionHTTP::ProcessRequest(void)
void cConnectionHTTP::Flushed(void) void cConnectionHTTP::Flushed(void)
{ {
std::string line;
if (m_Status != hsBody) if (m_Status != hsBody)
return; return;
switch (m_Job) { if (m_ChannelList) {
case hjListing: if (m_ChannelList->HasNext()) {
if (m_ChannelList) { if (!Respond("%s", true, m_ChannelList->Next().c_str()))
if (m_ChannelList->HasNext()) {
if (!Respond("%s", true, m_ChannelList->Next().c_str()))
DeferClose();
}
else {
DELETENULL(m_ChannelList);
m_Status = hsFinished;
DeferClose(); DeferClose();
}
return;
} }
// should never be reached else {
esyslog("streamdev-server cConnectionHTTP::Flushed(): no channel list"); DELETENULL(m_ChannelList);
m_Status = hsFinished; m_Status = hsFinished;
break; DeferClose();
}
case hjTransfer: return;
}
else if (m_Channel != NULL) {
Dprintf("streamer start\n"); Dprintf("streamer start\n");
m_LiveStreamer->Start(this); m_LiveStreamer->Start(this);
m_Status = hsFinished; m_Status = hsFinished;
break; }
else {
// should never be reached
esyslog("streamdev-server cConnectionHTTP::Flushed(): no job to do");
m_Status = hsFinished;
} }
} }
bool cConnectionHTTP::CmdGET(const std::string &Opts) cChannelList* cConnectionHTTP::ChannelListFromString(const std::string& Path, const std::string& Filebase, const std::string& Fileext) const
{ {
const char *ptr, *sp, *pp, *fp, *xp, *qp, *ep; // keys for Headers() hash
const cChannel *chan; const static std::string QUERY_STRING("QUERY_STRING");
int apid = 0; const static std::string HOST("HTTP_HOST");
ptr = Opts.c_str(); tStrStrMap::const_iterator it_query = Headers().find(QUERY_STRING);
const std::string& query = it_query == Headers().end() ? "" : it_query->second;
// find begin of URL std::string groupTarget;
sp = skipspace(ptr); cChannelIterator *iterator = NULL;
// find end of URL (\0 or first space character)
for (ep = sp; *ep && !isspace(*ep); ep++) if (Filebase.compare("tree") == 0) {
; const cChannel* c = NULL;
// find begin of query string (first ?) size_t groupIndex = query.find("group=");
for (qp = sp; qp < ep && *qp != '?'; qp++) if (groupIndex != std::string::npos)
; c = cChannelList::GetGroup(atoi(query.c_str() + groupIndex + 6));
// find begin of filename (last /) iterator = new cListTree(c);
for (fp = qp; fp > sp && *fp != '/'; --fp) groupTarget = Filebase + Fileext;
; } else if (Filebase.compare("groups") == 0) {
// find begin of section params (first ;) iterator = new cListGroups();
for (pp = sp; pp < fp && *pp != ';'; pp++) groupTarget = (std::string) "group" + Fileext;
; } else if (Filebase.compare("group") == 0) {
// find filename extension (first .) const cChannel* c = NULL;
for (xp = fp; xp < qp && *xp != '.'; xp++) size_t groupIndex = query.find("group=");
; if (groupIndex != std::string::npos)
if (qp - xp > 5) // too long for a filename extension c = cChannelList::GetGroup(atoi(query.c_str() + groupIndex + 6));
xp = qp; iterator = new cListGroup(c);
} else if (Filebase.compare("channels") == 0) {
iterator = new cListChannels();
} else if (Filebase.compare("all") == 0 ||
(Filebase.empty() && Fileext.empty())) {
iterator = new cListAll();
}
if (iterator) {
if (Filebase.empty() || Fileext.compare(".htm") == 0 || Fileext.compare(".html") == 0) {
std::string self = Filebase + Fileext;
if (!query.empty())
self += '?' + query;
return new cHtmlChannelList(iterator, m_StreamType, self.c_str(), groupTarget.c_str());
} else if (Fileext.compare(".m3u") == 0) {
std::string base;
tStrStrMap::const_iterator it = Headers().find(HOST);
if (it != Headers().end())
base = "http://" + it->second + "/";
else
base = (std::string) "http://" + LocalIp() + ":" +
(const char*) itoa(StreamdevServerSetup.HTTPServerPort) + "/";
base += Path;
return new cM3uChannelList(iterator, base.c_str());
} else {
delete iterator;
}
}
return NULL;
}
bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
{
std::string filespec, fileext;
size_t file_pos = PathInfo.rfind('/');
if (file_pos != std::string::npos) {
size_t ext_pos = PathInfo.rfind('.');
// file basename with leading / stripped off
filespec = PathInfo.substr(file_pos + 1, ext_pos - file_pos - 1);
if (ext_pos != std::string::npos)
// file extension including leading .
fileext = PathInfo.substr(ext_pos);
}
if (fileext.length() > 5) {
//probably not an extension
filespec += fileext;
fileext.clear();
}
std::string type, filespec, fileext, query;
// Streamtype with leading / stripped off // Streamtype with leading / stripped off
if (pp > sp) std::string type = PathInfo.substr(1, PathInfo.find_first_of("/;", 1) - 1);
type = Opts.substr(sp - ptr + 1, pp - sp - 1);
// Section parameters with leading ; stripped off
if (fp > pp)
m_StreamerParameter = Opts.substr(pp - ptr + 1, fp - pp - 1);
// file basename with leading / stripped off
if (xp > fp)
filespec = Opts.substr(fp - ptr + 1, xp - fp - 1);
// file extension including leading .
fileext = Opts.substr(xp - ptr, qp - xp);
// query string including leading ?
query = Opts.substr(qp - ptr, ep - qp);
Dprintf("before channelfromstring: type(%s) param(%s) filespec(%s) fileext(%s) query(%s)\n", type.c_str(), m_StreamerParameter.c_str(), filespec.c_str(), fileext.c_str(), query.c_str());
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;
@@ -189,80 +343,24 @@ bool cConnectionHTTP::CmdGET(const std::string &Opts)
m_StreamType = stTS; m_StreamType = stTS;
} else if (strcasecmp(pType, "ES") == 0) { } else if (strcasecmp(pType, "ES") == 0) {
m_StreamType = stES; m_StreamType = stES;
} else if (strcasecmp(pType, "Extern") == 0) { } else if (strcasecmp(pType, "EXT") == 0) {
m_StreamType = stExtern; m_StreamType = stEXT;
} }
std::string groupTarget; Dprintf("before channelfromstring: type(%s) filespec(%s) fileext(%s)\n", type.c_str(), filespec.c_str(), fileext.c_str());
cChannelIterator *iterator = NULL;
if (filespec.compare("tree") == 0) { if ((m_ChannelList = ChannelListFromString(PathInfo.substr(1, file_pos), filespec.c_str(), fileext.c_str())) != NULL) {
const cChannel* c = NULL; Dprintf("Channel list requested\n");
size_t groupIndex = query.find("group="); return true;
if (groupIndex != std::string::npos) } else if ((m_Channel = ChannelFromString(filespec.c_str(), &m_Apid[0], &m_Dpid[0])) != NULL) {
c = cChannelList::GetGroup(atoi(query.c_str() + groupIndex + 6)); Dprintf("Channel found. Apid/Dpid is %d/%d\n", m_Apid[0], m_Dpid[0]);
iterator = new cListTree(c); return true;
groupTarget = filespec + fileext;
} else if (filespec.compare("groups") == 0) {
iterator = new cListGroups();
groupTarget = (std::string) "group" + fileext;
} else if (filespec.compare("group") == 0) {
const cChannel* c = NULL;
size_t groupIndex = query.find("group=");
if (groupIndex != std::string::npos)
c = cChannelList::GetGroup(atoi(query.c_str() + groupIndex + 6));
iterator = new cListGroup(c);
} else if (filespec.compare("channels") == 0) {
iterator = new cListChannels();
} else if (filespec.compare("all") == 0 ||
(filespec.empty() && fileext.empty())) {
iterator = new cListAll();
}
if (iterator) {
if (filespec.empty() || fileext.compare(".htm") == 0 || fileext.compare(".html") == 0) {
m_ChannelList = new cHtmlChannelList(iterator, m_StreamType, (filespec + fileext + query).c_str(), groupTarget.c_str());
m_Job = hjListing;
} else if (fileext.compare(".m3u") == 0) {
std::string base;
if (*(m_Host.c_str()))
base = "http://" + m_Host + "/";
else
base = (std::string) "http://" + LocalIp() + ":" +
(const char*) itoa(StreamdevServerSetup.HTTPServerPort) + "/";
if (type.empty())
{
switch (m_StreamType)
{
case stTS: base += "TS/"; break;
case stPS: base += "PS/"; break;
case stPES: base += "PES/"; break;
case stES: base += "ES/"; break;
case stExtern: base += "Extern/"; break;
default: break;
}
} else {
base += type;
if (!m_StreamerParameter.empty())
base += ";" + m_StreamerParameter;
base += "/";
}
m_ChannelList = new cM3uChannelList(iterator, base.c_str());
m_Job = hjListing;
} else {
delete iterator;
return false;
}
} else if ((chan = ChannelFromString(filespec.c_str(), &apid)) != NULL) {
m_Channel = chan;
m_Apid = apid;
Dprintf("Apid is %d\n", apid);
m_Job = hjTransfer;
} else } else
return false; return false;
Dprintf("after channelfromstring\n");
return true;
} }
cString cConnectionHTTP::ToText() const
{
cString str = cServerConnection::ToText();
return m_LiveStreamer ? cString::sprintf("%s\t%s", *str, *m_LiveStreamer->ToText()) : str;
}

View File

@@ -1,5 +1,5 @@
/* /*
* $Id: connectionHTTP.h,v 1.5 2008/03/28 15:11:40 schmirl Exp $ * $Id: connectionHTTP.h,v 1.7 2010/07/19 13:49:31 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEV_SERVERS_CONNECTIONHTTP_H #ifndef VDR_STREAMDEV_SERVERS_CONNECTIONHTTP_H
@@ -8,6 +8,7 @@
#include "connection.h" #include "connection.h"
#include "server/livestreamer.h" #include "server/livestreamer.h"
#include <map>
#include <tools/select.h> #include <tools/select.h>
class cChannel; class cChannel;
@@ -23,25 +24,19 @@ private:
hsFinished, hsFinished,
}; };
enum eHTTPJob { std::string m_Authorization;
hjTransfer,
hjListing,
};
std::string m_Request;
std::string m_Host;
//std::map<std::string,std::string> m_Headers; TODO: later?
eHTTPStatus m_Status; eHTTPStatus m_Status;
eHTTPJob m_Job;
// job: transfer // job: transfer
cStreamdevLiveStreamer *m_LiveStreamer; cStreamdevLiveStreamer *m_LiveStreamer;
std::string m_StreamerParameter;
const cChannel *m_Channel; const cChannel *m_Channel;
int m_Apid; int m_Apid[2];
int m_Dpid[2];
eStreamType m_StreamType; eStreamType m_StreamType;
// job: listing // job: listing
cChannelList *m_ChannelList; cChannelList *m_ChannelList;
cChannelList* ChannelListFromString(const std::string &PathInfo, const std::string &Filebase, const std::string &Fileext) const;
bool ProcessURI(const std::string &PathInfo);
protected: protected:
bool ProcessRequest(void); bool ProcessRequest(void);
@@ -52,8 +47,11 @@ public:
virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); } virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); }
virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); } virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); }
virtual cString ToText() const;
virtual bool CanAuthenticate(void);
virtual bool Command(char *Cmd); virtual bool Command(char *Cmd);
bool CmdGET(const std::string &Opts);
virtual bool Abort(void) const; virtual bool Abort(void) const;
virtual void Flushed(void); virtual void Flushed(void);
@@ -61,7 +59,7 @@ public:
inline bool cConnectionHTTP::Abort(void) const inline bool cConnectionHTTP::Abort(void) const
{ {
return m_LiveStreamer && m_LiveStreamer->Abort(); return !IsOpen() || (m_LiveStreamer && m_LiveStreamer->Abort());
} }
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H #endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H

78
server/connectionIGMP.c Normal file
View File

@@ -0,0 +1,78 @@
/*
* $Id: connectionIGMP.c,v 1.3 2010/08/03 10:46:41 schmirl Exp $
*/
#include <ctype.h>
#include "server/connectionIGMP.h"
#include "server/server.h"
#include "server/setup.h"
#include <vdr/channels.h>
cConnectionIGMP::cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType) :
cServerConnection(Name, SOCK_DGRAM),
m_LiveStreamer(NULL),
m_ClientPort(ClientPort),
m_StreamType(StreamType),
m_Channel(NULL)
{
}
cConnectionIGMP::~cConnectionIGMP()
{
delete m_LiveStreamer;
}
bool cConnectionIGMP::SetChannel(cChannel *Channel, in_addr_t Dst)
{
if (Channel) {
m_Channel = Channel;
struct in_addr ip;
ip.s_addr = Dst;
if (Connect(inet_ntoa(ip), m_ClientPort))
return true;
else
esyslog("streamdev-server IGMP: Connect failed: %m");
return false;
}
else
esyslog("streamdev-server IGMP: Channel not found");
return false;
}
void cConnectionIGMP::Welcome()
{
cDevice *device = NULL;
if (ProvidesChannel(m_Channel, 0))
device = GetDevice(m_Channel, 0);
if (device != NULL) {
device->SwitchChannel(m_Channel, false);
m_LiveStreamer = new cStreamdevLiveStreamer(0, this);
if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType)) {
m_LiveStreamer->SetDevice(device);
if (!SetDSCP())
LOG_ERROR_STR("unable to set DSCP sockopt");
Dprintf("streamer start\n");
m_LiveStreamer->Start(this);
}
else {
esyslog("streamdev-server IGMP: SetChannel failed");
DELETENULL(m_LiveStreamer);
}
}
else
esyslog("streamdev-server IGMP: GetDevice failed");
}
bool cConnectionIGMP::Close()
{
if (m_LiveStreamer)
m_LiveStreamer->Stop();
return cServerConnection::Close();
}
cString cConnectionIGMP::ToText() const
{
cString str = cServerConnection::ToText();
return m_LiveStreamer ? cString::sprintf("%s\t%s", *str, *m_LiveStreamer->ToText()) : str;
}

48
server/connectionIGMP.h Normal file
View File

@@ -0,0 +1,48 @@
/*
* $Id: connectionIGMP.h,v 1.1 2009/02/13 10:39:22 schmirl Exp $
*/
#ifndef VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H
#define VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H
#include "connection.h"
#include "server/livestreamer.h"
#include <tools/select.h>
#define MULTICAST_PRIV_MIN ((uint32_t) 0xefff0000)
#define MULTICAST_PRIV_MAX ((uint32_t) 0xeffffeff)
class cStreamdevLiveStreamer;
class cConnectionIGMP: public cServerConnection {
private:
cStreamdevLiveStreamer *m_LiveStreamer;
int m_ClientPort;
eStreamType m_StreamType;
cChannel *m_Channel;
public:
cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType);
virtual ~cConnectionIGMP();
bool SetChannel(cChannel *Channel, in_addr_t Dst);
virtual void Welcome(void);
virtual cString ToText() const;
/* Not used here */
virtual bool Command(char *Cmd) { return false; }
virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); }
virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); }
virtual bool Close(void);
virtual bool Abort(void) const;
};
inline bool cConnectionIGMP::Abort(void) const
{
return !IsOpen() || !m_LiveStreamer || m_LiveStreamer->Abort();
}
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H

File diff suppressed because it is too large Load Diff

View File

@@ -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,28 +10,37 @@ 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;
// if your compiler doesn't understand the following statement #if !defined __GNUC__ || __GNUC__ >= 3
// (e.g. gcc 2.x), simply remove it and try again ;-)
using cServerConnection::Respond; using cServerConnection::Respond;
#endif
private: private:
cTBSocket *m_LiveSocket; cTBSocket *m_LiveSocket;
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;
// Priority is only known in PROV command
// Store in here for later use in TUNE call
const cChannel *m_TuneChannel;
int m_TunePriority;
// 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>
@@ -43,6 +53,8 @@ public:
virtual void Welcome(void); virtual void Welcome(void);
virtual void Reject(void); virtual void Reject(void);
virtual cString ToText() const;
virtual bool Abort(void) const; virtual bool Abort(void) const;
virtual void Detach(void); virtual void Detach(void);
virtual void Attach(void); virtual void Attach(void);
@@ -51,7 +63,11 @@ 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 CmdPRIO(char *Opts);
bool CmdSGNL(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 +80,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)));

View File

@@ -1,14 +1,11 @@
/* /*
* $Id: livefilter.c,v 1.5 2008/04/07 14:27:31 schmirl Exp $ * $Id: livefilter.c,v 1.7 2009/02/13 13:02:40 schmirl Exp $
*/ */
#include "server/livefilter.h" #include "server/livefilter.h"
#include "server/streamer.h" #include "server/streamer.h"
#include "common.h" #include "common.h"
#ifndef TS_SIZE
# define TS_SIZE 188
#endif
#ifndef TS_SYNC_BYTE #ifndef TS_SYNC_BYTE
# define TS_SYNC_BYTE 0x47 # define TS_SYNC_BYTE 0x47
#endif #endif
@@ -29,6 +26,7 @@ void cStreamdevLiveFilter::Process(u_short Pid, u_char Tid, const u_char *Data,
buffer[1] = ((Pid >> 8) & 0x3f) | (pos==0 ? 0x40 : 0); /* bit 6: payload unit start indicator (PUSI) */ buffer[1] = ((Pid >> 8) & 0x3f) | (pos==0 ? 0x40 : 0); /* bit 6: payload unit start indicator (PUSI) */
buffer[2] = Pid & 0xff; buffer[2] = Pid & 0xff;
buffer[3] = Tid; buffer[3] = Tid;
// this makes it a proprietary stream
buffer[4] = (uchar)chunk; buffer[4] = (uchar)chunk;
memcpy(buffer + 5, Data + pos, chunk); memcpy(buffer + 5, Data + pos, chunk);
length -= chunk; length -= chunk;

View File

@@ -3,16 +3,18 @@
#include <libsi/section.h> #include <libsi/section.h>
#include <libsi/descriptor.h> #include <libsi/descriptor.h>
#include "remux/ts2ps.h"
#include "remux/ts2pes.h"
#include "remux/ts2es.h"
#include "remux/extern.h"
#include <vdr/ringbuffer.h> #include <vdr/ringbuffer.h>
#include "server/livestreamer.h" #include "server/livestreamer.h"
#include "server/livefilter.h" #include "server/livefilter.h"
#include "remux/ts2ps.h"
#include "remux/ts2es.h"
#include "remux/extern.h"
#include "common.h" #include "common.h"
#define TSPATREPACKER using namespace Streamdev;
// --- cStreamdevLiveReceiver ------------------------------------------------- // --- cStreamdevLiveReceiver -------------------------------------------------
@@ -27,25 +29,24 @@ protected:
virtual void Receive(uchar *Data, int Length); virtual void Receive(uchar *Data, int Length);
public: public:
#if VDRVERSNUM < 10500 cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, const cChannel *Channel, int Priority, const int *Pids);
cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, int Ca, int Priority, const int *Pids);
#else
cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, tChannelID ChannelID, int Priority, const int *Pids);
#endif
virtual ~cStreamdevLiveReceiver(); virtual ~cStreamdevLiveReceiver();
}; };
#if VDRVERSNUM < 10500 cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, const cChannel *Channel,
cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, int Ca,
int Priority, const int *Pids): int Priority, const int *Pids):
cReceiver(Ca, Priority, 0, Pids), #if APIVERSNUM >= 10712
cReceiver(Channel, Priority),
#else #else
cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, tChannelID ChannelID, cReceiver(Channel->GetChannelID(), Priority, 0, Pids),
int Priority, const int *Pids):
cReceiver(ChannelID, Priority, 0, Pids),
#endif #endif
m_Streamer(Streamer) m_Streamer(Streamer)
{ {
#if APIVERSNUM >= 10712
// clears all PIDs but channel remains set
SetPids(NULL);
AddPids(Pids);
#endif
} }
cStreamdevLiveReceiver::~cStreamdevLiveReceiver() cStreamdevLiveReceiver::~cStreamdevLiveReceiver()
@@ -73,6 +74,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;
@@ -82,11 +85,13 @@ 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\")", Channel->Name()); Dprintf("cStreamdevPatFilter(\"%s\")\n", Channel->Name());
assert(Streamer); assert(Streamer);
m_Channel = Channel; m_Channel = Channel;
m_Streamer = Streamer; m_Streamer = Streamer;
@@ -94,6 +99,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[] = {
@@ -145,7 +173,7 @@ int cStreamdevPatFilter::GetPid(SI::PMT::Stream& stream)
case 0x10: // ISO/IEC 14496-2 Visual (MPEG-4) case 0x10: // ISO/IEC 14496-2 Visual (MPEG-4)
case 0x11: // ISO/IEC 14496-3 Audio with LATM transport syntax case 0x11: // ISO/IEC 14496-3 Audio with LATM transport syntax
case 0x1b: // ISO/IEC 14496-10 Video (MPEG-4 part 10/AVC, aka H.264) case 0x1b: // ISO/IEC 14496-10 Video (MPEG-4 part 10/AVC, aka H.264)
Dprintf("cStreamdevPatFilter PMT scanner adding PID %d (%s)", Dprintf("cStreamdevPatFilter PMT scanner adding PID %d (%s)\n",
stream.getPid(), psStreamTypes[stream.getStreamType()]); stream.getPid(), psStreamTypes[stream.getStreamType()]);
return stream.getPid(); return stream.getPid();
case 0x05: // ISO/IEC 13818-1 private sections case 0x05: // ISO/IEC 13818-1 private sections
@@ -153,19 +181,23 @@ int cStreamdevPatFilter::GetPid(SI::PMT::Stream& stream)
for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) { for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
switch (d->getDescriptorTag()) { switch (d->getDescriptorTag()) {
case SI::AC3DescriptorTag: case SI::AC3DescriptorTag:
Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s", case SI::EnhancedAC3DescriptorTag:
Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s\n",
stream.getPid(), psStreamTypes[stream.getStreamType()], "AC3"); stream.getPid(), psStreamTypes[stream.getStreamType()], "AC3");
delete d;
return stream.getPid(); return stream.getPid();
case SI::TeletextDescriptorTag: case SI::TeletextDescriptorTag:
Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s", Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s\n",
stream.getPid(), psStreamTypes[stream.getStreamType()], "Teletext"); stream.getPid(), psStreamTypes[stream.getStreamType()], "Teletext");
delete d;
return stream.getPid(); return stream.getPid();
case SI::SubtitlingDescriptorTag: case SI::SubtitlingDescriptorTag:
Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s", Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s\n",
stream.getPid(), psStreamTypes[stream.getStreamType()], "DVBSUB"); stream.getPid(), psStreamTypes[stream.getStreamType()], "DVBSUB");
delete d;
return stream.getPid(); return stream.getPid();
default: default:
Dprintf("cStreamdevPatFilter PMT scanner: NOT adding PID %d (%s) %s", Dprintf("cStreamdevPatFilter PMT scanner: NOT adding PID %d (%s) %s\n",
stream.getPid(), psStreamTypes[stream.getStreamType()], "UNKNOWN"); stream.getPid(), psStreamTypes[stream.getStreamType()], "UNKNOWN");
break; break;
} }
@@ -195,6 +227,7 @@ int cStreamdevPatFilter::GetPid(SI::PMT::Stream& stream)
stream.getPid(), stream.getStreamType(), stream.getPid(), stream.getStreamType(),
d->getLength(), rawdata[2], rawdata[3], d->getLength(), rawdata[2], rawdata[3],
rawdata[4], rawdata[5]); rawdata[4], rawdata[5]);
delete d;
return stream.getPid(); return stream.getPid();
} }
} }
@@ -210,7 +243,7 @@ int cStreamdevPatFilter::GetPid(SI::PMT::Stream& stream)
return stream.getPid(); return stream.getPid();
} }
} }
Dprintf("cStreamdevPatFilter PMT scanner: NOT adding PID %d (%s) %s", Dprintf("cStreamdevPatFilter PMT scanner: NOT adding PID %d (%s) %s\n",
stream.getPid(), psStreamTypes[stream.getStreamType()<0x1c?stream.getStreamType():0], "UNKNOWN"); stream.getPid(), psStreamTypes[stream.getStreamType()<0x1c?stream.getStreamType():0], "UNKNOWN");
break; break;
} }
@@ -220,7 +253,7 @@ int cStreamdevPatFilter::GetPid(SI::PMT::Stream& stream)
void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length) void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
{ {
if (Pid == 0x00) { if (Pid == 0x00) {
if (Tid == 0x00 && !pmtPid) { if (Tid == 0x00) {
SI::PAT pat(Data, false); SI::PAT pat(Data, false);
if (!pat.CheckCRCAndParse()) if (!pat.CheckCRCAndParse())
return; return;
@@ -229,58 +262,46 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
if (!assoc.isNITPid()) { if (!assoc.isNITPid()) {
const cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId()); const cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId());
if (Channel && (Channel == m_Channel)) { if (Channel && (Channel == m_Channel)) {
int prevPmtPid = pmtPid;
if (0 != (pmtPid = assoc.getPid())) { if (0 != (pmtPid = assoc.getPid())) {
Dprintf("cStreamdevPatFilter: PMT pid for channel %s: %d", 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)
memset(tspat_buf, 0xff, TS_SIZE); tspat_buf[10] = 0xc0 | ((pat.getVersionNumber() << 1) & 0x3e) |
memset(tspat_buf, 0x0, 4 + 12 + 5); // TS_HDR_LEN + PAT_TABLE_LEN + 5 pat.getCurrentNextIndicator();// Version number, Current next indicator
ts_id = Channel->Tid(); // Get transport stream id of the channel tspat_buf[13] = (pmtSid >> 8); // Program number (bits 8-15)
tspat_buf[0] = TS_SYNC_BYTE; // Transport packet header sunchronization byte (1000011 = 0x47h) tspat_buf[14] = (pmtSid & 0xff); // Program number (bits 0-7)
tspat_buf[1] = 0x40; // Set payload unit start indicator bit tspat_buf[15] = 0xe0 | (pmtPid >> 8); // Network ID (bits 8-12)
tspat_buf[2] = 0x0; // PID tspat_buf[16] = (pmtPid & 0xff); // Network ID (bits 0-7)
tspat_buf[3] = 0x10; // Set payload flag to indicate precence of payload data crc = 0xffffffff;
tspat_buf[4] = 0x0; // PSI len = 12; // PAT_TABLE_LEN
tspat_buf[5] = 0x0; // PAT table id tmp = &tspat_buf[4 + 1]; // TS_HDR_LEN + 1
tspat_buf[6] = 0xb0; // Section syntax indicator bit and reserved bits set while (len--) {
tspat_buf[7] = 12 + 1; // Section length (12 bit): PAT_TABLE_LEN + 1 crc ^= *tmp++ << 24;
tspat_buf[8] = (ts_id >> 8) & 0xff; // Transport stream ID (bits 8-15) for (i = 0; i < 8; i++)
tspat_buf[9] = (ts_id & 0xff); // Transport stream ID (bits 0-7) crc = (crc << 1) ^ ((crc & 0x80000000) ? 0x04c11db7 : 0); // CRC32POLY
tspat_buf[10] = 0x01; // Version number 0, Current next indicator bit set }
tspat_buf[11] = 0x0; // Section number tspat_buf[17] = crc >> 24 & 0xff; // Checksum
tspat_buf[12] = 0x0; // Last section number tspat_buf[18] = crc >> 16 & 0xff; // Checksum
tspat_buf[13] = (pmtSid >> 8) & 0xff; // Program number (bits 8-15) tspat_buf[19] = crc >> 8 & 0xff; // Checksum
tspat_buf[14] = (pmtSid & 0xff); // Program number (bits 0-7) tspat_buf[20] = crc & 0xff; // Checksum
tspat_buf[15] = (pmtPid >> 8) & 0xff; // Network ID (bits 8-12) int written = siBuffer.PutTS(tspat_buf, TS_SIZE);
tspat_buf[16] = (pmtPid & 0xff); // Network ID (bits 0-7) if (written != TS_SIZE)
crc = 0xffffffff; siBuffer.ReportOverflow(TS_SIZE - written);
len = 12; // PAT_TABLE_LEN if (pmtPid != prevPmtPid) {
tmp = &tspat_buf[4 + 1]; // TS_HDR_LEN + 1 m_Streamer->SetPid(pmtPid, true);
while (len--) { Add(pmtPid, 0x02);
crc ^= *tmp++ << 24; pmtVersion = -1;
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);
m_Streamer->SetPids(pmtPid);
Add(pmtPid, 0x02);
pmtVersion = -1;
return; return;
} }
} }
@@ -295,8 +316,8 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
return; // skip broken PMT records return; // skip broken PMT records
if (pmtVersion != -1) { if (pmtVersion != -1) {
if (pmtVersion != pmt.getVersionNumber()) { if (pmtVersion != pmt.getVersionNumber()) {
Dprintf("cStreamdevPatFilter: PMT version changed, detaching all pids"); 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;
@@ -308,9 +329,9 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
pids[npids++] = pmtPid; pids[npids++] = pmtPid;
#if 0 #if 0
pids[npids++] = 0x10; // pid 0x10, tid 0x40: NIT pids[npids++] = 0x10; // pid 0x10, tid 0x40: NIT
#endif
pids[npids++] = 0x11; // pid 0x11, tid 0x42: SDT pids[npids++] = 0x11; // pid 0x11, tid 0x42: SDT
pids[npids++] = 0x14; // pid 0x14, tid 0x70: TDT pids[npids++] = 0x14; // pid 0x14, tid 0x70: TDT
#endif
pids[npids++] = 0x12; // pid 0x12, tid 0x4E...0x6F: EIT pids[npids++] = 0x12; // pid 0x12, tid 0x4E...0x6F: EIT
for (SI::Loop::Iterator it; pmt.streamLoop.getNext(stream, it); ) for (SI::Loop::Iterator it; pmt.streamLoop.getNext(stream, it); )
if (0 != (pids[npids] = GetPid(stream)) && npids < MAXRECEIVEPIDS) if (0 != (pids[npids] = GetPid(stream)) && npids < MAXRECEIVEPIDS)
@@ -323,20 +344,16 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
// --- cStreamdevLiveStreamer ------------------------------------------------- // --- cStreamdevLiveStreamer -------------------------------------------------
cStreamdevLiveStreamer::cStreamdevLiveStreamer(int Priority, std::string Parameter): cStreamdevLiveStreamer::cStreamdevLiveStreamer(int Priority, const cServerConnection *Connection):
cStreamdevStreamer("streamdev-livestreaming"), cStreamdevStreamer("streamdev-livestreaming", Connection),
m_Priority(Priority), m_Priority(Priority),
m_Parameter(Parameter),
m_NumPids(0), m_NumPids(0),
m_StreamType(stTSPIDS), m_StreamType(stTSPIDS),
m_Channel(NULL), m_Channel(NULL),
m_Device(NULL), m_Device(NULL),
m_Receiver(NULL), m_Receiver(NULL),
m_PatFilter(NULL), m_PatFilter(NULL),
m_PESRemux(NULL), m_Remux(NULL)
m_ESRemux(NULL),
m_PSRemux(NULL),
m_ExtRemux(NULL)
{ {
} }
@@ -349,10 +366,7 @@ cStreamdevLiveStreamer::~cStreamdevLiveStreamer()
DELETENULL(m_PatFilter); DELETENULL(m_PatFilter);
} }
DELETENULL(m_Receiver); DELETENULL(m_Receiver);
delete m_PESRemux; delete m_Remux;
delete m_ESRemux;
delete m_PSRemux;
delete m_ExtRemux;
} }
bool cStreamdevLiveStreamer::HasPid(int Pid) bool cStreamdevLiveStreamer::HasPid(int Pid)
@@ -429,148 +443,136 @@ bool cStreamdevLiveStreamer::SetPids(int Pid, const int *Pids1, const int *Pids2
return true; return true;
} }
void cStreamdevLiveStreamer::StartReceiver(void) void cStreamdevLiveStreamer::SetPriority(int Priority)
{ {
DELETENULL(m_Receiver); m_Priority = Priority;
if (m_NumPids > 0) { StartReceiver();
Dprintf("Creating Receiver to respect changed pids\n"); }
#if VDRVERSNUM < 10500
m_Receiver = new cStreamdevLiveReceiver(this, m_Channel->Ca(), m_Priority, m_Pids); void cStreamdevLiveStreamer::GetSignal(int *DevNum, int *Strength, int *Quality) const
#else {
m_Receiver = new cStreamdevLiveReceiver(this, m_Channel->GetChannelID(), m_Priority, m_Pids); if (m_Device) {
*DevNum = m_Device->DeviceNumber() + 1;
#if APIVERSNUM >= 10719
*Strength = m_Device->SignalStrength();
*Quality = m_Device->SignalQuality();
#endif #endif
if (IsRunning() && m_Device != NULL) {
Dprintf("Attaching new receiver\n");
Attach();
}
} }
} }
bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType StreamType, int Apid) cString cStreamdevLiveStreamer::ToText() const
{
if (m_Device && m_Channel) {
return cString::sprintf("DVB%-2d %3d %s", m_Device->DeviceNumber() + 1, m_Channel->Number(), m_Channel->Name());
}
return cString("");
}
void cStreamdevLiveStreamer::StartReceiver(void)
{
if (m_NumPids > 0) {
Dprintf("Creating Receiver to respect changed pids\n");
cReceiver *current = m_Receiver;
m_Receiver = new cStreamdevLiveReceiver(this, m_Channel, m_Priority, m_Pids);
cThreadLock ThreadLock(m_Device);
if (IsRunning())
Attach();
delete current;
}
else
DELETENULL(m_Receiver);
}
bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType StreamType, const int* Apid, const int *Dpid)
{ {
Dprintf("Initializing Remuxer for full channel transfer\n"); Dprintf("Initializing Remuxer for full channel transfer\n");
//printf("ca pid: %d\n", Channel->Ca()); //printf("ca pid: %d\n", Channel->Ca());
m_Channel = Channel; m_Channel = Channel;
m_StreamType = StreamType; m_StreamType = StreamType;
int apid[2] = { Apid, 0 }; const int *Apids = Apid ? Apid : m_Channel->Apids();
const int *Apids = Apid ? apid : m_Channel->Apids(); const int *Dpids = Dpid ? Dpid : m_Channel->Dpids();
const int *Dpids = Apid ? NULL : m_Channel->Dpids();
switch (m_StreamType) { switch (m_StreamType) {
case stES: case stES:
{ {
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 && Apid[0])
pid = Apid; pid = Apid[0];
m_ESRemux = new cTS2ESRemux(pid); else if (Dpid && Dpid[0])
pid = Dpid[0];
m_Remux = new cTS2ESRemux(pid);
return SetPids(pid); return SetPids(pid);
} }
case stPES: case stPES:
m_PESRemux = new cRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(), m_Remux = new cTS2PESRemux(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
m_Channel->Spids(), false);
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids()); return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
case stPS: case stPS:
m_PSRemux = new cTS2PSRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(), m_Remux = new cTS2PSRemux(m_Channel->Vpid(), Apids, 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());
case stEXT:
m_Remux = new cExternRemux(Connection(), m_Channel, Apids, Dpids);
// fall through
case stTS: case stTS:
// This should never happen, but ... // This should never happen, but ...
if (m_PatFilter) { if (m_PatFilter) {
Detach(); Detach();
DELETENULL(m_PatFilter); DELETENULL(m_PatFilter);
} }
// Set pids from cChannel
SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
if (m_Channel->Vpid() != m_Channel->Ppid())
SetPid(m_Channel->Ppid(), true);
// Set pids from PMT // Set pids from PMT
m_PatFilter = new cStreamdevPatFilter(this, m_Channel); m_PatFilter = new cStreamdevPatFilter(this, m_Channel);
return true; return true;
case stExtern:
m_ExtRemux = new cExternRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
m_Channel->Spids(), m_Parameter);
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
case stTSPIDS: case stTSPIDS:
Dprintf("pid streaming mode\n"); Dprintf("pid streaming mode\n");
return true; return true;
default:
return false;
} }
return false;
} }
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) {
case stPES: if (m_Remux)
return m_PESRemux->Put(Data, Count); siCount = m_Remux->Put(siData, siCount);
else
case stES: siCount = cStreamdevStreamer::Put(siData, siCount);
return m_ESRemux->Put(Data, Count); if (siCount)
m_PatFilter->Del(siCount);
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);
case stPES:
return m_PESRemux->Get(Count);
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;
case stPES:
m_PESRemux->Del(Count);
break;
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)
@@ -665,12 +667,10 @@ void cStreamdevFilterStreamer::SetDevice(cDevice *Device)
{ {
Dprintf("cStreamdevFilterStreamer::SetDevice()\n"); Dprintf("cStreamdevFilterStreamer::SetDevice()\n");
LOCK_THREAD; LOCK_THREAD;
if(Device != m_Device) { Detach();
Detach(); m_Device = Device;
m_Device = Device; //m_Channel = NULL;
//m_Channel = NULL; Attach();
Attach();
}
} }
bool cStreamdevFilterStreamer::SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On) bool cStreamdevFilterStreamer::SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On)

View File

@@ -7,10 +7,9 @@
#include "server/streamer.h" #include "server/streamer.h"
#include "common.h" #include "common.h"
class cTS2PSRemux; namespace Streamdev {
class cTS2ESRemux; class cTSRemux;
class cExternRemux; }
class cRemux;
class cStreamdevPatFilter; class cStreamdevPatFilter;
class cStreamdevLiveReceiver; class cStreamdevLiveReceiver;
@@ -19,7 +18,6 @@ class cStreamdevLiveReceiver;
class cStreamdevLiveStreamer: public cStreamdevStreamer { class cStreamdevLiveStreamer: public cStreamdevStreamer {
private: private:
int m_Priority; int m_Priority;
std::string m_Parameter;
int m_Pids[MAXRECEIVEPIDS + 1]; int m_Pids[MAXRECEIVEPIDS + 1];
int m_NumPids; int m_NumPids;
eStreamType m_StreamType; eStreamType m_StreamType;
@@ -27,22 +25,22 @@ private:
cDevice *m_Device; cDevice *m_Device;
cStreamdevLiveReceiver *m_Receiver; cStreamdevLiveReceiver *m_Receiver;
cStreamdevPatFilter *m_PatFilter; cStreamdevPatFilter *m_PatFilter;
cRemux *m_PESRemux; Streamdev::cTSRemux *m_Remux;
cTS2ESRemux *m_ESRemux;
cTS2PSRemux *m_PSRemux;
cExternRemux *m_ExtRemux;
void StartReceiver(void); void StartReceiver(void);
bool HasPid(int Pid); bool HasPid(int Pid);
public: public:
cStreamdevLiveStreamer(int Priority, std::string Parameter = ""); cStreamdevLiveStreamer(int Priority, const cServerConnection *Connection);
virtual ~cStreamdevLiveStreamer(); virtual ~cStreamdevLiveStreamer();
void SetDevice(cDevice *Device) { m_Device = Device; } void SetDevice(cDevice *Device) { m_Device = Device; }
bool SetPid(int Pid, bool On); bool SetPid(int Pid, bool On);
bool SetPids(int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL); bool SetPids(int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL);
bool SetChannel(const cChannel *Channel, eStreamType StreamType, int Apid = 0); bool SetChannel(const cChannel *Channel, eStreamType StreamType, const int* Apid = NULL, const int* Dpid = NULL);
void SetPriority(int Priority);
void GetSignal(int *DevNum, int *Strength, int *Quality) const;
cString ToText() const;
virtual int Put(const uchar *Data, int Count); virtual int Put(const uchar *Data, int Count);
virtual uchar *Get(int &Count); virtual uchar *Get(int &Count);

68
server/menu.c Normal file
View File

@@ -0,0 +1,68 @@
/*
* $Id: menu.c,v 1.10 2010/07/19 13:49:31 schmirl Exp $
*/
#include <vdr/menuitems.h>
#include <vdr/thread.h>
#include <vdr/player.h>
#include "server/menu.h"
#include "server/setup.h"
#include "server/server.h"
#include "server/suspend.h"
cStreamdevServerMenu::cStreamdevServerMenu(): cOsdMenu(tr("Streamdev Connections"), 4, 20) {
cThreadLock lock;
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
for (cServerConnection *s = clients.First(); s; s = clients.Next(s))
Add(new cOsdItem(s->ToText()));
SetHelpKeys();
Display();
}
cStreamdevServerMenu::~cStreamdevServerMenu() {
}
void cStreamdevServerMenu::SetHelpKeys() {
SetHelp(Count() ? tr("Disconnect") : NULL, NULL, NULL, StreamdevServerSetup.SuspendMode == smOffer ? tr("Suspend") : NULL);
}
eOSState cStreamdevServerMenu::Disconnect() {
cOsdItem *item = Get(Current());
if (item) {
cThreadLock lock;
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
const char *text = item->Text();
for (cServerConnection *s = clients.First(); s; s = clients.Next(s)) {
if (!strcmp(text, s->ToText())) {
s->Close();
Del(Current());
SetHelpKeys();
Display();
break;
}
}
}
return osContinue;
}
eOSState cStreamdevServerMenu::Suspend() {
if (StreamdevServerSetup.SuspendMode == smOffer && !cSuspendCtl::IsActive()) {
cControl::Launch(new cSuspendCtl);
return osBack;
}
return osContinue;
}
eOSState cStreamdevServerMenu::ProcessKey(eKeys Key) {
eOSState state = cOsdMenu::ProcessKey(Key);
if (state == osUnknown) {
switch (Key) {
case kRed: return Disconnect();
case kBlue: return Suspend();
case kOk: return osBack;
default: break;
}
}
return state;
}

24
server/menu.h Normal file
View File

@@ -0,0 +1,24 @@
/*
* $Id: menu.h,v 1.4 2010/07/19 13:49:31 schmirl Exp $
*/
#ifndef VDR_STREAMDEV_MENU_H
#define VDR_STREAMDEV_MENU_H
#include <vdr/osdbase.h>
#include "connection.h"
class cStreamdevServerMenu: public cOsdMenu {
private:
void SetHelpKeys();
eOSState Disconnect();
eOSState Suspend();
protected:
virtual eOSState ProcessKey(eKeys Key);
public:
cStreamdevServerMenu();
virtual ~cStreamdevServerMenu();
};
#endif // VDR_STREAMDEV_MENU_H

View File

@@ -2,7 +2,7 @@
#include "server/menuHTTP.h" #include "server/menuHTTP.h"
//**************************** cChannelIterator ************** //**************************** cChannelIterator **************
cChannelIterator::cChannelIterator(cChannel *First): channel(First) cChannelIterator::cChannelIterator(const cChannel *First): channel(First)
{} {}
const cChannel* cChannelIterator::Next() const cChannel* cChannelIterator::Next()
@@ -19,7 +19,7 @@ cListAll::cListAll(): cChannelIterator(Channels.First())
const cChannel* cListAll::NextChannel(const cChannel *Channel) const cChannel* cListAll::NextChannel(const cChannel *Channel)
{ {
if (Channel) if (Channel)
Channel = Channels.Next(Channel); Channel = SkipFakeGroups(Channels.Next(Channel));
return Channel; return Channel;
} }
@@ -46,14 +46,19 @@ const cChannel* cListGroups::NextChannel(const cChannel *Channel)
} }
// //
// ********************* cListGroup **************** // ********************* cListGroup ****************
cListGroup::cListGroup(const cChannel *Group): cChannelIterator((Group && Group->GroupSep() && Channels.Next(Group) && !Channels.Next(Group)->GroupSep()) ? Channels.Next(Group) : NULL) cListGroup::cListGroup(const cChannel *Group): cChannelIterator(GetNextChannelInGroup(Group))
{} {}
const cChannel* cListGroup::GetNextChannelInGroup(const cChannel *Channel)
{
if (Channel)
Channel = SkipFakeGroups(Channels.Next(Channel));
return Channel && !Channel->GroupSep() ? Channel : NULL;
}
const cChannel* cListGroup::NextChannel(const cChannel *Channel) const cChannel* cListGroup::NextChannel(const cChannel *Channel)
{ {
if (Channel) return GetNextChannelInGroup(Channel);
Channel = Channels.Next(Channel);
return (Channel && !Channel->GroupSep()) ? Channel : NULL;
} }
// //
// ********************* cListTree **************** // ********************* cListTree ****************
@@ -68,7 +73,7 @@ const cChannel* cListTree::NextChannel(const cChannel *Channel)
if (currentGroup == selectedGroup) if (currentGroup == selectedGroup)
{ {
if (Channel) if (Channel)
Channel = Channels.Next(Channel); Channel = SkipFakeGroups(Channels.Next(Channel));
if (Channel && Channel->GroupSep()) if (Channel && Channel->GroupSep())
currentGroup = Channel; currentGroup = Channel;
} }
@@ -112,10 +117,10 @@ const cChannel* cChannelList::GetGroup(int Index)
// ******************** cHtmlChannelList ****************** // ******************** cHtmlChannelList ******************
const char* cHtmlChannelList::menu = const char* cHtmlChannelList::menu =
"[<a href=\"/\">Home</a> (<a href=\"all.html\">no script</a>)] " "[<a href=\"/\">Home</a> (<a href=\"all.html\" tvid=\"RED\">no script</a>)] "
"[<a href=\"tree.html\">Tree View</a>] " "[<a href=\"tree.html\" tvid=\"GREEN\">Tree View</a>] "
"[<a href=\"groups.html\">Groups</a> (<a href=\"groups.m3u\">Playlist</a>)] " "[<a href=\"groups.html\" tvid=\"YELLOW\">Groups</a> (<a href=\"groups.m3u\">Playlist</a>)] "
"[<a href=\"channels.html\">Channels</a> (<a href=\"channels.m3u\">Playlist</a>)] "; "[<a href=\"channels.html\" tvid=\"BLUE\">Channels</a> (<a href=\"channels.m3u\">Playlist</a>)] ";
const char* cHtmlChannelList::css = const char* cHtmlChannelList::css =
"<style type=\"text/css\">\n" "<style type=\"text/css\">\n"
@@ -205,8 +210,8 @@ std::string cHtmlChannelList::StreamTypeMenu()
(std::string) "[<a href=\"/PES/" + self + "\">PES</a>] "); (std::string) "[<a href=\"/PES/" + self + "\">PES</a>] ");
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 == stEXT ? (std::string) "[EXT] " :
(std::string) "[<a href=\"/Extern/" + self + "\">Extern</a>] "); (std::string) "[<a href=\"/EXT/" + self + "\">EXT</a>] ");
return typeMenu; return typeMenu;
} }
@@ -336,9 +341,24 @@ std::string cHtmlChannelList::GroupTitle()
std::string cHtmlChannelList::ItemText() std::string cHtmlChannelList::ItemText()
{ {
std::string line; std::string line;
std::string suffix;
switch (streamType) {
case stTS: suffix = (std::string) ".ts"; break;
case stPS: suffix = (std::string) ".vob"; break;
// for Network Media Tank
case stPES: suffix = (std::string) ".vdr"; break;
default: suffix = "";
}
line += (std::string) "<li value=\"" + (const char*) itoa(current->Number()) + "\">"; line += (std::string) "<li value=\"" + (const char*) itoa(current->Number()) + "\">";
line += (std::string) "<a href=\"" + (std::string) current->GetChannelID().ToString() + "\">" + line += (std::string) "<a href=\"" + (std::string) current->GetChannelID().ToString() + suffix + "\"";
current->Name() + "</a>";
// for Network Media Tank
line += (std::string) " vod ";
if (current->Number() < 1000)
line += (std::string) " tvid=\"" + (const char*) itoa(current->Number()) + "\"";
line += (std::string) ">" + current->Name() + "</a>";
int count = 0; int count = 0;
for (int i = 0; current->Apid(i) != 0; ++i, ++count) for (int i = 0; current->Apid(i) != 0; ++i, ++count)
@@ -351,11 +371,11 @@ std::string cHtmlChannelList::ItemText()
int index = 1; int index = 1;
for (int i = 0; current->Apid(i) != 0; ++i, ++index) { for (int i = 0; current->Apid(i) != 0; ++i, ++index) {
line += (std::string) " <a href=\"" + (std::string) current->GetChannelID().ToString() + line += (std::string) " <a href=\"" + (std::string) current->GetChannelID().ToString() +
"+" + (const char*)itoa(index) + "\" class=\"apid\">" + current->Alang(i) + "</a>"; "+" + (const char*)itoa(index) + suffix + "\" class=\"apid\" vod>" + current->Alang(i) + "</a>";
} }
for (int i = 0; current->Dpid(i) != 0; ++i, ++index) { for (int i = 0; current->Dpid(i) != 0; ++i, ++index) {
line += (std::string) " <a href=\"" + (std::string) current->GetChannelID().ToString() + line += (std::string) " <a href=\"" + (std::string) current->GetChannelID().ToString() +
"+" + (const char*)itoa(index) + "\" class=\"dpid\">" + current->Dlang(i) + "</a>"; "+" + (const char*)itoa(index) + suffix + "\" class=\"dpid\" vod>" + current->Dlang(i) + "</a>";
} }
} }
line += "</li>"; line += "</li>";
@@ -364,10 +384,8 @@ std::string cHtmlChannelList::ItemText()
// ******************** cM3uChannelList ****************** // ******************** cM3uChannelList ******************
cM3uChannelList::cM3uChannelList(cChannelIterator *Iterator, const char* Base) cM3uChannelList::cM3uChannelList(cChannelIterator *Iterator, const char* Base)
: cChannelList(Iterator) : cChannelList(Iterator),
#if defined(APIVERSNUM) && APIVERSNUM >= 10503 m_IConv(cCharSetConv::SystemCharacterTable(), "UTF-8")
, m_IConv(cCharSetConv::SystemCharacterTable(), "UTF-8")
#endif
{ {
base = strdup(Base); base = strdup(Base);
m3uState = msFirst; m3uState = msFirst;
@@ -398,21 +416,17 @@ std::string cM3uChannelList::Next()
return ""; return "";
} }
#if defined(APIVERSNUM) && APIVERSNUM >= 10503
std::string name = (std::string) m_IConv.Convert(channel->Name()); std::string name = (std::string) m_IConv.Convert(channel->Name());
#else
std::string name = channel->Name();
#endif
if (channel->GroupSep()) if (channel->GroupSep())
{ {
return (std::string) "#EXTINF:0," + name + "\r\n" + return (std::string) "#EXTINF:-1," + name + "\r\n" +
base + "group.m3u?group=" + base + "group.m3u?group=" +
(const char*) itoa(cChannelList::GetGroupIndex(channel)); (const char*) itoa(cChannelList::GetGroupIndex(channel));
} }
else else
{ {
return (std::string) "#EXTINF:0," + return (std::string) "#EXTINF:-1," +
(const char*) itoa(channel->Number()) + " " + name + "\r\n" + (const char*) itoa(channel->Number()) + " " + name + "\r\n" +
base + (std::string) channel->GetChannelID().ToString(); base + (std::string) channel->GetChannelID().ToString();
} }

View File

@@ -13,9 +13,10 @@ class cChannelIterator
const cChannel *channel; const cChannel *channel;
protected: protected:
virtual const cChannel* NextChannel(const cChannel *Channel) = 0; virtual const cChannel* NextChannel(const cChannel *Channel) = 0;
static inline const cChannel* SkipFakeGroups(const cChannel *Channel);
public: public:
const cChannel* Next(); const cChannel* Next();
cChannelIterator(cChannel *First); cChannelIterator(const cChannel *First);
virtual ~cChannelIterator() {}; virtual ~cChannelIterator() {};
}; };
@@ -48,6 +49,8 @@ class cListGroups: public cChannelIterator
class cListGroup: public cChannelIterator class cListGroup: public cChannelIterator
{ {
private:
static const cChannel* GetNextChannelInGroup(const cChannel *Channel);
protected: protected:
virtual const cChannel* NextChannel(const cChannel *Channel); virtual const cChannel* NextChannel(const cChannel *Channel);
public: public:
@@ -113,7 +116,12 @@ class cHtmlChannelList: public cChannelList
std::string ItemText(); std::string ItemText();
std::string PageBottom(); std::string PageBottom();
public: public:
virtual std::string HttpHeader() { return cChannelList::HttpHeader() + "Content-type: text/html\r\n\r\n"; } virtual std::string HttpHeader() {
return cChannelList::HttpHeader()
+ "Content-type: text/html; charset="
+ (cCharSetConv::SystemCharacterTable() ? cCharSetConv::SystemCharacterTable() : "UTF-8")
+ "\r\n";
}
virtual bool HasNext(); virtual bool HasNext();
virtual std::string Next(); virtual std::string Next();
cHtmlChannelList(cChannelIterator *Iterator, eStreamType StreamType, const char *Self, const char *GroupTarget); cHtmlChannelList(cChannelIterator *Iterator, eStreamType StreamType, const char *Self, const char *GroupTarget);
@@ -126,15 +134,20 @@ class cM3uChannelList: public cChannelList
char *base; char *base;
enum eM3uState { msFirst, msContinue, msLast }; enum eM3uState { msFirst, msContinue, msLast };
eM3uState m3uState; eM3uState m3uState;
#if defined(APIVERSNUM) && APIVERSNUM >= 10503
cCharSetConv m_IConv; cCharSetConv m_IConv;
#endif
public: public:
virtual std::string HttpHeader() { return cChannelList::HttpHeader() + "Content-type: audio/x-mpegurl\r\n"; }; virtual std::string HttpHeader() { return cChannelList::HttpHeader() + "Content-type: audio/x-mpegurl; charset=UTF-8\r\n"; };
virtual bool HasNext(); virtual bool HasNext();
virtual std::string Next(); virtual std::string Next();
cM3uChannelList(cChannelIterator *Iterator, const char* Base); cM3uChannelList(cChannelIterator *Iterator, const char* Base);
virtual ~cM3uChannelList(); virtual ~cM3uChannelList();
}; };
inline const cChannel* cChannelIterator::SkipFakeGroups(const cChannel* Group)
{
while (Group && Group->GroupSep() && !*Group->Name())
Group = Channels.Next(Group);
return Group;
}
#endif #endif

88
server/po/de_DE.po Normal file
View File

@@ -0,0 +1,88 @@
# VDR streamdev plugin language source file.
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
# This file is distributed under the same license as the VDR streamdev package.
# Frank Schmirler <vdrdev@schmirler.de>, 2008
#
msgid ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
"Last-Translator: Frank Schmirler <vdrdev@schmirler.de>\n"
"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "VDR Streaming Server"
msgstr "VDR Streaming Server"
msgid "Streaming active"
msgstr "Streamen im Gange"
msgid "Streamdev Connections"
msgstr "Streamdev Verbindungen"
msgid "Disconnect"
msgstr "Trennen"
msgid "Suspend"
msgstr "Pausieren"
msgid "Offer suspend mode"
msgstr "Pausieren anbieten"
msgid "Always suspended"
msgstr "Immer pausiert"
msgid "Never suspended"
msgstr "Nie pausiert"
msgid "Common Settings"
msgstr "Allgemeines"
msgid "Maximum Number of Clients"
msgstr "Maximalanzahl an Clients"
msgid "Suspend behaviour"
msgstr "Pausierverhalten"
msgid "Client may suspend"
msgstr "Client darf pausieren"
msgid "VDR-to-VDR Server"
msgstr "VDR-zu-VDR Server"
msgid "Start VDR-to-VDR Server"
msgstr "VDR-zu-VDR Server starten"
msgid "VDR-to-VDR Server Port"
msgstr "Port des VDR-zu-VDR Servers"
msgid "Bind to IP"
msgstr "Binde an IP"
msgid "HTTP Server"
msgstr "HTTP Server"
msgid "Start HTTP Server"
msgstr "HTTP Server starten"
msgid "HTTP Server Port"
msgstr "Port des HTTP Servers"
msgid "HTTP Streamtype"
msgstr "HTTP Streamtyp"
msgid "Multicast Streaming Server"
msgstr "Multicast Streaming Server"
msgid "Start IGMP Server"
msgstr "IGMP Server starten"
msgid "Multicast Client Port"
msgstr "Port des Multicast Clients"
msgid "Multicast Streamtype"
msgstr "Multicast Streamtyp"

88
server/po/es_ES.po Normal file
View File

@@ -0,0 +1,88 @@
# VDR streamdev plugin language source file.
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
# This file is distributed under the same license as the VDR streamdev package.
# Javier Bradineras <jbradi@hotmail.com>, 2011
#
msgid ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
"Last-Translator: Javier Bradineras <jbradi@hotmail.com>\n"
"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "VDR Streaming Server"
msgstr "Servidor de transmisiones del VDR"
msgid "Streaming active"
msgstr "Trasmisión activa"
msgid "Streamdev Connections"
msgstr ""
msgid "Disconnect"
msgstr ""
msgid "Suspend"
msgstr "Suspender"
msgid "Offer suspend mode"
msgstr "Ofrecer modo de suspensión"
msgid "Always suspended"
msgstr "Siempre suspendido"
msgid "Never suspended"
msgstr "Nunca suspendido"
msgid "Common Settings"
msgstr "Configuración común"
msgid "Maximum Number of Clients"
msgstr "Numero máximo de clientes"
msgid "Suspend behaviour"
msgstr "Comportamiento de la suspensión"
msgid "Client may suspend"
msgstr "Permitir suspender al cliente"
msgid "VDR-to-VDR Server"
msgstr "Servidor VDR-a-VDR"
msgid "Start VDR-to-VDR Server"
msgstr "Iniciar Servidor VDR-a-VDR"
msgid "VDR-to-VDR Server Port"
msgstr "Puerto del Servidor VDR-a-VDR"
msgid "Bind to IP"
msgstr "IP asociada"
msgid "HTTP Server"
msgstr "Servidor HTTP"
msgid "Start HTTP Server"
msgstr "Iniciar Servidor HTTP"
msgid "HTTP Server Port"
msgstr "Puerto del Servidor HTTP"
msgid "HTTP Streamtype"
msgstr "Tipo de flujo HTTP"
msgid "Multicast Streaming Server"
msgstr "Servidor de transmisión Multicast"
msgid "Start IGMP Server"
msgstr "Iniciar Servidor IGMP"
msgid "Multicast Client Port"
msgstr "Puerto del Cliente Multicast"
msgid "Multicast Streamtype"
msgstr "Tipo de flujo Multicast"

88
server/po/fi_FI.po Normal file
View File

@@ -0,0 +1,88 @@
# VDR streamdev plugin language source file.
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
# This file is distributed under the same license as the VDR streamdev package.
# Rolf Ahrenberg <rahrenbe@cc.hut.fi>, 2008
#
msgid ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
"Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"
"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "VDR Streaming Server"
msgstr "VDR-suoratoistopalvelin"
msgid "Streaming active"
msgstr "Suoratoistopalvelin aktiivinen"
msgid "Streamdev Connections"
msgstr ""
msgid "Disconnect"
msgstr ""
msgid "Suspend"
msgstr "Pysäytä"
msgid "Offer suspend mode"
msgstr "tyrkytä"
msgid "Always suspended"
msgstr "aina"
msgid "Never suspended"
msgstr "ei koskaan"
msgid "Common Settings"
msgstr "Yleiset asetukset"
msgid "Maximum Number of Clients"
msgstr "Suurin sallittu asiakkaiden määrä"
msgid "Suspend behaviour"
msgstr "Pysäytystoiminto"
msgid "Client may suspend"
msgstr "Asiakas saa pysäyttää palvelimen"
msgid "VDR-to-VDR Server"
msgstr "VDR-palvelin"
msgid "Start VDR-to-VDR Server"
msgstr "Käynnistä VDR-palvelin"
msgid "VDR-to-VDR Server Port"
msgstr "VDR-palvelimen portti"
msgid "Bind to IP"
msgstr "Sido osoitteeseen"
msgid "HTTP Server"
msgstr "HTTP-palvelin"
msgid "Start HTTP Server"
msgstr "Käynnistä HTTP-palvelin"
msgid "HTTP Server Port"
msgstr "HTTP-palvelimen portti"
msgid "HTTP Streamtype"
msgstr "HTTP-lähetysmuoto"
msgid "Multicast Streaming Server"
msgstr "Multicast-suoratoistopalvelin"
msgid "Start IGMP Server"
msgstr "Käynnistä IGMP-palvelin"
msgid "Multicast Client Port"
msgstr "Multicast-portti"
msgid "Multicast Streamtype"
msgstr "Multicast-lähetysmuoto"

88
server/po/fr_FR.po Normal file
View File

@@ -0,0 +1,88 @@
# VDR streamdev plugin language source file.
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
# This file is distributed under the same license as the VDR streamdev package.
# Frank Schmirler <vdrdev@schmirler.de>, 2008
#
msgid ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
"Last-Translator: micky979 <micky979@free.fr>\n"
"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "VDR Streaming Server"
msgstr "Serveur de streaming VDR"
msgid "Streaming active"
msgstr "Streaming actif"
msgid "Streamdev Connections"
msgstr ""
msgid "Disconnect"
msgstr ""
msgid "Suspend"
msgstr "Suspendre"
msgid "Offer suspend mode"
msgstr "Offrir le mode suspendre"
msgid "Always suspended"
msgstr "Toujours suspendre"
msgid "Never suspended"
msgstr "Jamais suspendre"
msgid "Common Settings"
msgstr "Paramètres communs"
msgid "Maximum Number of Clients"
msgstr "Nombre maximun de clients"
msgid "Suspend behaviour"
msgstr "Suspendre"
msgid "Client may suspend"
msgstr "Le client peut suspendre"
msgid "VDR-to-VDR Server"
msgstr "VDR-to-VDR Serveur"
msgid "Start VDR-to-VDR Server"
msgstr "Démarrer le serveur VDR-to-VDR"
msgid "VDR-to-VDR Server Port"
msgstr "Port du serveur VDR-to-VDR"
msgid "Bind to IP"
msgstr "Attacher aux IP"
msgid "HTTP Server"
msgstr "Serveur HTTP"
msgid "Start HTTP Server"
msgstr "Démarrer le serveur HTTP"
msgid "HTTP Server Port"
msgstr "Port du serveur HTTP"
msgid "HTTP Streamtype"
msgstr "Type de Streaming HTTP"
msgid "Multicast Streaming Server"
msgstr ""
msgid "Start IGMP Server"
msgstr ""
msgid "Multicast Client Port"
msgstr ""
msgid "Multicast Streamtype"
msgstr ""

90
server/po/it_IT.po Normal file
View File

@@ -0,0 +1,90 @@
# VDR streamdev plugin language source file.
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
# This file is distributed under the same license as the VDR streamdev package.
# Alberto Carraro <bertocar@tin.it>, 2001
# Antonio Ospite <ospite@studenti.unina.it>, 2003
# Sean Carlos <seanc@libero.it>, 2005
#
msgid ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "VDR Streaming Server"
msgstr "Server trasmissione VDR"
msgid "Streaming active"
msgstr "Trasmissione attiva"
msgid "Streamdev Connections"
msgstr ""
msgid "Disconnect"
msgstr ""
msgid "Suspend"
msgstr "Sospendi"
msgid "Offer suspend mode"
msgstr "Offri mod. sospensione"
msgid "Always suspended"
msgstr "Sempre sospeso"
msgid "Never suspended"
msgstr "Mai sospeso"
msgid "Common Settings"
msgstr "Impostazioni comuni"
msgid "Maximum Number of Clients"
msgstr "Numero massimo di Client"
msgid "Suspend behaviour"
msgstr "Tipo sospensione"
msgid "Client may suspend"
msgstr "Permetti sospensione al Client"
msgid "VDR-to-VDR Server"
msgstr "Server VDR-a-VDR"
msgid "Start VDR-to-VDR Server"
msgstr "Avvia Server VDR-a-VDR"
msgid "VDR-to-VDR Server Port"
msgstr "Porta Server VDR-a-VDR"
msgid "Bind to IP"
msgstr "IP associati"
msgid "HTTP Server"
msgstr "Server HTTP"
msgid "Start HTTP Server"
msgstr "Avvia Server HTTP"
msgid "HTTP Server Port"
msgstr "Porta Server HTTP"
msgid "HTTP Streamtype"
msgstr "Tipo flusso HTTP"
msgid "Multicast Streaming Server"
msgstr "Server trasmissione Multicast"
msgid "Start IGMP Server"
msgstr "Avvia Server IGMP"
msgid "Multicast Client Port"
msgstr "Porta Client Multicast"
msgid "Multicast Streamtype"
msgstr "Tipo flusso Multicast"

88
server/po/lt_LT.po Normal file
View File

@@ -0,0 +1,88 @@
# VDR streamdev plugin language source file.
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
# This file is distributed under the same license as the VDR streamdev package.
# Frank Schmirler <vdrdev@schmirler.de>, 2008
#
msgid ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: 2009-11-26 21:57+0200\n"
"Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
"Language-Team: Lietuvių\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "VDR Streaming Server"
msgstr "VDR transliavimo serveris"
msgid "Streaming active"
msgstr "Transliavimas vyksta"
msgid "Streamdev Connections"
msgstr ""
msgid "Disconnect"
msgstr ""
msgid "Suspend"
msgstr "Pristabdyti"
msgid "Offer suspend mode"
msgstr "Klausti dėl sustabdymo"
msgid "Always suspended"
msgstr "Visada stabdyti"
msgid "Never suspended"
msgstr "Niekada nestabdyti"
msgid "Common Settings"
msgstr "Bendri nustatymai"
msgid "Maximum Number of Clients"
msgstr "Maksimalus klientų skaičius"
msgid "Suspend behaviour"
msgstr "Pristabdyti veikimą"
msgid "Client may suspend"
msgstr "Klientas gali pristabdyti"
msgid "VDR-to-VDR Server"
msgstr "VDR-su-VDR Serveris"
msgid "Start VDR-to-VDR Server"
msgstr "Paleisti VDR-su-VDR serverį"
msgid "VDR-to-VDR Server Port"
msgstr "VDR-su-VDR Serverio portas"
msgid "Bind to IP"
msgstr "Pririšti IP"
msgid "HTTP Server"
msgstr "HTTP Serveris"
msgid "Start HTTP Server"
msgstr "Paleisti HTTP serverį"
msgid "HTTP Server Port"
msgstr "HTTP serverio portas"
msgid "HTTP Streamtype"
msgstr "HTTP transliavimo tipas"
msgid "Multicast Streaming Server"
msgstr "Multicast transliavimo serveris"
msgid "Start IGMP Server"
msgstr "Paleisti IGMP serverį"
msgid "Multicast Client Port"
msgstr "Multicast kliento portas"
msgid "Multicast Streamtype"
msgstr "Multicast transliavimo tipas"

88
server/po/ru_RU.po Normal file
View File

@@ -0,0 +1,88 @@
# VDR streamdev plugin language source file.
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
# This file is distributed under the same license as the VDR streamdev package.
# Frank Schmirler <vdrdev@schmirler.de>, 2008
#
msgid ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: 2008-06-26 15:36+0100\n"
"Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n"
"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-5\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "VDR Streaming Server"
msgstr "VDR Streaming áÕàÒÕà"
msgid "Streaming active"
msgstr "ÁâàØÜØÝÓ ÐÚâØÒÕÝ"
msgid "Streamdev Connections"
msgstr ""
msgid "Disconnect"
msgstr ""
msgid "Suspend"
msgstr "¾áâÐÝÞÒÚÐ"
msgid "Offer suspend mode"
msgstr "¿àÕÔÛÐÓÐâì ÞáâÐÝÞÒÚã"
msgid "Always suspended"
msgstr "²áÕÓÔÐ ÞáâÐÝÞÒÛÕÝ"
msgid "Never suspended"
msgstr "½ØÚÞÓÔÐ ÝÕ ÞáâÐÝÞÒÛÕÝ"
msgid "Common Settings"
msgstr "½ÐáâàÞÙÚØ"
msgid "Maximum Number of Clients"
msgstr "¼ÐÚá. ÚÞÛØçÕáâÒÞ ÚÛØÕÝâÞÒ"
msgid "Suspend behaviour"
msgstr "¿ÞÒÕÔÕÝØÕ ÞáâÐÝÞÒÚØ"
msgid "Client may suspend"
msgstr "ºÛØÕÝâ ÜÞÖÕâ ÞáâÐÝÐÒÛØÒÐâì"
msgid "VDR-to-VDR Server"
msgstr "VDR-to-VDR áÕàÒÕà"
msgid "Start VDR-to-VDR Server"
msgstr "ÁâÐàâ VDR-to-VDR áÕàÒÕà"
msgid "VDR-to-VDR Server Port"
msgstr "VDR-to-VDR ßÞàâ áÕàÒÕàÐ"
msgid "Bind to IP"
msgstr "¿àØáÞÕÔØÝØâìáï Ú IP"
msgid "HTTP Server"
msgstr "HTTP áÕàÒÕà"
msgid "Start HTTP Server"
msgstr "ÁâÐàâ HTTP áÕàÒÕàÐ"
msgid "HTTP Server Port"
msgstr "HTTP áÕàÒÕà ¿Þàâ"
msgid "HTTP Streamtype"
msgstr "ÂØß HTTP ßÞâÞÚÐ"
msgid "Multicast Streaming Server"
msgstr ""
msgid "Start IGMP Server"
msgstr ""
msgid "Multicast Client Port"
msgstr ""
msgid "Multicast Streamtype"
msgstr ""

90
server/po/sk_SK.po Normal file
View File

@@ -0,0 +1,90 @@
# VDR streamdev plugin language source file.
# Copyright (C) 2009 streamdev development team. See http://streamdev.vdr-developer.org
# This file is distributed under the same license as the VDR streamdev package.
# Milan Hrala <hrala.milan@gmail.com>, 2009
#
msgid ""
msgstr ""
"Project-Id-Version: streamdev_SK\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: \n"
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
"Language-Team: Slovak <hrala.milan@gmail.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-2\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Slovak\n"
"X-Poedit-Country: SLOVAKIA\n"
msgid "VDR Streaming Server"
msgstr "VDR prúdový server"
msgid "Streaming active"
msgstr "streamovanie aktivne"
msgid "Streamdev Connections"
msgstr ""
msgid "Disconnect"
msgstr ""
msgid "Suspend"
msgstr "Pozastavenie"
msgid "Offer suspend mode"
msgstr "Výber re¾ímu pozastavenia"
msgid "Always suspended"
msgstr "V¾dy pozastavi»"
msgid "Never suspended"
msgstr "Nikdy nepozastavi»"
msgid "Common Settings"
msgstr "V¹eobecné nastavenia"
msgid "Maximum Number of Clients"
msgstr "Maximály poèet klientov"
msgid "Suspend behaviour"
msgstr "Správanie preru¹enia"
msgid "Client may suspend"
msgstr "Klient mô¾e pozastavi»"
msgid "VDR-to-VDR Server"
msgstr "VDR-do-VDR server"
msgid "Start VDR-to-VDR Server"
msgstr "Spusti» VDR-do-VDR Server"
msgid "VDR-to-VDR Server Port"
msgstr "Port serveru pre VDR-do-VDR"
msgid "Bind to IP"
msgstr "viaza» na IP"
msgid "HTTP Server"
msgstr "server HTTP"
msgid "Start HTTP Server"
msgstr "Spusti» HTTP Server"
msgid "HTTP Server Port"
msgstr "Port serveru HTTP"
msgid "HTTP Streamtype"
msgstr "typ prúdu HTTP"
msgid "Multicast Streaming Server"
msgstr "Multicast prúdový server"
msgid "Start IGMP Server"
msgstr "Spusti» IGMP Server"
msgid "Multicast Client Port"
msgstr "Port klienta Multicast"
msgid "Multicast Streamtype"
msgstr "Multicast typ streamu"

291
server/recplayer.c Normal file
View File

@@ -0,0 +1,291 @@
/*
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"
// for TSPLAY patch detection
#include "vdr/device.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 || defined(TSPLAY_PATCH_VERSION)
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 || !defined(TSPLAY_PATCH_VERSION)
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 || defined(TSPLAY_PATCH_VERSION)
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 || defined(TSPLAY_PATCH_VERSION)
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
View 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

View File

@@ -1,10 +1,11 @@
/* /*
* $Id: server.c,v 1.5 2007/04/02 10:32:34 schmirl Exp $ * $Id: server.c,v 1.10 2009/02/13 10:39:22 schmirl Exp $
*/ */
#include "server/server.h" #include "server/server.h"
#include "server/componentVTP.h" #include "server/componentVTP.h"
#include "server/componentHTTP.h" #include "server/componentHTTP.h"
#include "server/componentIGMP.h"
#include "server/setup.h" #include "server/setup.h"
#include <vdr/tools.h> #include <vdr/tools.h>
@@ -13,14 +14,15 @@
#include <errno.h> #include <errno.h>
cSVDRPhosts StreamdevHosts; cSVDRPhosts StreamdevHosts;
char *opt_auth = NULL;
char *opt_remux = NULL;
cStreamdevServer *cStreamdevServer::m_Instance = NULL; cStreamdevServer *cStreamdevServer::m_Instance = NULL;
cList<cServerComponent> cStreamdevServer::m_Servers; cList<cServerComponent> cStreamdevServer::m_Servers;
cList<cServerConnection> cStreamdevServer::m_Clients; cList<cServerConnection> cStreamdevServer::m_Clients;
cStreamdevServer::cStreamdevServer(void): cStreamdevServer::cStreamdevServer(void):
cThread("streamdev server"), cThread("streamdev server")
m_Active(false)
{ {
Start(); Start();
} }
@@ -35,6 +37,12 @@ void cStreamdevServer::Initialize(void)
if (m_Instance == NULL) { if (m_Instance == NULL) {
if (StreamdevServerSetup.StartVTPServer) Register(new cComponentVTP); if (StreamdevServerSetup.StartVTPServer) Register(new cComponentVTP);
if (StreamdevServerSetup.StartHTTPServer) Register(new cComponentHTTP); if (StreamdevServerSetup.StartHTTPServer) Register(new cComponentHTTP);
if (StreamdevServerSetup.StartIGMPServer) {
if (strcmp(StreamdevServerSetup.IGMPBindIP, "0.0.0.0") == 0)
esyslog("streamdev-server: Not starting IGMP. IGMP must be bound to a local IP");
else
Register(new cComponentIGMP);
}
m_Instance = new cStreamdevServer; m_Instance = new cStreamdevServer;
} }
@@ -47,10 +55,8 @@ void cStreamdevServer::Destruct(void)
void cStreamdevServer::Stop(void) void cStreamdevServer::Stop(void)
{ {
if (m_Active) { if (Running())
m_Active = false;
Cancel(3); Cancel(3);
}
} }
void cStreamdevServer::Register(cServerComponent *Server) void cStreamdevServer::Register(cServerComponent *Server)
@@ -60,8 +66,6 @@ void cStreamdevServer::Register(cServerComponent *Server)
void cStreamdevServer::Action(void) void cStreamdevServer::Action(void)
{ {
m_Active = true;
/* Initialize Server components, deleting those that failed */ /* Initialize Server components, deleting those that failed */
for (cServerComponent *c = m_Servers.First(); c;) { for (cServerComponent *c = m_Servers.First(); c;) {
cServerComponent *next = m_Servers.Next(c); cServerComponent *next = m_Servers.Next(c);
@@ -72,11 +76,11 @@ void cStreamdevServer::Action(void)
if (m_Servers.Count() == 0) { if (m_Servers.Count() == 0) {
esyslog("ERROR: no streamdev server activated, exiting"); esyslog("ERROR: no streamdev server activated, exiting");
m_Active = false; Cancel(-1);
} }
cTBSelect select; cTBSelect select;
while (m_Active) { while (Running()) {
select.Clear(); select.Clear();
/* Ask all Server components to register to the selector */ /* Ask all Server components to register to the selector */
@@ -102,9 +106,9 @@ void cStreamdevServer::Action(void)
sel = 0; sel = 0;
} }
} }
} while (sel < 0 && errno == ETIMEDOUT && m_Active); } while (sel < 0 && errno == ETIMEDOUT && Running());
if (!m_Active) if (!Running())
break; break;
if (sel < 0) { if (sel < 0) {
esyslog("fatal error, server exiting: %m"); esyslog("fatal error, server exiting: %m");
@@ -115,13 +119,17 @@ void cStreamdevServer::Action(void)
for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c)){ for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c)){
if (sel && select.CanRead(c->Socket())) { if (sel && select.CanRead(c->Socket())) {
cServerConnection *client = c->Accept(); cServerConnection *client = c->Accept();
if (!client)
continue;
Lock();
m_Clients.Add(client); m_Clients.Add(client);
Unlock();
if (m_Clients.Count() > StreamdevServerSetup.MaxClients) { if (m_Clients.Count() > StreamdevServerSetup.MaxClients) {
esyslog("streamdev: too many clients, rejecting %s:%d", esyslog("streamdev: too many clients, rejecting %s:%d",
client->RemoteIp().c_str(), client->RemotePort()); client->RemoteIp().c_str(), client->RemotePort());
client->Reject(); client->Reject();
} else if (!StreamdevHosts.Acceptable(client->RemoteIpAddr())) { } else if (!client->CanAuthenticate() && !StreamdevHosts.Acceptable(client->RemoteIpAddr())) {
esyslog("streamdev: client %s:%d not allowed to connect", esyslog("streamdev: client %s:%d not allowed to connect",
client->RemoteIp().c_str(), client->RemotePort()); client->RemoteIp().c_str(), client->RemotePort());
client->Reject(); client->Reject();
@@ -144,26 +152,33 @@ void cStreamdevServer::Action(void)
cServerConnection *next = m_Clients.Next(s); cServerConnection *next = m_Clients.Next(s);
if (!result) { if (!result) {
isyslog("streamdev: closing streamdev connection to %s:%d", if (s->IsOpen())
s->RemoteIp().c_str(), s->RemotePort()); s->Close();
s->Close(); Lock();
m_Clients.Del(s); m_Clients.Del(s);
Unlock();
} }
s = next; s = next;
} }
} }
Lock();
while (m_Clients.Count() > 0) { while (m_Clients.Count() > 0) {
cServerConnection *s = m_Clients.First(); cServerConnection *s = m_Clients.First();
s->Close(); s->Close();
m_Clients.Del(s); m_Clients.Del(s);
} }
Unlock();
while (m_Servers.Count() > 0) { while (m_Servers.Count() > 0) {
cServerComponent *c = m_Servers.First(); cServerComponent *c = m_Servers.First();
c->Destruct(); c->Destruct();
m_Servers.Del(c); m_Servers.Del(c);
} }
}
m_Active = false;
const cList<cServerConnection>& cStreamdevServer::Clients(cThreadLock& Lock)
{
Lock.Lock(m_Instance);
return m_Clients;
} }

View File

@@ -1,5 +1,5 @@
/* /*
* $Id: server.h,v 1.3 2008/04/07 14:50:33 schmirl Exp $ * $Id: server.h,v 1.6 2008/10/22 11:59:32 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEV_SERVER_H #ifndef VDR_STREAMDEV_SERVER_H
@@ -10,13 +10,14 @@
#include "server/component.h" #include "server/component.h"
#include "server/connection.h" #include "server/connection.h"
#define EXTERNREMUXPATH (*AddDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N), "externremux.sh")) #define DEFAULT_EXTERNREMUX (*AddDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N), "externremux.sh"))
#define STREAMDEVHOSTSPATH (*AddDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N), "streamdevhosts.conf")) #define STREAMDEVHOSTSPATH (*AddDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N), "streamdevhosts.conf"))
extern char *opt_auth;
extern char *opt_remux;
class cStreamdevServer: public cThread { class cStreamdevServer: public cThread {
private: private:
bool m_Active;
static cStreamdevServer *m_Instance; static cStreamdevServer *m_Instance;
static cList<cServerComponent> m_Servers; static cList<cServerComponent> m_Servers;
static cList<cServerConnection> m_Clients; static cList<cServerConnection> m_Clients;
@@ -35,6 +36,8 @@ public:
static void Initialize(void); static void Initialize(void);
static void Destruct(void); static void Destruct(void);
static bool Active(void); static bool Active(void);
static const cList<cServerConnection>& Clients(cThreadLock& Lock);
}; };
inline bool cStreamdevServer::Active(void) inline bool cStreamdevServer::Active(void)

View File

@@ -1,12 +1,11 @@
/* /*
* $Id: setup.c,v 1.3 2008/04/07 14:50:33 schmirl Exp $ * $Id: setup.c,v 1.10 2010/07/19 13:49:31 schmirl Exp $
*/ */
#include <vdr/menuitems.h> #include <vdr/menuitems.h>
#include "server/setup.h" #include "server/setup.h"
#include "server/server.h" #include "server/server.h"
#include "i18n.h"
cStreamdevServerSetup StreamdevServerSetup; cStreamdevServerSetup StreamdevServerSetup;
@@ -16,11 +15,15 @@ cStreamdevServerSetup::cStreamdevServerSetup(void) {
VTPServerPort = 2004; VTPServerPort = 2004;
StartHTTPServer = true; StartHTTPServer = true;
HTTPServerPort = 3000; HTTPServerPort = 3000;
HTTPStreamType = stPES; HTTPStreamType = stTS;
StartIGMPServer = false;
IGMPClientPort = 1234;
IGMPStreamType = stTS;
SuspendMode = smAlways; SuspendMode = smAlways;
AllowSuspend = false; AllowSuspend = false;
strcpy(VTPBindIP, "0.0.0.0"); strcpy(VTPBindIP, "0.0.0.0");
strcpy(HTTPBindIP, "0.0.0.0"); strcpy(HTTPBindIP, "0.0.0.0");
strcpy(IGMPBindIP, "0.0.0.0");
} }
bool cStreamdevServerSetup::SetupParse(const char *Name, const char *Value) { bool cStreamdevServerSetup::SetupParse(const char *Name, const char *Value) {
@@ -32,37 +35,82 @@ bool cStreamdevServerSetup::SetupParse(const char *Name, const char *Value) {
else if (strcmp(Name, "HTTPServerPort") == 0) HTTPServerPort = atoi(Value); else if (strcmp(Name, "HTTPServerPort") == 0) HTTPServerPort = atoi(Value);
else if (strcmp(Name, "HTTPStreamType") == 0) HTTPStreamType = atoi(Value); else if (strcmp(Name, "HTTPStreamType") == 0) HTTPStreamType = atoi(Value);
else if (strcmp(Name, "HTTPBindIP") == 0) strcpy(HTTPBindIP, Value); else if (strcmp(Name, "HTTPBindIP") == 0) strcpy(HTTPBindIP, Value);
else if (strcmp(Name, "StartIGMPServer") == 0) StartIGMPServer = atoi(Value);
else if (strcmp(Name, "IGMPClientPort") == 0) IGMPClientPort = atoi(Value);
else if (strcmp(Name, "IGMPStreamType") == 0) IGMPStreamType = atoi(Value);
else if (strcmp(Name, "IGMPBindIP") == 0) strcpy(IGMPBindIP, Value);
else if (strcmp(Name, "SuspendMode") == 0) SuspendMode = atoi(Value); else if (strcmp(Name, "SuspendMode") == 0) SuspendMode = atoi(Value);
else if (strcmp(Name, "AllowSuspend") == 0) AllowSuspend = atoi(Value); else if (strcmp(Name, "AllowSuspend") == 0) AllowSuspend = atoi(Value);
else return false; else return false;
return true; return true;
} }
const char* cStreamdevServerMenuSetupPage::StreamTypes[st_Count - 1] = {
"TS",
"PES",
"PS",
"ES",
"EXT"
};
const char* cStreamdevServerMenuSetupPage::SuspendModes[sm_Count] = {
trNOOP("Offer suspend mode"),
trNOOP("Always suspended"),
trNOOP("Never suspended")
};
cStreamdevServerMenuSetupPage::cStreamdevServerMenuSetupPage(void) { cStreamdevServerMenuSetupPage::cStreamdevServerMenuSetupPage(void) {
m_NewSetup = StreamdevServerSetup; m_NewSetup = StreamdevServerSetup;
AddCategory (tr("Common Settings")); Set();
AddRangeEdit(tr("Maximum Number of Clients"), m_NewSetup.MaxClients, 0, 100);
AddSuspEdit (tr("Suspend behaviour"), m_NewSetup.SuspendMode);
AddBoolEdit (tr("Client may suspend"), m_NewSetup.AllowSuspend);
AddCategory (tr("VDR-to-VDR Server"));
AddBoolEdit (tr("Start VDR-to-VDR Server"), m_NewSetup.StartVTPServer);
AddShortEdit(tr("VDR-to-VDR Server Port"), m_NewSetup.VTPServerPort);
AddIpEdit (tr("Bind to IP"), m_NewSetup.VTPBindIP);
AddCategory (tr("HTTP Server"));
AddBoolEdit (tr("Start HTTP Server"), m_NewSetup.StartHTTPServer);
AddShortEdit(tr("HTTP Server Port"), m_NewSetup.HTTPServerPort);
AddTypeEdit (tr("HTTP Streamtype"), m_NewSetup.HTTPStreamType);
AddIpEdit (tr("Bind to IP"), m_NewSetup.HTTPBindIP);
SetCurrent(Get(1));
} }
cStreamdevServerMenuSetupPage::~cStreamdevServerMenuSetupPage() { cStreamdevServerMenuSetupPage::~cStreamdevServerMenuSetupPage() {
} }
void cStreamdevServerMenuSetupPage::Set(void) {
static const char* modes[sm_Count];
for (int i = 0; i < sm_Count; i++)
modes[i] = tr(SuspendModes[i]);
int current = Current();
Clear();
AddCategory (tr("Common Settings"));
Add(new cMenuEditIntItem (tr("Maximum Number of Clients"), &m_NewSetup.MaxClients, 0, 100));
Add(new cMenuEditStraItem(tr("Suspend behaviour"), &m_NewSetup.SuspendMode, sm_Count, modes));
if (m_NewSetup.SuspendMode == smOffer)
Add(new cMenuEditBoolItem(tr("Client may suspend"), &m_NewSetup.AllowSuspend));
AddCategory (tr("VDR-to-VDR Server"));
Add(new cMenuEditBoolItem(tr("Start VDR-to-VDR Server"), &m_NewSetup.StartVTPServer));
Add(new cMenuEditIntItem (tr("VDR-to-VDR Server Port"), &m_NewSetup.VTPServerPort, 0, 65535));
Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.VTPBindIP));
AddCategory (tr("HTTP Server"));
Add(new cMenuEditBoolItem(tr("Start HTTP Server"), &m_NewSetup.StartHTTPServer));
Add(new cMenuEditIntItem (tr("HTTP Server Port"), &m_NewSetup.HTTPServerPort, 0, 65535));
Add(new cMenuEditStraItem(tr("HTTP Streamtype"), &m_NewSetup.HTTPStreamType, st_Count - 1, StreamTypes));
Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.HTTPBindIP));
AddCategory (tr("Multicast Streaming Server"));
Add(new cMenuEditBoolItem(tr("Start IGMP Server"), &m_NewSetup.StartIGMPServer));
Add(new cMenuEditIntItem (tr("Multicast Client Port"), &m_NewSetup.IGMPClientPort, 0, 65535));
Add(new cMenuEditStraItem(tr("Multicast Streamtype"), &m_NewSetup.IGMPStreamType, st_Count - 1, StreamTypes));
Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.IGMPBindIP));
SetCurrent(Get(current));
Display();
}
void cStreamdevServerMenuSetupPage::AddCategory(const char *Title) {
cString str = cString::sprintf("--- %s -------------------------------------------------"
"---------------", Title );
cOsdItem *item = new cOsdItem(*str);
item->SetSelectable(false);
Add(item);
}
void cStreamdevServerMenuSetupPage::Store(void) { void cStreamdevServerMenuSetupPage::Store(void) {
bool restart = false; bool restart = false;
if (m_NewSetup.StartVTPServer != StreamdevServerSetup.StartVTPServer if (m_NewSetup.StartVTPServer != StreamdevServerSetup.StartVTPServer
@@ -70,7 +118,10 @@ void cStreamdevServerMenuSetupPage::Store(void) {
|| strcmp(m_NewSetup.VTPBindIP, StreamdevServerSetup.VTPBindIP) != 0 || strcmp(m_NewSetup.VTPBindIP, StreamdevServerSetup.VTPBindIP) != 0
|| m_NewSetup.StartHTTPServer != StreamdevServerSetup.StartHTTPServer || m_NewSetup.StartHTTPServer != StreamdevServerSetup.StartHTTPServer
|| m_NewSetup.HTTPServerPort != StreamdevServerSetup.HTTPServerPort || m_NewSetup.HTTPServerPort != StreamdevServerSetup.HTTPServerPort
|| strcmp(m_NewSetup.HTTPBindIP, StreamdevServerSetup.HTTPBindIP) != 0) { || strcmp(m_NewSetup.HTTPBindIP, StreamdevServerSetup.HTTPBindIP) != 0
|| m_NewSetup.StartIGMPServer != StreamdevServerSetup.StartIGMPServer
|| m_NewSetup.IGMPClientPort != StreamdevServerSetup.IGMPClientPort
|| strcmp(m_NewSetup.IGMPBindIP, StreamdevServerSetup.IGMPBindIP) != 0) {
restart = true; restart = true;
cStreamdevServer::Destruct(); cStreamdevServer::Destruct();
} }
@@ -83,6 +134,10 @@ void cStreamdevServerMenuSetupPage::Store(void) {
SetupStore("HTTPServerPort", m_NewSetup.HTTPServerPort); SetupStore("HTTPServerPort", m_NewSetup.HTTPServerPort);
SetupStore("HTTPStreamType", m_NewSetup.HTTPStreamType); SetupStore("HTTPStreamType", m_NewSetup.HTTPStreamType);
SetupStore("HTTPBindIP", m_NewSetup.HTTPBindIP); SetupStore("HTTPBindIP", m_NewSetup.HTTPBindIP);
SetupStore("StartIGMPServer", m_NewSetup.StartIGMPServer);
SetupStore("IGMPClientPort", m_NewSetup.IGMPClientPort);
SetupStore("IGMPStreamType", m_NewSetup.IGMPStreamType);
SetupStore("IGMPBindIP", m_NewSetup.IGMPBindIP);
SetupStore("SuspendMode", m_NewSetup.SuspendMode); SetupStore("SuspendMode", m_NewSetup.SuspendMode);
SetupStore("AllowSuspend", m_NewSetup.AllowSuspend); SetupStore("AllowSuspend", m_NewSetup.AllowSuspend);
@@ -92,3 +147,10 @@ void cStreamdevServerMenuSetupPage::Store(void) {
cStreamdevServer::Initialize(); cStreamdevServer::Initialize();
} }
eOSState cStreamdevServerMenuSetupPage::ProcessKey(eKeys Key) {
int oldMode = m_NewSetup.SuspendMode;
eOSState state = cMenuSetupPage::ProcessKey(Key);
if (oldMode != m_NewSetup.SuspendMode)
Set();
return state;
}

View File

@@ -1,5 +1,5 @@
/* /*
* $Id: setup.h,v 1.1 2004/12/30 22:44:21 lordjaxom Exp $ * $Id: setup.h,v 1.4 2010/07/19 13:49:31 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEV_SETUPSERVER_H #ifndef VDR_STREAMDEV_SETUPSERVER_H
@@ -20,18 +20,27 @@ struct cStreamdevServerSetup {
int HTTPServerPort; int HTTPServerPort;
int HTTPStreamType; int HTTPStreamType;
char HTTPBindIP[20]; char HTTPBindIP[20];
int StartIGMPServer;
int IGMPClientPort;
int IGMPStreamType;
char IGMPBindIP[20];
int SuspendMode; int SuspendMode;
int AllowSuspend; int AllowSuspend;
}; };
extern cStreamdevServerSetup StreamdevServerSetup; extern cStreamdevServerSetup StreamdevServerSetup;
class cStreamdevServerMenuSetupPage: public cStreamdevMenuSetupPage { class cStreamdevServerMenuSetupPage: public cMenuSetupPage {
private: private:
static const char* StreamTypes[];
static const char* SuspendModes[];
cStreamdevServerSetup m_NewSetup; cStreamdevServerSetup m_NewSetup;
void AddCategory(const char *Title);
void Set();
protected: protected:
virtual void Store(void); virtual void Store(void);
virtual eOSState ProcessKey(eKeys Key);
public: public:
cStreamdevServerMenuSetupPage(void); cStreamdevServerMenuSetupPage(void);

View File

@@ -3,22 +3,21 @@
* *
* 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.7 2008/04/07 14:27:27 schmirl Exp $ * $Id: streamdev-server.c,v 1.2 2010/07/19 13:49:32 schmirl Exp $
*/ */
#include <getopt.h> #include <getopt.h>
#include <vdr/tools.h>
#include "streamdev-server.h" #include "streamdev-server.h"
#include "server/menu.h"
#include "server/setup.h" #include "server/setup.h"
#include "server/server.h" #include "server/server.h"
#include "server/suspend.h"
#include "remux/extern.h"
#include "i18n.h"
#if VDRVERSNUM < 10400 #if !defined(APIVERSNUM) || APIVERSNUM < 10516
#error "VDR-1.4.0 or greater is required" #error "VDR-1.5.16 API version or greater is required!"
#endif #endif
const char *cPluginStreamdevServer::DESCRIPTION = "VDR Streaming Server"; const char *cPluginStreamdevServer::DESCRIPTION = trNOOP("VDR Streaming Server");
cPluginStreamdevServer::cPluginStreamdevServer(void) cPluginStreamdevServer::cPluginStreamdevServer(void)
{ {
@@ -26,6 +25,8 @@ cPluginStreamdevServer::cPluginStreamdevServer(void)
cPluginStreamdevServer::~cPluginStreamdevServer() cPluginStreamdevServer::~cPluginStreamdevServer()
{ {
free(opt_auth);
free(opt_remux);
} }
const char *cPluginStreamdevServer::Description(void) const char *cPluginStreamdevServer::Description(void)
@@ -36,22 +37,39 @@ const char *cPluginStreamdevServer::Description(void)
const char *cPluginStreamdevServer::CommandLineHelp(void) const char *cPluginStreamdevServer::CommandLineHelp(void)
{ {
// return a string that describes all known command line options. // return a string that describes all known command line options.
return " -r <CMD>, --remux=<CMD> Define an external command for remuxing.\n"; return
" -a <LOGIN:PASSWORD>, --auth=<LOGIN:PASSWORD> Credentials for HTTP authentication.\n"
" -r <CMD>, --remux=<CMD> Define an external command for remuxing.\n"
;
} }
bool cPluginStreamdevServer::ProcessArgs(int argc, char *argv[]) bool cPluginStreamdevServer::ProcessArgs(int argc, char *argv[])
{ {
// implement command line argument processing here if applicable. // implement command line argument processing here if applicable.
static const struct option long_options[] = { static const struct option long_options[] = {
{ "auth", required_argument, NULL, 'a' },
{ "remux", required_argument, NULL, 'r' }, { "remux", required_argument, NULL, 'r' },
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
}; };
int c; int c;
while((c = getopt_long(argc, argv, "r:", long_options, NULL)) != -1) { while((c = getopt_long(argc, argv, "a:r:", long_options, NULL)) != -1) {
switch (c) { switch (c) {
case 'a':
{
if (opt_auth)
free(opt_auth);
int l = strlen(optarg);
cBase64Encoder Base64((uchar*) optarg, l, l * 4 / 3 + 3);
const char *s = Base64.NextLine();
if (s)
opt_auth = strdup(s);
}
break;
case 'r': case 'r':
g_ExternRemux = optarg; if (opt_remux)
free(opt_remux);
opt_remux = strdup(optarg);
break; break;
default: default:
return false; return false;
@@ -62,9 +80,7 @@ bool cPluginStreamdevServer::ProcessArgs(int argc, char *argv[])
bool cPluginStreamdevServer::Start(void) bool cPluginStreamdevServer::Start(void)
{ {
i18n_name = Name(); I18nRegister(PLUGIN_NAME_I18N);
RegisterI18n(Phrases);
if (!StreamdevHosts.Load(STREAMDEVHOSTSPATH, true, true)) { if (!StreamdevHosts.Load(STREAMDEVHOSTSPATH, true, true)) {
esyslog("streamdev-server: error while loading %s", STREAMDEVHOSTSPATH); esyslog("streamdev-server: error while loading %s", STREAMDEVHOSTSPATH);
fprintf(stderr, "streamdev-server: error while loading %s\n", STREAMDEVHOSTSPATH); fprintf(stderr, "streamdev-server: error while loading %s\n", STREAMDEVHOSTSPATH);
@@ -77,6 +93,8 @@ bool cPluginStreamdevServer::Start(void)
} }
return false; return false;
} }
if (!opt_remux)
opt_remux = strdup(DEFAULT_EXTERNREMUX);
cStreamdevServer::Initialize(); cStreamdevServer::Initialize();
@@ -101,15 +119,20 @@ cString cPluginStreamdevServer::Active(void)
const char *cPluginStreamdevServer::MainMenuEntry(void) const char *cPluginStreamdevServer::MainMenuEntry(void)
{ {
if (StreamdevServerSetup.SuspendMode == smOffer && !cSuspendCtl::IsActive()) return tr("Streamdev Connections");
return tr("Suspend Live TV");
return NULL;
} }
cOsdObject *cPluginStreamdevServer::MainMenuAction(void) cOsdObject *cPluginStreamdevServer::MainMenuAction(void)
{ {
cControl::Launch(new cSuspendCtl); return new cStreamdevServerMenu();
return NULL; }
void cPluginStreamdevServer::MainThreadHook(void)
{
cThreadLock lock;
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
for (cServerConnection *s = clients.First(); s; s = clients.Next(s))
s->MainThreadHook();
} }
cMenuSetupPage *cPluginStreamdevServer::SetupMenu(void) cMenuSetupPage *cPluginStreamdevServer::SetupMenu(void)

View File

@@ -1,5 +1,5 @@
/* /*
* $Id: streamdev-server.h,v 1.4 2007/02/19 12:08:16 schmirl Exp $ * $Id: streamdev-server.h,v 1.2 2010/07/19 13:49:32 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEVSERVER_H #ifndef VDR_STREAMDEVSERVER_H
@@ -26,6 +26,7 @@ public:
virtual cString Active(void); virtual cString Active(void);
virtual const char *MainMenuEntry(void); virtual const char *MainMenuEntry(void);
virtual cOsdObject *MainMenuAction(void); virtual cOsdObject *MainMenuAction(void);
virtual void MainThreadHook(void);
virtual cMenuSetupPage *SetupMenu(void); virtual cMenuSetupPage *SetupMenu(void);
virtual bool SetupParse(const char *Name, const char *Value); virtual bool SetupParse(const char *Name, const char *Value);
}; };

View File

@@ -1,5 +1,5 @@
/* /*
* $Id: streamer.c,v 1.16 2007/09/21 11:45:53 schmirl Exp $ * $Id: streamer.c,v 1.21 2010/07/30 10:01:11 schmirl Exp $
*/ */
#include <vdr/ringbuffer.h> #include <vdr/ringbuffer.h>
@@ -14,22 +14,28 @@
#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,
cStreamdevStreamer *Streamer): cStreamdevStreamer *Streamer):
cThread("streamdev-writer"), cThread("streamdev-writer"),
m_Streamer(Streamer), m_Streamer(Streamer),
m_Socket(Socket), m_Socket(Socket)
m_Active(false)
{ {
} }
cStreamdevWriter::~cStreamdevWriter() cStreamdevWriter::~cStreamdevWriter()
{ {
Dprintf("destructing writer\n"); Dprintf("destructing writer\n");
m_Active = false; if (Running())
Cancel(3); Cancel(3);
} }
void cStreamdevWriter::Action(void) void cStreamdevWriter::Action(void)
@@ -39,57 +45,78 @@ void cStreamdevWriter::Action(void)
int max = 0; int max = 0;
uchar *block = NULL; uchar *block = NULL;
int count, offset = 0; int count, offset = 0;
m_Active = true; int timeout = 0;
#if APIVERSNUM >= 10705
SetPriority(-3);
#endif
sel.Clear(); sel.Clear();
sel.Add(*m_Socket, true); sel.Add(*m_Socket, true);
while (m_Active) { while (Running()) {
if (block == NULL) { if (block == NULL) {
block = m_Streamer->Get(count); block = m_Streamer->Get(count);
offset = 0; offset = 0;
} }
if (block != NULL) { if (block != NULL) {
if (sel.Select(15000) == -1) { if (sel.Select(600) == -1) {
if (errno == ETIMEDOUT && timeout++ < 20)
continue; // still Running()?
esyslog("ERROR: streamdev-server: couldn't send data: %m"); esyslog("ERROR: streamdev-server: couldn't send data: %m");
break; break;
} }
timeout = 0;
if (sel.CanWrite(*m_Socket)) { if (sel.CanWrite(*m_Socket)) {
int written; int written;
if ((written = m_Socket->Write(block + offset, count)) == -1) { int pkgsize = count;
esyslog("ERROR: streamdev-server: couldn't send data: %m"); // SOCK_DGRAM indicates multicast
if (m_Socket->Type() == SOCK_DGRAM) {
// don't fragment multicast packets
// max. payload on standard local ethernet is 1416 to 1456 bytes
// and some STBs expect complete TS packets
// so let's always limit to 7 * TS_SIZE = 1316
if (pkgsize > 7 * TS_SIZE)
pkgsize = 7 * TS_SIZE;
else
pkgsize -= pkgsize % TS_SIZE;
}
if ((written = m_Socket->Write(block + offset, pkgsize)) == -1) {
esyslog("ERROR: streamdev-server: couldn't send %d bytes: %m", pkgsize);
break; break;
} }
// statistics
if (count > max) if (count > max)
max = count; max = count;
offset += written; offset += written;
count -= written; count -= written;
if (count == 0) {
// less than one TS packet left:
// delete what we've written so far and get next chunk
if (count < TS_SIZE) {
m_Streamer->Del(offset); m_Streamer->Del(offset);
block = NULL; block = NULL;
} }
} }
} }
} }
m_Active = false;
Dprintf("Max. Transmit Blocksize was: %d\n", max); Dprintf("Max. Transmit Blocksize was: %d\n", max);
} }
// --- cStreamdevStreamer ----------------------------------------------------- // --- cStreamdevStreamer -----------------------------------------------------
cStreamdevStreamer::cStreamdevStreamer(const char *Name): cStreamdevStreamer::cStreamdevStreamer(const char *Name, const cServerConnection *Connection):
cThread(Name), cThread(Name),
m_Active(false), m_Connection(Connection),
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()
@@ -103,13 +130,12 @@ 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();
} }
void cStreamdevStreamer::Activate(bool On) void cStreamdevStreamer::Activate(bool On)
{ {
if (On && !m_Active) { if (On && !Active()) {
Dprintf("activate streamer\n"); Dprintf("activate streamer\n");
m_Writer->Start(); m_Writer->Start();
cThread::Start(); cThread::Start();
@@ -118,22 +144,22 @@ void cStreamdevStreamer::Activate(bool On)
void cStreamdevStreamer::Stop(void) void cStreamdevStreamer::Stop(void)
{ {
if (m_Active) { if (Running()) {
Dprintf("stopping streamer\n"); Dprintf("stopping streamer\n");
m_Active = false;
Cancel(3); Cancel(3);
} }
if (m_Running) { if (m_Writer) {
Detach(); Detach();
m_Running = false;
DELETENULL(m_Writer); DELETENULL(m_Writer);
} }
} }
void cStreamdevStreamer::Action(void) void cStreamdevStreamer::Action(void)
{ {
m_Active = true; #if APIVERSNUM >= 10705
while (m_Active) { SetPriority(-3);
#endif
while (Running()) {
int got; int got;
uchar *block = m_RingBuffer->Get(got); uchar *block = m_RingBuffer->Get(got);
@@ -141,8 +167,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);
} }
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
* $Id: streamer.h,v 1.8 2007/04/02 10:32:34 schmirl Exp $ * $Id: streamer.h,v 1.12 2010/07/19 13:49:32 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEV_STREAMER_H #ifndef VDR_STREAMDEV_STREAMER_H
@@ -11,9 +11,40 @@
class cTBSocket; class cTBSocket;
class cStreamdevStreamer; class cStreamdevStreamer;
class cServerConnection;
#define STREAMERBUFSIZE MEGABYTE(4) #ifndef TS_SIZE
#define WRITERBUFSIZE KILOBYTE(256) #define TS_SIZE 188
#endif
#define STREAMERBUFSIZE (20000 * TS_SIZE)
#define WRITERBUFSIZE (20000 * 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 -------------------------------------------------------
@@ -21,7 +52,6 @@ class cStreamdevWriter: public cThread {
private: private:
cStreamdevStreamer *m_Streamer; cStreamdevStreamer *m_Streamer;
cTBSocket *m_Socket; cTBSocket *m_Socket;
bool m_Active;
protected: protected:
virtual void Action(void); virtual void Action(void);
@@ -29,38 +59,37 @@ protected:
public: public:
cStreamdevWriter(cTBSocket *Socket, cStreamdevStreamer *Streamer); cStreamdevWriter(cTBSocket *Socket, cStreamdevStreamer *Streamer);
virtual ~cStreamdevWriter(); virtual ~cStreamdevWriter();
bool IsActive(void) const { return m_Active; }
}; };
// --- cStreamdevStreamer ----------------------------------------------------- // --- cStreamdevStreamer -----------------------------------------------------
class cStreamdevStreamer: public cThread { class cStreamdevStreamer: public cThread {
private: private:
bool m_Active; const cServerConnection *m_Connection;
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, const cServerConnection *Connection = NULL);
virtual ~cStreamdevStreamer(); virtual ~cStreamdevStreamer();
const cServerConnection* Connection(void) const { return m_Connection; }
virtual void Start(cTBSocket *Socket); virtual void Start(cTBSocket *Socket);
virtual void Stop(void); virtual void Stop(void);
bool Abort(void) const; 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); }
@@ -68,9 +97,9 @@ public:
virtual void Attach(void) {} virtual void Attach(void) {}
}; };
inline bool cStreamdevStreamer::Abort(void) const inline bool cStreamdevStreamer::Abort(void)
{ {
return m_Active && !m_Writer->IsActive(); return Active() && !m_Writer->Active();
} }
#endif // VDR_STREAMDEV_STREAMER_H #endif // VDR_STREAMDEV_STREAMER_H

View File

@@ -1,5 +1,5 @@
/* /*
* $Id: suspend.c,v 1.2 2008/04/07 14:27:31 schmirl Exp $ * $Id: suspend.c,v 1.3 2008/10/22 11:59:32 schmirl Exp $
*/ */
#include "server/suspend.h" #include "server/suspend.h"
@@ -12,6 +12,7 @@ cSuspendLive::cSuspendLive(void)
} }
cSuspendLive::~cSuspendLive() { cSuspendLive::~cSuspendLive() {
Stop();
Detach(); Detach();
} }
@@ -24,17 +25,14 @@ void cSuspendLive::Activate(bool On) {
} }
void cSuspendLive::Stop(void) { void cSuspendLive::Stop(void) {
if (m_Active) { if (Running())
m_Active = false;
Cancel(3); Cancel(3);
}
} }
void cSuspendLive::Action(void) { void cSuspendLive::Action(void) {
m_Active = true; while (Running()) {
while (m_Active) {
DeviceStillPicture(suspend_mpg, sizeof(suspend_mpg)); DeviceStillPicture(suspend_mpg, sizeof(suspend_mpg));
usleep(100000); cCondWait::SleepMs(100);
} }
} }
@@ -51,7 +49,7 @@ cSuspendCtl::~cSuspendCtl() {
} }
eOSState cSuspendCtl::ProcessKey(eKeys Key) { eOSState cSuspendCtl::ProcessKey(eKeys Key) {
if (!m_Suspend->IsActive() || Key == kBack) { if (!m_Suspend->Active() || Key == kBack) {
DELETENULL(m_Suspend); DELETENULL(m_Suspend);
return osEnd; return osEnd;
} }

View File

@@ -1,5 +1,5 @@
/* /*
* $Id: suspend.h,v 1.1 2004/12/30 22:44:26 lordjaxom Exp $ * $Id: suspend.h,v 1.2 2008/10/22 11:59:32 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEV_SUSPEND_H #ifndef VDR_STREAMDEV_SUSPEND_H
@@ -7,10 +7,7 @@
#include <vdr/player.h> #include <vdr/player.h>
class cSuspendLive: public cPlayer, cThread { class cSuspendLive: public cPlayer, public cThread {
private:
bool m_Active;
protected: protected:
virtual void Activate(bool On); virtual void Activate(bool On);
virtual void Action(void); virtual void Action(void);
@@ -20,8 +17,6 @@ protected:
public: public:
cSuspendLive(void); cSuspendLive(void);
virtual ~cSuspendLive(); virtual ~cSuspendLive();
bool IsActive(void) const { return m_Active; }
}; };
class cSuspendCtl: public cControl { class cSuspendCtl: public cControl {

323
streamdev-server/externremux.sh Executable file
View File

@@ -0,0 +1,323 @@
#!/bin/bash
#
# externremux.sh - sample remux script using mencoder for remuxing.
#
# Install this script as VDRCONFDIR/plugins/streamdev-server/externremux.sh
#
# The parameter QUALITY selects the default remux parameters. Adjust
# to your needs and point your web browser to http://servername:3000/ext/
# To select different remux parameters on the fly, insert a semicolon
# followed by the name and value of the requested parameter, e.g:
# e.g. http://servername:3000/ext;QUALITY=WLAN11;VBR=512/
# The following parameters are recognized:
#
# PROG actual remux program
# VC video codec
# VBR video bitrate (kbit)
# VOPTS custom video options
# WIDTH scale video to width
# HEIGHT scale video to height
# FPS output frames per second
# AC audio codec
# ABR audio bitrate (kbit)
# AOPTS custom audio options
#
##########################################################################
### GENERAL CONFIG START
###
# Pick one of DSL1000/DSL2000/DSL3000/DSL6000/DSL16000/LAN10/WLAN11/WLAN54
QUALITY='DSL1000'
# Program used for logging (logging disabled if empty)
LOGGER=logger
# Path and name of FIFO
FIFO=/tmp/externremux-${RANDOM:-$$}
# Default remux program (cat/mencoder/ogg)
PROG=mencoder
# Use mono if $ABR is lower than this value
ABR_MONO=64
###
### GENERAL CONFIG END
### MENCODER CONFIG START
###
# mencoder binary
MENCODER=mencoder
# verbosity from all=-1 to all=9 (-msglevel ...)
MENCODER_MSGLEVEL=all=1
### video part
# Default video codec (e.g. lavc/x264/copy)
MENCODER_VC=lavc
# Default video options if lavc is used (-ovc lavc -lavcopts ...)
MENCODER_LAVC_VOPTS=vcodec=mpeg4
# Default video options if x264 is used (-ovc x264 -x264encopts ...)
MENCODER_X264_VOPTS=threads=auto
### audio part
# Audio language to use if several audio PIDs are available (-alang ...)
MENCODER_ALANG=eng
# Default audio codec (e.g. lavc/mp3lame/faac/copy)
MENCODER_AC=mp3lame
# Default audio options if lavc is used (-oac lavc -lavcopts ...)
MENCODER_LAVC_AOPTS=acodec=mp2
# Default audio options if mp3lame is used (-oac mp3lame -lameopts ...)
MENCODER_LAME_AOPTS=
# Default audio options if faac is used (-oac faac -faacopts ...)
MENCODER_FAAC_AOPTS=
###
### MENCODER CONFIG END
### OGG CONFIG START
###
# ffmpeg2theora binary
OGG=ffmpeg2theora
# speedlevel - lower value gives better quality but is slower (0..2)
OGG_SPEED=1
# videoquality - higher value gives better quality but is slower (0..10)
OGG_VQUALITY=0
# audioquality - higher value gives better quality but is slower (0..10)
OGG_AQUALITY=0
# aspect ratio used for scaling if only one of HEIGHT/WIDTH given (16/9 or 4/3)
OGG_ASPECT='4 / 3'
###
### OGG CONFIG END
##########################################################################
function hasOpt { echo "$1" | grep -q "\b${2}\b"; }
# $1: concatenation of already set option=value pairs
# $2-$n: option=value pairs to be echod if the option is not present in $1
function addOpts
{
local opts="$1"
shift
while [ $# -gt 0 ]; do
hasOpt "$opts" ${1%%=*}= || echo $1
shift
done
}
function isNumeric() { echo "$@" | grep -q '^-\?[0-9]\{1,\}$'; }
function remux_cat
{
startReply
exec 3<&0
cat 0<&3 >"$FIFO" &
}
function remux_mencoder
{
# lavc may be used for video and audio
LAVCOPTS=()
# Assemble video options
VC=${REMUX_PARAM_VC:-$MENCODER_VC}
VOPTS=${REMUX_PARAM_VOPTS}
FPS=${REMUX_PARAM_FPS:-$FPS}
# if only one of HEIGHT/WIDTH given:
# have mencoder calculate other value depending on actual aspect ratio
if [ "$HEIGHT" -a -z "$WIDTH" ]; then
WIDTH=-3
elif [ "$WIDTH" -a -z "$HEIGHT" ]; then
HEIGHT=-3
fi
case "$VC" in
lavc)
LAVCOPTS=(
${VOPTS}
$(IFS=$IFS:; addOpts "$VOPTS" $MENCODER_LAVC_VOPTS)
${VBR:+vbitrate=$VBR}
)
[ ${#LAVCOPTS[*]} -gt 0 ] && VOPTS=$(IFS=:; echo -lavcopts "${LAVCOPTS[*]}")
;;
x264)
isNumeric "$HEIGHT" && [ $HEIGHT -lt 0 -a $HEIGHT -gt -8 ] && ((HEIGHT-=8))
isNumeric "$WIDTH" && [ $WIDTH -lt 0 -a $WIDTH -gt -8 ] && ((WIDTH-=8))
X264OPTS=(
${VOPTS}
$(IFS=$IFS:; addOpts "$VOPTS" $MENCODER_X264_VOPTS)
${VBR:+bitrate=$VBR}
)
[ ${#X264OPTS[*]} -gt 0 ] && VOPTS=$(IFS=:; echo -x264encopts "${X264OPTS[*]}")
;;
copy)
VOPTS=
;;
*)
error "Unknown video codec '$VC'"
;;
esac
# Assemble audio options
AC=${REMUX_PARAM_AC:-$MENCODER_AC}
AOPTS=${REMUX_PARAM_AOPTS}
case "$AC" in
lavc)
LAVCOPTS=(
${LAVCOPTS[*]}
${AOPTS}
$(IFS=$IFS:; addOpts "$AOPTS" $MENCODER_LAVC_AOPTS)
${ABR:+abitrate=$ABR}
)
[ ${#LAVCOPTS[*]} -gt 0 ] && AOPTS=$(IFS=:; echo -lavcopts "${LAVCOPTS[*]}")
# lavc used for video and audio decoding - wipe out VOPTS as video options became part of AOPTS
[ "$VC" = lavc ] && VOPTS=
;;
mp3lame)
LAMEOPTS=(
${AOPTS}
$(isNumeric "${ABR}" && [ "${ABR}" -lt "$ABR_MONO" ] && ! hasOpt "${AOPTS}" mode ] && echo 'mode=3')
$(IFS=$IFS:; addOpts "$AOPTS" $MENCODER_LAME_AOPTS)
${ABR:+preset=$ABR}
)
[ ${#LAMEOPTS[*]} -gt 0 ] && AOPTS=$(IFS=:; echo -lameopts "${LAMEOPTS[*]}")
;;
faac)
FAACOPTS=(
${AOPTS}
$(IFS=$IFS:; addOpts "$AOPTS" $MENCODER_FAAC_AOPTS)
${ABR:+br=$ABR}
)
[ ${#FAACOPTS[*]} -gt 0 ] && AOPTS=$(IFS=:; echo -faacopts "${FAACOPTS[*]}")
;;
copy)
AOPTS=
;;
*)
error "Unknown audio codec '$AC'"
;;
esac
startReply
exec 3<&0
echo $MENCODER \
${MENCODER_MSGLEVEL:+-msglevel $MENCODER_MSGLEVEL} \
-ovc $VC $VOPTS \
-oac $AC $AOPTS \
${MENCODER_ALANG:+-alang $MENCODER_ALANG} \
${WIDTH:+-vf scale=$WIDTH:$HEIGHT -zoom} \
${FPS:+-ofps $FPS} \
-o "$FIFO" -- - >&2
$MENCODER \
${MENCODER_MSGLEVEL:+-msglevel $MENCODER_MSGLEVEL} \
-ovc $VC $VOPTS \
-oac $AC $AOPTS \
${MENCODER_ALANG:+-alang $MENCODER_ALANG} \
${WIDTH:+-vf scale=$WIDTH:$HEIGHT -zoom} \
${FPS:+-ofps $FPS} \
-o "$FIFO" -- - 0<&3 >/dev/null &
}
function remux_ogg
{
VOPTS=${REMUX_PARAM_VOPTS//[:=]/ }
AOPTS=${REMUX_PARAM_AOPTS//[:=]/ }
# if only one of HEIGHT/WIDTH given:
# calculate other value depending on configured aspect ratio
# trim to multiple of 8
if [ "$HEIGHT" -a -z "$WIDTH" ]; then
WIDTH=$((HEIGHT * $OGG_ASPECT / 8 * 8))
elif [ "$WIDTH" -a -z "$HEIGHT" ]; then
HEIGHT=$(($WIDTH * $( echo $OGG_ASPECT | sed 's#^\([0-9]\+\) */ *\([0-9]\+\)$#\2 / \1#') / 8 * 8))
fi
OGGOPTS=(
${VOPTS}
${VBR:+--videobitrate $VBR}
$(hasOpt "${VOPTS}" videoquality || echo "--videoquality $OGG_VQUALITY")
$(hasOpt "${VOPTS}" speedlevel || echo "--speedlevel $OGG_SPEED")
${AOPTS}
${ABR:+--audiobitrate $ABR}
$(isNumeric "${ABR}" && [ "${ABR}" -lt "$ABR_MONO" ] && ! hasOpt "${AOPTS}" channels ] && echo '--channels 1')
$(hasOpt "${AOPTS}" audioquality || echo "--audioquality $OGG_AQUALITY")
$(hasOpt "${AOPTS}" audiostream || echo '--audiostream 1')
)
startReply
exec 3<&0
echo $OGG --format ts \
${OGGOPTS[*]} \
${WIDTH:+--width $WIDTH --height $HEIGHT} \
--title "VDR Streamdev: ${REMUX_CHANNEL_NAME}" \
--output "$FIFO" -- - 0<&3 >&2
$OGG --format ts \
${OGGOPTS[*]} \
${WIDTH:+--width $WIDTH --height $HEIGHT} \
--title "VDR Streamdev: ${REMUX_CHANNEL_NAME}" \
--output "$FIFO" -- - 0<&3 >/dev/null &
}
function error
{
if [ "$SERVER_PROTOCOL" = HTTP ]; then
echo -ne "Content-type: text/plain\r\n"
echo -ne '\r\n'
echo "$*"
fi
echo "$*" >&2
exit 1
}
function startReply
{
if [ "$SERVER_PROTOCOL" = HTTP ]; then
# send content-type and custom headers
echo -ne "Content-type: ${CONTENTTYPE}\r\n"
for header in "${HEADER[@]}"; do echo -ne "$header\r\n"; done
echo -ne '\r\n'
# abort after headers
[ "$REQUEST_METHOD" = HEAD ] && exit 0
fi
# create FIFO and read from it in the background
mkfifo "$FIFO"
trap "trap '' EXIT HUP INT TERM ABRT PIPE CHLD; kill -INT 0; sleep 1; fuser -k '$FIFO'; rm '$FIFO'" EXIT HUP INT TERM ABRT PIPE CHLD
cat "$FIFO" <&- &
}
HEADER=()
[ "$LOGGER" ] && exec 2> >($LOGGER -t "vdr: [$$] ${0##*/}" 2>&-)
# set default content-types
case "$REMUX_VPID" in
''|0|1) CONTENTTYPE='audio/mpeg';;
*) CONTENTTYPE='video/mpeg';;
esac
QUALITY=${REMUX_PARAM_QUALITY:-$QUALITY}
case "$QUALITY" in
DSL1000|dsl1000) VBR=96; ABR=16; WIDTH=160;;
DSL2000|dsl2000) VBR=128; ABR=16; WIDTH=160;;
DSL3000|dsl3000) VBR=256; ABR=16; WIDTH=320;;
DSL6000|dsl6000) VBR=378; ABR=32; WIDTH=320;;
DSL16000|dsl16000) VBR=512; ABR=32; WIDTH=480;;
WLAN11|wlan11) VBR=768; ABR=64; WIDTH=640;;
WLAN54|wlan54) VBR=2048; ABR=128; WIDTH=;;
LAN10|lan10) VBR=4096; ABR=; WIDTH=;;
*) error "Unknown quality '$QUALITY'";;
esac
ABR=${REMUX_PARAM_ABR:-$ABR}
VBR=${REMUX_PARAM_VBR:-$VBR}
WIDTH=${REMUX_PARAM_WIDTH:-$WIDTH}
HEIGHT=${REMUX_PARAM_HEIGHT:-$HEIGHT}
PROG=${REMUX_PARAM_PROG:-$PROG}
case "$PROG" in
cat) remux_cat;;
mencoder) remux_mencoder;;
ogg) remux_ogg;;
*) error "Unknown remuxer '$PROG'";;
esac
set -o monitor
wait

View File

@@ -10,4 +10,5 @@
127.0.0.1 # always accept localhost 127.0.0.1 # always accept localhost
#192.168.100.0/24 # any host on the local net #192.168.100.0/24 # any host on the local net
#204.152.189.113 # a specific host #204.152.189.113 # a specific host
#0.0.0.0/0 # any host on any net (USE THIS WITH CARE!) #239.255.0.0/16 # uncomment for IGMP multicast streaming
#0.0.0.0/0 # any host on any net (DON'T DO THAT! USE AUTHENTICATION)

View File

@@ -1,48 +0,0 @@
#!/bin/sh
#
# externremux.sh - sample remux script using mencoder for remuxing.
#
# Install this script as VDRCONFDIR/plugins/streamdev/externremux.sh
#
# The parameter STREAMQUALITY selects the default remux parameters. Adjust
# to your needs and point your web browser to http://servername:3000/extern/
# To select different remux parameters on the fly, insert a semicolon and
# the name of the requested quality: http://servername:3000/extern;WLAN11/
# CONFIG START
STREAMQUALITY="DSL6000" # DSL{1,2,3,6}000, LAN10, WLAN{11,54}, IPAQ
TMP=/tmp/externremux-${RANDOM:-$$}
MENCODER=mencoder
# CONFIG END
mkdir -p $TMP
mkfifo $TMP/out.avi
(trap "rm -rf $TMP" EXIT HUP INT TERM ABRT; cat $TMP/out.avi) &
case ${1:-$STREAMQUALITY} in
DSL1000) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=100 \
-oac mp3lame -lameopts preset=15:mode=3 -vf scale=160:104 \
-o $TMP/out.avi -- - &>$TMP/out.log ;;
DSL2000) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=128 \
-oac mp3lame -lameopts preset=15:mode=3 -vf scale=160:104 \
-o $TMP/out.avi -- - &>$TMP/out.log ;;
DSL3000) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=250 \
-oac mp3lame -lameopts preset=15:mode=3 -vf scale=320:208 \
-o $TMP/out.avi -- - &>$TMP/out.log ;;
DSL6000) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=350 \
-oac mp3lame -lameopts preset=15:mode=3 -vf scale=320:208 \
-o $TMP/out.avi -- - &>$TMP/out.log ;;
LAN10) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=4096 \
-oac mp3lame -lameopts preset=standard \
-o $TMP/out.avi -- - &>$TMP/out.log ;;
WLAN11) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=768 \
-oac mp3lame -lameopts preset=standard -vf scale=640:408 \
-o $TMP/out.avi -- - &>$TMP/out.log ;;
WLAN54) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=2048 \
-oac mp3lame -lameopts preset=standard \
-o $TMP/out.avi -- - &>$TMP/out.log ;;
IPAQ) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=350 \
-oac mp3lame -lameopts preset=15:mode=3 -vf scale=320:208 \
-o $TMP/out.avi -- - &>$TMP/out.log ;;
*) touch $TMP/out.avi ;;
esac

34
tools/Makefile Normal file
View File

@@ -0,0 +1,34 @@
#
# Makefile for a Video Disk Recorder plugin
#
# $Id: Makefile,v 1.2 2010/07/19 13:49:44 schmirl Exp $
### The object files (add further files here):
OBJS = select.o socket.o source.o tools.o
### The main target:
.PHONY: clean
sockettools.a: $(OBJS)
ar -rcs sockettools.a $(OBJS)
### Implicit rules:
%.o: %.c
$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
### Dependencies:
MAKEDEP = $(CXX) -MM -MG
DEPFILE = .dependencies
$(DEPFILE): Makefile
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
-include $(DEPFILE)
### Targets:
clean:
@-rm -f $(OBJS) $(DEPFILE) *.a core* *~

View File

@@ -1,5 +1,6 @@
#include "tools/socket.h" #include "tools/socket.h"
#include <vdr/tools.h>
#include <string.h> #include <string.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <unistd.h> #include <unistd.h>
@@ -15,10 +16,11 @@
// actual DSCP value used // actual DSCP value used
#define STREAMDEV_DSCP DSCP_AF41 #define STREAMDEV_DSCP DSCP_AF41
cTBSocket::cTBSocket(int Type) { cTBSocket::cTBSocket(int Type, int Protocol) {
memset(&m_LocalAddr, 0, sizeof(m_LocalAddr)); memset(&m_LocalAddr, 0, sizeof(m_LocalAddr));
memset(&m_RemoteAddr, 0, sizeof(m_RemoteAddr)); memset(&m_RemoteAddr, 0, sizeof(m_RemoteAddr));
m_Type = Type; m_Type = Type;
m_Protocol = Protocol;
} }
cTBSocket::~cTBSocket() { cTBSocket::~cTBSocket() {
@@ -31,7 +33,7 @@ bool cTBSocket::Connect(const std::string &Host, unsigned int Port) {
if (IsOpen()) Close(); if (IsOpen()) Close();
if ((socket = ::socket(PF_INET, m_Type, IPPROTO_IP)) == -1) if ((socket = ::socket(PF_INET, m_Type, m_Protocol)) == -1)
return false; return false;
m_LocalAddr.sin_family = AF_INET; m_LocalAddr.sin_family = AF_INET;
@@ -52,10 +54,12 @@ bool cTBSocket::Connect(const std::string &Host, unsigned int Port) {
return false; return false;
} }
len = sizeof(struct sockaddr_in); if (m_Type == SOCK_STREAM) {
if (::getpeername(socket, (struct sockaddr*)&m_RemoteAddr, &len) == -1) { len = sizeof(struct sockaddr_in);
::close(socket); if (::getpeername(socket, (struct sockaddr*)&m_RemoteAddr, &len) == -1) {
return false; ::close(socket);
return false;
}
} }
len = sizeof(struct sockaddr_in); len = sizeof(struct sockaddr_in);
@@ -64,7 +68,11 @@ bool cTBSocket::Connect(const std::string &Host, unsigned int Port) {
return false; return false;
} }
return cTBSource::Open(socket); if (!cTBSource::Open(socket)) {
::close(socket);
return false;
}
return true;
} }
bool cTBSocket::Listen(const std::string &Ip, unsigned int Port, int BackLog) { bool cTBSocket::Listen(const std::string &Ip, unsigned int Port, int BackLog) {
@@ -74,7 +82,7 @@ bool cTBSocket::Listen(const std::string &Ip, unsigned int Port, int BackLog) {
if (IsOpen()) Close(); if (IsOpen()) Close();
if ((socket = ::socket(PF_INET, m_Type, IPPROTO_IP)) == -1) if ((socket = ::socket(PF_INET, m_Type, m_Protocol)) == -1)
return false; return false;
val = 1; val = 1;
@@ -116,6 +124,10 @@ bool cTBSocket::Accept(const cTBSocket &Listener) {
if (::getsockname(socket, (struct sockaddr*)&m_LocalAddr, &addrlen) == -1) if (::getsockname(socket, (struct sockaddr*)&m_LocalAddr, &addrlen) == -1)
return false; return false;
int sol=1;
// Ignore possible errors here, proceed as usual
::setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &sol, sizeof(sol));
if (!cTBSource::Open(socket)) if (!cTBSource::Open(socket))
return false; return false;

View File

@@ -18,9 +18,10 @@ private:
struct sockaddr_in m_RemoteAddr; struct sockaddr_in m_RemoteAddr;
int m_Type; int m_Type;
int m_Protocol;
public: public:
cTBSocket(int Type = SOCK_STREAM); cTBSocket(int Type = SOCK_STREAM, int Protocol = 0);
virtual ~cTBSocket(); virtual ~cTBSocket();
/* See cTBSource::SysRead() /* See cTBSource::SysRead()
@@ -97,15 +98,22 @@ public:
}; };
inline ssize_t cTBSocket::SysRead(void *Buffer, size_t Length) const { inline ssize_t cTBSocket::SysRead(void *Buffer, size_t Length) const {
if (m_Type == SOCK_DGRAM) { if (m_Type == SOCK_STREAM)
return ::recv(*this, Buffer, Length, 0);
else {
socklen_t len = sizeof(m_RemoteAddr); socklen_t len = sizeof(m_RemoteAddr);
return ::recvfrom(*this, Buffer, Length, 0, (sockaddr*)&m_RemoteAddr, &len); return ::recvfrom(*this, Buffer, Length, 0, (sockaddr*)&m_RemoteAddr, &len);
} else }
return ::recv(*this, Buffer, Length, 0);
} }
inline ssize_t cTBSocket::SysWrite(const void *Buffer, size_t Length) const { inline ssize_t cTBSocket::SysWrite(const void *Buffer, size_t Length) const {
return ::send(*this, Buffer, Length, 0); return ::send(*this, Buffer, Length, 0);
if (m_Type == SOCK_STREAM)
return ::send(*this, Buffer, Length, 0);
else {
socklen_t len = sizeof(m_RemoteAddr);
return ::sendto(*this, Buffer, Length, 0, (sockaddr*)&m_RemoteAddr, len);
}
} }
#endif // TOOLBOX_SOCKET_H #endif // TOOLBOX_SOCKET_H