5 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
87 changed files with 3476 additions and 5311 deletions

25
.gitignore vendored
View File

@@ -1,25 +0,0 @@
# Compiled Object files
*.slo
*.lo
*.o
# Compiled Dynamic libraries
*.so
*.dylib
# Compiled Static libraries
*.lai
*.la
*.a
# Eclipse project files
.project
.cproject
.settings
# Generated dependency file
.dependencies
# translations
*.mo
*.pot

View File

@@ -39,9 +39,6 @@ Rolf Ahrenberg
for requesting replacement of asprintf calls for requesting replacement of asprintf calls
for suggesting to change the URL path from EXTERN to EXT for suggesting to change the URL path from EXTERN to EXT
for suggesting increased thread priorities for cStreamdevWriter/Streamer for suggesting increased thread priorities for cStreamdevWriter/Streamer
for adding "Hide mainmenu entry" option
for polishing po file headers
for adding the special meaning "show current channel" to channel 0
Rantanen Teemu Rantanen Teemu
for providing vdr-incompletesections.diff for providing vdr-incompletesections.diff
@@ -119,10 +116,9 @@ Jori Hamalainen
for extensive testing while making stream compatible to Network Media Tank for extensive testing while making stream compatible to Network Media Tank
for adding Network Media Tank browser support to HTML pages for adding Network Media Tank browser support to HTML pages
Oliver Wagner owagner
for pointing out a problem with the encrypted channel switching fix for pointing out a problem with the encrypted channel switching fix
for suggesting use of SO_KEEPALIVE socket option to detect dead sockets for suggesting use of SO_KEEPALIVE socket option to detect dead sockets
for making cStatus::ChannelChange re-tune only if CA IDs changed
Joachim König-Baltes Joachim König-Baltes
for fixing Min/MaxPriority parsing for fixing Min/MaxPriority parsing
@@ -202,60 +198,3 @@ Ville Skytt
for restricting VTP command RENR to liemikuutio patch < 1.32 for restricting VTP command RENR to liemikuutio patch < 1.32
for fixing memory and filedescriptor leaks in libdvbmpeg for fixing memory and filedescriptor leaks in libdvbmpeg
for code cleanup and optimization for code cleanup and optimization
for correcting typos
Methodus
for suggesting to use HTTP code 503 for unavailable channels
Uwe
for reporting a compiler error in client/device.c with VDR < 1.7.22
Chris Tallon
for his kind permission to use VOMP's recplayer for replaying recordings
macmenot
for adapting Makefiles to VDR 1.7.36+
thomasjfox
for fixing cSuspendCtl preventing idle shutdown
hivdr
for adding the pos= parameter for replaying recordings from a certain position
for suggesting to add the HTTP "Server" header
hummel99
for reporting and helping to debug channel switch issues with priority > 0
for reporting a race condition when switching the server's LiveTV device
Henrik Niehaus
for fixing replay of large TS files on 32-bit systems
Guy Martin
for adding SVDRP commands to list and disconnect clients
Martin1234
for suggesting a service call, returning the number of clients
for implementing GetCurrentlyTunedTransponder() on client
Toerless Eckert
for converting suspend.dat into proper PES format
for investigating and fixing problems caused by filter streaming
for fixing TimedWrite() so it doesn't fail after a slow but successful write
for suggesting to double the size of client's filter buffer
Tomasz Maciej Nowak
for providing Polish language texts
Christopher Reimer
for providing an initial compatibility patch for VDR 2.3.1
Matthias Senzel
for refining the compatibility patch for VDR 2.3.1
David Binderman
for fixing an lseek error check in libdvbmpeg
Jasmin J
for fixing some warnings in libdvbmpeg
for adding .gitignore
for fixing compilation for VDR 2.3.7

128
HISTORY
View File

@@ -1,134 +1,6 @@
VDR Plugin 'streamdev' Revision History VDR Plugin 'streamdev' Revision History
--------------------------------------- ---------------------------------------
- fixed compilation for VDR 2.3.7 (thanks to Jasmin J)
- added .gitignore (thanks to Jasmin J)
- fixed some warnings in libdvbmpeg (thanks to Jasmin J)
- fixed lseek error check in libdvbmpeg (thanks to David Binderman)
- server compatibility with VDR 2.3.1 (thanks to Christopher Reimer and
Matthias Senzel)
- client compatibility with VDR 2.3.1
- use cReceiver::SetPriority(...) in VDR 2.1.4+
- doubled size of client's filter buffer (suggested by Toerless Eckert)
- make sure TimedWrite(...) doesn't return failure after a slow but successful
write operation (thanks to Toerless Eckert)
- fixed problems related to VTP filter streaming like ringbuffer overflows,
stuttering or aborting video stream (thanks to Toerless Eckert)
- added Polish translation (thanks to Tomasz Maciej Nowak)
- converted suspend.dat into proper PES format (thanks to Toerless Eckert)
- implemented GetCurrentlyTunedTransponder() on client (thanks to Martin1234)
- added service call returning the number of clients (suggested by Martin1234)
- added SVDRP commands to list and disconnect clients (thanks to Guy Martin)
- fixed recplayer issues with large TS files (>4GB)
- Don't abort externremux when internal read buffer is empty
- Implemented remuxing of recordings
- Make ChannelChange retune only if CA IDs changed (thanks to Oliver Wagner)
- Implemented VDR 2.1.4 cStatus::ChannelChange(...)
- Call detach only if receiver is attached
- Try changing to other device when receiver got detached
- In TSPIDS mode, create and attach receiver with empty pid list to occupy dev
- Restructured server classes
- New option for server side live TV buffer to prevent buffer underruns
2013-11-28: Version 0.6.1
- Updated Slovak translation (thanks to Milan Hrala)
- Updated Finnish translation (thanks to Rolf Ahrenberg)
- Disabled PS remuxer which is said to produce anything but PS
- The patches intcamdevices and ignore_missing_cam are no longer required
on VDR >= 1.7.30. The localchannelprovide patch became obsolete with VDR
1.7.21.
- Added option to suspend live TV when the server starts
- Set device occupied when streamdev switches away LiveTV on the server, to
reduce the risk that the VDR main loop immediately switches back, resulting
in a black screen on the client (reported by hummel99)
- Fixed channel switch issues with priority > 0 (reported by hummel99)
- Removed noisy debug messages
- Fixed HTTP menu destruction
- API change of VDR 2.1.2
- Fixed priority handling, messed up when adding multi-device support
- Added HTTP "Server" header (suggested by hivdr)
- Ignore dummy file extensions (.ts, .vob, .vdr) when parsing HTTP URIs
- Select start position for replaying a recording by parameter pos=. Supported
values are resume, mark.#, time.#, frame.# or a plain # representing a
percentage if < 100 or a byte position otherwise (thanks to hivdr)
- Start cSuspendCtl hidden or it will prevent idle shutdown (thanks to
thomasjfox)
- Fixed recordings menu inode numbers: ino_t is a long long on some systems
- Updated Slovak translation (thanks to Milan Hrala)
- Adapted Makefiles to VDR 1.7.36+ (thanks to macmenot). Old makefiles have
been renamed to Makefile-1.7.33.
- API changes of VDR 1.7.38 (thanks to mal@vdr-developer)
- Added simple recordings menu in HTTP server
- Restructured menuHTTP classes
- Added RSS format for HTTP menus
- Recordings can now also be selected by struct stat "st_dev:st_ino.rec"
- Implemented multi-device support for streamdev client (suggested by johns)
- Basic support for HTTP streaming of recordings
- Close writer when streamer is finished
- Don't abort VTP connection if filter stream is broken
- Restructured cStreamdevStreamer: Moved inbound buffer into actual subclass.
- In cStreamdevStreamer dropped Activate(bool) and moved its code into Start().
- Moved cStreamdevFilterStreamer to livefilter.[hc]
- Return HTTP/1.1 compliant response headers plus some always useful headers
- Return HTTP URL parameters ending with ".dlna.org" as response headers
- Store HTTP URL parameters in a map
- Support HTTP HEAD requests with external remuxer
- Fixed always using priority 0 for HTTP HEAD requests
- Start writer right after creating it
- Corrected typos (thanks to Ville Skyttä)
- Fixed compiler error in client/device.c with VDR < 1.7.22 (reported by
Uwe@vdrportal)
- Updated Italian translation (thanks to Diego Pierotto)
- Added DeviceName() and DeviceType() to client device. The server IP and the
number of the device used on the server are returned respectively.
2012-05-29: Version 0.6.0
- Reimplemented some client device methods
- Proper fix for "client sends ABRT after TUNE". Obsoletes many hacks in client
- Added CLOCK_MONOTONIC timestamp and thread id to Dprintf
- Silenced warning (thanks to Rolf Ahrenberg)
- Updated Finnish translation (thanks to Rolf Ahrenberg)
- Replaced server-side suspend modes with priority based precedence handling
- Client-side priority handling for VDR >= 1.7.25 and servers running VTP > 1.0
- Introduced VTP protocol version numbering for easier compatibility handling
between different client and server versions. The server includes the protocol
version in its greeting string, the client reports its version with the new
command "VERS".
- Dropped compatibility of streamdev-server with VDR < 1.7.25
2012-05-12: Version 0.5.2
- Use fileno() to retrieve the fd from a FILE structure (submitted by an
anonymous user)
- New special meaning "show current channel" when channel 0 is requested.
Applies to HTTP streaming only (thanks to Rolf Ahrenberg)
- Fixed ProvidesChannel() on client always returning true since the new timeout
option has been added.
- Updated Finnish translation (thanks to Rolf Ahrenberg)
- With VDR 1.7.25 priorities down to -99 will be used. Please update
"Minimum Priority" in streamdev-client setup.
- Use the new streamdev-client setup option "Live TV Priority" to control
precedence among multiple clients. The VDR option "Primary Limit" which
has previouly been used for this purpose has been dropped in VDR 1.7.25.
- Timout for network operations now configurable in streamdev-client setup
- Added timeout to Connect()
- Report the server-side HTTP status "503 Service unavailable" instead of
the client-side error "409 Conflict" when a channel is unavailable
(suggested by Methodus)
- Update of po headers and Finnish translation (thanks to Rolf Ahrenberg)
- support for non-cycle-free setups (e.g. where two VDRs mutually share
their DVB cards through streamdev-client/-server). Must be enabled in
streamdev-server setup. Obsoletes recursion patches.
- API change of VDR 1.7.22
- VDR 1.7.22 obsoletes cap_net_raw patch. Added cap_net_raw patch for VDR
1.7.5 - 1.7.21.
- Update and UTF-8 conversion of Finnish po files (thanks to Rolf Ahrenberg)
- Added "Hide mainmenu entry" option on server (thanks to Rolf Ahrenberg)
- 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.
- code cleanup and optimization (thanks to Ville Skyttä) - code cleanup and optimization (thanks to Ville Skyttä)
- properly shutdown IGMP timeout handler thread when the plugin is stopped. - properly shutdown IGMP timeout handler thread when the plugin is stopped.
Fixes occasional segfaults on VDR exit. Fixes occasional segfaults on VDR exit.

View File

@@ -1,44 +1,57 @@
# #
# Makefile for a Video Disk Recorder plugin # Makefile for a Video Disk Recorder plugin
# #
# $Id: $ # $Id: Makefile,v 1.23 2010/08/02 10:36:59 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.
# The main source file 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 directory environment: ### The directory environment:
# Use package data if installed...otherwise assume we're under the VDR source directory: VDRDIR = ../../..
PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc)) LIBDIR = ../../lib
LIBDIR = $(call PKGCFG,libdir) TMPDIR = /tmp
LOCDIR = $(call PKGCFG,locdir)
PLGCFG = $(call PKGCFG,plgcfg)
#
TMPDIR ?= /tmp
### The compiler options: ### The version number of VDR (taken from VDR's "config.h"):
export CFLAGS = $(call PKGCFG,cflags) APIVERSION = $(shell grep 'define APIVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
export CXXFLAGS = $(call PKGCFG,cxxflags) 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 }')
### The version number of VDR's plugin API:
APIVERSION = $(call PKGCFG,apiversion)
### Allow user defined options to overwrite defaults: ### Allow user defined options to overwrite defaults:
-include $(PLGCFG) 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 ### 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) LIBDIR := $(shell cd $(LIBDIR) >/dev/null 2>&1 && pwd)
LOCDIR := $(shell cd $(LOCDIR) >/dev/null 2>&1 && pwd)
export export
unexport PLUGIN unexport PLUGIN
@@ -50,7 +63,6 @@ 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..
export INCLUDES
DEFINES += -D_GNU_SOURCE DEFINES += -D_GNU_SOURCE
@@ -63,7 +75,7 @@ endif
### The main target: ### The main target:
.PHONY: all client server install install-client install-server dist clean .PHONY: all client server dist clean
all: client server all: client server
### Targets: ### Targets:
@@ -71,28 +83,20 @@ all: client server
client: client:
$(MAKE) -C ./tools $(MAKE) -C ./tools
$(MAKE) -C ./client $(MAKE) -C ./client
# installs to $(LIBDIR)/libvdr-streamdev-client.so.$(APIVERSION)
server: server:
$(MAKE) -C ./tools $(MAKE) -C ./tools
$(MAKE) -C ./libdvbmpeg $(MAKE) -C ./libdvbmpeg
$(MAKE) -C ./remux $(MAKE) -C ./remux
$(MAKE) -C ./server $(MAKE) -C ./server
install-client: client
$(MAKE) -C ./client install
# installs to $(LIBDIR)/libvdr-streamdev-client.so.$(APIVERSION)
install-server: server
$(MAKE) -C ./server install
# installs to $(LIBDIR)/libvdr-streamdev-server.so.$(APIVERSION) # installs to $(LIBDIR)/libvdr-streamdev-server.so.$(APIVERSION)
install: install-client install-server
dist: clean dist: clean
@-rm -rf $(TMPDIR)/$(ARCHIVE) @-rm -rf $(TMPDIR)/$(ARCHIVE)
@mkdir $(TMPDIR)/$(ARCHIVE) @mkdir $(TMPDIR)/$(ARCHIVE)
@cp -a * $(TMPDIR)/$(ARCHIVE) @cp -a * $(TMPDIR)/$(ARCHIVE)
@tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) @tar czf $(PACKAGE).tgz --exclude CVS -C $(TMPDIR) $(ARCHIVE)
@-rm -rf $(TMPDIR)/$(ARCHIVE) @-rm -rf $(TMPDIR)/$(ARCHIVE)
@echo Distribution package created as $(PACKAGE).tgz @echo Distribution package created as $(PACKAGE).tgz

View File

@@ -1,108 +0,0 @@
#
# Makefile for a Video Disk Recorder plugin
#
# $Id: Makefile,v 1.23 2010/08/02 10:36:59 schmirl Exp $
# The main source file name.
#
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):
VERSION = $(shell grep 'const char \*VERSION *=' common.c | awk '{ print $$5 }' | sed -e 's/[";]//g')
### The directory environment:
VDRDIR = ../../..
LIBDIR = ../../lib
TMPDIR = /tmp
### 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')
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:
ARCHIVE = $(PLUGIN)-$(VERSION)
PACKAGE = vdr-$(ARCHIVE)
### Includes and Defines (add further entries here):
INCLUDES += -I$(VDRDIR)/include -I..
DEFINES += -D_GNU_SOURCE
ifdef DEBUG
DEFINES += -DDEBUG
endif
ifdef STREAMDEV_DEBUG
DEFINES += -DDEBUG
endif
### The main target:
.PHONY: all client server dist clean
all: client server
### Targets:
client:
$(MAKE) -C ./tools
$(MAKE) -C ./client
# installs to $(LIBDIR)/libvdr-streamdev-client.so.$(APIVERSION)
server:
$(MAKE) -C ./tools
$(MAKE) -C ./libdvbmpeg
$(MAKE) -C ./remux
$(MAKE) -C ./server
# installs to $(LIBDIR)/libvdr-streamdev-server.so.$(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:
$(MAKE) -C ./tools clean
$(MAKE) -C ./libdvbmpeg clean
$(MAKE) -C ./remux clean
$(MAKE) -C ./client clean
$(MAKE) -C ./server clean

252
README
View File

@@ -101,17 +101,13 @@ as otherwise -r will be passed to VDR and not to streamdev.
2.1 Compatibility: 2.1 Compatibility:
------------------ ------------------
This version is not compatible to VDR releases older than 1.7.25. Use one of This version is not compatible to VDR releases older than 1.5.9. Take one of
the streamdev-0.5.x releases for older versions. 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 Compiling: 2.2 Compiling:
-------------- --------------
The Makefiles are for VDR 1.7.36 and above. For VDR 1.7.33 and below, please
replace the Makefiles in the main directory and in the client/ and server/
subdirectories with the corresponding Makefile-1.7.33 files. With VDR 1.7.34 and
1.7.35 YMMV ;)
cd vdr-1.X.X/PLUGINS/src cd vdr-1.X.X/PLUGINS/src
tar xvfz vdr-streamdev-0.5.0.tgz tar xvfz vdr-streamdev-0.5.0.tgz
ln -s streamdev-0.5.0 streamdev ln -s streamdev-0.5.0 streamdev
@@ -134,24 +130,6 @@ 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, perform some additional steps. Check which version you've been running before,
then read below for the necessary changes. then read below for the necessary changes.
* Priorities:
-------------
(Affected: 0.5.x and older)
The server-side setting "Suspend behaviour" has been dropped in 0.6.0 in favour
of priority based precedence. A priority of 0 and above means that clients
have precedence. A negative priority gives precedence to local live TV on the
server. So if "Suspend behaviour" was previously set to "Client may suspend" or
"Never suspended", you will have to configure a negative priority. If the
"Suspend behaviour" was set to "Always suspended", the default values should do.
Configure the desired priorities for HTTP and IGMP Multicast streaming in the
settings of streamdev-server. If you haven't updated all your streamdev-clients
to at least 0.5.2, configure "Legacy Client Priority", too.
In streamdev-client, you should set "Minimum Priority" to -99. Adjust "Live TV
Priority" if necessary.
* Location of files: * Location of files:
-------------------- --------------------
(Affected: 0.3.x, 0.4.x, 0.4.0pre, 0.5.0pre) (Affected: 0.3.x, 0.4.x, 0.4.0pre, 0.5.0pre)
@@ -197,41 +175,31 @@ 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.
Precedence between multiple clients and between client and server is controlled
with priorities. For HTTP and IGMP Multicast, the priority is configured in
streamdev-server's setup menu. A negative priority gives precedence to local
live TV on the server. Zero and positive values give precedence to the client.
The priority for VDR clients watching live TV is configured in the plugin setup
of streamdev-client. For other client tasks (e.g. recording a client side timer)
the same priority as on the client is used. With the parameter "Legacy client
Priority" in streamdev-server's setup menu you can configure the priority for
clients which cannot be configured to use negative priorities. It is used
when an old client is detected an it requests priority "0".
On the server, the main menu entry "Streamdev Connections" gives you a list 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. 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 Note that depending on connection type and client, the client might re-connect
sooner or later. sooner or later. Depending on the server setup, the "blue" key might be enabled
as well. Please read below.
The "blue" key in the server's main menu will suspend live TV on server. An The parameter "Suspend behaviour" allows you to specify how the server should
image is displayed instead. This would allow a low priority client to switch react in case the client requests a channel that would require switching the
to a different transponder. Enable "Client may suspend" in the server setup primary device (i.e. disrupt live-tv). If set to "Offer suspend mode", you
to allow VDR clients to suspend live TV remotely. 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
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 behaviour to "Always suspended" (the default), there will be normal live-tv
on the server, but whenever a client decides to switch the transponder, the
server will lose it's live-tv. Set to "Never suspended", the server always
prevents the client from switching transponders. If you set "Client may
suspend" to yes, the client can suspend the server remotely (this only applies
if "Offer suspend mode" is selected).
In the server's setup there's also an option to suspend live TV when starting NOTE: This mainly applies to One-Card-Systems, since with multiple cards there
the server. The "auto" option will suspend live TV if there's no device with is no need to switch transponders on the primary interface, if on of the other
an MPEG decoder available which is typically the case on a headless server. cards is idle (i.e. if it is not blocked by a recording). If all cards are in
use (i.e. when something is recorded, or by multiple clients), this applies to
NOTE: Precedence is mainly an issue on One-Card-Systems, since with multiple Multiple-Card-Systems as well.
cards there is no need to switch transponders on the primary interface, if one
of the other cards is idle (i.e. if it is not blocked by a recording). If all
cards are in use (i.e. when something is recorded, or by multiple clients), this
applies to Multiple-Card-Systems as well.
If your client suffers from buffer underruns while watching live TV, you can
configure buffering on the server side. Enter a reasonable value (e.g. 300ms)
as "Live TV buffer delay (ms)" in the server setup.
3.1 Usage HTTP server: 3.1 Usage HTTP server:
---------------------- ----------------------
@@ -245,6 +213,7 @@ 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)
ES Elementary Stream (only Video, if available, otherwise only Audio) ES Elementary Stream (only Video, if available, otherwise only Audio)
EXT Pass stream through external script (e.g. for converting with mencoder) EXT Pass stream through external script (e.g. for converting with mencoder)
@@ -262,18 +231,16 @@ streams directly like this:
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. Use the special channel number 0 will request the channel by unique channel id. In addition, you can specify
to see the server's current live TV channel. the desired stream type as a path to the channel.
In addition, you can specify 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', 'ES' and 'EXT'. You need to specify the ES Possible values are 'PES', 'TS', 'PS', 'ES' and 'EXT'. You need to specify
format explicitly if you want to listen to radio channels. Play them back i.e. the ES format explicitly if you want to listen to radio channels. Play them
with mpg123. back i.e. with mpg123.
mpg123 http://hostname:3000/ES/200 mpg123 http://hostname:3000/ES/200
@@ -309,7 +276,7 @@ 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 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 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 all clients must be connected to this same segment. There must not be a router
in between. Also note that the client must not run on the streamdev-server inbetween. Also note that the client must not run on the streamdev-server
machine. machine.
Each channel is offered on a different multicast IP. Channel 1 is available from Each channel is offered on a different multicast IP. Channel 1 is available from
@@ -319,11 +286,16 @@ reserved according to RFC-2365).
Before you can use streamdev's multicast server, you might need to patch VDR. 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. 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 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 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 VDR server's LAN ethernet card. The multicast server will refuse to start with
the default bind address "0.0.0.0". the default bind adresse "0.0.0.0".
Now edit your streamdevhosts.conf. To allow streaming of all channels, it must 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 contain "239.255.0.0/16". Note that you cannot limit connections by client IP
@@ -363,13 +335,6 @@ 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.
If both, streamdev-client and streamdev-server are installed, the additional
option "Loop prevention" will show up in the streamdev-server setup. If enabled,
streamdev-client won't be considered when streamdev-server is looking for a
device which is able to receive some channel. This is required if two or more
VDRs mutually share their DVB devices through streamdev. Otherwise you would
end up in a loop.
3.4 Usage VDR-to-VDR client: 3.4 Usage VDR-to-VDR client:
---------------------------- ----------------------------
@@ -378,9 +343,9 @@ setup parameter "Hide Mainmenu Entry" you can hide this menu item if you don't
need it. "Suspend Server" is only useful if the server runs in "Offer suspend need it. "Suspend Server" is only useful if the server runs in "Offer suspend
mode" with "Client may suspend" enabled. mode" with "Client may suspend" enabled.
The parameter "Remote IP" uses an IP-Address-Editor, where you can just enter The parameter "Remote IP" uses an IP-Adress-Editor, where you can just enter
the IP number with the number keys on your remote. After three digits (or if the IP number with the number keys on your remote. After three digits (or if
the next digit would result in an invalid IP address, or if the first digit is the next digit would result in an invalid IP adress, or if the first digit is
0), the current position jumps to the next one. You can change positions with 0), the current position jumps to the next one. You can change positions with
the left and right buttons, and you can cycle the current position using up the left and right buttons, and you can cycle the current position using up
and down. To confirm the entered address, press OK. So, if you want to enter and down. To confirm the entered address, press OK. So, if you want to enter
@@ -389,83 +354,66 @@ type "127001<OK>" on your remote. If you want to enter "192.168.1.12", type
"1921681<Right>12<OK>". "1921681<Right>12<OK>".
The parameters "Remote IP" and "Remote Port" in the client's setup specify the The parameters "Remote IP" and "Remote Port" in the client's setup specify the
address of the remote VDR-to-VDR server to connect to. The client is disabled address of the remote VDR-to-VDR server to connect to. Activate the client by
by default, because it wouldn't make much sense to start the client without setting "Start Client" to yes. It is disabled by default, because it wouldn't
specifying a server anyway. Activate the client by setting "Simultaneously used make much sense to start the client without specifying a server anyway. The
Devices" to at least 1. Streamdev-client will allocate as many VDR devices as client is activated after you push the OK button, so there's no need to restart
you configure here. Each of these devices opens one connection to the server VDR. Deactivation on-the-fly is not possible, so in order to deactivate the
and becomes associated with one of the server's devices (typically a DVB card) client, you will have to restart VDR. However requests to switch channels will
on demand. be refused by streamdev-client once it has been deactivated. All other settings
can be changed without restarting VDR.
The client will try to connect to the server (in case it isn't yet) whenever
a remote channel is requested. Just activate the client and switch to a
channel that's not available by local devices. If anything goes wrong with the
connection between the two, you will see it in the logfile instantly. If you
now switch the client to a channel which isn't covered by it's own local
devices, it will ask the server for it. If the server can (currently) receive
that channel, the client will show it until you switch again, or until the
server needs that card (if no other is free) for a recording on a different
transponder.
Only the needed PIDs are transferred, and additional PIDs can be turned on Only the needed PIDs are transferred, and additional PIDs can be turned on
during an active transfer. This makes it possible to switch languages, receive during an active transfer. This makes it possible to switch languages, receive
additional channels on the same transponder and use plugins that use receivers additional channels (for recording on the client) and use plugins that use
themselves (like osdteletext). receivers themselves (like osdteletext).
So for viewing live TV a single device is sufficient. But if the client needs
to receive channels from different transponders simultaneously (e.g. for PiP or
client side recordings) a second device becomes necessary.
To allocate additional devices, just increase the number and push the OK button.
There's no need to restart VDR. Deleting VDR devices on-the-fly is not possible.
However requests to switch channels will be refused by redundant devices.
The default timeout of 2 seconds for network operations should be sufficient in
most cases. Increase "Timeout" if you get frequent timeout errors in the log.
With "Filter Streaming" enabled, the client will receive meta information like 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.
If you have TV programs with dynamically changing PIDs (such as some german With maximum and minimum priority you can keep VDR from considering streamdev
regional programs like NDR), then you need to enable "Filter Streaming" to in certain cases. If for instance you have a streamdev client with its own DVB
correctly receive them. You also need to set in VDRs DVB setup the option card, VDR might use streamdev for recording. If this is not what you want, you
"Update channels" to at least "PIDs only" (or "names and PIDs") for this could set the maximum priority to 0. As recordings usually have a much higher
to work. priority (default 50), streamdev is now no longer used for recordings. The two
parameters define the inclusive range of priorities for which streamdev will
accept to tune. Setting the minimum priority to a higher value than the maximum,
you will get two ranges: "up to maximum" and "minimum and above".
"Filter streaming" uses internally a socketpair(2) to copy meta data to If you are running at least VDR 1.7.0, you can also configure the "Broadcast
VDR. This socketpair may require larger than default buffering. If Systems / Cost" of the streamdev-client device. On a pure streamdev-client only
you see a mesage like the following in syslog, 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).
cStreamdevFilter::PutSection(Pid:18 Tid: 64): Dropped 2995 bytes, max queue: 328640 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
then you should increase the streamdev client "FilterSockBufSize" value. A additional instances of streamdev-client and you will be able to receive as many
good value is 3072000. You will need to first configure your linux to transponders at a time. The same trick allows a client to receive channels from
permit such a large buffer size: different servers. To create an additional instance, copy the streamdev-client
binary to a different name (e.g. streamdev-client2):
sysctl net.core.wmem_max=3072000
The precedence among multiple client VDRs receiving live TV from the same
server is controlled with "Live TV Priority".
With "Maximum Priority" and "Minimum Priority" you can keep VDR from considering
streamdev in certain cases. If for instance you have a streamdev client with its
own DVB card, VDR might use streamdev for recording. If this is not what you
want, you could set the maximum priority to 0. As recordings usually have a much
higher priority (default 50), streamdev is now no longer used for recordings.
The two parameters define the inclusive range of priorities for which streamdev
will accept to tune. Setting the minimum priority to a higher value than the
maximum, you will get two ranges: "up to maximum" and "minimum and above".
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). 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).
To receive channels from multiple servers, create additional instances of the
streamdev-client plugin. Simply copy (don't link!) the binary to a different
name (e.g. streamdev-client2):
cd VDRPLUGINLIBDIR cd VDRPLUGINLIBDIR
cp libvdr-streamdev-client.so.1.X.X libvdr-streamdev-client2.so.1.X.X cp libvdr-streamdev-client.so.1.X.X libvdr-streamdev-client2.so.1.X.X
@@ -571,10 +519,14 @@ The script should perform the following steps (pseudocode):
6. Known Problems: 6. Known Problems:
------------------ ------------------
* In VDR before 1.7.30 viewing encrypted channels is an issue as Streamdev * There have been reports that channel switching with VDR 1.5.x/1.6.x clients
doesn't provide a (dummy) CAM. So out of the box, VDR won't ever try to receive sometimes fails. Current version includes a workaround which seems to work, but
encrypted channels from streamdev. Pick one of the following solutions to work YMMV ;)
around the problem:
* 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 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 channels.conf if you know how to do this) and set the CA field of all channels
@@ -584,9 +536,9 @@ 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 hexadecimal values if you are using an editor to modify your channels.conf
(number 10 becomes an "a", number 11 a "b", ...). (number 10 becomes an "a", number 11 a "b", ...).
2. Apply either patch "patches/vdr-1.6.0-1.7.29-intcamdevices.patch" or patch 2. Apply either patch "patches/vdr-1.6.0-intcamdevices.patch" or patch
"patches/vdr-1.6.0-1.7.29-ignore_missing_cam.diff" to your client VDR. "patches/vdr-1.6.0-ignore_missing_cam.diff" to your client VDR. Intcamdevices
Intcamdevices is the clean solution, but it modifies the VDR API. So you will is the clean solution, but it modifies the VDR API. So you will need to
need to recompile all of your plugins. The ignore_missing_cam patch is trivial, recompile all of your plugins. The ignore_missing_cam patch is trivial, no need
no need to recompile other plugins. However it is not suitable for clients with to recompile other plugins. However it is not suitable for clients with a DVB
a DVB card of their own. card of their own.

View File

@@ -1,18 +1,14 @@
# #
# Makefile for a Video Disk Recorder plugin # Makefile for a Video Disk Recorder plugin
# #
# $Id: $ # $Id: Makefile,v 1.2 2010/07/19 13:49:25 schmirl Exp $
# The official name of this plugin. # The official name of this plugin.
# This name will be used in the '-P...' option of VDR to load the plugin. # This name will be used in the '-P...' option of VDR to load the plugin.
# By default the main source file also carries this name. # By default the main source file also carries this name.
#
PLUGIN = streamdev-client PLUGIN = streamdev-client
### The name of the shared object file:
SOFILE = libvdr-$(PLUGIN).so
### Includes and Defines (add further entries here): ### Includes and Defines (add further entries here):
DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
@@ -26,7 +22,8 @@ CLIENTOBJS = $(PLUGIN).o \
### The main target: ### The main target:
all: $(SOFILE) i18n .PHONY: all i18n dist clean
all: libvdr-$(PLUGIN).so i18n
### Implicit rules: ### Implicit rules:
@@ -37,47 +34,51 @@ all: $(SOFILE) i18n
MAKEDEP = $(CXX) -MM -MG MAKEDEP = $(CXX) -MM -MG
DEPFILE = .dependencies DEPFILE = .dependencies
$(DEPFILE): Makefile $(DEPFILE): Makefile
@$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(CLIENTOBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) > $@ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(CLIENTOBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) > $@
-include $(DEPFILE) -include $(DEPFILE)
### Internationalization (I18N): ### Internationalization (I18N):
PODIR = po PODIR = po
LOCALEDIR = $(VDRDIR)/locale
I18Npo = $(wildcard $(PODIR)/*.po) I18Npo = $(wildcard $(PODIR)/*.po)
I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file)))) I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
I18Npot = $(PODIR)/$(PLUGIN).pot I18Npot = $(PODIR)/$(PLUGIN).pot
%.mo: %.po %.mo: %.po
msgfmt -c -o $@ $< msgfmt -c -o $@ $<
$(I18Npot): $(CLIENTOBJS:%.o=%.c) $(I18Npot): $(CLIENTOBJS:%.o=%.c)
xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='<vdrdev@schmirler.de>' -o $@ `ls $^` xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --msgid-bugs-address='<http://www.vdr-developer.org/mantisbt/>' -o $@ $^
%.po: $(I18Npot) %.po: $(I18Npot)
msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $< msgmerge -U --no-wrap --no-location --backup=none -q $@ $<
@touch $@ @touch $@
$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo $(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
install -D -m644 $< $@ @mkdir -p $(dir $@)
cp $< $@
.PHONY: i18n i18n: $(I18Nmsgs)
i18n: $(I18Nmo) $(I18Npot)
install-i18n: $(I18Nmsgs)
### Targets: ### Targets:
$(SOFILE): $(CLIENTOBJS) $(COMMONOBJS) ../tools/sockettools.a libvdr-$(PLUGIN).so: $(CLIENTOBJS) $(COMMONOBJS) ../tools/sockettools.a
$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $^ -o $@
install-lib: $(SOFILE) %.so:
install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION) $(CXX) $(CXXFLAGS) -shared $^ -o $@
@cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION)
install: install-lib install-i18n 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: clean:
@-rm -f $(PODIR)/*.mo $(PODIR)/*.pot @-rm -f $(COMMONOBJS) $(CLIENTOBJS) $(DEPFILE) $(PODIR)/*.mo $(PODIR)/*.pot *.so *.tgz core* *~
@-rm -f $(COMMONOBJS) $(CLIENTOBJS) $(DEPFILE) *.so *.tgz core* *~

View File

@@ -1,84 +0,0 @@
#
# 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

@@ -19,38 +19,32 @@
using namespace std; using namespace std;
#ifndef LIVEPRIORITY
#define LIVEPRIORITY 0
#endif
#ifndef TRANSFERPRIORITY
#define TRANSFERPRIORITY -1
#endif
#define VIDEOBUFSIZE MEGABYTE(3) #define VIDEOBUFSIZE MEGABYTE(3)
const cChannel *cStreamdevDevice::m_DenyChannel = NULL; cStreamdevDevice *cStreamdevDevice::m_Device = NULL;
cStreamdevDevice::cStreamdevDevice(void) { cStreamdevDevice::cStreamdevDevice(void) {
m_Disabled = false;
m_ClientSocket = new cClientSocket();
m_Channel = NULL; m_Channel = NULL;
m_TSBuffer = NULL; m_TSBuffer = NULL;
m_Filters = new cStreamdevFilters(m_ClientSocket); m_Filters = new cStreamdevFilters;
StartSectionHandler(); StartSectionHandler();
isyslog("streamdev-client: got device number %d", CardIndex() + 1); isyslog("streamdev-client: got device number %d", CardIndex() + 1);
m_Device = this;
m_Pids = 0; m_Pids = 0;
m_Priority = -1;
m_DvrClosed = true;
} }
cStreamdevDevice::~cStreamdevDevice() { cStreamdevDevice::~cStreamdevDevice() {
Dprintf("Device gets destructed\n"); Dprintf("Device gets destructed\n");
Lock(); Lock();
m_Device = NULL;
m_Filters->SetConnection(-1); m_Filters->SetConnection(-1);
m_ClientSocket->Quit(); ClientSocket.Quit();
m_ClientSocket->Reset(); ClientSocket.Reset();
Unlock(); Unlock();
Cancel(3); Cancel(3);
@@ -58,7 +52,6 @@ cStreamdevDevice::~cStreamdevDevice() {
StopSectionHandler(); StopSectionHandler();
DELETENULL(m_Filters); DELETENULL(m_Filters);
DELETENULL(m_TSBuffer); DELETENULL(m_TSBuffer);
delete m_ClientSocket;
} }
#if APIVERSNUM >= 10700 #if APIVERSNUM >= 10700
@@ -77,29 +70,27 @@ bool cStreamdevDevice::ProvidesTransponder(const cChannel *Channel) const
return true; return true;
} }
#if APIVERSNUM >= 10722
bool cStreamdevDevice::IsTunedToTransponder(const cChannel *Channel) const
#else
bool cStreamdevDevice::IsTunedToTransponder(const cChannel *Channel) bool cStreamdevDevice::IsTunedToTransponder(const cChannel *Channel)
#endif
{ {
return m_ClientSocket->DataSocket(siLive) != NULL && bool res = false;
m_Channel != NULL && if (ClientSocket.DataSocket(siLive) != NULL
Channel->Transponder() == m_Channel->Transponder(); && TRANSPONDER(Channel, m_Channel)
} && Channel->Ca() == CA_FTA
&& m_Channel->Ca() == CA_FTA)
const cChannel *cStreamdevDevice::GetCurrentlyTunedTransponder(void) const { res = true;
if (m_ClientSocket->DataSocket(siLive) != NULL) return res;
return m_Channel;
return NULL;
} }
bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
bool *NeedsDetachReceivers) const { bool *NeedsDetachReceivers) const {
if (m_Disabled || Channel == m_DenyChannel) bool res = false;
bool prio = Priority < 0 || Priority > this->Priority();
bool ndr = false;
if (!StreamdevClientSetup.StartClient)
return false; return false;
Dprintf("ProvidesChannel, Channel=%s, Priority=%d, SocketPrio=%d\n", Channel->Name(), Priority, m_ClientSocket->Priority()); Dprintf("ProvidesChannel, Channel=%s, Prio=%d\n", Channel->Name(), Priority);
if (StreamdevClientSetup.MinPriority <= StreamdevClientSetup.MaxPriority) if (StreamdevClientSetup.MinPriority <= StreamdevClientSetup.MaxPriority)
{ {
@@ -114,42 +105,13 @@ bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
return false; return false;
} }
int newPrio = Priority; if (ClientSocket.DataSocket(siLive) != NULL
if (Priority == LIVEPRIORITY) { && TRANSPONDER(Channel, m_Channel))
if (m_ClientSocket->ServerVersion() >= 100 || StreamdevClientSetup.LivePriority >= 0)
newPrio = StreamdevClientSetup.LivePriority;
}
#if APIVERSNUM >= 10725
bool prio = Priority == IDLEPRIORITY || newPrio >= m_ClientSocket->Priority();
#else
bool prio = Priority < 0 || newPrio > m_ClientSocket->Priority();
#endif
bool res = prio;
bool ndr = false;
#if APIVERSNUM >= 10722
if (IsTunedToTransponder(Channel)) {
#else
if (const_cast<cStreamdevDevice*>(this)->IsTunedToTransponder(Channel)) {
#endif
if (Channel->Ca() < CA_ENCRYPTED_MIN ||
(Channel->Vpid() && HasPid(Channel->Vpid())) ||
(Channel->Apid(0) && HasPid(Channel->Apid(0))))
res = true; res = true;
else else {
res = prio && ClientSocket.ProvidesChannel(Channel, Priority);
ndr = true; ndr = true;
} }
else if (prio) {
if (Priority == LIVEPRIORITY && m_ClientSocket->ServerVersion() >= 100)
UpdatePriority(true);
res = m_ClientSocket->ProvidesChannel(Channel, newPrio);
ndr = Receiving();
if (m_ClientSocket->ServerVersion() >= 100)
UpdatePriority(false);
}
if (NeedsDetachReceivers) if (NeedsDetachReceivers)
*NeedsDetachReceivers = ndr; *NeedsDetachReceivers = ndr;
@@ -159,93 +121,139 @@ bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
bool cStreamdevDevice::SetChannelDevice(const cChannel *Channel, bool cStreamdevDevice::SetChannelDevice(const cChannel *Channel,
bool LiveView) { bool LiveView) {
bool res;
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; LOCK_THREAD;
m_UpdatePriority = ClientSocket.SupportsPrio();
if (LiveView) if (LiveView)
return false; return false;
if (Receiving() && IsTunedToTransponder(Channel) && ( if (ClientSocket.DataSocket(siLive) != NULL
Channel->Ca() < CA_ENCRYPTED_MIN || && TRANSPONDER(Channel, m_Channel)
(Channel->Vpid() && HasPid(Channel->Vpid())) || && Channel->Ca() == CA_FTA
(Channel->Apid(0) && HasPid(Channel->Apid(0))))) { && m_Channel->Ca() == CA_FTA)
res = true; return true;
}
else {
DetachAllReceivers(); DetachAllReceivers();
m_Channel = Channel; m_Channel = Channel;
// Old servers delete cStreamdevLiveStreamer in ABRT. bool r = ClientSocket.SetChannelDevice(m_Channel);
// Delete it now or it will happen after we tuned to new channel Dprintf("setchanneldevice r=%d\n", r);
if (m_ClientSocket->ServerVersion() < 100) return r;
CloseDvr();
res = m_ClientSocket->SetChannelDevice(m_Channel);
}
Dprintf("setchanneldevice res=%d\n", res);
return res;
} }
bool cStreamdevDevice::SetPid(cPidHandle *Handle, int Type, bool On) { bool cStreamdevDevice::SetPid(cPidHandle *Handle, int Type, bool On) {
Dprintf("SetPid, Pid=%d, Type=%d, On=%d, used=%d\n", Handle->pid, Type, On, Handle->used); Dprintf("SetPid, Pid=%d, Type=%d, On=%d, used=%d\n", Handle->pid, Type, On,
Handle->used);
LOCK_THREAD; LOCK_THREAD;
m_UpdatePriority = ClientSocket.SupportsPrio();
if (On && !m_TSBuffer) {
Dprintf("SetPid: no data connection -> OpenDvr()");
OpenDvrInt();
}
bool res = true; bool res = true;
if (Handle->pid && (On || !Handle->used)) { if (Handle->pid && (On || !Handle->used)) {
res = m_ClientSocket->SetPid(Handle->pid, On); res = ClientSocket.SetPid(Handle->pid, On);
m_Pids += (!res) ? 0 : On ? 1 : -1; m_Pids += (!res) ? 0 : On ? 1 : -1;
if (m_Pids < 0) if (m_Pids < 0)
m_Pids = 0; m_Pids = 0;
if(m_Pids < 1 && m_DvrClosed) {
Dprintf("SetPid: 0 pids left -> CloseDvr()");
CloseDvrInt();
} }
}
return res; return res;
} }
bool cStreamdevDevice::OpenDvrInt(void) {
Dprintf("OpenDvrInt\n");
LOCK_THREAD;
CloseDvrInt();
if (m_TSBuffer) {
Dprintf("cStreamdevDevice::OpenDvrInt(): DVR connection already open\n");
return true;
}
Dprintf("cStreamdevDevice::OpenDvrInt(): Connecting ...\n");
if (ClientSocket.CreateDataConnection(siLive)) {
m_TSBuffer = new cTSBuffer(*ClientSocket.DataSocket(siLive), MEGABYTE(2), CardIndex() + 1);
return true;
}
esyslog("cStreamdevDevice::OpenDvrInt(): DVR connection FAILED");
return false;
}
bool cStreamdevDevice::OpenDvr(void) { bool cStreamdevDevice::OpenDvr(void) {
Dprintf("OpenDvr\n"); Dprintf("OpenDvr\n");
LOCK_THREAD; LOCK_THREAD;
CloseDvr(); m_DvrClosed = false;
if (m_ClientSocket->CreateDataConnection(siLive)) { return OpenDvrInt();
m_TSBuffer = new cTSBuffer(*m_ClientSocket->DataSocket(siLive), MEGABYTE(2), CardIndex() + 1);
}
else {
esyslog("cStreamdevDevice::OpenDvr(): DVR connection FAILED");
}
return m_TSBuffer != NULL;
} }
void cStreamdevDevice::CloseDvrInt(void) {
Dprintf("CloseDvrInt\n");
LOCK_THREAD;
if (ClientSocket.CheckConnection()) {
if (!m_DvrClosed) {
Dprintf("cStreamdevDevice::CloseDvrInt(): m_DvrClosed=false -> not closing yet\n");
return;
}
if (m_Pids > 0) {
Dprintf("cStreamdevDevice::CloseDvrInt(): %d active pids -> not closing yet\n", m_Pids);
return;
}
} else {
Dprintf("cStreamdevDevice::CloseDvrInt(): Control connection gone !\n");
}
Dprintf("cStreamdevDevice::CloseDvrInt(): Closing DVR connection\n");
// Hack for VDR 1.5.x clients (sometimes sending ABRT after TUNE)
// TODO: Find a clean solution to fix this
ClientSocket.SetChannelDevice(m_Channel);
ClientSocket.CloseDvr();
DELETENULL(m_TSBuffer);
}
void cStreamdevDevice::CloseDvr(void) { void cStreamdevDevice::CloseDvr(void) {
Dprintf("CloseDvr\n"); Dprintf("CloseDvr\n");
LOCK_THREAD; LOCK_THREAD;
m_ClientSocket->CloseDvr(); m_DvrClosed = true;
DELETENULL(m_TSBuffer); CloseDvrInt();
} }
bool cStreamdevDevice::GetTSPacket(uchar *&Data) { bool cStreamdevDevice::GetTSPacket(uchar *&Data) {
if (m_TSBuffer) { if (m_TSBuffer && m_Device) {
Data = m_TSBuffer->Get(); Data = m_TSBuffer->Get();
#if 1 // TODO: this should be fixed in vdr cTSBuffer #if 1 // TODO: this should be fixed in vdr cTSBuffer
// simple disconnect detection // simple disconnect detection
static int m_TSFails = 0; static int m_TSFails = 0;
if (!Data) { if (!Data) {
LOCK_THREAD; LOCK_THREAD;
if(!m_ClientSocket->DataSocket(siLive)) { if(!ClientSocket.DataSocket(siLive)) {
return false; // triggers CloseDvr() + OpenDvr() in cDevice return false; // triggers CloseDvr() + OpenDvr() in cDevice
} }
cPoller Poller(*m_ClientSocket->DataSocket(siLive)); cPoller Poller(*ClientSocket.DataSocket(siLive));
errno = 0; errno = 0;
if (Poller.Poll() && !errno) { if (Poller.Poll() && !errno) {
char tmp[1]; char tmp[1];
if (recv(*m_ClientSocket->DataSocket(siLive), tmp, 1, MSG_PEEK) == 0 && !errno) { if (recv(*ClientSocket.DataSocket(siLive), tmp, 1, MSG_PEEK) == 0 && !errno) {
esyslog("cStreamDevice::GetTSPacket: GetChecked: NOTHING (%d)", m_TSFails); esyslog("cStreamDevice::GetTSPacket: GetChecked: NOTHING (%d)", m_TSFails);
m_TSFails++; m_TSFails++;
if (m_TSFails > 10) { if (m_TSFails > 10) {
isyslog("cStreamdevDevice::GetTSPacket(): disconnected"); isyslog("cStreamdevDevice::GetTSPacket(): disconnected");
m_Pids = 0; m_Pids = 0;
CloseDvr(); CloseDvrInt();
m_TSFails = 0; m_TSFails = 0;
return false; return false;
} }
@@ -267,89 +275,67 @@ int cStreamdevDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask) {
return -1; return -1;
if (!m_ClientSocket->DataSocket(siLiveFilter)) { if (!ClientSocket.DataSocket(siLiveFilter)) {
if (m_ClientSocket->CreateDataConnection(siLiveFilter)) { if (ClientSocket.CreateDataConnection(siLiveFilter)) {
m_Filters->SetConnection(*m_ClientSocket->DataSocket(siLiveFilter)); m_Filters->SetConnection(*ClientSocket.DataSocket(siLiveFilter));
} else { } else {
isyslog("cStreamdevDevice::OpenFilter: connect failed: %m"); isyslog("cStreamdevDevice::OpenFilter: connect failed: %m");
return -1; return -1;
} }
} }
if (m_ClientSocket->SetFilter(Pid, Tid, Mask, true)) if (ClientSocket.SetFilter(Pid, Tid, Mask, true))
return m_Filters->OpenFilter(Pid, Tid, Mask); return m_Filters->OpenFilter(Pid, Tid, Mask);
return -1; return -1;
} }
void cStreamdevDevice::CloseFilter(int Handle) { bool cStreamdevDevice::Init(void) {
if (m_Device == NULL && StreamdevClientSetup.StartClient)
if(m_Filters) new cStreamdevDevice;
m_Filters->CloseFilter(Handle);
else
esyslog("cStreamdevDevice::CloseFilter called while m_Filters is null");
}
bool cStreamdevDevice::ReInit(bool Disable) {
LOCK_THREAD;
m_Disabled = Disable;
m_Filters->SetConnection(-1);
m_Pids = 0;
m_ClientSocket->Quit();
m_ClientSocket->Reset();
//DELETENULL(m_TSBuffer);
return true; return true;
} }
void cStreamdevDevice::UpdatePriority(bool SwitchingChannels) const { bool cStreamdevDevice::ReInit(void) {
if (!m_Disabled) { if(m_Device) {
//LOCK_THREAD; m_Device->Lock();
const_cast<cStreamdevDevice*>(this)->Lock(); m_Device->m_Filters->SetConnection(-1);
if (m_ClientSocket->SupportsPrio() && m_ClientSocket->DataSocket(siLive)) { m_Device->m_Pids = 0;
int Priority = this->Priority();
// override TRANSFERPRIORITY (-1) with live TV priority from setup
if (Priority == TRANSFERPRIORITY && this == cDevice::ActualDevice()) {
Priority = StreamdevClientSetup.LivePriority;
// temporarily lower priority
if (SwitchingChannels)
Priority--;
if (Priority < 0 && m_ClientSocket->ServerVersion() < 100)
Priority = 0;
} }
m_ClientSocket->SetPriority(Priority); ClientSocket.Quit();
} ClientSocket.Reset();
const_cast<cStreamdevDevice*>(this)->Unlock(); if (m_Device != NULL) {
//DELETENULL(m_Device->m_TSBuffer);
m_Device->Unlock();
} }
return StreamdevClientSetup.StartClient ? Init() : true;
} }
cString cStreamdevDevice::DeviceName(void) const { void cStreamdevDevice::UpdatePriority(void) {
return StreamdevClientSetup.RemoteIp; if (m_Device) {
} m_Device->Lock();
if (m_Device->m_UpdatePriority && ClientSocket.DataSocket(siLive)) {
cString cStreamdevDevice::DeviceType(void) const { int Priority = m_Device->Priority();
static int dev = -1; if (m_Device == cDevice::ActualDevice() && Priority < Setup.PrimaryLimit)
static cString devType("STRDev"); Priority = Setup.PrimaryLimit;
int d = -1; if (m_Device->m_Priority != Priority && ClientSocket.SetPriority(Priority))
if (m_ClientSocket->DataSocket(siLive) != NULL) m_Device->m_Priority = Priority;
m_ClientSocket->GetSignal(NULL, NULL, &d); }
if (d != dev) { m_Device->Unlock();
dev = d;
devType = d < 0 ? "STRDev" : *cString::sprintf("STRD%2d", d);
} }
return devType;
} }
int cStreamdevDevice::SignalStrength(void) const { int cStreamdevDevice::SignalStrength(void) const {
int strength = -1; int strength = -1;
if (m_ClientSocket->DataSocket(siLive) != NULL) if (ClientSocket.DataSocket(siLive) != NULL)
m_ClientSocket->GetSignal(&strength, NULL, NULL); ClientSocket.GetSignal(&strength, NULL);
return strength; return strength;
} }
int cStreamdevDevice::SignalQuality(void) const { int cStreamdevDevice::SignalQuality(void) const {
int quality = -1; int quality = -1;
if (m_ClientSocket->DataSocket(siLive) != NULL) if (ClientSocket.DataSocket(siLive) != NULL)
m_ClientSocket->GetSignal(NULL, &quality, NULL); ClientSocket.GetSignal(NULL, &quality);
return quality; return quality;
} }

View File

@@ -17,22 +17,22 @@ class cTBString;
class cStreamdevDevice: public cDevice { class cStreamdevDevice: public cDevice {
private: private:
bool m_Disabled;
cClientSocket *m_ClientSocket;
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;
static const cChannel *m_DenyChannel; static cStreamdevDevice *m_Device;
bool OpenDvrInt(void);
void CloseDvrInt(void);
protected: protected:
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView); virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
#if APIVERSNUM >= 10738
virtual bool HasLock(int TimeoutMs) const
#else
virtual bool HasLock(int TimeoutMs) virtual bool HasLock(int TimeoutMs)
#endif
{ {
//printf("HasLock is %d\n", (ClientSocket.DataSocket(siLive) != NULL)); //printf("HasLock is %d\n", (ClientSocket.DataSocket(siLive) != NULL));
//return ClientSocket.DataSocket(siLive) != NULL; //return ClientSocket.DataSocket(siLive) != NULL;
@@ -45,7 +45,6 @@ protected:
virtual bool GetTSPacket(uchar *&Data); virtual bool GetTSPacket(uchar *&Data);
virtual int OpenFilter(u_short Pid, u_char Tid, u_char Mask); virtual int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
virtual void CloseFilter(int Handle);
public: public:
cStreamdevDevice(void); cStreamdevDevice(void);
@@ -62,21 +61,15 @@ public:
#if APIVERSNUM >= 10719 #if APIVERSNUM >= 10719
virtual bool AvoidRecording(void) const { return true; } virtual bool AvoidRecording(void) const { return true; }
#endif #endif
#if APIVERSNUM >= 10722
virtual bool IsTunedToTransponder(const cChannel *Channel) const;
#else
virtual bool IsTunedToTransponder(const cChannel *Channel); virtual bool IsTunedToTransponder(const cChannel *Channel);
#endif
virtual const cChannel *GetCurrentlyTunedTransponder(void) const;
virtual cString DeviceName(void) const;
virtual cString DeviceType(void) const;
virtual int SignalStrength(void) const; virtual int SignalStrength(void) const;
virtual int SignalQuality(void) const; virtual int SignalQuality(void) const;
bool ReInit(bool Disable); static void UpdatePriority(void);
void UpdatePriority(bool SwitchingChannels = false) const; static bool Init(void);
bool SuspendServer() { return m_ClientSocket->SuspendServer(); } static bool ReInit(void);
static void DenyChannel(const cChannel *Channel) { m_DenyChannel = Channel; }
static cStreamdevDevice *GetDevice(void) { return m_Device; }
}; };
#endif // VDR_STREAMDEV_DEVICE_H #endif // VDR_STREAMDEV_DEVICE_H

View File

@@ -6,28 +6,20 @@
#include "client/socket.h" #include "client/socket.h"
#include "tools/select.h" #include "tools/select.h"
#include "common.h" #include "common.h"
#include <sys/ioctl.h>
#include <string.h>
#include <vdr/device.h> #include <vdr/device.h>
#define PID_MASK_HI 0x1F #define PID_MASK_HI 0x1F
// --- cStreamdevFilter ------------------------------------------------------ // --- cStreamdevFilter ------------------------------------------------------
static int FilterSockBufSize_warn = 0;
class cStreamdevFilter: public cListObject { class cStreamdevFilter: public cListObject {
private: private:
uchar m_Buffer[8192]; uchar m_Buffer[4096];
int m_Used; int m_Used;
int m_Pipe[2]; int m_Pipe[2];
u_short m_Pid; u_short m_Pid;
u_char m_Tid; u_char m_Tid;
u_char m_Mask; u_char m_Mask;
#ifdef TIOCOUTQ
unsigned long m_maxq;
unsigned long m_flushed;
#endif
public: public:
cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask); cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask);
@@ -37,6 +29,7 @@ public:
bool PutSection(const uchar *Data, int Length, bool Pusi); bool PutSection(const uchar *Data, int Length, bool Pusi);
int ReadPipe(void) const { return m_Pipe[0]; } int ReadPipe(void) const { return m_Pipe[0]; }
bool IsClosed(void);
void Reset(void); void Reset(void);
u_short Pid(void) const { return m_Pid; } u_short Pid(void) const { return m_Pid; }
@@ -54,10 +47,6 @@ cStreamdevFilter::cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask) {
m_Tid = Tid; m_Tid = Tid;
m_Mask = Mask; m_Mask = Mask;
m_Pipe[0] = m_Pipe[1] = -1; m_Pipe[0] = m_Pipe[1] = -1;
#ifdef TIOCOUTQ
m_flushed = 0;
m_maxq = 0;
#endif
#ifdef SOCK_SEQPACKET #ifdef SOCK_SEQPACKET
// SOCK_SEQPACKET (since kernel 2.6.4) // SOCK_SEQPACKET (since kernel 2.6.4)
@@ -69,46 +58,7 @@ cStreamdevFilter::cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask) {
esyslog("streamdev-client: couldn't open section filter socket: %m"); esyslog("streamdev-client: couldn't open section filter socket: %m");
} }
// Set buffer for socketpair. During certain situations, such as startup, channel/transponder else if(fcntl(m_Pipe[0], F_SETFL, O_NONBLOCK) != 0 ||
// change, VDR may lag in reading data. Instead of discarding it, we can buffer it.
// Buffer size required may be up to 4MByte.
if(StreamdevClientSetup.FilterSockBufSize) {
int sbs = StreamdevClientSetup.FilterSockBufSize;
int sbs2;
unsigned int sbss = sizeof(sbs);
int r;
r = setsockopt(m_Pipe[1], SOL_SOCKET, SO_SNDBUF, (char *)&sbs, sbss);
if(r < 0) {
isyslog("streamdev-client: setsockopt(SO_SNDBUF, %d) = %s", sbs, strerror(errno));
}
sbs2 = 0;
r = getsockopt(m_Pipe[1], SOL_SOCKET, SO_SNDBUF, (char *)&sbs2, &sbss);
if(r < 0 || !sbss || !sbs2) {
isyslog("streamdev-client: getsockopt(SO_SNDBUF, &%d, &%d) = %s", sbs2, sbss, strerror(errno));
} else {
// Linux actually returns double the requested size
// if everything works fine. And it actually buffers up to that double amount
// as can be seen from observing TIOCOUTQ (kernel 3.7/2014).
if(sbs2 > sbs)
sbs2 /= 2;
if(sbs2 < sbs) {
if(FilterSockBufSize_warn != sbs2) {
isyslog("streamdev-client: ******************************************************");
isyslog("streamdev-client: getsockopt(SO_SNDBUF) = %d < %d (configured).", sbs2, sbs);
isyslog("streamdev-client: Consider increasing system buffer size:");
isyslog("streamdev-client: 'sysctl net.core.wmem_max=%d'", sbs);
isyslog("streamdev-client: ******************************************************");
FilterSockBufSize_warn = sbs2;
}
}
}
}
if(fcntl(m_Pipe[0], F_SETFL, O_NONBLOCK) != 0 ||
fcntl(m_Pipe[1], F_SETFL, O_NONBLOCK) != 0) { fcntl(m_Pipe[1], F_SETFL, O_NONBLOCK) != 0) {
esyslog("streamdev-client: couldn't set section filter socket to non-blocking mode: %m"); esyslog("streamdev-client: couldn't set section filter socket to non-blocking mode: %m");
} }
@@ -117,12 +67,11 @@ cStreamdevFilter::cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask) {
cStreamdevFilter::~cStreamdevFilter() { cStreamdevFilter::~cStreamdevFilter() {
Dprintf("~cStreamdevFilter %p\n", this); Dprintf("~cStreamdevFilter %p\n", this);
if (m_Pipe[0] >= 0) { // ownership of handle m_Pipe[0] has been transferred to VDR section handler
close(m_Pipe[0]); //if (m_Pipe[0] >= 0)
} // close(m_Pipe[0]);
if (m_Pipe[1] >= 0) { if (m_Pipe[1] >= 0)
close(m_Pipe[1]); close(m_Pipe[1]);
}
} }
bool cStreamdevFilter::PutSection(const uchar *Data, int Length, bool Pusi) { bool cStreamdevFilter::PutSection(const uchar *Data, int Length, bool Pusi) {
@@ -145,42 +94,13 @@ bool cStreamdevFilter::PutSection(const uchar *Data, int Length, bool Pusi) {
int length = (((m_Buffer[1] & 0x0F) << 8) | m_Buffer[2]) + 3; int length = (((m_Buffer[1] & 0x0F) << 8) | m_Buffer[2]) + 3;
if (m_Used == length) { if (m_Used == length) {
m_Used = 0; m_Used = 0;
#ifdef TIOCOUTQ if (write(m_Pipe[1], m_Buffer, length) < 0) {
// If we can determine the queue size of the socket, if(errno == EAGAIN || errno == EWOULDBLOCK)
// we flush rather then let the socket drop random packets. dsyslog("cStreamdevFilter::PutSection socket overflow, "
// This ensures that we have more contiguous set of packets "Pid %4d Tid %3d", m_Pid, m_Tid);
// on the receiver side.
if(m_flushed) {
unsigned long queue = 0;
ioctl(m_Pipe[1], TIOCOUTQ, &queue);
if(queue > m_maxq)
m_maxq = queue;
if(queue * 2 < m_maxq) {
dsyslog("cStreamdevFilter::PutSection(Pid:%d Tid: %d): "
"Flushed %ld bytes, max queue: %ld",
m_Pid, m_Tid, m_flushed, m_maxq);
m_flushed = m_maxq = 0;
} else { else
m_flushed += length;
}
}
if(!m_flushed)
#endif
if(write(m_Pipe[1], m_Buffer, length) < 0) {
if(errno != EAGAIN && errno != EWOULDBLOCK) {
dsyslog("cStreamdevFilter::PutSection(Pid:%d Tid: %d): error: %s",
m_Pid, m_Tid, strerror(errno));
return false; return false;
} else {
#ifdef TIOCOUTQ
m_flushed += length;
#else
dsyslog("cStreamdevFilter::PutSection(Pid:%d Tid: %d): "
"Dropping packet %ld bytes (queue overflow)",
m_Pid, m_Tid, length);
#endif
}
} }
} }
@@ -203,11 +123,29 @@ void cStreamdevFilter::Reset(void) {
m_Used = 0; m_Used = 0;
} }
bool cStreamdevFilter::IsClosed(void) {
char m_Buffer[3] = {0,0,0}; /* tid 0, 0 bytes */
// Test if pipe/socket has been closed by writing empty section
if (write(m_Pipe[1], m_Buffer, 3) < 0 &&
errno != EAGAIN &&
errno != EWOULDBLOCK) {
if (errno != ECONNREFUSED &&
errno != ECONNRESET &&
errno != EPIPE)
esyslog("cStreamdevFilter::IsClosed failed: %m");
return true;
}
return false;
}
// --- cStreamdevFilters ----------------------------------------------------- // --- cStreamdevFilters -----------------------------------------------------
cStreamdevFilters::cStreamdevFilters(cClientSocket *ClientSocket): cStreamdevFilters::cStreamdevFilters(void):
cThread("streamdev-client: sections assembler") { cThread("streamdev-client: sections assembler") {
m_ClientSocket = ClientSocket;
m_TSBuffer = NULL; m_TSBuffer = NULL;
} }
@@ -216,27 +154,43 @@ cStreamdevFilters::~cStreamdevFilters() {
} }
int cStreamdevFilters::OpenFilter(u_short Pid, u_char Tid, u_char Mask) { int cStreamdevFilters::OpenFilter(u_short Pid, u_char Tid, u_char Mask) {
CarbageCollect();
cStreamdevFilter *f = new cStreamdevFilter(Pid, Tid, Mask); cStreamdevFilter *f = new cStreamdevFilter(Pid, Tid, Mask);
int fh = f->ReadPipe(); int fh = f->ReadPipe();
LOCK_THREAD; Lock();
Add(f); Add(f);
Unlock();
return fh; return fh;
} }
void cStreamdevFilters::CloseFilter(int Handle) { void cStreamdevFilters::CarbageCollect(void) {
LOCK_THREAD; LOCK_THREAD;
for (cStreamdevFilter *fi = First(); fi;) {
if (fi->IsClosed()) {
if (errno == ECONNREFUSED ||
errno == ECONNRESET ||
errno == EPIPE) {
ClientSocket.SetFilter(fi->Pid(), fi->Tid(), fi->Mask(), false);
Dprintf("cStreamdevFilters::CarbageCollector: filter closed: Pid %4d, Tid %3d, Mask %2x (%d filters left)",
(int)fi->Pid(), (int)fi->Tid(), fi->Mask(), Count()-1);
for (cStreamdevFilter *fi = First(); fi; fi = Next(fi)) { cStreamdevFilter *next = Prev(fi);
if(fi->ReadPipe() == Handle) {
// isyslog("cStreamdevFilters::CloseFilter(%d): Pid %4d, Tid %3d, Mask %2x (%d filters left)\n",
// Handle, (int)fi->Pid(), (int)fi->Tid(), fi->Mask(), Count()-1);
Del(fi); Del(fi);
return; fi = next ? Next(next) : First();
} else {
esyslog("cStreamdevFilters::CarbageCollector() error: "
"Pid %4d, Tid %3d, Mask %2x (%d filters left) failed",
(int)fi->Pid(), (int)fi->Tid(), fi->Mask(), Count()-1);
LOG_ERROR;
fi = Next(fi);
}
} else {
fi = Next(fi);
} }
} }
esyslog("cStreamdevFilters::CloseFilter(%d): failed (%d filters left)\n", Handle, Count()-1);
} }
bool cStreamdevFilters::ReActivateFilters(void) bool cStreamdevFilters::ReActivateFilters(void)
@@ -244,8 +198,9 @@ bool cStreamdevFilters::ReActivateFilters(void)
LOCK_THREAD; LOCK_THREAD;
bool res = true; bool res = true;
CarbageCollect();
for (cStreamdevFilter *fi = First(); fi; fi = Next(fi)) { for (cStreamdevFilter *fi = First(); fi; fi = Next(fi)) {
res = m_ClientSocket->SetFilter(fi->Pid(), fi->Tid(), fi->Mask(), true) && res; res = ClientSocket.SetFilter(fi->Pid(), fi->Tid(), fi->Mask(), true) && res;
Dprintf("ReActivateFilters(%d, %d, %d) -> %s", fi->Pid(), fi->Tid(), fi->Mask(), res ? "Ok" :"FAIL"); Dprintf("ReActivateFilters(%d, %d, %d) -> %s", fi->Pid(), fi->Tid(), fi->Mask(), res ? "Ok" :"FAIL");
} }
return res; return res;
@@ -296,7 +251,7 @@ void cStreamdevFilters::Action(void) {
Dprintf("FATAL ERROR: %m\n"); Dprintf("FATAL ERROR: %m\n");
esyslog("streamdev-client: couldn't send section packet: %m"); esyslog("streamdev-client: couldn't send section packet: %m");
} }
m_ClientSocket->SetFilter(f->Pid(), f->Tid(), f->Mask(), false); ClientSocket.SetFilter(f->Pid(), f->Tid(), f->Mask(), false);
Del(f); Del(f);
// Filter was closed. // Filter was closed.
// - need to check remaining filters for another match // - need to check remaining filters for another match
@@ -306,7 +261,7 @@ void cStreamdevFilters::Action(void) {
} else { } else {
#if 1 // TODO: this should be fixed in vdr cTSBuffer #if 1 // TODO: this should be fixed in vdr cTSBuffer
// Check disconnection // Check disconnection
int fd = *m_ClientSocket->DataSocket(siLiveFilter); int fd = *ClientSocket.DataSocket(siLiveFilter);
if(fd < 0) if(fd < 0)
break; break;
cPoller Poller(fd); cPoller Poller(fd);
@@ -318,7 +273,7 @@ void cStreamdevFilters::Action(void) {
++fails; ++fails;
if (fails >= 10) { if (fails >= 10) {
esyslog("cStreamdevFilters::Action(): stream disconnected ?"); esyslog("cStreamdevFilters::Action(): stream disconnected ?");
m_ClientSocket->CloseDataConnection(siLiveFilter); ClientSocket.CloseDataConnection(siLiveFilter);
break; break;
} }
} else { } else {

View File

@@ -11,24 +11,23 @@
class cTSBuffer; class cTSBuffer;
class cStreamdevFilter; class cStreamdevFilter;
class cClientSocket;
class cStreamdevFilters: public cList<cStreamdevFilter>, public cThread { class cStreamdevFilters: public cList<cStreamdevFilter>, public cThread {
private: private:
cClientSocket *m_ClientSocket;
cTSBuffer *m_TSBuffer; cTSBuffer *m_TSBuffer;
protected: protected:
virtual void Action(void); virtual void Action(void);
void CarbageCollect(void);
bool ReActivateFilters(void); bool ReActivateFilters(void);
public: public:
cStreamdevFilters(cClientSocket *ClientSocket); cStreamdevFilters(void);
virtual ~cStreamdevFilters(); virtual ~cStreamdevFilters();
void SetConnection(int Handle); void SetConnection(int Handle);
int OpenFilter(u_short Pid, u_char Tid, u_char Mask); int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
void CloseFilter(int Handle);
}; };
#endif // VDR_STREAMDEV_FILTER_H #endif // VDR_STREAMDEV_FILTER_H

View File

@@ -6,49 +6,15 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: streamdev 0.5.0\n" "Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n" "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2014-10-20 22:57+0200\n" "POT-Creation-Date: 2011-02-16 08:49+0100\n"
"PO-Revision-Date: 2008-03-30 02:11+0200\n" "PO-Revision-Date: 2008-03-30 02:11+0200\n"
"Last-Translator: Frank Schmirler <vdrdev@schmirler.de>\n" "Last-Translator: Frank Schmirler <vdrdev@schmirler.de>\n"
"Language-Team: German <vdr@linuxtv.org>\n" "Language-Team: <vdr@linuxtv.org>\n"
"Language: de\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-15\n" "Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
msgid "Hide Mainmenu Entry"
msgstr "Hauptmenüeintrag verstecken"
msgid "Simultaneously used Devices"
msgstr "Gleichzeitig genutzte DVB-Karten"
msgid "Remote IP"
msgstr "IP der Gegenseite"
msgid "Remote Port"
msgstr "Port der Gegenseite"
msgid "Timeout (s)"
msgstr "Timeout (s)"
msgid "Filter Streaming"
msgstr "Filter-Daten streamen"
msgid "Filter SockBufSize"
msgstr "Filter Socket Puffergröße"
msgid "Live TV Priority"
msgstr "Live TV Priorität"
msgid "Minimum Priority"
msgstr "Minimale Priorität"
msgid "Maximum Priority"
msgstr "Maximale Priorität"
msgid "Broadcast Systems / Cost"
msgstr "Empfangssysteme / Kosten"
msgid "VTP Streaming Client" msgid "VTP Streaming Client"
msgstr "VTP Streaming Client" msgstr "VTP Streaming Client"
@@ -60,3 +26,27 @@ msgstr "Server ist pausiert"
msgid "Couldn't suspend Server!" msgid "Couldn't suspend Server!"
msgstr "Konnte Server nicht pausieren!" 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"

View File

@@ -6,49 +6,15 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: streamdev 0.5.0\n" "Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n" "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2015-01-16 22:32+0100\n" "POT-Creation-Date: 2010-06-14 13:05+0200\n"
"PO-Revision-Date: 2010-06-19 03:58+0100\n" "PO-Revision-Date: 2010-06-19 03:58+0100\n"
"Last-Translator: Javier Bradineras <jbradi@hotmail.com>\n" "Last-Translator: Javier Bradineras <jbradi@hotmail.com>\n"
"Language-Team: Spanish <vdr@linuxtv.org>\n" "Language-Team: <vdr@linuxtv.org>\n"
"Language: es\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-15\n" "Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
msgid "Hide Mainmenu Entry"
msgstr "Ocultar entrada en menú principal"
msgid "Simultaneously used Devices"
msgstr ""
msgid "Remote IP"
msgstr "Indicar IP del Servidor"
msgid "Remote Port"
msgstr "Indicar puerto remoto del Servidor"
msgid "Timeout (s)"
msgstr ""
msgid "Filter Streaming"
msgstr "Filtrar transmisión"
msgid "Filter SockBufSize"
msgstr ""
msgid "Live TV Priority"
msgstr ""
msgid "Minimum Priority"
msgstr "Prioridad mínima"
msgid "Maximum Priority"
msgstr "Prioridad máxima"
msgid "Broadcast Systems / Cost"
msgstr ""
msgid "VTP Streaming Client" msgid "VTP Streaming Client"
msgstr "Cliente trasmisión VTP" msgstr "Cliente trasmisión VTP"
@@ -60,3 +26,26 @@ msgstr "Servidor en suspensi
msgid "Couldn't suspend Server!" msgid "Couldn't suspend Server!"
msgstr "Imposible suspender el servidor!" 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"

View File

@@ -1,62 +1,50 @@
# VDR streamdev plugin language source file. # VDR streamdev plugin language source file.
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org # 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. # This file is distributed under the same license as the VDR streamdev package.
# Rolf Ahrenberg, 2008- # Rolf Ahrenberg <rahrenbe@cc.hut.fi>, 2008
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: streamdev 0.5.0\n" "Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n" "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2015-01-16 22:32+0100\n" "POT-Creation-Date: 2010-06-14 13:05+0200\n"
"PO-Revision-Date: 2008-03-30 02:11+0200\n" "PO-Revision-Date: 2008-03-30 02:11+0200\n"
"Last-Translator: Rolf Ahrenberg\n" "Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"
"Language-Team: Finnish <vdr@linuxtv.org>\n" "Language-Team: <vdr@linuxtv.org>\n"
"Language: fi\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
msgid "Hide Mainmenu Entry" msgid "VTP Streaming Client"
msgstr "Piilota valinta päävalikosta" msgstr "VTP-suoratoistoasiakas"
msgid "Simultaneously used Devices" msgid "Suspend Server"
msgstr "Yhtäaikaiset laitteet" 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" msgid "Remote IP"
msgstr "Etäkoneen IP-osoite" msgstr "Etäkoneen IP-osoite"
msgid "Remote Port" msgid "Remote Port"
msgstr "Etäkoneen portti" msgstr "Etäkoneen portti"
msgid "Timeout (s)"
msgstr "Yhteyden aikakatkaisu (s)"
msgid "Filter Streaming" msgid "Filter Streaming"
msgstr "Suodatetun tiedon suoratoisto" msgstr "Suodatetun tiedon suoratoisto"
msgid "Filter SockBufSize"
msgstr ""
msgid "Live TV Priority"
msgstr "Live-katselun prioriteetti"
msgid "Minimum Priority" msgid "Minimum Priority"
msgstr "Pienin prioriteetti" msgstr "Pienin prioriteetti"
msgid "Maximum Priority" msgid "Maximum Priority"
msgstr "Suurin prioriteetti" msgstr "Suurin prioriteetti"
msgid "Broadcast Systems / Cost"
msgstr "Lähetysjärjestelmien suhdeluku"
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!"

View File

@@ -6,49 +6,15 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: streamdev 0.5.0\n" "Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n" "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2015-01-16 22:32+0100\n" "POT-Creation-Date: 2010-06-14 13:05+0200\n"
"PO-Revision-Date: 2008-03-30 02:11+0200\n" "PO-Revision-Date: 2008-03-30 02:11+0200\n"
"Last-Translator: micky979 <micky979@free.fr>\n" "Last-Translator: micky979 <micky979@free.fr>\n"
"Language-Team: French <vdr@linuxtv.org>\n" "Language-Team: <vdr@linuxtv.org>\n"
"Language: fr\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-15\n" "Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
msgid "Hide Mainmenu Entry"
msgstr "Masquer dans le menu principal"
msgid "Simultaneously used Devices"
msgstr ""
msgid "Remote IP"
msgstr "Adresse IP du serveur"
msgid "Remote Port"
msgstr "Port du serveur"
msgid "Timeout (s)"
msgstr ""
msgid "Filter Streaming"
msgstr "Filtre streaming"
msgid "Filter SockBufSize"
msgstr ""
msgid "Live TV Priority"
msgstr ""
msgid "Minimum Priority"
msgstr ""
msgid "Maximum Priority"
msgstr ""
msgid "Broadcast Systems / Cost"
msgstr ""
msgid "VTP Streaming Client" msgid "VTP Streaming Client"
msgstr "Client de streaming VTP" msgstr "Client de streaming VTP"
@@ -60,3 +26,25 @@ msgstr "Le serveur est suspendu"
msgid "Couldn't suspend Server!" msgid "Couldn't suspend Server!"
msgstr "Impossible de suspendre le serveur!" 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 ""

View File

@@ -8,49 +8,15 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: streamdev 0.5.0\n" "Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n" "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2015-01-16 22:32+0100\n" "POT-Creation-Date: 2010-06-14 13:05+0200\n"
"PO-Revision-Date: 2012-06-10 20:34+0100\n" "PO-Revision-Date: 2010-06-19 03:58+0100\n"
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n" "Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
"Language-Team: Italian <vdr@linuxtv.org>\n" "Language-Team: <vdr@linuxtv.org>\n"
"Language: it\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
msgid "Hide Mainmenu Entry"
msgstr "Nascondi voce menu principale"
msgid "Simultaneously used Devices"
msgstr ""
msgid "Remote IP"
msgstr "Indirizzo IP del Server"
msgid "Remote Port"
msgstr "Porta Server Remoto"
msgid "Timeout (s)"
msgstr "Scadenza (s)"
msgid "Filter Streaming"
msgstr "Filtra trasmissione"
msgid "Filter SockBufSize"
msgstr ""
msgid "Live TV Priority"
msgstr "Priorità TV dal vivo"
msgid "Minimum Priority"
msgstr "Priorità minima"
msgid "Maximum Priority"
msgstr "Priorità massima"
msgid "Broadcast Systems / Cost"
msgstr "Costo / sistemi trasmissione"
msgid "VTP Streaming Client" msgid "VTP Streaming Client"
msgstr "Client trasmissione VTP" msgstr "Client trasmissione VTP"
@@ -62,3 +28,25 @@ msgstr "Server sospeso"
msgid "Couldn't suspend Server!" msgid "Couldn't suspend Server!"
msgstr "Impossibile sospendere il 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"

View File

@@ -6,49 +6,15 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: streamdev 0.5.0\n" "Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n" "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2015-01-16 22:32+0100\n" "POT-Creation-Date: 2010-06-14 13:05+0200\n"
"PO-Revision-Date: 2009-11-26 21:57+0200\n" "PO-Revision-Date: 2009-11-26 21:57+0200\n"
"Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n" "Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
"Language-Team: Lithuanian <vdr@linuxtv.org>\n" "Language-Team: Lietuvių\n"
"Language: lt\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
msgid "Hide Mainmenu Entry"
msgstr "Paslėpti pagrindinio meniu įrašą"
msgid "Simultaneously used Devices"
msgstr ""
msgid "Remote IP"
msgstr "Nuotolinis IP adresas"
msgid "Remote Port"
msgstr "Nuotolinis portas"
msgid "Timeout (s)"
msgstr ""
msgid "Filter Streaming"
msgstr "Filtruoti transliavimą"
msgid "Filter SockBufSize"
msgstr ""
msgid "Live TV Priority"
msgstr ""
msgid "Minimum Priority"
msgstr "Minimalus prioritetas"
msgid "Maximum Priority"
msgstr "Maksimalus prioritetas"
msgid "Broadcast Systems / Cost"
msgstr ""
msgid "VTP Streaming Client" msgid "VTP Streaming Client"
msgstr "VTP transliavimo standartas" msgstr "VTP transliavimo standartas"
@@ -60,3 +26,25 @@ msgstr "Serveris sustabdytas"
msgid "Couldn't suspend Server!" msgid "Couldn't suspend Server!"
msgstr "Negali sustabdyti serverio!" 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"

View File

@@ -1,63 +0,0 @@
# VDR streamdev plugin language source file.
# Copyright (C) 2008 streamdev development team. See
# This file is distributed under the same license as the VDR streamdev package.
# Tomasz Maciej Nowak, 2014
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-streamdev-client 0.6.1-git\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
"POT-Creation-Date: 2015-01-16 22:32+0100\n"
"PO-Revision-Date: 2014-11-24 18:07+0100\n"
"Last-Translator: Tomasz Maciej Nowak <tomek_n@o2.pl>\n"
"Language-Team: Polish <vdr@linuxtv.org>\n"
"Language: pl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-2\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.6.10\n"
msgid "Hide Mainmenu Entry"
msgstr "Ukryj pozycjê w g³ównym menu"
msgid "Simultaneously used Devices"
msgstr "Jednocze¶nie u¿ywane urz±dzenia"
msgid "Remote IP"
msgstr "Adres serwera"
msgid "Remote Port"
msgstr "Port serwera"
msgid "Timeout (s)"
msgstr "Czas oczekiwania (s)"
msgid "Filter Streaming"
msgstr "Filtruj strumieñ"
msgid "Filter SockBufSize"
msgstr ""
msgid "Live TV Priority"
msgstr "Priorytet lokalnych ur±dzeñ"
msgid "Minimum Priority"
msgstr "Minimalny priorytet"
msgid "Maximum Priority"
msgstr "Maksymalny priorytet"
msgid "Broadcast Systems / Cost"
msgstr "Koszt / systemy transmisji"
msgid "VTP Streaming Client"
msgstr "Klient strumieniowania VTP"
msgid "Suspend Server"
msgstr "Wstrzymaj serwer"
msgid "Server is suspended"
msgstr "Serwer jest wstrzymany"
msgid "Couldn't suspend Server!"
msgstr "Nie mo¿na wstrzymaæ serwera!"

View File

@@ -6,49 +6,15 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: streamdev 0.5.0\n" "Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n" "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2015-01-16 22:32+0100\n" "POT-Creation-Date: 2010-06-14 13:05+0200\n"
"PO-Revision-Date: 2008-06-26 15:36+0100\n" "PO-Revision-Date: 2008-06-26 15:36+0100\n"
"Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n" "Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n"
"Language-Team: Russian <vdr@linuxtv.org>\n" "Language-Team: <vdr@linuxtv.org>\n"
"Language: ru\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-5\n" "Content-Type: text/plain; charset=ISO-8859-5\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
msgid "Hide Mainmenu Entry"
msgstr "ÁßàïâÐâì Ò ÓÛÐÒÝÞÜ ÜÕÝî"
msgid "Simultaneously used Devices"
msgstr ""
msgid "Remote IP"
msgstr "ÃÔÐÛÕÝÝëÙ IP"
msgid "Remote Port"
msgstr "ÃÔÐÛÕÝÝëÙ ßÞàâ"
msgid "Timeout (s)"
msgstr ""
msgid "Filter Streaming"
msgstr "ÄØÛìâà ßÞâÞÚÐ"
msgid "Filter SockBufSize"
msgstr ""
msgid "Live TV Priority"
msgstr ""
msgid "Minimum Priority"
msgstr ""
msgid "Maximum Priority"
msgstr ""
msgid "Broadcast Systems / Cost"
msgstr ""
msgid "VTP Streaming Client" msgid "VTP Streaming Client"
msgstr "VTP Streaming ÚÛØÕÝâ" msgstr "VTP Streaming ÚÛØÕÝâ"
@@ -60,3 +26,25 @@ msgstr "
msgid "Couldn't suspend Server!" msgid "Couldn't suspend Server!"
msgstr "ÝÕ ÜÞÓã ÞáâÐÝÞÒØâì áÕàÒÕà" 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 ""

50
client/po/sk_SK.po Executable file → Normal file
View File

@@ -6,23 +6,34 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: streamdev_SK\n" "Project-Id-Version: streamdev_SK\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n" "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2015-01-16 22:32+0100\n" "POT-Creation-Date: 2010-06-14 13:05+0200\n"
"PO-Revision-Date: \n" "PO-Revision-Date: \n"
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n" "Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
"Language-Team: Slovak <hrala.milan@gmail.com>\n" "Language-Team: Slovak <hrala.milan@gmail.com>\n"
"Language: sk\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-2\n" "Content-Type: text/plain; charset=iso-8859-2\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Slovak\n" "X-Poedit-Language: Slovak\n"
"X-Poedit-Country: SLOVAKIA\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" msgid "Hide Mainmenu Entry"
msgstr "Schova» polo¾ku v hlavnom menu" msgstr "Schova» polo¾ku v hlavnom menu"
msgid "Simultaneously used Devices" msgid "Start Client"
msgstr "Súbe¾ne pou¾íva» zariadenia" msgstr "Spusti» Klienta"
msgid "Remote IP" msgid "Remote IP"
msgstr "Vzdialená IP" msgstr "Vzdialená IP"
@@ -30,35 +41,12 @@ msgstr "Vzdialen
msgid "Remote Port" msgid "Remote Port"
msgstr "Vzdialený port" msgstr "Vzdialený port"
msgid "Timeout (s)"
msgstr "Èasový limit (s)"
msgid "Filter Streaming" msgid "Filter Streaming"
msgstr "Filtrova» dátový prúd" msgstr "filtrova» prúdy"
msgid "Filter SockBufSize"
msgstr ""
msgid "Live TV Priority"
msgstr "Priorita ¾ivého vysielania"
msgid "Minimum Priority" msgid "Minimum Priority"
msgstr "Minimálna priorita" msgstr "minimálna priorita"
msgid "Maximum Priority" msgid "Maximum Priority"
msgstr "Maximálna priorita" msgstr "maximálna priorita"
msgid "Broadcast Systems / Cost"
msgstr "Systémy vysielania / Hodnota"
msgid "VTP Streaming Client"
msgstr "VDR klient streamovania"
msgid "Suspend Server"
msgstr "Pozastavi» server"
msgid "Server is suspended"
msgstr "Server je doèasne pozastavený"
msgid "Couldn't suspend Server!"
msgstr "Server sa nepodarilo pozastavi»!"

View File

@@ -5,28 +5,21 @@
#include <vdr/menuitems.h> #include <vdr/menuitems.h>
#include "client/setup.h" #include "client/setup.h"
#include "client/streamdev-client.h" #include "client/device.h"
#ifndef MINPRIORITY
#define MINPRIORITY -MAXPRIORITY
#endif
cStreamdevClientSetup StreamdevClientSetup; cStreamdevClientSetup StreamdevClientSetup;
cStreamdevClientSetup::cStreamdevClientSetup(void) { cStreamdevClientSetup::cStreamdevClientSetup(void) {
StartClient = false; StartClient = false;
RemotePort = 2004; RemotePort = 2004;
Timeout = 2;
StreamFilters = false; StreamFilters = false;
HideMenuEntry = false; HideMenuEntry = false;
LivePriority = 0; MinPriority = -1;
MinPriority = MINPRIORITY;
MaxPriority = MAXPRIORITY; MaxPriority = MAXPRIORITY;
#if APIVERSNUM >= 10700 #if APIVERSNUM >= 10700
NumProvidedSystems = 1; NumProvidedSystems = 1;
#endif #endif
strcpy(RemoteIp, ""); strcpy(RemoteIp, "");
FilterSockBufSize = 0;
} }
bool cStreamdevClientSetup::SetupParse(const char *Name, const char *Value) { bool cStreamdevClientSetup::SetupParse(const char *Name, const char *Value) {
@@ -38,13 +31,10 @@ bool cStreamdevClientSetup::SetupParse(const char *Name, const char *Value) {
strcpy(RemoteIp, Value); strcpy(RemoteIp, Value);
} }
else if (strcmp(Name, "RemotePort") == 0) RemotePort = atoi(Value); else if (strcmp(Name, "RemotePort") == 0) RemotePort = atoi(Value);
else if (strcmp(Name, "Timeout") == 0) Timeout = atoi(Value);
else if (strcmp(Name, "StreamFilters") == 0) StreamFilters = atoi(Value); else if (strcmp(Name, "StreamFilters") == 0) StreamFilters = atoi(Value);
else if (strcmp(Name, "HideMenuEntry") == 0) HideMenuEntry = atoi(Value); else if (strcmp(Name, "HideMenuEntry") == 0) HideMenuEntry = atoi(Value);
else if (strcmp(Name, "LivePriority") == 0) LivePriority = atoi(Value);
else if (strcmp(Name, "MinPriority") == 0) MinPriority = atoi(Value); else if (strcmp(Name, "MinPriority") == 0) MinPriority = atoi(Value);
else if (strcmp(Name, "MaxPriority") == 0) MaxPriority = atoi(Value); else if (strcmp(Name, "MaxPriority") == 0) MaxPriority = atoi(Value);
else if (strcmp(Name, "FilterSockBufSize") == 0) FilterSockBufSize = atoi(Value);
#if APIVERSNUM >= 10700 #if APIVERSNUM >= 10700
else if (strcmp(Name, "NumProvidedSystems") == 0) NumProvidedSystems = atoi(Value); else if (strcmp(Name, "NumProvidedSystems") == 0) NumProvidedSystems = atoi(Value);
#endif #endif
@@ -52,21 +42,16 @@ bool cStreamdevClientSetup::SetupParse(const char *Name, const char *Value) {
return true; return true;
} }
cStreamdevClientMenuSetupPage::cStreamdevClientMenuSetupPage(cPluginStreamdevClient *Plugin) { cStreamdevClientMenuSetupPage::cStreamdevClientMenuSetupPage(void) {
m_Plugin = Plugin;
m_NewSetup = StreamdevClientSetup; m_NewSetup = StreamdevClientSetup;
Add(new cMenuEditBoolItem(tr("Hide Mainmenu Entry"), &m_NewSetup.HideMenuEntry)); Add(new cMenuEditBoolItem(tr("Hide Mainmenu Entry"), &m_NewSetup.HideMenuEntry));
Add(new cMenuEditIntItem (tr("Simultaneously used Devices"), &m_NewSetup.StartClient, 0, STREAMDEV_MAXDEVICES)); Add(new cMenuEditBoolItem(tr("Start Client"), &m_NewSetup.StartClient));
Add(new cMenuEditIpItem (tr("Remote IP"), m_NewSetup.RemoteIp)); Add(new cMenuEditIpItem (tr("Remote IP"), m_NewSetup.RemoteIp));
Add(new cMenuEditIntItem (tr("Remote Port"), &m_NewSetup.RemotePort, 0, 65535)); Add(new cMenuEditIntItem (tr("Remote Port"), &m_NewSetup.RemotePort, 0, 65535));
Add(new cMenuEditIntItem (tr("Timeout (s)"), &m_NewSetup.Timeout, 1, 15));
Add(new cMenuEditBoolItem(tr("Filter Streaming"), &m_NewSetup.StreamFilters)); Add(new cMenuEditBoolItem(tr("Filter Streaming"), &m_NewSetup.StreamFilters));
if(m_NewSetup.StreamFilters) Add(new cMenuEditIntItem (tr("Minimum Priority"), &m_NewSetup.MinPriority, -1, MAXPRIORITY));
Add(new cMenuEditIntItem (tr("Filter SockBufSize"), &m_NewSetup.FilterSockBufSize, 0, 8192000)); Add(new cMenuEditIntItem (tr("Maximum Priority"), &m_NewSetup.MaxPriority, -1, MAXPRIORITY));
Add(new cMenuEditIntItem (tr("Live TV Priority"), &m_NewSetup.LivePriority, MINPRIORITY, MAXPRIORITY));
Add(new cMenuEditIntItem (tr("Minimum Priority"), &m_NewSetup.MinPriority, MINPRIORITY, MAXPRIORITY));
Add(new cMenuEditIntItem (tr("Maximum Priority"), &m_NewSetup.MaxPriority, MINPRIORITY, MAXPRIORITY));
#if APIVERSNUM >= 10715 #if APIVERSNUM >= 10715
Add(new cMenuEditIntItem (tr("Broadcast Systems / Cost"), &m_NewSetup.NumProvidedSystems, 1, 15)); Add(new cMenuEditIntItem (tr("Broadcast Systems / Cost"), &m_NewSetup.NumProvidedSystems, 1, 15));
#elif APIVERSNUM >= 10700 #elif APIVERSNUM >= 10700
@@ -79,25 +64,27 @@ cStreamdevClientMenuSetupPage::~cStreamdevClientMenuSetupPage() {
} }
void cStreamdevClientMenuSetupPage::Store(void) { void cStreamdevClientMenuSetupPage::Store(void) {
if (m_NewSetup.StartClient != StreamdevClientSetup.StartClient) {
if (m_NewSetup.StartClient)
cStreamdevDevice::Init();
}
SetupStore("StartClient", m_NewSetup.StartClient); SetupStore("StartClient", m_NewSetup.StartClient);
if (strcmp(m_NewSetup.RemoteIp, "") == 0) if (strcmp(m_NewSetup.RemoteIp, "") == 0)
SetupStore("RemoteIp", "-none-"); SetupStore("RemoteIp", "-none-");
else else
SetupStore("RemoteIp", m_NewSetup.RemoteIp); SetupStore("RemoteIp", m_NewSetup.RemoteIp);
SetupStore("RemotePort", m_NewSetup.RemotePort); SetupStore("RemotePort", m_NewSetup.RemotePort);
SetupStore("Timeout", m_NewSetup.Timeout);
SetupStore("StreamFilters", m_NewSetup.StreamFilters); SetupStore("StreamFilters", m_NewSetup.StreamFilters);
SetupStore("HideMenuEntry", m_NewSetup.HideMenuEntry); SetupStore("HideMenuEntry", m_NewSetup.HideMenuEntry);
SetupStore("LivePriority", m_NewSetup.LivePriority);
SetupStore("MinPriority", m_NewSetup.MinPriority); SetupStore("MinPriority", m_NewSetup.MinPriority);
SetupStore("MaxPriority", m_NewSetup.MaxPriority); SetupStore("MaxPriority", m_NewSetup.MaxPriority);
#if APIVERSNUM >= 10700 #if APIVERSNUM >= 10700
SetupStore("NumProvidedSystems", m_NewSetup.NumProvidedSystems); SetupStore("NumProvidedSystems", m_NewSetup.NumProvidedSystems);
#endif #endif
SetupStore("FilterSockBufSize", m_NewSetup.FilterSockBufSize);
StreamdevClientSetup = m_NewSetup; StreamdevClientSetup = m_NewSetup;
m_Plugin->Initialize(); cStreamdevDevice::ReInit();
} }

View File

@@ -15,11 +15,8 @@ struct cStreamdevClientSetup {
int StartClient; int StartClient;
char RemoteIp[20]; char RemoteIp[20];
int RemotePort; int RemotePort;
int Timeout;
int StreamFilters; int StreamFilters;
int FilterSockBufSize;
int HideMenuEntry; int HideMenuEntry;
int LivePriority;
int MinPriority; int MinPriority;
int MaxPriority; int MaxPriority;
#if APIVERSNUM >= 10700 #if APIVERSNUM >= 10700
@@ -29,18 +26,15 @@ struct cStreamdevClientSetup {
extern cStreamdevClientSetup StreamdevClientSetup; extern cStreamdevClientSetup StreamdevClientSetup;
class cPluginStreamdevClient;
class cStreamdevClientMenuSetupPage: public cMenuSetupPage { class cStreamdevClientMenuSetupPage: public cMenuSetupPage {
private: private:
cPluginStreamdevClient *m_Plugin;
cStreamdevClientSetup m_NewSetup; cStreamdevClientSetup m_NewSetup;
protected: protected:
virtual void Store(void); virtual void Store(void);
public: public:
cStreamdevClientMenuSetupPage(cPluginStreamdevClient *Plugin); cStreamdevClientMenuSetupPage(void);
virtual ~cStreamdevClientMenuSetupPage(); virtual ~cStreamdevClientMenuSetupPage();
}; };

View File

@@ -11,24 +11,19 @@
#define MINLOGREPEAT 10 //don't log connect failures too often (seconds) #define MINLOGREPEAT 10 //don't log connect failures too often (seconds)
// timeout for writing to command socket
#define WRITE_TIMEOUT_MS 200
#define QUIT_TIMEOUT_MS 500
#include "client/socket.h" #include "client/socket.h"
#include "client/setup.h"
#include "common.h" #include "common.h"
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_ServerVersion = 0;
m_Priority = -100;
m_Prio = false; m_Prio = false;
m_Abort = false;
m_LastSignalUpdate = 0; m_LastSignalUpdate = 0;
m_LastSignalStrength = -1; m_LastSignalStrength = -1;
m_LastSignalQuality = -1; m_LastSignalQuality = -1;
m_LastDev = -1;
Reset(); Reset();
} }
@@ -40,62 +35,53 @@ cClientSocket::~cClientSocket()
void cClientSocket::Reset(void) void cClientSocket::Reset(void)
{ {
for (int it = 0; it < si_Count; ++it) for (int it = 0; it < si_Count; ++it) {
if (m_DataSockets[it] != NULL)
DELETENULL(m_DataSockets[it]); DELETENULL(m_DataSockets[it]);
m_Priority = -100; }
} }
cTBSocket *cClientSocket::DataSocket(eSocketId Id) const { cTBSocket *cClientSocket::DataSocket(eSocketId Id) const {
return m_DataSockets[Id]; return m_DataSockets[Id];
} }
bool cClientSocket::Command(const std::string &Command, uint Expected) bool cClientSocket::Command(const std::string &Command, uint Expected, uint TimeoutMs)
{ {
uint code = 0; errno = 0;
std::string buffer;
if (Send(Command) && Receive(Command, &code, &buffer)) {
if (code == Expected)
return true;
dsyslog("streamdev-client: Command '%s' rejected by %s:%d: %s",
Command.c_str(), RemoteIp().c_str(), RemotePort(), buffer.c_str());
}
return false;
}
bool cClientSocket::Send(const std::string &Command)
{
std::string pkt = Command + "\015\012"; std::string pkt = Command + "\015\012";
Dprintf("OUT: |%s|\n", Command.c_str()); Dprintf("OUT: |%s|\n", Command.c_str());
errno = 0; cTimeMs starttime;
if (!TimedWrite(pkt.c_str(), pkt.size(), WRITE_TIMEOUT_MS)) { if (!TimedWrite(pkt.c_str(), pkt.size(), TimeoutMs)) {
esyslog("ERROR: streamdev-client: Failed sending command '%s' to %s:%d: %s", esyslog("Streamdev: Lost connection to %s:%d: %s", RemoteIp().c_str(), RemotePort(),
Command.c_str(), RemoteIp().c_str(), RemotePort(), strerror(errno)); strerror(errno));
Close(); Close();
return false; return false;
} }
uint64_t elapsed = starttime.Elapsed();
if (Expected != 0) { // XXX+ What if elapsed > TimeoutMs?
TimeoutMs -= elapsed;
return Expect(Expected, NULL, TimeoutMs);
}
return true; return true;
} }
#define TIMEOUT_MS 1000 bool cClientSocket::Expect(uint Expected, std::string *Result, uint TimeoutMs) {
bool cClientSocket::Receive(const std::string &Command, uint *Code, std::string *Result, uint TimeoutMs) { char *endptr;
int bufcount; int bufcount;
do bool res;
{
errno = 0; errno = 0;
bufcount = ReadUntil(m_Buffer, sizeof(m_Buffer) - 1, "\012", TimeoutMs < TIMEOUT_MS ? TimeoutMs : TIMEOUT_MS);
if (bufcount == -1) { if ((bufcount = ReadUntil(m_Buffer, sizeof(m_Buffer) - 1, "\012", TimeoutMs)) == -1) {
if (m_Abort) esyslog("Streamdev: Lost connection to %s:%d: %s", RemoteIp().c_str(), RemotePort(),
return false; strerror(errno));
if (errno != ETIMEDOUT || TimeoutMs <= TIMEOUT_MS) {
esyslog("ERROR: streamdev-client: Failed reading reply to '%s' from %s:%d: %s",
Command.c_str(), RemoteIp().c_str(), RemotePort(), strerror(errno));
Close(); Close();
return false; return false;
} }
TimeoutMs -= TIMEOUT_MS;
}
} while (bufcount == -1);
if (m_Buffer[bufcount - 1] == '\015') if (m_Buffer[bufcount - 1] == '\015')
--bufcount; --bufcount;
m_Buffer[bufcount] = '\0'; m_Buffer[bufcount] = '\0';
@@ -103,9 +89,9 @@ bool cClientSocket::Receive(const std::string &Command, uint *Code, std::string
if (Result != NULL) if (Result != NULL)
*Result = m_Buffer; *Result = m_Buffer;
if (Code != NULL)
*Code = strtoul(m_Buffer, NULL, 10); res = strtoul(m_Buffer, &endptr, 10) == Expected;
return true; return res;
} }
bool cClientSocket::CheckConnection(void) { bool cClientSocket::CheckConnection(void) {
@@ -114,22 +100,25 @@ bool cClientSocket::CheckConnection(void) {
if (IsOpen()) { if (IsOpen()) {
cTBSelect select; cTBSelect select;
Dprintf("connection open\n");
// XXX+ check if connection is still alive (is there a better way?) // XXX+ check if connection is still alive (is there a better way?)
// There REALLY shouldn't be anything readable according to PROTOCOL here // There REALLY shouldn't be anything readable according to PROTOCOL here
// If there is, assume it's an eof signal (subseq. read would return 0) // If there is, assume it's an eof signal (subseq. read would return 0)
select.Add(*this, false); select.Add(*this, false);
int res; int res;
if ((res = select.Select(0)) == 0) { if ((res = select.Select(0)) == 0) {
Dprintf("select said nothing happened\n");
return true; return true;
} }
Dprintf("closing connection (res was %d)\n", res); Dprintf("closing connection (res was %d)", res);
Close(); Close();
} }
if (!Connect(StreamdevClientSetup.RemoteIp, StreamdevClientSetup.RemotePort, StreamdevClientSetup.Timeout * 1000)){ if (!Connect(StreamdevClientSetup.RemoteIp, StreamdevClientSetup.RemotePort)){
static time_t lastTime = 0; static time_t lastTime = 0;
if (time(NULL) - lastTime > MINLOGREPEAT) { if (time(NULL) - lastTime > MINLOGREPEAT) {
esyslog("ERROR: streamdev-client: Couldn't connect to %s:%d: %s", esyslog("ERROR: Streamdev: Couldn't connect to %s:%d: %s",
(const char*)StreamdevClientSetup.RemoteIp, (const char*)StreamdevClientSetup.RemoteIp,
StreamdevClientSetup.RemotePort, strerror(errno)); StreamdevClientSetup.RemotePort, strerror(errno));
lastTime = time(NULL); lastTime = time(NULL);
@@ -137,25 +126,18 @@ bool cClientSocket::CheckConnection(void) {
return false; return false;
} }
uint code = 0; if (!Expect(220)) {
std::string buffer; if (errno == 0)
if (!Receive("<connect>", &code, &buffer)) { esyslog("ERROR: Streamdev: Didn't receive greeting from %s:%d",
Close(); RemoteIp().c_str(), RemotePort());
return false;
}
if (code != 220) {
esyslog("ERROR: streamdev-client: Didn't receive greeting from %s:%d: %s",
RemoteIp().c_str(), RemotePort(), buffer.c_str());
Close(); Close();
return false; return false;
} }
unsigned int major, minor;
if (sscanf(buffer.c_str(), "%*u VTP/%u.%u", &major, &minor) == 2)
m_ServerVersion = major * 100 + minor;
if (m_ServerVersion == 0) {
if (!Command("CAPS TSPIDS", 220)) { if (!Command("CAPS TSPIDS", 220)) {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't negotiate capabilities on %s:%d",
RemoteIp().c_str(), RemotePort());
Close(); Close();
return false; return false;
} }
@@ -169,19 +151,9 @@ bool cClientSocket::CheckConnection(void) {
Prio = ",PRIO"; Prio = ",PRIO";
m_Prio = true; m_Prio = true;
} }
isyslog("streamdev-client: Connected to server %s:%d using capabilities TSPIDS%s%s",
RemoteIp().c_str(), RemotePort(), Filters, Prio);
}
else {
if(!Command("VERS 1.0", 220)) {
Close();
return false;
}
m_Prio = true;
isyslog("streamdev-client: Connected to server %s:%d using protocol version %u.%u",
RemoteIp().c_str(), RemotePort(), major, minor);
}
isyslog("Streamdev: Connected to server %s:%d using capabilities TSPIDS%s%s",
RemoteIp().c_str(), RemotePort(), Filters, Prio);
return true; return true;
} }
@@ -192,19 +164,17 @@ bool cClientSocket::ProvidesChannel(const cChannel *Channel, int Priority) {
std::string command = (std::string)"PROV " + (const char*)itoa(Priority) + " " std::string command = (std::string)"PROV " + (const char*)itoa(Priority) + " "
+ (const char*)Channel->GetChannelID().ToString(); + (const char*)Channel->GetChannelID().ToString();
if (!Send(command)) if (!Command(command))
return false; return false;
uint code;
std::string buffer; std::string buffer;
if (!Receive(command, &code, &buffer)) if (!Expect(220, &buffer)) {
return false; if (buffer.substr(0, 3) != "560" && errno == 0)
if (code != 220 && code != 560) { esyslog("ERROR: Streamdev: Couldn't check if %s:%d provides channel %s",
esyslog("streamdev-client: Unexpected reply to '%s' from %s:%d: %s", RemoteIp().c_str(), RemotePort(), Channel->Name());
command.c_str(), RemoteIp().c_str(), RemotePort(), buffer.c_str());
return false; return false;
} }
return code == 220; return true;
} }
bool cClientSocket::CreateDataConnection(eSocketId Id) { bool cClientSocket::CreateDataConnection(eSocketId Id) {
@@ -216,7 +186,7 @@ bool cClientSocket::CreateDataConnection(eSocketId Id) {
DELETENULL(m_DataSockets[Id]); DELETENULL(m_DataSockets[Id]);
if (!listen.Listen(LocalIp(), 0, 1)) { if (!listen.Listen(LocalIp(), 0, 1)) {
esyslog("ERROR: streamdev-client: Couldn't create data connection: %s", esyslog("ERROR: Streamdev: Couldn't create data connection: %s",
strerror(errno)); strerror(errno));
return false; return false;
} }
@@ -231,8 +201,13 @@ bool cClientSocket::CreateDataConnection(eSocketId Id) {
CMD_LOCK; CMD_LOCK;
if (!Command(command, 220)) if (!Command(command, 220)) {
Dprintf("error: %m\n");
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't establish data connection to %s:%d",
RemoteIp().c_str(), RemotePort());
return false; return false;
}
/* The server SHOULD do the following: /* The server SHOULD do the following:
* - get PORT command * - get PORT command
@@ -242,7 +217,7 @@ bool cClientSocket::CreateDataConnection(eSocketId Id) {
m_DataSockets[Id] = new cTBSocket; m_DataSockets[Id] = new cTBSocket;
if (!m_DataSockets[Id]->Accept(listen)) { if (!m_DataSockets[Id]->Accept(listen)) {
esyslog("ERROR: streamdev-client: Couldn't establish data connection to %s:%d%s%s", esyslog("ERROR: Streamdev: Couldn't establish data connection to %s:%d%s%s",
RemoteIp().c_str(), RemotePort(), errno == 0 ? "" : ": ", RemoteIp().c_str(), RemotePort(), errno == 0 ? "" : ": ",
errno == 0 ? "" : strerror(errno)); errno == 0 ? "" : strerror(errno));
DELETENULL(m_DataSockets[Id]); DELETENULL(m_DataSockets[Id]);
@@ -253,12 +228,18 @@ bool cClientSocket::CreateDataConnection(eSocketId Id) {
} }
bool cClientSocket::CloseDataConnection(eSocketId Id) { bool cClientSocket::CloseDataConnection(eSocketId Id) {
//if (!CheckConnection()) return false;
CMD_LOCK; CMD_LOCK;
if(Id == siLive || Id == siLiveFilter) if(Id == siLive || Id == siLiveFilter)
if (m_DataSockets[Id] != NULL) { if (m_DataSockets[Id] != NULL) {
std::string command = (std::string)"ABRT " + (const char*)itoa(Id); std::string command = (std::string)"ABRT " + (const char*)itoa(Id);
Command(command, 220); if (!Command(command, 220)) {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't cleanly close data connection");
//return false;
}
DELETENULL(m_DataSockets[Id]); DELETENULL(m_DataSockets[Id]);
} }
return true; return true;
@@ -271,41 +252,41 @@ bool cClientSocket::SetChannelDevice(const cChannel *Channel) {
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)
esyslog("ERROR: Streamdev: Couldn't tune %s:%d to channel %s",
RemoteIp().c_str(), RemotePort(), Channel->Name());
return false; return false;
}
m_LastSignalUpdate = 0; m_LastSignalUpdate = 0;
return true; return true;
} }
bool cClientSocket::SetPriority(int Priority) { bool cClientSocket::SetPriority(int Priority) {
if (Priority == m_Priority)
return true;
if (!CheckConnection()) return false; if (!CheckConnection()) return false;
CMD_LOCK; CMD_LOCK;
std::string command = (std::string)"PRIO " + (const char*)itoa(Priority); std::string command = (std::string)"PRIO " + (const char*)itoa(Priority);
if (!Command(command, 220)) if (!Command(command, 220)) {
if (errno == 0)
esyslog("Streamdev: Failed to update priority on %s:%d", RemoteIp().c_str(),
RemotePort());
return false; return false;
}
m_Priority = Priority;
return true; return true;
} }
bool cClientSocket::GetSignal(int *SignalStrength, int *SignalQuality, int *Dev) { bool cClientSocket::GetSignal(int *SignalStrength, int *SignalQuality) {
if (!CheckConnection()) return -1; if (!CheckConnection()) return -1;
CMD_LOCK; CMD_LOCK;
if (m_LastSignalUpdate != time(NULL)) { if (m_LastSignalUpdate != time(NULL)) {
uint code = 0;
std::string buffer; std::string buffer;
std::string command("SGNL"); if (!Command("SGNL") || !Expect(220, &buffer)
if (!Send(command) || !Receive(command, &code, &buffer) || code != 220 || sscanf(buffer.c_str(), "%*d %*d %d:%d", &m_LastSignalStrength, &m_LastSignalQuality) != 2) {
|| sscanf(buffer.c_str(), "%*d %d %d:%d", &m_LastDev, &m_LastSignalStrength, &m_LastSignalQuality) != 3) {
m_LastDev = -1;
m_LastSignalStrength = -1; m_LastSignalStrength = -1;
m_LastSignalQuality = -1; m_LastSignalQuality = -1;
} }
@@ -315,8 +296,6 @@ bool cClientSocket::GetSignal(int *SignalStrength, int *SignalQuality, int *Dev)
*SignalStrength = m_LastSignalStrength; *SignalStrength = m_LastSignalStrength;
if (SignalQuality) if (SignalQuality)
*SignalQuality = m_LastSignalQuality; *SignalQuality = m_LastSignalQuality;
if (Dev)
*Dev = m_LastDev;
return 0; return 0;
} }
@@ -326,7 +305,13 @@ bool cClientSocket::SetPid(int Pid, bool On) {
CMD_LOCK; CMD_LOCK;
std::string command = (std::string)(On ? "ADDP " : "DELP ") + (const char*)itoa(Pid); std::string command = (std::string)(On ? "ADDP " : "DELP ") + (const char*)itoa(Pid);
return Command(command, 220); if (!Command(command, 220)) {
if (errno == 0)
esyslog("Streamdev: Pid %d not available from %s:%d", Pid, RemoteIp().c_str(),
RemotePort());
return false;
}
return true;
} }
bool cClientSocket::SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On) { bool cClientSocket::SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On) {
@@ -336,7 +321,13 @@ bool cClientSocket::SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On) {
std::string command = (std::string)(On ? "ADDF " : "DELF ") + (const char*)itoa(Pid) std::string command = (std::string)(On ? "ADDF " : "DELF ") + (const char*)itoa(Pid)
+ " " + (const char*)itoa(Tid) + " " + (const char*)itoa(Mask); + " " + (const char*)itoa(Tid) + " " + (const char*)itoa(Mask);
return Command(command, 220); if (!Command(command, 220)) {
if (errno == 0)
esyslog("Streamdev: Filter %hu, %hhu, %hhu not available from %s:%d",
Pid, Tid, Mask, RemoteIp().c_str(), RemotePort());
return false;
}
return true;
} }
bool cClientSocket::CloseDvr(void) { bool cClientSocket::CloseDvr(void) {
@@ -346,20 +337,27 @@ bool cClientSocket::CloseDvr(void) {
if (m_DataSockets[siLive] != NULL) { if (m_DataSockets[siLive] != NULL) {
std::string command = (std::string)"ABRT " + (const char*)itoa(siLive); std::string command = (std::string)"ABRT " + (const char*)itoa(siLive);
if (!Command(command, 220)) if (!Command(command, 220)) {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't cleanly close data connection");
return false; return false;
}
DELETENULL(m_DataSockets[siLive]); DELETENULL(m_DataSockets[siLive]);
} }
return true; return true;
} }
bool cClientSocket::Quit(void) { bool cClientSocket::Quit(void) {
m_Abort = true; bool res;
if (!IsOpen()) return false;
CMD_LOCK; if (!CheckConnection()) return false;
std::string command("QUIT");
bool res = Send(command) && Receive(command, NULL, NULL, QUIT_TIMEOUT_MS); if (!(res = Command("QUIT", 221))) {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't quit command connection to %s:%d",
RemoteIp().c_str(), RemotePort());
}
Close(); Close();
return res; return res;
} }
@@ -369,5 +367,10 @@ bool cClientSocket::SuspendServer(void) {
CMD_LOCK; CMD_LOCK;
return Command("SUSP", 220); if (!Command("SUSP", 220)) {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't suspend server");
return false;
}
return true;
} }

View File

@@ -8,7 +8,6 @@
#include <tools/socket.h> #include <tools/socket.h>
#include "common.h" #include "common.h"
#include "client/setup.h"
#include <string> #include <string>
@@ -21,27 +20,23 @@ 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
unsigned int m_ServerVersion;
bool m_Prio; // server supports command PRIO bool m_Prio; // server supports command PRIO
int m_Priority; // current device priority
bool m_Abort; // quit command pending
time_t m_LastSignalUpdate; time_t m_LastSignalUpdate;
int m_LastSignalStrength; int m_LastSignalStrength;
int m_LastSignalQuality; int m_LastSignalQuality;
int m_LastDev;
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. */ Returns false on failure, setting errno appropriately if it has been
bool Command(const std::string &Command, uint Expected); a system failure. If Expected is zero, returns immediately after
sending the command. */
bool Command(const std::string &Command, uint Expected = 0, uint TimeoutMs = 1500);
/* Send the given command. Returns false on failure. */ /* Fetch results from an ongoing Command called with Expected == 0. Returns
bool Send(const std::string &Command); true if the response has the code Expected, returning an internal buffer
in the array pointer pointed to by Result. Returns false on failure,
/* Fetch results from an ongoing Command. The status code and the setting errno appropriately if it has been a system failure. */
buffer holding the server's response are stored in Code and Result bool Expect(uint Expected, std::string *Result = NULL, uint TimeoutMs = 1500);
if non-NULL. Returns false on failure. */
bool Receive(const std::string &Command, uint *Code = NULL, std::string *Result = NULL, uint TimeoutMs = StreamdevClientSetup.Timeout * 1000);
public: public:
cClientSocket(void); cClientSocket(void);
@@ -55,12 +50,10 @@ public:
bool CloseDataConnection(eSocketId Id); bool CloseDataConnection(eSocketId Id);
bool SetChannelDevice(const cChannel *Channel); bool SetChannelDevice(const cChannel *Channel);
bool SupportsPrio() { return m_Prio; } bool SupportsPrio() { return m_Prio; }
unsigned int ServerVersion() { return m_ServerVersion; }
int Priority() const { return m_Priority; }
bool SetPriority(int Priority); 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, int *Dev); bool GetSignal(int *SignalStrength, int *SignalQuality);
bool CloseDvr(void); bool CloseDvr(void);
bool SuspendServer(void); bool SuspendServer(void);
bool Quit(void); bool Quit(void);
@@ -68,4 +61,6 @@ public:
cTBSocket *DataSocket(eSocketId Id) const; cTBSocket *DataSocket(eSocketId Id) const;
}; };
extern class cClientSocket ClientSocket;
#endif // VDR_STREAMDEV_CLIENT_CONNECTION_H #endif // VDR_STREAMDEV_CLIENT_CONNECTION_H

View File

@@ -16,7 +16,7 @@
const char *cPluginStreamdevClient::DESCRIPTION = trNOOP("VTP Streaming Client"); const char *cPluginStreamdevClient::DESCRIPTION = trNOOP("VTP Streaming Client");
cPluginStreamdevClient::cPluginStreamdevClient(void): m_Devices() { cPluginStreamdevClient::cPluginStreamdevClient(void) {
} }
cPluginStreamdevClient::~cPluginStreamdevClient() { cPluginStreamdevClient::~cPluginStreamdevClient() {
@@ -26,13 +26,9 @@ const char *cPluginStreamdevClient::Description(void) {
return tr(DESCRIPTION); return tr(DESCRIPTION);
} }
bool cPluginStreamdevClient::Initialize(void) { bool cPluginStreamdevClient::Start(void) {
for (int i = 0; i < STREAMDEV_MAXDEVICES; i++) { I18nRegister(PLUGIN_NAME_I18N);
if (m_Devices[i]) cStreamdevDevice::Init();
m_Devices[i]->ReInit(i >= StreamdevClientSetup.StartClient);
else if (i < StreamdevClientSetup.StartClient)
m_Devices[i] = new cStreamdevDevice();
}
return true; return true;
} }
@@ -41,7 +37,7 @@ const char *cPluginStreamdevClient::MainMenuEntry(void) {
} }
cOsdObject *cPluginStreamdevClient::MainMenuAction(void) { cOsdObject *cPluginStreamdevClient::MainMenuAction(void) {
if (StreamdevClientSetup.StartClient && m_Devices[0]->SuspendServer()) if (ClientSocket.SuspendServer())
Skins.Message(mtInfo, tr("Server is suspended")); Skins.Message(mtInfo, tr("Server is suspended"));
else else
Skins.Message(mtError, tr("Couldn't suspend Server!")); Skins.Message(mtError, tr("Couldn't suspend Server!"));
@@ -49,24 +45,15 @@ cOsdObject *cPluginStreamdevClient::MainMenuAction(void) {
} }
cMenuSetupPage *cPluginStreamdevClient::SetupMenu(void) { cMenuSetupPage *cPluginStreamdevClient::SetupMenu(void) {
return new cStreamdevClientMenuSetupPage(this); return new cStreamdevClientMenuSetupPage;
} }
bool cPluginStreamdevClient::SetupParse(const char *Name, const char *Value) { bool cPluginStreamdevClient::SetupParse(const char *Name, const char *Value) {
return StreamdevClientSetup.SetupParse(Name, Value); return StreamdevClientSetup.SetupParse(Name, Value);
} }
bool cPluginStreamdevClient::Service(const char *Id, void *Data) {
if (!strcmp(Id, LOOP_PREVENTION_SERVICE)) {
cStreamdevDevice::DenyChannel((const cChannel*) Data);
return true;
}
return false;
}
void cPluginStreamdevClient::MainThreadHook(void) { void cPluginStreamdevClient::MainThreadHook(void) {
for (int i = 0; i < StreamdevClientSetup.StartClient; i++) cStreamdevDevice::UpdatePriority();
m_Devices[i]->UpdatePriority();
} }
VDRPLUGINCREATOR(cPluginStreamdevClient); // Don't touch this! VDRPLUGINCREATOR(cPluginStreamdevClient); // Don't touch this!

View File

@@ -9,25 +9,20 @@
#include <vdr/plugin.h> #include <vdr/plugin.h>
#define STREAMDEV_MAXDEVICES 8
class cStreamdevDevice;
class cPluginStreamdevClient : public cPlugin { class cPluginStreamdevClient : public cPlugin {
private: private:
static const char *DESCRIPTION; static const char *DESCRIPTION;
cStreamdevDevice *m_Devices[STREAMDEV_MAXDEVICES];
public: public:
cPluginStreamdevClient(void); cPluginStreamdevClient(void);
virtual ~cPluginStreamdevClient(); virtual ~cPluginStreamdevClient();
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 Initialize(void); virtual bool Start(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 bool Service(const char *Id, void *Data = NULL);
virtual void MainThreadHook(void); virtual void MainThreadHook(void);
}; };

View File

@@ -10,7 +10,7 @@
using namespace std; using namespace std;
const char *VERSION = "0.6.1-git"; const char *VERSION = "0.5.1-git";
const char cMenuEditIpItem::IpCharacters[] = "0123456789."; const char cMenuEditIpItem::IpCharacters[] = "0123456789.";

View File

@@ -17,23 +17,20 @@
#include "tools/socket.h" #include "tools/socket.h"
#ifdef DEBUG #ifdef DEBUG
#include <stdio.h> # include <stdio.h>
#include <time.h> # define Dprintf(x...) fprintf(stderr, x)
#define Dprintf(fmt, x...) {\
struct timespec ts;\
clock_gettime(CLOCK_MONOTONIC, &ts);\
fprintf(stderr, "%ld.%.3ld [%d] "fmt,\
ts.tv_sec, ts.tv_nsec / 1000000, cThread::ThreadId(), ##x);\
}
#else #else
#define Dprintf(x...) # define Dprintf(x...)
#endif
#if APIVERSNUM >= 10714
#define TRANSPONDER(c1, c2) (c1->Transponder() == c2->Transponder() && !c1->IsSourceType('V'))
#else
#define TRANSPONDER(c1, c2) (c1->Transponder() == c2->Transponder())
#endif #endif
#define MAXPARSEBUFFER KILOBYTE(16) #define MAXPARSEBUFFER KILOBYTE(16)
/* Service ID for loop prevention */
#define LOOP_PREVENTION_SERVICE "StreamdevLoopPrevention"
/* 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)
@@ -49,6 +46,13 @@ enum eStreamType {
st_Count st_Count
}; };
enum eSuspendMode {
smOffer,
smAlways,
smNever,
sm_Count
};
enum eSocketId { enum eSocketId {
siLive, siLive,
siReplay, siReplay,

View File

@@ -298,6 +298,7 @@ void write_pes(int fd, pes_packet *p){
} }
static unsigned int find_length(int f){ static unsigned int find_length(int f){
uint64_t p = 0;
uint64_t start = 0; uint64_t start = 0;
uint64_t q = 0; uint64_t q = 0;
int found = 0; int found = 0;
@@ -308,7 +309,7 @@ static unsigned int find_length(int f){
start -=2; start -=2;
lseek(f,start,SEEK_SET); lseek(f,start,SEEK_SET);
while ( neof > 0 && !found ){ while ( neof > 0 && !found ){
lseek(f,0,SEEK_CUR); p = lseek(f,0,SEEK_CUR);
neof = save_read(f,&sync4,4); neof = save_read(f,&sync4,4);
if (sync4[0] == 0x00 && sync4[1] == 0x00 && sync4[2] == 0x01) { if (sync4[0] == 0x00 && sync4[1] == 0x00 && sync4[2] == 0x01) {
switch ( sync4[3] ) { switch ( sync4[3] ) {
@@ -557,7 +558,7 @@ int read_pes(int f, pes_packet *p){
while (neof > 0 && !found) { while (neof > 0 && !found) {
po = lseek(f,0,SEEK_CUR); po = lseek(f,0,SEEK_CUR);
if (po == (off_t) -1) return -1; if (po < 0) return -1;
if ((neof = save_read(f,&sync4,4)) < 4) return -1; if ((neof = save_read(f,&sync4,4)) < 4) return -1;
if (sync4[0] == 0x00 && sync4[1] == 0x00 && sync4[2] == 0x01) { if (sync4[0] == 0x00 && sync4[1] == 0x00 && sync4[2] == 0x01) {
p->stream_id = sync4[3]; p->stream_id = sync4[3];
@@ -1333,7 +1334,7 @@ void tfilter(trans *p)
{ {
int l,c; int l,c;
int tpid; int tpid;
uint8_t flags; uint8_t flag,flags;
uint8_t adapt_length = 0; uint8_t adapt_length = 0;
uint8_t cpid[2]; uint8_t cpid[2];
@@ -1349,6 +1350,7 @@ void tfilter(trans *p)
tpid); tpid);
} }
flag = cpid[0];
flags = p->packet[3]; flags = p->packet[3];
if ( flags & ADAPT_FIELD ) { if ( flags & ADAPT_FIELD ) {

View File

@@ -756,6 +756,7 @@ int write_video_pes( Remux *rem, uint8_t *buf, int *vlength)
int pos = 0; int pos = 0;
int p = 0; int p = 0;
uint32_t pts = 0; uint32_t pts = 0;
uint32_t dts = 0;
int stuff = 0; int stuff = 0;
int length = *vlength; int length = *vlength;
long diff = 0; long diff = 0;
@@ -786,6 +787,7 @@ int write_video_pes( Remux *rem, uint8_t *buf, int *vlength)
if (add < 0) return -1; if (add < 0) return -1;
pos += add; pos += add;
rem->vpts_old = rem->vpts; rem->vpts_old = rem->vpts;
dts = rem->vdts;
rem->vpts = rem->vpts_list[0].PTS; rem->vpts = rem->vpts_list[0].PTS;
rem->vdts = rem->vpts_list[0].dts; rem->vdts = rem->vpts_list[0].dts;
if ( diff > 0) rem->SCR += diff; if ( diff > 0) rem->SCR += diff;

View File

@@ -0,0 +1,85 @@
# If you have two or more VDRs and you like them to mutually share
# there DVB cards you might need to apply this patch first.
#
# IMPORTANT: As this patch does not only modify streamdev-server but
# also an exported method of VDR, you will need to
#
# !!!!! RECOMPILE VDR AND ALL PLUGINS !!!!!
#
# Why do I need the patch?
# --------------------------
# Before switching channels VDR will consider all of its devices to
# find the one with the least impact. This includes the device provided
# by the streamdev-client plugin. Streamdev-client will forward the
# request to its server which in turn checks all of its devices. Now if
# the server is running streamdev-client, too, the request will again
# be forwarded to its server and finally you will endup in a loop.
#
# What does the patch do?
# -----------------------
# The patch adds the additional parameter "bool DVBCardsOnly" to VDR's
# device selection method cDevice::GetDevice(...). The parameter
# defaults to false which gives you the standard behaviour of GetDevice.
# When set to true, GetDevice will use only those devices with a card
# index < MAXDVBDEVICES, so only real DVB cards will be considered.
# Other devices like streamdev-client or DVB cards provided by plugin
# (Hauppauge PVR) won't be used.
#
# Author: Frank Schmirler (http://vdr.schmirler.de)
#
--- device.h.orig 2006-11-15 12:01:34.000000000 +0100
+++ device.h 2006-11-15 12:02:15.000000000 +0100
@@ -128,7 +128,7 @@
///< Gets the device with the given Index.
///< \param Index must be in the range 0..numDevices-1.
///< \return A pointer to the device, or NULL if the Index was invalid.
- static cDevice *GetDevice(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL);
+ static cDevice *GetDevice(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL, bool DVBCardsOnly = false);
///< Returns a device that is able to receive the given Channel at the
///< given Priority, with the least impact on active recordings and
///< live viewing.
--- device.c.orig 2006-11-15 12:01:30.000000000 +0100
+++ device.c 2006-11-22 12:28:05.000000000 +0100
@@ -8,6 +8,7 @@
*/
#include "device.h"
+#include "dvbdevice.h"
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
@@ -278,11 +279,13 @@
return (0 <= Index && Index < numDevices) ? device[Index] : NULL;
}
-cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers)
+cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers, bool DVBCardsOnly)
{
cDevice *d = NULL;
uint Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
for (int i = 0; i < numDevices; i++) {
+ if (DVBCardsOnly && device[i]->CardIndex() >= MAXDVBDEVICES)
+ continue;
bool ndr;
if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
// Put together an integer number that reflects the "impact" using
--- PLUGINS/src/streamdev/server/connection.c.orig 2006-11-15 12:10:11.000000000 +0100
+++ PLUGINS/src/streamdev/server/connection.c 2006-11-15 12:10:59.000000000 +0100
@@ -132,7 +132,7 @@
Dprintf(" * GetDevice(const cChannel*, int)\n");
Dprintf(" * -------------------------------\n");
- device = cDevice::GetDevice(Channel, Priority);
+ device = cDevice::GetDevice(Channel, Priority, NULL, true);
Dprintf(" * Found following device: %p (%d)\n", device,
device ? device->CardIndex() + 1 : 0);
@@ -150,7 +150,7 @@
const cChannel *current = Channels.GetByNumber(cDevice::CurrentChannel());
isyslog("streamdev-server: Detaching current receiver");
Detach();
- device = cDevice::GetDevice(Channel, Priority);
+ device = cDevice::GetDevice(Channel, Priority, NULL, true);
Attach();
Dprintf(" * Found following device: %p (%d)\n", device,
device ? device->CardIndex() + 1 : 0);

View File

@@ -0,0 +1,88 @@
# If you have two or more VDRs and you like them to mutually share
# there DVB cards you might need to apply this patch first.
#
# This is a modified version of the patch for VDRs with BIGPATCH.
# Thanks to p_body@vdrportal.
#
# IMPORTANT: As this patch does not only modify streamdev-server but
# also an exported method of VDR, you will need to
#
# !!!!! RECOMPILE VDR AND ALL PLUGINS !!!!!
#
# Why do I need the patch?
# --------------------------
# Before switching channels VDR will consider all of its devices to
# find the one with the least impact. This includes the device provided
# by the streamdev-client plugin. Streamdev-client will forward the
# request to its server which in turn checks all of its devices. Now if
# the server is running streamdev-client, too, the request will again
# be forwarded to its server and finally you will endup in a loop.
#
# What does the patch do?
# -----------------------
# The patch adds the additional parameter "bool DVBCardsOnly" to VDR's
# device selection method cDevice::GetDevice(...). The parameter
# defaults to false which gives you the standard behaviour of GetDevice.
# When set to true, GetDevice will use only those devices with a card
# index < MAXDVBDEVICES, so only real DVB cards will be considered.
# Other devices like streamdev-client or DVB cards provided by plugin
# (Hauppauge PVR) won't be used.
#
# Author: Frank Schmirler (http://vdr.schmirler.de)
#
--- device.h.orig 2006-11-15 12:01:34.000000000 +0100
+++ device.h 2006-11-15 12:02:15.000000000 +0100
@@ -128,7 +128,7 @@
///< Gets the device with the given Index.
///< \param Index must be in the range 0..numDevices-1.
///< \return A pointer to the device, or NULL if the Index was invalid.
- static cDevice *GetDevice(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL, bool LiveView = false);
+ static cDevice *GetDevice(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL, bool LiveView = false, bool DVBCardsOnly = false);
///< Returns a device that is able to receive the given Channel at the
///< given Priority, with the least impact on active recordings and
///< live viewing.
--- device.c.orig 2006-11-15 12:01:30.000000000 +0100
+++ device.c 2006-11-22 12:28:05.000000000 +0100
@@ -8,6 +8,7 @@
*/
#include "device.h"
+#include "dvbdevice.h"
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
@@ -278,11 +279,13 @@
return (0 <= Index && Index < numDevices) ? device[Index] : NULL;
}
-cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers, bool LiveView)
+cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers, bool LiveView, bool DVBCardsOnly)
{
cDevice *d = NULL;
uint Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
for (int i = 0; i < numDevices; i++) {
+ if (DVBCardsOnly && device[i]->CardIndex() >= MAXDVBDEVICES)
+ continue;
bool ndr;
if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
// Put together an integer number that reflects the "impact" using
--- PLUGINS/src/streamdev/server/connection.c.orig 2006-11-15 12:10:11.000000000 +0100
+++ PLUGINS/src/streamdev/server/connection.c 2006-11-15 12:10:59.000000000 +0100
@@ -132,7 +132,7 @@
Dprintf(" * GetDevice(const cChannel*, int)\n");
Dprintf(" * -------------------------------\n");
- device = cDevice::GetDevice(Channel, Priority);
+ device = cDevice::GetDevice(Channel, Priority, NULL, NULL, true);
Dprintf(" * Found following device: %p (%d)\n", device,
device ? device->CardIndex() + 1 : 0);
@@ -150,7 +150,7 @@
const cChannel *current = Channels.GetByNumber(cDevice::CurrentChannel());
isyslog("streamdev-server: Detaching current receiver");
Detach();
- device = cDevice::GetDevice(Channel, Priority);
+ device = cDevice::GetDevice(Channel, Priority, NULL, NULL, true);
Attach();
Dprintf(" * Found following device: %p (%d)\n", device,
device ? device->CardIndex() + 1 : 0);

View File

@@ -0,0 +1,102 @@
# Apply this patch to VDR if you want to use a fullfeatured DVB card
# as pure output device. Infact the patch will keep VDR from using the
# tuner of any local DVB card (also budget cards). It will not affect
# other input devices like e.g. streamdev-client or DVB cards provided
# by plugins (e.g. Hauppauge PVR).
#
# By default the patch is DISABLED. There will be a new OSD menu entry
# in Setup->DVB which allows you to enable or disable the patch at any
# time.
diff -ru vdr-1.4.3.orig/config.c vdr-1.4.3/config.c
--- vdr-1.4.3.orig/config.c 2006-07-22 13:57:51.000000000 +0200
+++ vdr-1.4.3/config.c 2006-11-16 08:16:37.000000000 +0100
@@ -273,6 +273,7 @@
CurrentChannel = -1;
CurrentVolume = MAXVOLUME;
CurrentDolby = 0;
+ LocalChannelProvide = 1;
InitialChannel = 0;
InitialVolume = -1;
}
@@ -434,6 +435,7 @@
else if (!strcasecmp(Name, "CurrentChannel")) CurrentChannel = atoi(Value);
else if (!strcasecmp(Name, "CurrentVolume")) CurrentVolume = atoi(Value);
else if (!strcasecmp(Name, "CurrentDolby")) CurrentDolby = atoi(Value);
+ else if (!strcasecmp(Name, "LocalChannelProvide")) LocalChannelProvide = atoi(Value);
else if (!strcasecmp(Name, "InitialChannel")) InitialChannel = atoi(Value);
else if (!strcasecmp(Name, "InitialVolume")) InitialVolume = atoi(Value);
else
@@ -502,6 +504,7 @@
Store("CurrentChannel", CurrentChannel);
Store("CurrentVolume", CurrentVolume);
Store("CurrentDolby", CurrentDolby);
+ Store("LocalChannelProvide",LocalChannelProvide);
Store("InitialChannel", InitialChannel);
Store("InitialVolume", InitialVolume);
diff -ru vdr-1.4.3.orig/config.h vdr-1.4.3/config.h
--- vdr-1.4.3.orig/config.h 2006-09-23 15:56:08.000000000 +0200
+++ vdr-1.4.3/config.h 2006-11-16 08:16:57.000000000 +0100
@@ -250,6 +250,7 @@
int CurrentChannel;
int CurrentVolume;
int CurrentDolby;
+ int LocalChannelProvide;
int InitialChannel;
int InitialVolume;
int __EndData__;
diff -ru vdr-1.4.3.orig/dvbdevice.c vdr-1.4.3/dvbdevice.c
--- vdr-1.4.3.orig/dvbdevice.c 2006-08-14 11:38:32.000000000 +0200
+++ vdr-1.4.3/dvbdevice.c 2006-11-16 08:17:58.000000000 +0100
@@ -766,6 +766,8 @@
bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
{
+ if (Setup.LocalChannelProvide != 1)
+ return false;
bool result = false;
bool hasPriority = Priority < 0 || Priority > this->Priority();
bool needsDetachReceivers = false;
diff -ru vdr-1.4.3.orig/i18n.c vdr-1.4.3/i18n.c
--- vdr-1.4.3.orig/i18n.c 2006-09-16 11:08:30.000000000 +0200
+++ vdr-1.4.3/i18n.c 2006-11-16 08:36:53.000000000 +0100
@@ -3546,6 +3546,28 @@
"Foretrukket sprog",
"Preferovaný jazyk",
},
+ { "Setup.DVB$Use DVB receivers",
+ "DVB Empfangsteile benutzen",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
{ "Setup.DVB$Primary DVB interface",
"Primäres DVB-Interface",
"Primarna naprava",
diff -ru vdr-1.4.3.orig/menu.c vdr-1.4.3/menu.c
--- vdr-1.4.3.orig/menu.c 2006-07-23 11:23:11.000000000 +0200
+++ vdr-1.4.3/menu.c 2006-11-16 08:37:27.000000000 +0100
@@ -2354,6 +2354,7 @@
Clear();
+ Add(new cMenuEditBoolItem(tr("Setup.DVB$Use DVB receivers"), &data.LocalChannelProvide));
Add(new cMenuEditIntItem( tr("Setup.DVB$Primary DVB interface"), &data.PrimaryDVB, 1, cDevice::NumDevices()));
Add(new cMenuEditBoolItem(tr("Setup.DVB$Video format"), &data.VideoFormat, "4:3", "16:9"));
if (data.VideoFormat == 0)

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;

View File

@@ -3,7 +3,6 @@
#include "server/connection.h" #include "server/connection.h"
#include "server/streamer.h" #include "server/streamer.h"
#include <vdr/channels.h> #include <vdr/channels.h>
#include <vdr/remux.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>
@@ -26,7 +25,7 @@ protected:
virtual void Action(void); virtual void Action(void);
public: public:
cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connection, const cChannel *Channel, const cPatPmtParser *PatPmt, const int *Apids, const int *Dpids); 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);
@@ -35,7 +34,7 @@ public:
} // namespace Streamdev } // namespace Streamdev
using namespace Streamdev; using namespace Streamdev;
cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connection, const cChannel *Channel, const cPatPmtParser *PatPmt, const int *Apids, const int *Dpids): 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(-1), m_Process(-1),
@@ -74,24 +73,17 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
#define ADDENV(x...) if (asprintf(&env[i++], x) < 0) i-- #define ADDENV(x...) if (asprintf(&env[i++], x) < 0) i--
// add channel ID, name and pids to environment // add channel ID, name and pids to environment
if (Channel) {
ADDENV("REMUX_CHANNEL_ID=%s", *Channel->GetChannelID().ToString()); ADDENV("REMUX_CHANNEL_ID=%s", *Channel->GetChannelID().ToString());
ADDENV("REMUX_CHANNEL_NAME=%s", Channel->Name()); ADDENV("REMUX_CHANNEL_NAME=%s", Channel->Name());
#if APIVERSNUM >= 10701
ADDENV("REMUX_VTYPE=%d", Channel->Vtype()); ADDENV("REMUX_VTYPE=%d", Channel->Vtype());
#endif
if (Channel->Vpid()) if (Channel->Vpid())
ADDENV("REMUX_VPID=%d", Channel->Vpid()); ADDENV("REMUX_VPID=%d", Channel->Vpid());
if (Channel->Ppid() != Channel->Vpid()) if (Channel->Ppid() != Channel->Vpid())
ADDENV("REMUX_PPID=%d", Channel->Ppid()); ADDENV("REMUX_PPID=%d", Channel->Ppid());
if (Channel->Tpid()) if (Channel->Tpid())
ADDENV("REMUX_TPID=%d", Channel->Tpid()); ADDENV("REMUX_TPID=%d", Channel->Tpid());
}
else if (PatPmt) {
ADDENV("REMUX_VTYPE=%d", PatPmt->Vtype());
if (PatPmt->Vpid())
ADDENV("REMUX_VPID=%d", PatPmt->Vpid());
if (PatPmt->Ppid() != PatPmt->Vpid())
ADDENV("REMUX_PPID=%d", PatPmt->Ppid());
}
std::string buffer; std::string buffer;
if (Apids && *Apids) { if (Apids && *Apids) {
@@ -102,17 +94,10 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
buffer.clear(); buffer.clear();
for (const int *pid = Apids; *pid; pid++) { for (const int *pid = Apids; *pid; pid++) {
int j; int j;
if (Channel) {
for (j = 0; Channel->Apid(j) && Channel->Apid(j) != *pid; j++) for (j = 0; Channel->Apid(j) && Channel->Apid(j) != *pid; j++)
; ;
(buffer += Channel->Alang(j)) += (*(pid + 1) ? " " : ""); (buffer += Channel->Alang(j)) += (*(pid + 1) ? " " : "");
} }
else if (PatPmt) {
for (j = 0; PatPmt->Apid(j) && PatPmt->Apid(j) != *pid; j++)
;
(buffer += PatPmt->Alang(j)) += (*(pid + 1) ? " " : "");
}
}
ADDENV("REMUX_ALANG=%s", buffer.c_str()); ADDENV("REMUX_ALANG=%s", buffer.c_str());
} }
@@ -125,21 +110,14 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
buffer.clear(); buffer.clear();
for (const int *pid = Dpids; *pid; pid++) { for (const int *pid = Dpids; *pid; pid++) {
int j; int j;
if (Channel) {
for (j = 0; Channel->Dpid(j) && Channel->Dpid(j) != *pid; j++) for (j = 0; Channel->Dpid(j) && Channel->Dpid(j) != *pid; j++)
; ;
(buffer += Channel->Dlang(j)) += (*(pid + 1) ? " " : ""); (buffer += Channel->Dlang(j)) += (*(pid + 1) ? " " : "");
} }
else if (PatPmt) {
for (j = 0; PatPmt->Dpid(j) && PatPmt->Dpid(j) != *pid; j++)
;
(buffer += PatPmt->Dlang(j)) += (*(pid + 1) ? " " : "");
}
}
ADDENV("REMUX_DLANG=%s", buffer.c_str()); ADDENV("REMUX_DLANG=%s", buffer.c_str());
} }
if (Channel && Channel->Spid(0)) { if (Channel->Spid(0)) {
buffer.clear(); buffer.clear();
for (const int *pid = Channel->Spids(); *pid; pid++) for (const int *pid = Channel->Spids(); *pid; pid++)
(buffer += (const char *) itoa(*pid)) += (*(pid + 1) ? " " : ""); (buffer += (const char *) itoa(*pid)) += (*(pid + 1) ? " " : "");
@@ -150,17 +128,6 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
(buffer += Channel->Slang(j)) += (Channel->Spid(j + 1) ? " " : ""); (buffer += Channel->Slang(j)) += (Channel->Spid(j + 1) ? " " : "");
ADDENV("REMUX_SLANG=%s", buffer.c_str()); ADDENV("REMUX_SLANG=%s", buffer.c_str());
} }
else if (PatPmt && PatPmt->Spid(0)) {
buffer.clear();
for (const int *pid = PatPmt->Spids(); *pid; pid++)
(buffer += (const char *) itoa(*pid)) += (*(pid + 1) ? " " : "");
ADDENV("REMUX_SPID=%s", buffer.c_str());
buffer.clear();
for (int j = 0; PatPmt->Spid(j); j++)
(buffer += PatPmt->Slang(j)) += (PatPmt->Spid(j + 1) ? " " : "");
ADDENV("REMUX_SLANG=%s", buffer.c_str());
}
if (Connection) { if (Connection) {
// add vars for a CGI like interface // add vars for a CGI like interface
@@ -303,7 +270,7 @@ void cTSExt::Action(void)
dsyslog("streamdev-server: buffer full while reading from externremux"); dsyslog("streamdev-server: buffer full while reading from externremux");
if (result == -1) { if (result == -1) {
if (errno != EINTR && errno != EAGAIN) { if (errno != EINTR) {
LOG_ERROR_STR("read failed"); LOG_ERROR_STR("read failed");
m_Active = false; m_Active = false;
} }
@@ -330,14 +297,8 @@ void cTSExt::Put(const uchar *Data, int Count)
} }
cExternRemux::cExternRemux(const cServerConnection *Connection, const cChannel *Channel, const int *Apids, const int *Dpids): cExternRemux::cExternRemux(const cServerConnection *Connection, const cChannel *Channel, const int *Apids, const int *Dpids):
m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE)), m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE, TS_SIZE * 2)),
m_Remux(new cTSExt(m_ResultBuffer, Connection, Channel, NULL, Apids, Dpids)) m_Remux(new cTSExt(m_ResultBuffer, Connection, Channel, Apids, Dpids))
{
m_ResultBuffer->SetTimeouts(500, 100);
}
cExternRemux::cExternRemux(const cServerConnection *Connection, const cPatPmtParser *PatPmt, const int *Apids, const int *Dpids):
m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE)),
m_Remux(new cTSExt(m_ResultBuffer, Connection, NULL, PatPmt, Apids, Dpids))
{ {
m_ResultBuffer->SetTimeouts(500, 100); m_ResultBuffer->SetTimeouts(500, 100);
} }

View File

@@ -6,7 +6,6 @@
#include <string> #include <string>
class cChannel; class cChannel;
class cPatPmtParser;
class cServerConnection; class cServerConnection;
namespace Streamdev { namespace Streamdev {
@@ -20,7 +19,6 @@ private:
public: public:
cExternRemux(const cServerConnection *Connection, const cChannel *Channel, const int *APids, const int *Dpids); cExternRemux(const cServerConnection *Connection, const cChannel *Channel, const int *APids, const int *Dpids);
cExternRemux(const cServerConnection *Connection, const cPatPmtParser *PatPmt, const int *APids, const int *Dpids);
virtual ~cExternRemux(); virtual ~cExternRemux();
int Put(const uchar *Data, int Count); int Put(const uchar *Data, int Count);

View File

@@ -1,5 +1,3 @@
#ifdef STREAMDEV_PS
#include "remux/ts2ps.h" #include "remux/ts2ps.h"
#include "server/streamer.h" #include "server/streamer.h"
#include <vdr/channels.h> #include <vdr/channels.h>
@@ -218,4 +216,3 @@ uchar *cTS2PSRemux::Get(int &Count)
return resultData; return resultData;
} }
#endif

View File

@@ -1,5 +1,3 @@
#ifdef STREAMDEV_PS
#ifndef VDR_STREAMDEV_TS2PSREMUX_H #ifndef VDR_STREAMDEV_TS2PSREMUX_H
#define VDR_STREAMDEV_TS2PSREMUX_H #define VDR_STREAMDEV_TS2PSREMUX_H
@@ -36,5 +34,3 @@ public:
} // namespace Streamdev } // namespace Streamdev
#endif // VDR_STREAMDEV_TS2PSREMUX_H #endif // VDR_STREAMDEV_TS2PSREMUX_H
#endif

View File

@@ -1,18 +1,14 @@
# #
# Makefile for a Video Disk Recorder plugin # Makefile for a Video Disk Recorder plugin
# #
# $Id: $ # $Id: Makefile,v 1.2 2010/07/19 13:49:31 schmirl Exp $
# The official name of this plugin. # The official name of this plugin.
# This name will be used in the '-P...' option of VDR to load the plugin. # This name will be used in the '-P...' option of VDR to load the plugin.
# By default the main source file also carries this name. # By default the main source file also carries this name.
#
PLUGIN = streamdev-server PLUGIN = streamdev-server
### The name of the shared object file:
SOFILE = libvdr-$(PLUGIN).so
### Includes and Defines (add further entries here): ### Includes and Defines (add further entries here):
DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
@@ -26,12 +22,13 @@ SERVEROBJS = $(PLUGIN).o \
componentVTP.o connectionVTP.o \ componentVTP.o connectionVTP.o \
componentHTTP.o connectionHTTP.o menuHTTP.o \ componentHTTP.o connectionHTTP.o menuHTTP.o \
componentIGMP.o connectionIGMP.o \ componentIGMP.o connectionIGMP.o \
streamer.o livestreamer.o livefilter.o recstreamer.o recplayer.o \ streamer.o livestreamer.o livefilter.o recplayer.o \
menu.o suspend.o setup.o menu.o suspend.o setup.o
### The main target: ### The main target:
all: $(SOFILE) i18n .PHONY: all i18n clean
all: libvdr-$(PLUGIN).so i18n
### Implicit rules: ### Implicit rules:
@@ -42,48 +39,44 @@ all: $(SOFILE) i18n
MAKEDEP = $(CXX) -MM -MG MAKEDEP = $(CXX) -MM -MG
DEPFILE = .dependencies DEPFILE = .dependencies
$(DEPFILE): Makefile $(DEPFILE): Makefile
@$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(SERVEROBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) > $@ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(SERVEROBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) > $@
-include $(DEPFILE) -include $(DEPFILE)
### Internationalization (I18N): ### Internationalization (I18N):
PODIR = po PODIR = po
LOCALEDIR = $(VDRDIR)/locale
I18Npo = $(wildcard $(PODIR)/*.po) I18Npo = $(wildcard $(PODIR)/*.po)
I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file)))) I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
I18Npot = $(PODIR)/$(PLUGIN).pot I18Npot = $(PODIR)/$(PLUGIN).pot
%.mo: %.po %.mo: %.po
msgfmt -c -o $@ $< msgfmt -c -o $@ $<
$(I18Npot): $(SERVEROBJS:%.o=%.c) $(I18Npot): $(SERVEROBJS:%.o=%.c)
xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='<vdrdev@schmirler.de>' -o $@ `ls $^` xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --msgid-bugs-address='<http://www.vdr-developer.org/mantisbt/>' -o $@ $^
%.po: $(I18Npot) %.po: $(I18Npot)
msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $< msgmerge -U --no-wrap --no-location --backup=none -q $@ $<
@touch $@ @touch $@
$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo $(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
install -D -m644 $< $@ @mkdir -p $(dir $@)
cp $< $@
.PHONY: i18n i18n: $(I18Nmsgs)
i18n: $(I18Nmo) $(I18Npot)
install-i18n: $(I18Nmsgs)
### Targets: ### Targets:
$(SOFILE): $(SERVEROBJS) $(COMMONOBJS) \ libvdr-$(PLUGIN).so: $(SERVEROBJS) $(COMMONOBJS) \
../tools/sockettools.a ../remux/remux.a ../libdvbmpeg/libdvbmpegtools.a ../tools/sockettools.a ../remux/remux.a ../libdvbmpeg/libdvbmpegtools.a
$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $^ -o $@
install-lib: $(SOFILE) %.so:
install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION) $(CXX) $(CXXFLAGS) -shared $^ -o $@
@cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION)
install: install-lib install-i18n
clean: clean:
@-rm -f $(PODIR)/*.mo $(PODIR)/*.pot @-rm -f $(COMMONOBJS) $(SERVEROBJS) $(DEPFILE) $(PODIR)/*.mo $(PODIR)/*.pot *.so *.tgz core* *~
@-rm -f $(COMMONOBJS) $(SERVEROBJS) $(DEPFILE) *.so *.tgz core* *~

View File

@@ -1,82 +0,0 @@
#
# 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 recstreamer.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

@@ -105,11 +105,7 @@ cComponentIGMP::~cComponentIGMP(void)
{ {
} }
#if APIVERSNUM >= 20300
cMulticastGroup* cComponentIGMP::FindGroup(in_addr_t Group)
#else
cMulticastGroup* cComponentIGMP::FindGroup(in_addr_t Group) const cMulticastGroup* cComponentIGMP::FindGroup(in_addr_t Group) const
#endif
{ {
cMulticastGroup *group = m_Groups.First(); cMulticastGroup *group = m_Groups.First();
while (group && group->group != Group) while (group && group->group != Group)
@@ -121,12 +117,7 @@ bool cComponentIGMP::Initialize(void)
{ {
if (cServerComponent::Initialize() && IGMPMembership(IGMP_ALL_ROUTER)) if (cServerComponent::Initialize() && IGMPMembership(IGMP_ALL_ROUTER))
{ {
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
for (const cChannel *channel = Channels->First(); channel; channel = Channels->Next(channel))
#else
for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
#endif
{ {
if (channel->GroupSep()) if (channel->GroupSep())
continue; continue;
@@ -155,12 +146,7 @@ void cComponentIGMP::Destruct(void)
Cancel(-1); Cancel(-1);
m_CondWait.Signal(); m_CondWait.Signal();
Cancel(2); Cancel(2);
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
for (const cChannel *channel = Channels->First(); channel; channel = Channels->Next(channel))
#else
for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
#endif
{ {
if (channel->GroupSep()) if (channel->GroupSep())
continue; continue;
@@ -446,18 +432,9 @@ cServerConnection* cComponentIGMP::IGMPStartMulticast(cMulticastGroup* Group)
in_addr_t g = ntohl(Group->group); in_addr_t g = ntohl(Group->group);
if (g > MULTICAST_PRIV_MIN && g <= MULTICAST_PRIV_MAX) { if (g > MULTICAST_PRIV_MIN && g <= MULTICAST_PRIV_MAX) {
cThreadLock lock; cThreadLock lock;
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
const cChannel *channel = Channels->GetByNumber(g - MULTICAST_PRIV_MIN);
#else
cChannel *channel = Channels.GetByNumber(g - MULTICAST_PRIV_MIN); cChannel *channel = Channels.GetByNumber(g - MULTICAST_PRIV_MIN);
#endif
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock); const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
#if APIVERSNUM >= 20300
const cServerConnection *s = clients.First();
#else
cServerConnection *s = clients.First(); cServerConnection *s = clients.First();
#endif
while (s) { while (s) {
if (s->RemoteIpAddr() == Group->group) if (s->RemoteIpAddr() == Group->group)
break; break;
@@ -476,11 +453,7 @@ cServerConnection* cComponentIGMP::IGMPStartMulticast(cMulticastGroup* Group)
void cComponentIGMP::IGMPStopMulticast(cMulticastGroup* Group) void cComponentIGMP::IGMPStopMulticast(cMulticastGroup* Group)
{ {
cThreadLock lock; cThreadLock lock;
#if APIVERSNUM >= 20300
cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
#else
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock); const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
#endif
for (cServerConnection *s = clients.First(); s; s = clients.Next(s)) { for (cServerConnection *s = clients.First(); s; s = clients.Next(s)) {
if (s->RemoteIpAddr() == Group->group) if (s->RemoteIpAddr() == Group->group)
s->Close(); s->Close();

View File

@@ -9,7 +9,6 @@
#include <time.h> #include <time.h>
#include <vdr/thread.h> #include <vdr/thread.h>
#include "server/component.h" #include "server/component.h"
#include "../common.h"
class cMulticastGroup; class cMulticastGroup;
@@ -24,11 +23,7 @@ private:
bool m_Querier; bool m_Querier;
cCondWait m_CondWait; cCondWait m_CondWait;
#if APIVERSNUM >= 20300
cMulticastGroup* FindGroup(in_addr_t Group);
#else
cMulticastGroup* FindGroup(in_addr_t Group) const; cMulticastGroup* FindGroup(in_addr_t Group) const;
#endif
/* Add or remove local host to multicast group */ /* Add or remove local host to multicast group */
bool IGMPMembership(in_addr_t Group, bool Add = true); bool IGMPMembership(in_addr_t Group, bool Add = true);

View File

@@ -9,10 +9,59 @@
#include <vdr/tools.h> #include <vdr/tools.h>
#include <vdr/thread.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>
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): cServerConnection::cServerConnection(const char *Protocol, int Type):
cTBSocket(Type), cTBSocket(Type),
m_Protocol(Protocol), m_Protocol(Protocol),
@@ -20,14 +69,14 @@ cServerConnection::cServerConnection(const char *Protocol, int Type):
m_Pending(false), m_Pending(false),
m_ReadBytes(0), m_ReadBytes(0),
m_WriteBytes(0), m_WriteBytes(0),
m_WriteIndex(0), m_WriteIndex(0)
m_Streamer(NULL)
{ {
m_SwitchLive = new cSwitchLive();
} }
cServerConnection::~cServerConnection() cServerConnection::~cServerConnection()
{ {
delete(m_Streamer); delete m_SwitchLive;
} }
const cChannel* cServerConnection::ChannelFromString(const char *String, int *Apid, int *Dpid) { const cChannel* cServerConnection::ChannelFromString(const char *String, int *Apid, int *Dpid) {
@@ -44,31 +93,14 @@ const cChannel* cServerConnection::ChannelFromString(const char *String, int *Ap
if (isnumber(string)) { if (isnumber(string)) {
int temp = strtol(String, NULL, 10); int temp = strtol(String, NULL, 10);
if (temp == 0)
temp = cDevice::CurrentChannel();
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
if (temp >= 1 && temp <= Channels->MaxNumber())
channel = Channels->GetByNumber(temp);
#else
if (temp >= 1 && temp <= Channels.MaxNumber()) if (temp >= 1 && temp <= Channels.MaxNumber())
channel = Channels.GetByNumber(temp); channel = Channels.GetByNumber(temp);
#endif
} else { } else {
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
channel = Channels->GetByChannelID(tChannelID::FromString(string));
#else
channel = Channels.GetByChannelID(tChannelID::FromString(string)); channel = Channels.GetByChannelID(tChannelID::FromString(string));
#endif
if (channel == NULL) { if (channel == NULL) {
int i = 1; int i = 1;
#if APIVERSNUM >= 20300
while ((channel = Channels->GetByNumber(i, 1)) != NULL) {
#else
while ((channel = Channels.GetByNumber(i, 1)) != NULL) { while ((channel = Channels.GetByNumber(i, 1)) != NULL) {
#endif
if (String == channel->Name()) if (String == channel->Name())
break; break;
@@ -212,7 +244,176 @@ bool cServerConnection::Close()
return cTBSocket::Close(); return cTBSocket::Close();
} }
cString cServerConnection::ToText(char Delimiter) const #if APIVERSNUM >= 10700
static int GetClippedNumProvidedSystems(int AvailableBits, cDevice *Device)
{ {
return cString::sprintf("%s%c%s:%d", Protocol(), Delimiter, RemoteIp().c_str(), RemotePort()); 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)
{
// 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);
if (device && !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
device = m_SwitchLive->Switch(device, Channel);
}
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;
}
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

@@ -7,15 +7,15 @@
#include "tools/socket.h" #include "tools/socket.h"
#include "common.h" #include "common.h"
#include "server/streamer.h"
#include <map> #include <map>
#include <string>
typedef std::map<std::string,std::string> tStrStrMap; typedef std::map<std::string,std::string> tStrStrMap;
typedef std::pair<std::string,std::string> tStrStr; typedef std::pair<std::string,std::string> tStrStr;
class cChannel; class cChannel;
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 */
@@ -34,10 +34,19 @@ private:
uint m_WriteBytes; uint m_WriteBytes;
uint m_WriteIndex; uint m_WriteIndex;
cStreamdevStreamer *m_Streamer; cSwitchLive *m_SwitchLive;
tStrStrMap m_Headers; 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 */
@@ -54,12 +63,6 @@ protected:
/* Add a request header */ /* Add a request header */
void SetHeader(const char *Name, const char *Value, const char *Prefix = "") { m_Headers.insert(tStrStr(std::string(Prefix) + Name, Value)); } void SetHeader(const char *Name, const char *Value, const char *Prefix = "") { m_Headers.insert(tStrStr(std::string(Prefix) + Name, Value)); }
/* Set the streamer */
void SetStreamer(cStreamdevStreamer* Streamer) { delete m_Streamer; m_Streamer = Streamer; }
/* Return the streamer */
cStreamdevStreamer *Streamer() const { return m_Streamer; }
static const cChannel *ChannelFromString(const char *String, int *Apid = NULL, int *Dpid = NULL); static const cChannel *ChannelFromString(const char *String, int *Apid = NULL, int *Dpid = NULL);
public: public:
@@ -103,13 +106,27 @@ public:
/* Close the socket */ /* Close the socket */
virtual bool Close(void); 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 *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 Attach(void) = 0;
/* This connections protocol name */ /* This connections protocol name */
virtual const char* Protocol(void) const { return m_Protocol; } virtual const char* Protocol(void) const { return m_Protocol; }
/* Text description of stream */ /* Representation in menu */
virtual cString ToText(char Delimiter = ' ') const; virtual cString ToText(void) const;
/* std::map with additional information */ /* std::map with additional information */
const tStrStrMap& Headers(void) const { return m_Headers; } const tStrStrMap& Headers(void) const { return m_Headers; }

View File

@@ -3,15 +3,6 @@
*/ */
#include <ctype.h> #include <ctype.h>
#include <time.h>
#include <stdarg.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <vdr/thread.h>
#include <vdr/recording.h>
#include "server/connectionHTTP.h" #include "server/connectionHTTP.h"
#include "server/menuHTTP.h" #include "server/menuHTTP.h"
@@ -21,12 +12,10 @@
cConnectionHTTP::cConnectionHTTP(void): cConnectionHTTP::cConnectionHTTP(void):
cServerConnection("HTTP"), cServerConnection("HTTP"),
m_Status(hsRequest), m_Status(hsRequest),
m_StreamType((eStreamType)StreamdevServerSetup.HTTPStreamType), m_LiveStreamer(NULL),
m_Channel(NULL), m_Channel(NULL),
m_RecPlayer(NULL), m_StreamType((eStreamType)StreamdevServerSetup.HTTPStreamType),
m_ReplayPos(0), m_ChannelList(NULL)
m_ReplayFakeRange(false),
m_MenuList(NULL)
{ {
Dprintf("constructor hsRequest\n"); Dprintf("constructor hsRequest\n");
m_Apid[0] = m_Apid[1] = 0; m_Apid[0] = m_Apid[1] = 0;
@@ -35,9 +24,7 @@ cConnectionHTTP::cConnectionHTTP(void):
cConnectionHTTP::~cConnectionHTTP() cConnectionHTTP::~cConnectionHTTP()
{ {
SetStreamer(NULL); delete m_LiveStreamer;
delete m_RecPlayer;
delete m_MenuList;
} }
bool cConnectionHTTP::CanAuthenticate(void) bool cConnectionHTTP::CanAuthenticate(void)
@@ -61,24 +48,9 @@ bool cConnectionHTTP::Command(char *Cmd)
*v = 0; *v = 0;
SetHeader("REQUEST_METHOD", Cmd); SetHeader("REQUEST_METHOD", Cmd);
q = strchr(p, '?'); q = strchr(p, '?');
if (q) { if (q)
*q = 0; *q = 0;
SetHeader("QUERY_STRING", q + 1); SetHeader("QUERY_STRING", q ? ++q : "");
while (q++) {
char *n = strchr(q, '&');
if (n)
*n = 0;
char *e = strchr(q, '=');
if (e)
*e++ = 0;
else
e = n ? n : v;
m_Params.insert(tStrStr(q, e));
q = n;
}
}
else
SetHeader("QUERY_STRING", "");
SetHeader("PATH_INFO", p); SetHeader("PATH_INFO", p);
m_Status = hsHeaders; m_Status = hsHeaders;
return true; return true;
@@ -161,260 +133,97 @@ bool cConnectionHTTP::ProcessRequest(void)
} }
if (!authOk) { if (!authOk) {
isyslog("streamdev-server: HTTP authorization required"); isyslog("streamdev-server: HTTP authorization required");
return HttpResponse(401, true, NULL, "WWW-authenticate: basic Realm=\"Streamdev-Server\""); DeferClose();
return Respond("HTTP/1.0 401 Authorization Required")
&& Respond("WWW-authenticate: basic Realm=\"Streamdev-Server\")")
&& Respond("");
} }
} }
tStrStrMap::const_iterator it;
it = m_Params.find("apid");
if (it != m_Params.end())
m_Apid[0] = atoi(it->second.c_str());
it = m_Params.find("dpid");
if (it != m_Params.end())
m_Dpid[0] = atoi(it->second.c_str());
tStrStrMap::const_iterator it_method = Headers().find(REQUEST_METHOD); tStrStrMap::const_iterator it_method = Headers().find(REQUEST_METHOD);
tStrStrMap::const_iterator it_pathinfo = Headers().find(PATH_INFO); tStrStrMap::const_iterator it_pathinfo = Headers().find(PATH_INFO);
if (it_method == Headers().end() || it_pathinfo == Headers().end()) { if (it_method == Headers().end() || it_pathinfo == Headers().end()) {
// should never happen // should never happen
esyslog("streamdev-server connectionHTTP: Missing method or pathinfo"); esyslog("streamdev-server connectionHTTP: Missing method or pathinfo");
} else if (it_method->second.compare("GET") == 0 && ProcessURI(it_pathinfo->second)) { } else if (it_method->second.compare("GET") == 0 && ProcessURI(it_pathinfo->second)) {
if (m_MenuList) if (m_ChannelList)
return Respond("%s", true, m_MenuList->HttpHeader().c_str()); return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
else if (m_Channel != NULL) { else if (m_Channel != NULL) {
if (cStreamdevLiveStreamer::ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority)) { cDevice *device = NULL;
cStreamdevLiveStreamer* liveStreamer = new cStreamdevLiveStreamer(this, m_Channel, StreamdevServerSetup.HTTPPriority, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL); if (ProvidesChannel(m_Channel, 0))
if (liveStreamer->GetDevice()) { device = GetDevice(m_Channel, 0);
SetStreamer(liveStreamer); if (device != NULL) {
device->SwitchChannel(m_Channel, false);
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);
if (!SetDSCP()) if (!SetDSCP())
LOG_ERROR_STR("unable to set DSCP sockopt"); LOG_ERROR_STR("unable to set DSCP sockopt");
if (m_StreamType == stEXT) { if (m_StreamType == stEXT) {
return Respond("HTTP/1.0 200 OK"); return Respond("HTTP/1.0 200 OK");
} else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) { } else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) {
return HttpResponse(200, false, "audio/mpeg", "icy-name: %s", m_Channel->Name()); 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)) { } else if (ISRADIO(m_Channel)) {
return HttpResponse(200, false, "audio/mpeg"); return Respond("HTTP/1.0 200 OK")
&& Respond("Content-Type: audio/mpeg")
&& Respond("");
} else { } else {
return HttpResponse(200, false, "video/mpeg"); return Respond("HTTP/1.0 200 OK")
&& Respond("Content-Type: video/mpeg")
&& Respond("");
} }
} }
SetStreamer(NULL); DELETENULL(m_LiveStreamer);
delete liveStreamer;
} }
return HttpResponse(503, true); DeferClose();
} return Respond("HTTP/1.0 409 Channel not available")
else if (m_RecPlayer != NULL) { && Respond("");
Dprintf("GET recording\n");
bool isPes = m_RecPlayer->getCurrentRecording()->IsPesRecording();
// no remuxing for old PES recordings
if (isPes && m_StreamType != stPES)
return HttpResponse(503, true);
int64_t from, to;
bool hasRange = ParseRange(from, to);
cStreamdevRecStreamer* recStreamer;
if (from == 0 && hasRange && m_ReplayFakeRange) {
recStreamer = new cStreamdevRecStreamer(this, m_RecPlayer, m_StreamType, (int64_t) 0L, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
from += m_ReplayPos;
if (to >= 0)
to += m_ReplayPos;
}
else
recStreamer = new cStreamdevRecStreamer(this, m_RecPlayer, m_StreamType, m_ReplayPos, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
SetStreamer(recStreamer);
if (m_StreamType == stEXT)
return Respond("HTTP/1.0 200 OK");
else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0]))
return HttpResponse(200, false, "audio/mpeg");
const char* contentType = (isPes || m_RecPlayer->getPatPmtData()->Vpid()) ? "video/mpeg" : "audio/mpeg";
// range not supported when remuxing
if (m_StreamType != stTS && !isPes)
return HttpResponse(200, false, contentType);
uint64_t total = recStreamer->GetLength();
if (hasRange) {
int64_t length = recStreamer->SetRange(from, to);
Dprintf("range response: %lld-%lld/%lld, len %lld\n", (long long)from, (long long)to, (long long)total, (long long)length);
if (length < 0L)
return HttpResponse(416, true, contentType, "Accept-Ranges: bytes\r\nContent-Range: bytes */%llu", (unsigned long long) total);
else
return HttpResponse(206, false, contentType, "Accept-Ranges: bytes\r\nContent-Range: bytes %lld-%lld/%llu\r\nContent-Length: %lld", (long long) from, (long long) to, (unsigned long long) total, (long long) length);
}
else
return HttpResponse(200, false, contentType, "Accept-Ranges: bytes");
} }
else { else {
return HttpResponse(404, true); DeferClose();
return Respond("HTTP/1.0 404 not found")
&& Respond("");
} }
} else if (it_method->second.compare("HEAD") == 0 && ProcessURI(it_pathinfo->second)) { } else if (it_method->second.compare("HEAD") == 0 && ProcessURI(it_pathinfo->second)) {
if (m_MenuList) {
DeferClose(); DeferClose();
return Respond("%s", true, m_MenuList->HttpHeader().c_str()); if (m_ChannelList)
} return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
else if (m_Channel != NULL) { else if (m_Channel != NULL) {
if (cStreamdevLiveStreamer::ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority)) { if (ProvidesChannel(m_Channel, 0)) {
if (m_StreamType == stEXT) { if (m_StreamType == stEXT) {
cStreamdevLiveStreamer *liveStreamer = new cStreamdevLiveStreamer(this, m_Channel, IDLEPRIORITY, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL); // TODO
SetStreamer(liveStreamer); return Respond("HTTP/1.0 200 OK")
return Respond("HTTP/1.0 200 OK"); && Respond("");
} else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) { } else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) {
return HttpResponse(200, true, "audio/mpeg", "icy-name: %s", m_Channel->Name()); 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)) { } else if (ISRADIO(m_Channel)) {
return HttpResponse(200, true, "audio/mpeg"); return Respond("HTTP/1.0 200 OK")
&& Respond("Content-Type: audio/mpeg")
&& Respond("");
} else { } else {
return HttpResponse(200, true, "video/mpeg"); return Respond("HTTP/1.0 200 OK")
&& Respond("Content-Type: video/mpeg")
&& Respond("");
} }
} }
return HttpResponse(503, true); return Respond("HTTP/1.0 409 Channel not available")
} && Respond("");
else if (m_RecPlayer != NULL) {
Dprintf("HEAD recording\n");
bool isPes = m_RecPlayer->getCurrentRecording()->IsPesRecording();
// no remuxing for old PES recordings
if (isPes && m_StreamType != stPES)
return HttpResponse(503, true);
int64_t from, to;
bool hasRange = ParseRange(from, to);
cStreamdevRecStreamer* recStreamer;
if (from == 0 && hasRange && m_ReplayFakeRange) {
recStreamer = new cStreamdevRecStreamer(this, m_RecPlayer, m_StreamType, m_ReplayPos, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
from += m_ReplayPos;
if (to >= 0)
to += m_ReplayPos;
}
else
recStreamer = new cStreamdevRecStreamer(this, m_RecPlayer, m_StreamType, m_ReplayPos, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
SetStreamer(recStreamer);
if (m_StreamType == stEXT)
return Respond("HTTP/1.0 200 OK");
else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0]))
return HttpResponse(200, true, "audio/mpeg");
const char* contentType = (isPes || m_RecPlayer->getPatPmtData()->Vpid()) ? "video/mpeg" : "audio/mpeg";
// range not supported when remuxing
if (m_StreamType != stTS && !isPes)
return HttpResponse(200, false, contentType);
uint64_t total = recStreamer->GetLength();
if (hasRange) {
int64_t length = recStreamer->SetRange(from, to);
if (length < 0L)
return HttpResponse(416, true, contentType, "Accept-Ranges: bytes\r\nContent-Range: bytes */%llu", (unsigned long long) total);
else
return HttpResponse(206, true, contentType, "Accept-Ranges: bytes\r\nContent-Range: bytes %lld-%lld/%llu\r\nContent-Length: %lld", (long long) from, (long long) to, (unsigned long long) total, (long long) length);
}
else
return HttpResponse(200, true, contentType, "Accept-Ranges: bytes");
} }
else { else {
return HttpResponse(404, true); return Respond("HTTP/1.0 404 not found")
&& Respond("");
} }
} }
return HttpResponse(400, true);
}
static const char *AAA[] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
static const char *MMM[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
bool cConnectionHTTP::HttpResponse(int Code, bool Last, const char* ContentType, const char* Headers, ...)
{
va_list ap;
va_start(ap, Headers);
#if APIVERSNUM >= 10728
cString headers = cString::vsprintf(Headers, ap);
#else
cString headers = cString::sprintf(Headers, ap);
#endif
va_end(ap);
bool rc;
if (Last)
DeferClose(); DeferClose();
switch (Code) return Respond("HTTP/1.0 400 Bad Request")
{ && Respond("");
case 200: rc = Respond("HTTP/1.1 200 OK"); break;
case 206: rc = Respond("HTTP/1.1 206 Partial Content"); break;
case 400: rc = Respond("HTTP/1.1 400 Bad Request"); break;
case 401: rc = Respond("HTTP/1.1 401 Authorization Required"); break;
case 404: rc = Respond("HTTP/1.1 404 Not Found"); break;
case 416: rc = Respond("HTTP/1.1 416 Requested range not satisfiable"); break;
case 503: rc = Respond("HTTP/1.1 503 Service Unavailable"); break;
default: rc = Respond("HTTP/1.1 500 Internal Server Error");
}
if (rc && ContentType)
rc = Respond("Content-Type: %s", true, ContentType);
if (rc)
rc = Respond("Connection: close")
&& Respond("Pragma: no-cache")
&& Respond("Cache-Control: no-cache")
&& Respond("Server: VDR-%s / streamdev-server-%s", true, VDRVERSION, VERSION);
time_t t = time(NULL);
struct tm *gmt = gmtime(&t);
if (rc && gmt) {
char buf[] = "Date: AAA, DD MMM YYYY HH:MM:SS GMT";
if (snprintf(buf, sizeof(buf), "Date: %s, %.2d %s %.4d %.2d:%.2d:%.2d GMT", AAA[gmt->tm_wday], gmt->tm_mday, MMM[gmt->tm_mon], gmt->tm_year + 1900, gmt->tm_hour, gmt->tm_min, gmt->tm_sec) == sizeof(buf) - 1)
rc = Respond(buf);
}
if (rc && strlen(Headers) > 0)
rc = Respond(headers);
tStrStrMap::iterator it = m_Params.begin();
while (rc && it != m_Params.end()) {
static const char DLNA_POSTFIX[] = ".dlna.org";
if (it->first.rfind(DLNA_POSTFIX) + sizeof(DLNA_POSTFIX) - 1 == it->first.length())
rc = Respond("%s: %s", true, it->first.c_str(), it->second.c_str());
++it;
}
return rc && Respond("");
}
bool cConnectionHTTP::ParseRange(int64_t &From, int64_t &To) const
{
const static std::string RANGE("HTTP_RANGE");
From = To = 0L;
tStrStrMap::const_iterator it = Headers().find(RANGE);
if (it != Headers().end()) {
size_t b = it->second.find("bytes=");
if (b != std::string::npos) {
char* e = NULL;
const char* r = it->second.c_str() + b + sizeof("bytes=") - 1;
if (strchr(r, ',') != NULL)
esyslog("streamdev-server cConnectionHTTP::GetRange: Multi-ranges not supported");
From = strtol(r, &e, 10);
if (r != e) {
if (From < 0L) {
To = -1L;
return *e == 0 || *e == ',';
}
else if (*e == '-') {
r = e + 1;
if (*r == 0 || *e == ',') {
To = -1L;
return true;
}
To = strtol(r, &e, 10);
return r != e && To >= From &&
(*e == 0 || *e == ',');
}
}
}
}
return false;
} }
void cConnectionHTTP::Flushed(void) void cConnectionHTTP::Flushed(void)
@@ -422,21 +231,21 @@ void cConnectionHTTP::Flushed(void)
if (m_Status != hsBody) if (m_Status != hsBody)
return; return;
if (m_MenuList) { if (m_ChannelList) {
if (m_MenuList->HasNext()) { if (m_ChannelList->HasNext()) {
if (!Respond("%s", true, m_MenuList->Next().c_str())) if (!Respond("%s", true, m_ChannelList->Next().c_str()))
DeferClose(); DeferClose();
} }
else { else {
DELETENULL(m_MenuList); DELETENULL(m_ChannelList);
m_Status = hsFinished; m_Status = hsFinished;
DeferClose(); DeferClose();
} }
return; return;
} }
else if (Streamer()) { else if (m_Channel != NULL) {
Dprintf("streamer start\n"); Dprintf("streamer start\n");
Streamer()->Start(this); m_LiveStreamer->Start(this);
m_Status = hsFinished; m_Status = hsFinished;
} }
else { else {
@@ -446,35 +255,49 @@ void cConnectionHTTP::Flushed(void)
} }
} }
cMenuList* cConnectionHTTP::MenuListFromString(const std::string& Path, const std::string& Filebase, const std::string& Fileext) const cChannelList* cConnectionHTTP::ChannelListFromString(const std::string& Path, const std::string& Filebase, const std::string& Fileext) const
{ {
std::string groupTarget; // keys for Headers() hash
cItemIterator *iterator = NULL; const static std::string QUERY_STRING("QUERY_STRING");
const static std::string HOST("HTTP_HOST");
tStrStrMap::const_iterator it_query = Headers().find(QUERY_STRING);
const std::string& query = it_query == Headers().end() ? "" : it_query->second;
std::string groupTarget;
cChannelIterator *iterator = NULL;
const static std::string GROUP("group");
if (Filebase.compare("tree") == 0) { if (Filebase.compare("tree") == 0) {
tStrStrMap::const_iterator it = m_Params.find(GROUP); const cChannel* c = NULL;
iterator = new cListTree(it == m_Params.end() ? NULL : it->second.c_str()); size_t groupIndex = query.find("group=");
if (groupIndex != std::string::npos)
c = cChannelList::GetGroup(atoi(query.c_str() + groupIndex + 6));
iterator = new cListTree(c);
groupTarget = Filebase + Fileext; groupTarget = Filebase + Fileext;
} else if (Filebase.compare("groups") == 0) { } else if (Filebase.compare("groups") == 0) {
iterator = new cListGroups(); iterator = new cListGroups();
groupTarget = (std::string) "group" + Fileext; groupTarget = (std::string) "group" + Fileext;
} else if (Filebase.compare("group") == 0) { } else if (Filebase.compare("group") == 0) {
tStrStrMap::const_iterator it = m_Params.find(GROUP); const cChannel* c = NULL;
iterator = new cListGroup(it == m_Params.end() ? NULL : it->second.c_str()); 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 (Filebase.compare("channels") == 0) { } else if (Filebase.compare("channels") == 0) {
iterator = new cListChannels(); iterator = new cListChannels();
} else if (Filebase.compare("all") == 0 || } else if (Filebase.compare("all") == 0 ||
(Filebase.empty() && Fileext.empty())) { (Filebase.empty() && Fileext.empty())) {
iterator = new cListAll(); iterator = new cListAll();
} else if (Filebase.compare("recordings") == 0) {
iterator = new cRecordingsIterator(m_StreamType);
} }
if (iterator) { if (iterator) {
// assemble base url: http://host/path/ 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; std::string base;
const static std::string HOST("HTTP_HOST");
tStrStrMap::const_iterator it = Headers().find(HOST); tStrStrMap::const_iterator it = Headers().find(HOST);
if (it != Headers().end()) if (it != Headers().end())
base = "http://" + it->second + "/"; base = "http://" + it->second + "/";
@@ -482,25 +305,7 @@ cMenuList* cConnectionHTTP::MenuListFromString(const std::string& Path, const st
base = (std::string) "http://" + LocalIp() + ":" + base = (std::string) "http://" + LocalIp() + ":" +
(const char*) itoa(StreamdevServerSetup.HTTPServerPort) + "/"; (const char*) itoa(StreamdevServerSetup.HTTPServerPort) + "/";
base += Path; base += Path;
return new cM3uChannelList(iterator, base.c_str());
if (Filebase.empty() || Fileext.compare(".htm") == 0 || Fileext.compare(".html") == 0) {
std::string self = Filebase + Fileext;
std::string rss = Filebase + ".rss";
tStrStrMap::const_iterator it = Headers().find("QUERY_STRING");
if (it != Headers().end() && !it->second.empty()) {
self += '?' + it->second;
rss += '?' + it->second;
}
return new cHtmlMenuList(iterator, m_StreamType, self.c_str(), rss.c_str(), groupTarget.c_str());
} else if (Fileext.compare(".m3u") == 0) {
return new cM3uMenuList(iterator, base.c_str());
} else if (Fileext.compare(".rss") == 0) {
std::string html = Filebase + ".html";
tStrStrMap::const_iterator it = Headers().find("QUERY_STRING");
if (it != Headers().end() && !it->second.empty()) {
html += '?' + it->second;
}
return new cRssMenuList(iterator, base.c_str(), html.c_str());
} else { } else {
delete iterator; delete iterator;
} }
@@ -508,93 +313,6 @@ cMenuList* cConnectionHTTP::MenuListFromString(const std::string& Path, const st
return NULL; return NULL;
} }
RecPlayer* cConnectionHTTP::RecPlayerFromString(const char *FileBase, const char *FileExt)
{
RecPlayer *recPlayer = NULL;
if (strcasecmp(FileExt, ".rec") != 0)
return NULL;
char *p = NULL;
unsigned long l = strtoul(FileBase, &p, 0);
if (p != FileBase && l > 0L) {
if (*p == ':') {
// get recording by dev:inode
ino_t inode = (ino_t) strtoull(p + 1, &p, 0);
if (*p == 0 && inode > 0) {
struct stat st;
#if APIVERSNUM >= 20300
LOCK_RECORDINGS_READ;
for (const cRecording *rec = Recordings->First(); rec; rec = Recordings->Next(rec)) {
#else
cThreadLock RecordingsLock(&Recordings);
for (cRecording *rec = Recordings.First(); rec; rec = Recordings.Next(rec)) {
#endif
if (stat(rec->FileName(), &st) == 0 && st.st_dev == (dev_t) l && st.st_ino == inode)
recPlayer = new RecPlayer(rec->FileName());
}
}
}
else if (*p == 0) {
// get recording by index
#if APIVERSNUM >= 20300
LOCK_RECORDINGS_READ;
const cRecording *rec = Recordings->Get((int) l - 1);
#else
cThreadLock RecordingsLock(&Recordings);
cRecording *rec = Recordings.Get((int) l - 1);
#endif
if (rec)
recPlayer = new RecPlayer(rec->FileName());
}
if (recPlayer) {
const char *pos = NULL;
tStrStrMap::const_iterator it = m_Params.begin();
while (it != m_Params.end()) {
if (it->first == "pos") {
pos = it->second.c_str();
break;
}
++it;
}
if (pos) {
// With prefix "full_" we try to fool players
// by replying with a content range starting
// at the requested position instead of 0.
// This is a heavy violation of standards.
// Use at your own risk!
if (strncasecmp(pos, "full_", 5) == 0) {
m_ReplayFakeRange = true;
pos += 5;
}
if (strncasecmp(pos, "resume", 6) == 0) {
int id = pos[6] == '.' ? atoi(pos + 7) : 0;
m_ReplayPos = recPlayer->positionFromResume(id);
}
else if (strncasecmp(pos, "mark.", 5) == 0) {
int index = atoi(pos + 5);
m_ReplayPos = recPlayer->positionFromMark(index);
}
else if (strncasecmp(pos, "time.", 5) == 0) {
int seconds = atoi(pos + 5);
m_ReplayPos = recPlayer->positionFromTime(seconds);
}
else if (strncasecmp(pos, "frame.", 6) == 0) {
int frame = atoi(pos + 6);
m_ReplayPos = recPlayer->positionFromFrameNumber(frame);
}
else {
m_ReplayPos = atol(pos);
if (m_ReplayPos > 0L && m_ReplayPos < 100L)
m_ReplayPos = recPlayer->positionFromPercent((int) m_ReplayPos);
}
}
}
}
return recPlayer;
}
bool cConnectionHTTP::ProcessURI(const std::string& PathInfo) bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
{ {
std::string filespec, fileext; std::string filespec, fileext;
@@ -602,24 +320,11 @@ bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
if (file_pos != std::string::npos) { if (file_pos != std::string::npos) {
size_t ext_pos = PathInfo.rfind('.'); size_t ext_pos = PathInfo.rfind('.');
if (ext_pos != std::string::npos) {
// file extension including leading .
fileext = PathInfo.substr(ext_pos);
const char *ext = fileext.c_str();
// ignore dummy file extensions
if (strcasecmp(ext, ".ts") == 0 ||
strcasecmp(ext, ".vdr") == 0 ||
strcasecmp(ext, ".vob") == 0) {
size_t ext_end = ext_pos;
if (ext_pos > 0)
ext_pos = PathInfo.rfind('.', ext_pos - 1);
if (ext_pos == std::string::npos)
ext_pos = ext_end;
fileext = PathInfo.substr(ext_pos, ext_end - ext_pos);
}
}
// file basename with leading / stripped off // file basename with leading / stripped off
filespec = PathInfo.substr(file_pos + 1, ext_pos - file_pos - 1); 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) { if (fileext.length() > 5) {
//probably not an extension //probably not an extension
@@ -630,12 +335,10 @@ bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
// Streamtype with leading / stripped off // Streamtype with leading / stripped off
std::string type = PathInfo.substr(1, PathInfo.find_first_of("/;", 1) - 1); std::string type = PathInfo.substr(1, PathInfo.find_first_of("/;", 1) - 1);
const char* pType = type.c_str(); const char* pType = type.c_str();
if (strcasecmp(pType, "PES") == 0) { if (strcasecmp(pType, "PS") == 0) {
m_StreamType = stPES;
#ifdef STREAMDEV_PS
} else if (strcasecmp(pType, "PS") == 0) {
m_StreamType = stPS; m_StreamType = stPS;
#endif } else if (strcasecmp(pType, "PES") == 0) {
m_StreamType = stPES;
} else if (strcasecmp(pType, "TS") == 0) { } else if (strcasecmp(pType, "TS") == 0) {
m_StreamType = stTS; m_StreamType = stTS;
} else if (strcasecmp(pType, "ES") == 0) { } else if (strcasecmp(pType, "ES") == 0) {
@@ -646,12 +349,9 @@ bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
Dprintf("before channelfromstring: type(%s) filespec(%s) fileext(%s)\n", type.c_str(), filespec.c_str(), fileext.c_str()); Dprintf("before channelfromstring: type(%s) filespec(%s) fileext(%s)\n", type.c_str(), filespec.c_str(), fileext.c_str());
if ((m_MenuList = MenuListFromString(PathInfo.substr(1, file_pos), filespec.c_str(), fileext.c_str())) != NULL) { if ((m_ChannelList = ChannelListFromString(PathInfo.substr(1, file_pos), filespec.c_str(), fileext.c_str())) != NULL) {
Dprintf("Channel list requested\n"); Dprintf("Channel list requested\n");
return true; return true;
} else if ((m_RecPlayer = RecPlayerFromString(filespec.c_str(), fileext.c_str())) != NULL) {
Dprintf("Recording %s found\n", m_RecPlayer->getCurrentRecording()->Name());
return true;
} else if ((m_Channel = ChannelFromString(filespec.c_str(), &m_Apid[0], &m_Dpid[0])) != NULL) { } else if ((m_Channel = ChannelFromString(filespec.c_str(), &m_Apid[0], &m_Dpid[0])) != NULL) {
Dprintf("Channel found. Apid/Dpid is %d/%d\n", m_Apid[0], m_Dpid[0]); Dprintf("Channel found. Apid/Dpid is %d/%d\n", m_Apid[0], m_Dpid[0]);
return true; return true;
@@ -659,8 +359,8 @@ bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
return false; return false;
} }
cString cConnectionHTTP::ToText(char Delimiter) const cString cConnectionHTTP::ToText() const
{ {
cString str = cServerConnection::ToText(Delimiter); cString str = cServerConnection::ToText();
return Streamer() ? cString::sprintf("%s%c%s", *str, Delimiter, *Streamer()->ToText()) : str; return m_LiveStreamer ? cString::sprintf("%s\t%s", *str, *m_LiveStreamer->ToText()) : str;
} }

View File

@@ -7,13 +7,13 @@
#include "connection.h" #include "connection.h"
#include "server/livestreamer.h" #include "server/livestreamer.h"
#include "server/recstreamer.h"
#include <map> #include <map>
#include <tools/select.h> #include <tools/select.h>
class cChannel; class cChannel;
class cMenuList; class cStreamdevLiveStreamer;
class cChannelList;
class cConnectionHTTP: public cServerConnection { class cConnectionHTTP: public cServerConnection {
private: private:
@@ -26,32 +26,17 @@ private:
std::string m_Authorization; std::string m_Authorization;
eHTTPStatus m_Status; eHTTPStatus m_Status;
tStrStrMap m_Params;
eStreamType m_StreamType;
// job: transfer // job: transfer
cStreamdevLiveStreamer *m_LiveStreamer;
const cChannel *m_Channel; const cChannel *m_Channel;
int m_Apid[2]; int m_Apid[2];
int m_Dpid[2]; int m_Dpid[2];
// job: replay eStreamType m_StreamType;
RecPlayer *m_RecPlayer;
int64_t m_ReplayPos;
bool m_ReplayFakeRange;
// job: listing // job: listing
cMenuList *m_MenuList; cChannelList *m_ChannelList;
cMenuList* MenuListFromString(const std::string &PathInfo, const std::string &Filebase, const std::string &Fileext) const;
RecPlayer* RecPlayerFromString(const char* FileBase, const char* FileExt);
cChannelList* ChannelListFromString(const std::string &PathInfo, const std::string &Filebase, const std::string &Fileext) const;
bool ProcessURI(const std::string &PathInfo); bool ProcessURI(const std::string &PathInfo);
bool HttpResponse(int Code, bool Last, const char* ContentType = NULL, const char* Headers = "", ...);
//__attribute__ ((format (printf, 5, 6)));
/**
* Extract byte range from HTTP Range header. Returns false if no valid
* range is found. The contents of From and To are undefined in this
* case. From may be negative in which case To is undefined.
* TODO: support for multiple ranges.
*/
bool ParseRange(int64_t &From, int64_t &To) const;
protected: protected:
bool ProcessRequest(void); bool ProcessRequest(void);
@@ -59,7 +44,10 @@ public:
cConnectionHTTP(void); cConnectionHTTP(void);
virtual ~cConnectionHTTP(); virtual ~cConnectionHTTP();
virtual cString ToText(char Delimiter = ' ') const; virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); }
virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); }
virtual cString ToText() const;
virtual bool CanAuthenticate(void); virtual bool CanAuthenticate(void);
@@ -71,7 +59,7 @@ public:
inline bool cConnectionHTTP::Abort(void) const inline bool cConnectionHTTP::Abort(void) const
{ {
return !IsOpen() || (Streamer() && Streamer()->Abort()); return !IsOpen() || (m_LiveStreamer && m_LiveStreamer->Abort());
} }
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H #endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H

View File

@@ -11,6 +11,7 @@
cConnectionIGMP::cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType) : cConnectionIGMP::cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType) :
cServerConnection(Name, SOCK_DGRAM), cServerConnection(Name, SOCK_DGRAM),
m_LiveStreamer(NULL),
m_ClientPort(ClientPort), m_ClientPort(ClientPort),
m_StreamType(StreamType), m_StreamType(StreamType),
m_Channel(NULL) m_Channel(NULL)
@@ -19,13 +20,10 @@ cConnectionIGMP::cConnectionIGMP(const char* Name, int ClientPort, eStreamType S
cConnectionIGMP::~cConnectionIGMP() cConnectionIGMP::~cConnectionIGMP()
{ {
delete m_LiveStreamer;
} }
#if APIVERSNUM >= 20300
bool cConnectionIGMP::SetChannel(const cChannel *Channel, in_addr_t Dst)
#else
bool cConnectionIGMP::SetChannel(cChannel *Channel, in_addr_t Dst) bool cConnectionIGMP::SetChannel(cChannel *Channel, in_addr_t Dst)
#endif
{ {
if (Channel) { if (Channel) {
m_Channel = Channel; m_Channel = Channel;
@@ -44,34 +42,37 @@ bool cConnectionIGMP::SetChannel(cChannel *Channel, in_addr_t Dst)
void cConnectionIGMP::Welcome() void cConnectionIGMP::Welcome()
{ {
if (cStreamdevLiveStreamer::ProvidesChannel(m_Channel, StreamdevServerSetup.IGMPPriority)) { cDevice *device = NULL;
cStreamdevLiveStreamer * liveStreamer = new cStreamdevLiveStreamer(this, m_Channel, StreamdevServerSetup.IGMPPriority, m_StreamType); if (ProvidesChannel(m_Channel, 0))
if (liveStreamer->GetDevice()) { device = GetDevice(m_Channel, 0);
SetStreamer(liveStreamer); 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()) if (!SetDSCP())
LOG_ERROR_STR("unable to set DSCP sockopt"); LOG_ERROR_STR("unable to set DSCP sockopt");
Dprintf("streamer start\n"); Dprintf("streamer start\n");
liveStreamer->Start(this); m_LiveStreamer->Start(this);
} }
else { else {
SetStreamer(NULL);
delete liveStreamer;
esyslog("streamdev-server IGMP: SetChannel failed"); esyslog("streamdev-server IGMP: SetChannel failed");
DELETENULL(m_LiveStreamer);
} }
} }
else else
esyslog("streamdev-server IGMP: SwitchDevice failed"); esyslog("streamdev-server IGMP: GetDevice failed");
} }
bool cConnectionIGMP::Close() bool cConnectionIGMP::Close()
{ {
if (Streamer()) if (m_LiveStreamer)
Streamer()->Stop(); m_LiveStreamer->Stop();
return cServerConnection::Close(); return cServerConnection::Close();
} }
cString cConnectionIGMP::ToText(char Delimiter) const cString cConnectionIGMP::ToText() const
{ {
cString str = cServerConnection::ToText(Delimiter); cString str = cServerConnection::ToText();
return Streamer() ? cString::sprintf("%s%c%s", *str, Delimiter, *Streamer()->ToText()) : str; return m_LiveStreamer ? cString::sprintf("%s\t%s", *str, *m_LiveStreamer->ToText()) : str;
} }

View File

@@ -17,29 +17,24 @@ class cStreamdevLiveStreamer;
class cConnectionIGMP: public cServerConnection { class cConnectionIGMP: public cServerConnection {
private: private:
cStreamdevLiveStreamer *m_LiveStreamer;
int m_ClientPort; int m_ClientPort;
eStreamType m_StreamType; eStreamType m_StreamType;
#if APIVERSNUM >= 20300
const cChannel *m_Channel;
#else
cChannel *m_Channel; cChannel *m_Channel;
#endif
public: public:
cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType); cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType);
virtual ~cConnectionIGMP(); virtual ~cConnectionIGMP();
#if APIVERSNUM >= 20300
bool SetChannel(const cChannel *Channel, in_addr_t Dst);
#else
bool SetChannel(cChannel *Channel, in_addr_t Dst); bool SetChannel(cChannel *Channel, in_addr_t Dst);
#endif
virtual void Welcome(void); virtual void Welcome(void);
virtual cString ToText(char Delimiter = ' ') const; virtual cString ToText() const;
/* Not used here */ /* Not used here */
virtual bool Command(char *Cmd) { return false; } 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 Close(void);
virtual bool Abort(void) const; virtual bool Abort(void) const;
@@ -47,7 +42,7 @@ public:
inline bool cConnectionIGMP::Abort(void) const inline bool cConnectionIGMP::Abort(void) const
{ {
return !IsOpen() || !Streamer() || Streamer()->Abort(); return !IsOpen() || !m_LiveStreamer || m_LiveStreamer->Abort();
} }
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H #endif // VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,7 @@
#include "server/recplayer.h" #include "server/recplayer.h"
class cTBSocket; class cTBSocket;
class cStreamdevLiveStreamer;
class cStreamdevFilterStreamer; class cStreamdevFilterStreamer;
class cLSTEHandler; class cLSTEHandler;
class cLSTCHandler; class cLSTCHandler;
@@ -19,6 +20,7 @@ class cConnectionVTP: public cServerConnection {
private: private:
cTBSocket *m_LiveSocket; cTBSocket *m_LiveSocket;
cStreamdevLiveStreamer *m_LiveStreamer;
cTBSocket *m_FilterSocket; cTBSocket *m_FilterSocket;
cStreamdevFilterStreamer *m_FilterStreamer; cStreamdevFilterStreamer *m_FilterStreamer;
cTBSocket *m_RecSocket; cTBSocket *m_RecSocket;
@@ -26,9 +28,7 @@ private:
char *m_LastCommand; char *m_LastCommand;
eStreamType m_StreamType; eStreamType m_StreamType;
unsigned int m_ClientVersion;
bool m_FiltersSupport; bool m_FiltersSupport;
bool m_LoopPrevention;
RecPlayer *m_RecPlayer; RecPlayer *m_RecPlayer;
// Priority is only known in PROV command // Priority is only known in PROV command
@@ -53,7 +53,7 @@ public:
virtual void Welcome(void); virtual void Welcome(void);
virtual void Reject(void); virtual void Reject(void);
virtual cString ToText(char Delimiter = ' ') const; virtual cString ToText() const;
virtual bool Abort(void) const; virtual bool Abort(void) const;
virtual void Detach(void); virtual void Detach(void);
@@ -61,7 +61,6 @@ public:
virtual bool Command(char *Cmd); virtual bool Command(char *Cmd);
bool CmdCAPS(char *Opts); bool CmdCAPS(char *Opts);
bool CmdVERS(char *Opts);
bool CmdPROV(char *Opts); bool CmdPROV(char *Opts);
bool CmdPORT(char *Opts); bool CmdPORT(char *Opts);
bool CmdREAD(char *Opts); bool CmdREAD(char *Opts);

View File

@@ -2,52 +2,18 @@
* $Id: livefilter.c,v 1.7 2009/02/13 13:02:40 schmirl Exp $ * $Id: livefilter.c,v 1.7 2009/02/13 13:02:40 schmirl Exp $
*/ */
#include <vdr/filter.h>
#include "server/livefilter.h" #include "server/livefilter.h"
#include "server/streamer.h"
#include "common.h" #include "common.h"
#ifndef TS_SYNC_BYTE #ifndef TS_SYNC_BYTE
# define TS_SYNC_BYTE 0x47 # define TS_SYNC_BYTE 0x47
#endif #endif
#define FILTERBUFSIZE (1000 * TS_SIZE) cStreamdevLiveFilter::cStreamdevLiveFilter(cStreamdevStreamer *Streamer) {
// --- cStreamdevLiveFilter -------------------------------------------------
class cStreamdevLiveFilter: public cFilter {
private:
cStreamdevFilterStreamer *m_Streamer;
bool m_On;
protected:
virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
virtual void SetStatus(bool On);
public:
cStreamdevLiveFilter(cStreamdevFilterStreamer *Streamer);
virtual bool IsAttached(void) const { return m_On; };
void Set(u_short Pid, u_char Tid, u_char Mask) {
cFilter::Set(Pid, Tid, Mask);
}
void Del(u_short Pid, u_char Tid, u_char Mask) {
cFilter::Del(Pid, Tid, Mask);
}
};
cStreamdevLiveFilter::cStreamdevLiveFilter(cStreamdevFilterStreamer *Streamer) {
m_On = false;
m_Streamer = Streamer; m_Streamer = Streamer;
} }
void cStreamdevLiveFilter::SetStatus(bool On)
{
m_On = On;
cFilter::SetStatus(On);
}
void cStreamdevLiveFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length) void cStreamdevLiveFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
{ {
uchar buffer[TS_SIZE]; uchar buffer[TS_SIZE];
@@ -66,86 +32,8 @@ void cStreamdevLiveFilter::Process(u_short Pid, u_char Tid, const u_char *Data,
length -= chunk; length -= chunk;
pos += chunk; pos += chunk;
m_Streamer->Receive(buffer); int p = m_Streamer->Put(buffer, TS_SIZE);
}
}
// --- cStreamdevFilterStreamer -------------------------------------------------
cStreamdevFilterStreamer::cStreamdevFilterStreamer():
cStreamdevStreamer("streamdev-filterstreaming"),
m_Device(NULL),
m_Filter(NULL)/*,
m_Channel(NULL)*/
{
m_ReceiveBuffer = new cStreamdevBuffer(FILTERBUFSIZE, TS_SIZE);
m_ReceiveBuffer->SetTimeouts(0, 500);
}
cStreamdevFilterStreamer::~cStreamdevFilterStreamer()
{
Dprintf("Desctructing Filter streamer\n");
Detach();
m_Device = NULL;
DELETENULL(m_Filter);
Stop();
delete m_ReceiveBuffer;
}
void cStreamdevFilterStreamer::Receive(uchar *Data)
{
int p = m_ReceiveBuffer->PutTS(Data, TS_SIZE);
if (p != TS_SIZE) if (p != TS_SIZE)
m_ReceiveBuffer->ReportOverflow(TS_SIZE - p); m_Streamer->ReportOverflow(TS_SIZE - p);
}
void cStreamdevFilterStreamer::Attach(void)
{
Dprintf("cStreamdevFilterStreamer::Attach()\n");
LOCK_THREAD;
if(m_Device && m_Filter)
m_Device->AttachFilter(m_Filter);
}
void cStreamdevFilterStreamer::Detach(void)
{
Dprintf("cStreamdevFilterStreamer::Detach()\n");
LOCK_THREAD;
if(m_Device && m_Filter)
m_Device->Detach(m_Filter);
}
void cStreamdevFilterStreamer::SetDevice(cDevice *Device)
{
Dprintf("cStreamdevFilterStreamer::SetDevice()\n");
LOCK_THREAD;
Detach();
m_Device = Device;
Attach();
}
bool cStreamdevFilterStreamer::IsReceiving(void) const
{
return m_Filter && m_Filter->IsAttached();
}
bool cStreamdevFilterStreamer::SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On)
{
Dprintf("cStreamdevFilterStreamer::SetFilter(%u,0x%x,0x%x,%s)\n", Pid, Tid, Mask, On?"On":"Off");
if(!m_Device)
return false;
if (On) {
if (m_Filter == NULL) {
m_Filter = new cStreamdevLiveFilter(this);
Dprintf("attaching filter to device\n");
Attach();
} }
m_Filter->Set(Pid, Tid, Mask);
} else if (m_Filter != NULL)
m_Filter->Del(Pid, Tid, Mask);
return true;
} }

View File

@@ -5,33 +5,28 @@
#ifndef VDR_STREAMEV_LIVEFILTER_H #ifndef VDR_STREAMEV_LIVEFILTER_H
#define VDR_STREAMEV_LIVEFILTER_H #define VDR_STREAMEV_LIVEFILTER_H
#include "server/streamer.h" #include <vdr/config.h>
class cDevice; #include <vdr/filter.h>
class cStreamdevLiveFilter;
class cStreamdevFilterStreamer: public cStreamdevStreamer { class cStreamdevStreamer;
class cStreamdevLiveFilter: public cFilter {
private: private:
cDevice *m_Device; cStreamdevStreamer *m_Streamer;
cStreamdevLiveFilter *m_Filter;
cStreamdevBuffer *m_ReceiveBuffer;
protected: protected:
virtual uchar* GetFromReceiver(int &Count) { return m_ReceiveBuffer->Get(Count); } virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
virtual void DelFromReceiver(int Count) { m_ReceiveBuffer->Del(Count); }
public: public:
cStreamdevFilterStreamer(); cStreamdevLiveFilter(cStreamdevStreamer *Streamer);
virtual ~cStreamdevFilterStreamer();
void SetDevice(cDevice *Device); void Set(u_short Pid, u_char Tid, u_char Mask) {
bool SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On); cFilter::Set(Pid, Tid, Mask);
}
virtual bool IsReceiving(void) const; void Del(u_short Pid, u_char Tid, u_char Mask) {
void Receive(uchar *Data); cFilter::Del(Pid, Tid, Mask);
}
virtual void Attach(void);
virtual void Detach(void);
}; };
#endif // VDR_STREAMEV_LIVEFILTER_H #endif // VDR_STREAMEV_LIVEFILTER_H

View File

@@ -8,47 +8,45 @@
#include "remux/ts2es.h" #include "remux/ts2es.h"
#include "remux/extern.h" #include "remux/extern.h"
#include <vdr/transfer.h> #include <vdr/ringbuffer.h>
#include "server/livestreamer.h" #include "server/livestreamer.h"
#include "server/setup.h" #include "server/livefilter.h"
#include "common.h" #include "common.h"
using namespace Streamdev; using namespace Streamdev;
// device occupied timeout to prevent VDR main loop to immediately switch back
// when streamdev switched the live TV channel.
// Note that there is still a gap between the GetDevice() and SetOccupied()
// calls where the VDR main loop could strike
#define STREAMDEVTUNETIMEOUT 5
// --- cStreamdevLiveReceiver ------------------------------------------------- // --- cStreamdevLiveReceiver -------------------------------------------------
class cStreamdevLiveReceiver: public cReceiver { class cStreamdevLiveReceiver: public cReceiver {
friend class cStreamdevStreamer; friend class cStreamdevStreamer;
private: private:
cStreamdevLiveStreamer *m_Streamer; cStreamdevStreamer *m_Streamer;
protected: protected:
#if APIVERSNUM >= 20300 virtual void Activate(bool On);
virtual void Receive(const uchar *Data, int Length);
#else
virtual void Receive(uchar *Data, int Length); virtual void Receive(uchar *Data, int Length);
#endif
public: public:
cStreamdevLiveReceiver(cStreamdevLiveStreamer *Streamer, const cChannel *Channel, int Priority, const int *Pids); cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, const cChannel *Channel, int Priority, const int *Pids);
virtual ~cStreamdevLiveReceiver(); virtual ~cStreamdevLiveReceiver();
}; };
cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevLiveStreamer *Streamer, const cChannel *Channel, int Priority, const int *Pids): cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, const cChannel *Channel,
int Priority, const int *Pids):
#if APIVERSNUM >= 10712
cReceiver(Channel, Priority), cReceiver(Channel, Priority),
#else
cReceiver(Channel->GetChannelID(), Priority, 0, Pids),
#endif
m_Streamer(Streamer) m_Streamer(Streamer)
{ {
#if APIVERSNUM >= 10712
// clears all PIDs but channel remains set // clears all PIDs but channel remains set
SetPids(NULL); SetPids(NULL);
AddPids(Pids); AddPids(Pids);
#endif
} }
cStreamdevLiveReceiver::~cStreamdevLiveReceiver() cStreamdevLiveReceiver::~cStreamdevLiveReceiver()
@@ -57,12 +55,16 @@ cStreamdevLiveReceiver::~cStreamdevLiveReceiver()
Detach(); Detach();
} }
#if APIVERSNUM >= 20300
void cStreamdevLiveReceiver::Receive(const uchar *Data, int Length) {
#else
void cStreamdevLiveReceiver::Receive(uchar *Data, int Length) { void cStreamdevLiveReceiver::Receive(uchar *Data, int Length) {
#endif int p = m_Streamer->Receive(Data, Length);
m_Streamer->Receive(Data, Length); if (p != Length)
m_Streamer->ReportOverflow(Length - p);
}
inline void cStreamdevLiveReceiver::Activate(bool On)
{
Dprintf("LiveReceiver->Activate(%d)\n", On);
m_Streamer->Activate(On);
} }
// --- cStreamdevPatFilter ---------------------------------------------------- // --- cStreamdevPatFilter ----------------------------------------------------
@@ -258,12 +260,7 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
SI::PAT::Association assoc; SI::PAT::Association assoc;
for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); ) { for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); ) {
if (!assoc.isNITPid()) { if (!assoc.isNITPid()) {
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
const cChannel *Channel = Channels->GetByServiceID(Source(), Transponder(), assoc.getServiceId());
#else
const cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId()); const cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId());
#endif
if (Channel && (Channel == m_Channel)) { if (Channel && (Channel == m_Channel)) {
int prevPmtPid = pmtPid; int prevPmtPid = pmtPid;
if (0 != (pmtPid = assoc.getPid())) { if (0 != (pmtPid = assoc.getPid())) {
@@ -347,36 +344,29 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
// --- cStreamdevLiveStreamer ------------------------------------------------- // --- cStreamdevLiveStreamer -------------------------------------------------
cStreamdevLiveStreamer::cStreamdevLiveStreamer(const cServerConnection *Connection, const cChannel *Channel, int Priority, eStreamType StreamType, const int* Apid, const int *Dpid) : cStreamdevLiveStreamer::cStreamdevLiveStreamer(int Priority, const cServerConnection *Connection):
cStreamdevStreamer("streamdev-livestreaming", Connection), cStreamdevStreamer("streamdev-livestreaming", Connection),
m_Priority(Priority), m_Priority(Priority),
m_NumPids(0), m_NumPids(0),
m_Channel(Channel), m_StreamType(stTSPIDS),
m_Channel(NULL),
m_Device(NULL), m_Device(NULL),
m_Receiver(NULL), m_Receiver(NULL),
m_PatFilter(NULL), m_PatFilter(NULL),
m_SwitchLive(false) m_Remux(NULL)
{ {
m_ReceiveBuffer = new cStreamdevBuffer(LIVEBUFSIZE, TS_SIZE *2, true, "streamdev-livestreamer"),
m_ReceiveBuffer->SetTimeouts(0, 100);
if (Priority == IDLEPRIORITY) {
SetChannel(StreamType, Apid, Dpid);
}
else {
m_Device = SwitchDevice(Channel, Priority);
if (m_Device)
SetChannel(StreamType, Apid, Dpid);
memcpy(m_Caids,Channel->Caids(),sizeof(m_Caids));
}
} }
cStreamdevLiveStreamer::~cStreamdevLiveStreamer() cStreamdevLiveStreamer::~cStreamdevLiveStreamer()
{ {
Dprintf("Desctructing Live streamer\n"); Dprintf("Desctructing Live streamer\n");
Stop(); Stop();
if(m_PatFilter) {
Detach();
DELETENULL(m_PatFilter); DELETENULL(m_PatFilter);
}
DELETENULL(m_Receiver); DELETENULL(m_Receiver);
delete m_ReceiveBuffer; delete m_Remux;
} }
bool cStreamdevLiveStreamer::HasPid(int Pid) bool cStreamdevLiveStreamer::HasPid(int Pid)
@@ -456,12 +446,6 @@ bool cStreamdevLiveStreamer::SetPids(int Pid, const int *Pids1, const int *Pids2
void cStreamdevLiveStreamer::SetPriority(int Priority) void cStreamdevLiveStreamer::SetPriority(int Priority)
{ {
m_Priority = Priority; m_Priority = Priority;
#if VDRVERSNUM >= 20104
cThreadLock ThreadLock(m_Device);
if (m_Receiver)
m_Receiver->SetPriority(Priority);
else
#endif
StartReceiver(); StartReceiver();
} }
@@ -469,8 +453,10 @@ void cStreamdevLiveStreamer::GetSignal(int *DevNum, int *Strength, int *Quality)
{ {
if (m_Device) { if (m_Device) {
*DevNum = m_Device->DeviceNumber() + 1; *DevNum = m_Device->DeviceNumber() + 1;
#if APIVERSNUM >= 10719
*Strength = m_Device->SignalStrength(); *Strength = m_Device->SignalStrength();
*Quality = m_Device->SignalQuality(); *Quality = m_Device->SignalQuality();
#endif
} }
} }
@@ -482,19 +468,13 @@ cString cStreamdevLiveStreamer::ToText() const
return cString(""); return cString("");
} }
bool cStreamdevLiveStreamer::IsReceiving(void) const void cStreamdevLiveStreamer::StartReceiver(void)
{ {
cThreadLock ThreadLock(m_Device); if (m_NumPids > 0) {
return m_Receiver && m_Receiver->IsAttached();
}
void cStreamdevLiveStreamer::StartReceiver(bool Force)
{
if (m_NumPids > 0 || Force) {
Dprintf("Creating Receiver to respect changed pids\n"); Dprintf("Creating Receiver to respect changed pids\n");
cReceiver *current = m_Receiver; cReceiver *current = m_Receiver;
cThreadLock ThreadLock(m_Device);
m_Receiver = new cStreamdevLiveReceiver(this, m_Channel, m_Priority, m_Pids); m_Receiver = new cStreamdevLiveReceiver(this, m_Channel, m_Priority, m_Pids);
cThreadLock ThreadLock(m_Device);
if (IsRunning()) if (IsRunning())
Attach(); Attach();
delete current; delete current;
@@ -503,15 +483,17 @@ void cStreamdevLiveStreamer::StartReceiver(bool Force)
DELETENULL(m_Receiver); DELETENULL(m_Receiver);
} }
bool cStreamdevLiveStreamer::SetChannel(eStreamType StreamType, const int* Apid, const int *Dpid) 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_StreamType = StreamType;
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 = Dpid ? Dpid : m_Channel->Dpids();
switch (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();
@@ -519,22 +501,20 @@ bool cStreamdevLiveStreamer::SetChannel(eStreamType StreamType, const int* Apid,
pid = Apid[0]; pid = Apid[0];
else if (Dpid && Dpid[0]) else if (Dpid && Dpid[0])
pid = Dpid[0]; pid = Dpid[0];
SetRemux(new cTS2ESRemux(pid)); m_Remux = new cTS2ESRemux(pid);
return SetPids(pid); return SetPids(pid);
} }
case stPES: case stPES:
SetRemux(new cTS2PESRemux(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids())); m_Remux = new cTS2PESRemux(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids()); return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
#ifdef STREAMDEV_PS
case stPS: case stPS:
SetRemux(new cTS2PSRemux(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids())); m_Remux = new cTS2PSRemux(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids()); return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
#endif
case stEXT: case stEXT:
SetRemux(new cExternRemux(Connection(), m_Channel, Apids, Dpids)); m_Remux = new cExternRemux(Connection(), m_Channel, Apids, Dpids);
// fall through // fall through
case stTS: case stTS:
// This should never happen, but ... // This should never happen, but ...
@@ -552,40 +532,12 @@ bool cStreamdevLiveStreamer::SetChannel(eStreamType StreamType, const int* Apid,
case stTSPIDS: case stTSPIDS:
Dprintf("pid streaming mode\n"); Dprintf("pid streaming mode\n");
// No PIDs requested yet. Start receiver anyway to occupy device
StartReceiver(true);
return true; return true;
default: default:
return false; return false;
} }
} }
#if APIVERSNUM >= 20300
void cStreamdevLiveStreamer::Receive(const uchar *Data, int Length)
#else
void cStreamdevLiveStreamer::Receive(uchar *Data, int Length)
#endif
{
int p = m_ReceiveBuffer->PutTS(Data, Length);
if (p != Length)
m_ReceiveBuffer->ReportOverflow(Length - p);
}
void cStreamdevLiveStreamer::Action(void)
{
if (StreamdevServerSetup.LiveBufferMs) {
// wait for first data block
int count = 0;
while (Running()) {
if (m_ReceiveBuffer->Get(count) != NULL) {
cCondWait::SleepMs(StreamdevServerSetup.LiveBufferMs);
break;
}
}
}
cStreamdevStreamer::Action();
}
int cStreamdevLiveStreamer::Put(const uchar *Data, int Count) int cStreamdevLiveStreamer::Put(const uchar *Data, int Count)
{ {
// insert si data // insert si data
@@ -593,20 +545,41 @@ int cStreamdevLiveStreamer::Put(const uchar *Data, int Count)
int siCount; int siCount;
uchar *siData = m_PatFilter->Get(siCount); uchar *siData = m_PatFilter->Get(siCount);
if (siData) { if (siData) {
if (m_Remux)
siCount = m_Remux->Put(siData, siCount);
else
siCount = cStreamdevStreamer::Put(siData, siCount); siCount = cStreamdevStreamer::Put(siData, siCount);
if (siCount) if (siCount)
m_PatFilter->Del(siCount); m_PatFilter->Del(siCount);
} }
} }
if (m_Remux)
return m_Remux->Put(Data, Count);
else
return cStreamdevStreamer::Put(Data, Count); return cStreamdevStreamer::Put(Data, Count);
} }
uchar *cStreamdevLiveStreamer::Get(int &Count)
{
if (m_Remux)
return m_Remux->Get(Count);
else
return cStreamdevStreamer::Get(Count);
}
void cStreamdevLiveStreamer::Del(int Count)
{
if (m_Remux)
m_Remux->Del(Count);
else
cStreamdevStreamer::Del(Count);
}
void cStreamdevLiveStreamer::Attach(void) void cStreamdevLiveStreamer::Attach(void)
{ {
Dprintf("cStreamdevLiveStreamer::Attach()\n"); Dprintf("cStreamdevLiveStreamer::Attach()\n");
if (m_Device) { if (m_Device) {
if (m_Receiver) { if (m_Receiver) {
if (m_Receiver->IsAttached())
m_Device->Detach(m_Receiver); m_Device->Detach(m_Receiver);
m_Device->AttachReceiver(m_Receiver); m_Device->AttachReceiver(m_Receiver);
} }
@@ -628,101 +601,6 @@ void cStreamdevLiveStreamer::Detach(void)
} }
} }
bool cStreamdevLiveStreamer::UsedByLiveTV(cDevice *device)
{
return device == cTransferControl::ReceiverDevice() ||
(device->IsPrimaryDevice() && device->HasDecoder() && !device->Replaying());
}
cDevice *cStreamdevLiveStreamer::SwitchDevice(const cChannel *Channel, int Priority)
{
cDevice *device = cDevice::GetDevice(Channel, Priority, false);
if (!device) {
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());
}
else if (!device->IsTunedToTransponder(Channel) && UsedByLiveTV(device)) {
// make sure VDR main loop doesn't switch back
device->SetOccupied(STREAMDEVTUNETIMEOUT);
if (device->SwitchChannel(Channel, false)) {
// switched away live TV
m_SwitchLive = true;
}
else {
dsyslog("streamdev: SwitchChannel (live) failed for channel %d (%s) at priority %d (PrimaryDevice=%d, ActualDevice=%d, device=%d)", Channel->Number(), Channel->Name(), Priority, cDevice::PrimaryDevice()->CardIndex(), cDevice::ActualDevice()->CardIndex(), device->CardIndex());
device->SetOccupied(0);
device = NULL;
}
}
else if (!device->SwitchChannel(Channel, false)) {
dsyslog("streamdev: SwitchChannel failed for channel %d (%s) at priority %d (PrimaryDevice=%d, ActualDevice=%d, device=%d)", Channel->Number(), Channel->Name(), Priority, cDevice::PrimaryDevice()->CardIndex(), cDevice::ActualDevice()->CardIndex(), device->CardIndex());
device = NULL;
}
return device;
}
bool cStreamdevLiveStreamer::ProvidesChannel(const cChannel *Channel, int Priority)
{
cDevice *device = cDevice::GetDevice(Channel, Priority, false, true);
if (!device)
dsyslog("streamdev: No device provides channel %d (%s) at priority %d", Channel->Number(), Channel->Name(), Priority);
return device;
}
void cStreamdevLiveStreamer::ChannelChange(const cChannel *Channel)
{
if (Running() && m_Device && m_Channel == Channel) {
// Check whether the Caids actually changed
// If not, no need to re-tune, probably just an Audio PID update
if (!memcmp(m_Caids, Channel->Caids(), sizeof(m_Caids))) {
dsyslog("streamdev: channel %d (%s) changed, but caids remained the same, not re-tuning", Channel->Number(), Channel->Name());
}
else {
Detach();
if (m_Device->SwitchChannel(m_Channel, false)) {
Attach();
dsyslog("streamdev: channel %d (%s) changed, re-tuned", Channel->Number(), Channel->Name());
memcpy(m_Caids, Channel->Caids(), sizeof(m_Caids));
}
else
isyslog("streamdev: failed to re-tune after channel %d (%s) changed", Channel->Number(), Channel->Name());
}
}
}
void cStreamdevLiveStreamer::MainThreadHook()
{
if (!m_SwitchLive && Running() && m_Device && !m_Device->IsTunedToTransponder(m_Channel) && !IsReceiving()) {
cDevice *dev = SwitchDevice(m_Channel, m_Priority);
if (dev) {
dsyslog("streamdev: Lost channel %d (%s) on device %d. Continuing on device %d.", m_Channel->Number(), m_Channel->Name(), m_Device->CardIndex(), dev->CardIndex());
m_Device = dev;
StartReceiver();
}
else {
isyslog("streamdev: Lost channel %d (%s) on device %d.", m_Channel->Number(), m_Channel->Name(), m_Device->CardIndex());
Stop();
}
}
if (m_SwitchLive) {
// switched away live TV. Try previous channel on other device first
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
if (!Channels->SwitchTo(cDevice::CurrentChannel())) {
// switch to streamdev channel otherwise
Channels->SwitchTo(m_Channel->Number());
#else
if (!Channels.SwitchTo(cDevice::CurrentChannel())) {
// switch to streamdev channel otherwise
Channels.SwitchTo(m_Channel->Number());
#endif
Skins.Message(mtInfo, tr("Streaming active"));
}
if (m_Device)
m_Device->SetOccupied(0);
m_SwitchLive = false;
}
}
std::string cStreamdevLiveStreamer::Report(void) std::string cStreamdevLiveStreamer::Report(void)
{ {
std::string result; std::string result;
@@ -739,3 +617,105 @@ std::string cStreamdevLiveStreamer::Report(void)
result += "\n"; result += "\n";
return result; return result;
} }
// --- cStreamdevFilterStreamer -------------------------------------------------
cStreamdevFilterStreamer::cStreamdevFilterStreamer():
cStreamdevStreamer("streamdev-filterstreaming"),
m_Device(NULL),
m_Filter(NULL)/*,
m_Channel(NULL)*/
{
}
cStreamdevFilterStreamer::~cStreamdevFilterStreamer()
{
Dprintf("Desctructing Filter streamer\n");
Detach();
m_Device = NULL;
DELETENULL(m_Filter);
Stop();
}
void cStreamdevFilterStreamer::Attach(void)
{
Dprintf("cStreamdevFilterStreamer::Attach()\n");
LOCK_THREAD;
if(m_Device && m_Filter)
m_Device->AttachFilter(m_Filter);
}
void cStreamdevFilterStreamer::Detach(void)
{
Dprintf("cStreamdevFilterStreamer::Detach()\n");
LOCK_THREAD;
if(m_Device && m_Filter)
m_Device->Detach(m_Filter);
}
#if 0
void cStreamdevFilterStreamer::SetChannel(const cChannel *Channel)
{
LOCK_THREAD;
Dprintf("cStreamdevFilterStreamer::SetChannel(%s : %s)", Channel?Channel->Name():"<null>",
Channel ? *Channel->GetChannelID().ToString() : "");
m_Channel = Channel;
}
#endif
void cStreamdevFilterStreamer::SetDevice(cDevice *Device)
{
Dprintf("cStreamdevFilterStreamer::SetDevice()\n");
LOCK_THREAD;
Detach();
m_Device = Device;
//m_Channel = NULL;
Attach();
}
bool cStreamdevFilterStreamer::SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On)
{
Dprintf("cStreamdevFilterStreamer::SetFilter(%u,0x%x,0x%x,%s)\n", Pid, Tid, Mask, On?"On":"Off");
if(!m_Device)
return false;
if (On) {
if (m_Filter == NULL) {
m_Filter = new cStreamdevLiveFilter(this);
Dprintf("attaching filter to device\n");
Attach();
}
m_Filter->Set(Pid, Tid, Mask);
} else if (m_Filter != NULL)
m_Filter->Del(Pid, Tid, Mask);
return true;
}
#if 0
void cStreamdevFilterStreamer::ChannelSwitch(const cDevice *Device, int ChannelNumber) {
LOCK_THREAD;
if(Device == m_Device) {
if(ChannelNumber > 0) {
cChannel *ch = Channels.GetByNumber(ChannelNumber);
if(ch != NULL) {
if(m_Filter != NULL &&
m_Channel != NULL &&
(! TRANSPONDER(ch, m_Channel))) {
isyslog("***** LiveFilterStreamer: transponder changed ! %s",
*ch->GetChannelID().ToString());
uchar buffer[TS_SIZE] = {TS_SYNC_BYTE, 0xff, 0xff, 0xff, 0x7f, 0};
strcpy((char*)(buffer + 5), ch->GetChannelID().ToString());
int p = Put(buffer, TS_SIZE);
if (p != TS_SIZE)
ReportOverflow(TS_SIZE - p);
}
m_Channel = ch;
}
}
}
}
#endif

View File

@@ -2,88 +2,83 @@
#define VDR_STREAMDEV_LIVESTREAMER_H #define VDR_STREAMDEV_LIVESTREAMER_H
#include <vdr/config.h> #include <vdr/config.h>
#include <vdr/status.h>
#include <vdr/receiver.h> #include <vdr/receiver.h>
#include "server/streamer.h" #include "server/streamer.h"
#include "server/streamdev-server.h"
#include "common.h" #include "common.h"
#define LIVEBUFSIZE (20000 * TS_SIZE) namespace Streamdev {
class cTSRemux;
}
class cStreamdevPatFilter; class cStreamdevPatFilter;
class cStreamdevLiveReceiver; class cStreamdevLiveReceiver;
// --- cStreamdevLiveStreamer ------------------------------------------------- // --- cStreamdevLiveStreamer -------------------------------------------------
class cStreamdevLiveStreamer: public cStreamdevStreamer, public cMainThreadHookSubscriber class cStreamdevLiveStreamer: public cStreamdevStreamer {
#if VDRVERSNUM >= 20104
, public cStatus
#endif
{
private: private:
int m_Priority; int m_Priority;
int m_Pids[MAXRECEIVEPIDS + 1]; int m_Pids[MAXRECEIVEPIDS + 1];
int m_NumPids; int m_NumPids;
int m_Caids[MAXCAIDS + 1]; eStreamType m_StreamType;
const cChannel *m_Channel; const cChannel *m_Channel;
cDevice *m_Device; cDevice *m_Device;
cStreamdevLiveReceiver *m_Receiver; cStreamdevLiveReceiver *m_Receiver;
cStreamdevBuffer *m_ReceiveBuffer;
cStreamdevPatFilter *m_PatFilter; cStreamdevPatFilter *m_PatFilter;
bool m_SwitchLive; Streamdev::cTSRemux *m_Remux;
void StartReceiver(bool Force = false); void StartReceiver(void);
bool HasPid(int Pid); bool HasPid(int Pid);
/* 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);
/* Find a suitable device and tune it to the requested channel. */
cDevice *SwitchDevice(const cChannel *Channel, int Priority);
bool SetChannel(eStreamType StreamType, const int* Apid = NULL, const int* Dpid = NULL);
protected:
virtual uchar* GetFromReceiver(int &Count) { return m_ReceiveBuffer->Get(Count); }
virtual void DelFromReceiver(int Count) { m_ReceiveBuffer->Del(Count); }
virtual int Put(const uchar *Data, int Count);
virtual void Action(void);
virtual void ChannelChange(const cChannel *Channel);
public: public:
cStreamdevLiveStreamer(const cServerConnection *Connection, const cChannel *Channel, int Priority, eStreamType StreamType, const int* Apid = NULL, const int* Dpid = NULL); cStreamdevLiveStreamer(int Priority, const cServerConnection *Connection);
virtual ~cStreamdevLiveStreamer(); virtual ~cStreamdevLiveStreamer();
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, const int* Apid = NULL, const int* Dpid = NULL);
void SetPriority(int Priority); void SetPriority(int Priority);
void GetSignal(int *DevNum, int *Strength, int *Quality) const; void GetSignal(int *DevNum, int *Strength, int *Quality) const;
virtual cString ToText() const; cString ToText() const;
#if APIVERSNUM >= 20300 virtual int Put(const uchar *Data, int Count);
void Receive(const uchar *Data, int Length); virtual uchar *Get(int &Count);
#else virtual void Del(int Count);
void Receive(uchar *Data, int Length);
#endif
virtual bool IsReceiving(void) const;
virtual void Attach(void); virtual void Attach(void);
virtual void Detach(void); virtual void Detach(void);
cDevice *GetDevice() const { return m_Device; }
/* Test if a call to GetDevice would return a usable device. */
static bool ProvidesChannel(const cChannel *Channel, int Priority);
/* Do things which must be done in VDR's main loop */
void MainThreadHook();
// Statistical purposes: // Statistical purposes:
virtual std::string Report(void); virtual std::string Report(void);
}; };
// --- cStreamdevFilterStreamer -------------------------------------------------
//#include <vdr/status.h>
class cStreamdevLiveFilter;
class cStreamdevFilterStreamer: public cStreamdevStreamer /*, public cStatus*/ {
private:
cDevice *m_Device;
cStreamdevLiveFilter *m_Filter;
//const cChannel *m_Channel;
public:
cStreamdevFilterStreamer();
virtual ~cStreamdevFilterStreamer();
void SetDevice(cDevice *Device);
//void SetChannel(const cChannel *Channel);
bool SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On);
virtual void Attach(void);
virtual void Detach(void);
// cStatus message handlers
//virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber);
};
#endif // VDR_STREAMDEV_LIVESTREAMER_H #endif // VDR_STREAMDEV_LIVESTREAMER_H

View File

@@ -13,13 +13,9 @@
cStreamdevServerMenu::cStreamdevServerMenu(): cOsdMenu(tr("Streamdev Connections"), 4, 20) { cStreamdevServerMenu::cStreamdevServerMenu(): cOsdMenu(tr("Streamdev Connections"), 4, 20) {
cThreadLock lock; cThreadLock lock;
#if APIVERSNUM >= 20300
cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
#else
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock); const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
#endif
for (cServerConnection *s = clients.First(); s; s = clients.Next(s)) for (cServerConnection *s = clients.First(); s; s = clients.Next(s))
Add(new cOsdItem(s->ToText('\t'))); Add(new cOsdItem(s->ToText()));
SetHelpKeys(); SetHelpKeys();
Display(); Display();
} }
@@ -28,21 +24,17 @@ cStreamdevServerMenu::~cStreamdevServerMenu() {
} }
void cStreamdevServerMenu::SetHelpKeys() { void cStreamdevServerMenu::SetHelpKeys() {
SetHelp(Count() ? tr("Disconnect") : NULL, NULL, NULL, tr("Suspend")); SetHelp(Count() ? tr("Disconnect") : NULL, NULL, NULL, StreamdevServerSetup.SuspendMode == smOffer ? tr("Suspend") : NULL);
} }
eOSState cStreamdevServerMenu::Disconnect() { eOSState cStreamdevServerMenu::Disconnect() {
cOsdItem *item = Get(Current()); cOsdItem *item = Get(Current());
if (item) { if (item) {
cThreadLock lock; cThreadLock lock;
#if APIVERSNUM >= 20300
cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
#else
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock); const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
#endif
const char *text = item->Text(); const char *text = item->Text();
for (cServerConnection *s = clients.First(); s; s = clients.Next(s)) { for (cServerConnection *s = clients.First(); s; s = clients.Next(s)) {
if (!strcmp(text, s->ToText('\t'))) { if (!strcmp(text, s->ToText())) {
s->Close(); s->Close();
Del(Current()); Del(Current());
SetHelpKeys(); SetHelpKeys();
@@ -55,7 +47,7 @@ eOSState cStreamdevServerMenu::Disconnect() {
} }
eOSState cStreamdevServerMenu::Suspend() { eOSState cStreamdevServerMenu::Suspend() {
if (!cSuspendCtl::IsActive()) { if (StreamdevServerSetup.SuspendMode == smOffer && !cSuspendCtl::IsActive()) {
cControl::Launch(new cSuspendCtl); cControl::Launch(new cSuspendCtl);
return osBack; return osBack;
} }

View File

@@ -1,244 +1,58 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <vdr/channels.h> #include <vdr/channels.h>
#include "server/menuHTTP.h" #include "server/menuHTTP.h"
//**************************** cRecordingIterator **************
#if APIVERSNUM >= 20300
cRecordingsIterator::cRecordingsIterator(eStreamType StreamType)
#else
cRecordingsIterator::cRecordingsIterator(eStreamType StreamType): RecordingsLock(&Recordings)
#endif
{
streamType = StreamType;
#if APIVERSNUM >= 20300
LOCK_RECORDINGS_READ;
first = NextSuitable(Recordings->First());
#else
first = NextSuitable(Recordings.First());
#endif
current = NULL;
}
const cRecording* cRecordingsIterator::NextSuitable(const cRecording *Recording)
{
while (Recording)
{
bool isPes = Recording->IsPesRecording();
if (!isPes || (isPes && streamType == stPES))
break;
#if APIVERSNUM >= 20300
LOCK_RECORDINGS_READ;
Recording = Recordings->Next(Recording);
#else
Recording = Recordings.Next(Recording);
#endif
}
return Recording;
}
bool cRecordingsIterator::Next()
{
#if APIVERSNUM >= 20300
LOCK_RECORDINGS_READ;
#endif
if (first)
{
current = first;
first = NULL;
}
else
#if APIVERSNUM >= 20300
current = NextSuitable(Recordings->Next(current));
#else
current = NextSuitable(Recordings.Next(current));
#endif
return current;
}
const cString cRecordingsIterator::ItemRessource() const
{
struct stat st;
if (stat(current->FileName(), &st) == 0)
return cString::sprintf("%lu:%llu.rec", (unsigned long) st.st_dev, (unsigned long long) st.st_ino);
return "";
}
//**************************** cChannelIterator ************** //**************************** cChannelIterator **************
cChannelIterator::cChannelIterator(const cChannel *First) cChannelIterator::cChannelIterator(const cChannel *First): channel(First)
{ {}
first = First;
current = NULL;
}
bool cChannelIterator::Next() const cChannel* cChannelIterator::Next()
{ {
if (first) const cChannel *current = channel;
{ channel = NextChannel(channel);
current = first;
first = NULL;
}
else
current = NextChannel(current);
return current; return current;
} }
const cString cChannelIterator::ItemId() const
{
if (current)
{
if (current->GroupSep())
{
int index = 0;
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
for (int curr = Channels->GetNextGroup(-1); curr >= 0; curr = Channels->GetNextGroup(curr))
{
if (Channels->Get(curr) == current)
#else
for (int curr = Channels.GetNextGroup(-1); curr >= 0; curr = Channels.GetNextGroup(curr))
{
if (Channels.Get(curr) == current)
#endif
return itoa(index);
index++;
}
}
else
{
return itoa(current->Number());
}
}
return cString("-1");
}
const cChannel* cChannelIterator::GetGroup(const char* GroupId)
{
int group = -1;
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
#endif
if (GroupId)
{
int Index = atoi(GroupId);
#if APIVERSNUM >= 20300
group = Channels->GetNextGroup(-1);
while (Index-- && group >= 0)
group = Channels->GetNextGroup(group);
}
return group >= 0 ? Channels->Get(group) : NULL;
#else
group = Channels.GetNextGroup(-1);
while (Index-- && group >= 0)
group = Channels.GetNextGroup(group);
}
return group >= 0 ? Channels.Get(group) : NULL;
#endif
}
const cChannel* cChannelIterator::FirstChannel()
{
const cChannel *Channel;
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
Channel = Channels->First();
#else
Channel = Channels.First();
#endif
return Channel;
}
const cChannel* cChannelIterator::NextNormal()
{
const cChannel *Channel;
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
Channel = Channels->Get(Channels->GetNextNormal(-1));
#else
Channel = Channels.Get(Channels.GetNextNormal(-1));
#endif
return Channel;
}
const cChannel* cChannelIterator::NextGroup()
{
const cChannel *Channel;
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
Channel = Channels->Get(Channels->GetNextGroup(-1));
#else
Channel = Channels.Get(Channels.GetNextGroup(-1));
#endif
return Channel;
}
//**************************** cListAll ************** //**************************** cListAll **************
cListAll::cListAll(): cChannelIterator(FirstChannel()) cListAll::cListAll(): cChannelIterator(Channels.First())
{} {}
const cChannel* cListAll::NextChannel(const cChannel *Channel) const cChannel* cListAll::NextChannel(const cChannel *Channel)
{ {
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
if (Channel)
Channel = SkipFakeGroups(Channels->Next(Channel));
#else
if (Channel) if (Channel)
Channel = SkipFakeGroups(Channels.Next(Channel)); Channel = SkipFakeGroups(Channels.Next(Channel));
#endif
return Channel; return Channel;
} }
//**************************** cListChannels ************** //**************************** cListChannels **************
cListChannels::cListChannels(): cChannelIterator(NextNormal()) cListChannels::cListChannels(): cChannelIterator(Channels.Get(Channels.GetNextNormal(-1)))
{} {}
const cChannel* cListChannels::NextChannel(const cChannel *Channel) const cChannel* cListChannels::NextChannel(const cChannel *Channel)
{ {
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
if (Channel)
Channel = Channels->Get(Channels->GetNextNormal(Channel->Index()));
#else
if (Channel) if (Channel)
Channel = Channels.Get(Channels.GetNextNormal(Channel->Index())); Channel = Channels.Get(Channels.GetNextNormal(Channel->Index()));
#endif
return Channel; return Channel;
} }
// ********************* cListGroups **************** // ********************* cListGroups ****************
cListGroups::cListGroups(): cChannelIterator(NextGroup()) cListGroups::cListGroups(): cChannelIterator(Channels.Get(Channels.GetNextGroup(-1)))
{} {}
const cChannel* cListGroups::NextChannel(const cChannel *Channel) const cChannel* cListGroups::NextChannel(const cChannel *Channel)
{ {
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
if (Channel)
Channel = Channels->Get(Channels->GetNextGroup(Channel->Index()));
#else
if (Channel) if (Channel)
Channel = Channels.Get(Channels.GetNextGroup(Channel->Index())); Channel = Channels.Get(Channels.GetNextGroup(Channel->Index()));
#endif
return Channel; return Channel;
} }
// //
// ********************* cListGroup **************** // ********************* cListGroup ****************
cListGroup::cListGroup(const char *GroupId): cChannelIterator(GetNextChannelInGroup(GetGroup(GroupId))) cListGroup::cListGroup(const cChannel *Group): cChannelIterator(GetNextChannelInGroup(Group))
{} {}
const cChannel* cListGroup::GetNextChannelInGroup(const cChannel *Channel) const cChannel* cListGroup::GetNextChannelInGroup(const cChannel *Channel)
{ {
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
if (Channel)
Channel = SkipFakeGroups(Channels->Next(Channel));
#else
if (Channel) if (Channel)
Channel = SkipFakeGroups(Channels.Next(Channel)); Channel = SkipFakeGroups(Channels.Next(Channel));
#endif
return Channel && !Channel->GroupSep() ? Channel : NULL; return Channel && !Channel->GroupSep() ? Channel : NULL;
} }
@@ -248,65 +62,67 @@ const cChannel* cListGroup::NextChannel(const cChannel *Channel)
} }
// //
// ********************* cListTree **************** // ********************* cListTree ****************
cListTree::cListTree(const char *SelectedGroupId): cChannelIterator(NextGroup()) cListTree::cListTree(const cChannel *SelectedGroup): cChannelIterator(Channels.Get(Channels.GetNextGroup(-1)))
{ {
selectedGroup = GetGroup(SelectedGroupId); selectedGroup = SelectedGroup;
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
currentGroup = Channels->Get(Channels->GetNextGroup(-1));
#else
currentGroup = Channels.Get(Channels.GetNextGroup(-1)); currentGroup = Channels.Get(Channels.GetNextGroup(-1));
#endif
} }
const cChannel* cListTree::NextChannel(const cChannel *Channel) const cChannel* cListTree::NextChannel(const cChannel *Channel)
{ {
if (currentGroup == selectedGroup) if (currentGroup == selectedGroup)
{ {
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
if (Channel)
Channel = SkipFakeGroups(Channels->Next(Channel));
#else
if (Channel) if (Channel)
Channel = SkipFakeGroups(Channels.Next(Channel)); Channel = SkipFakeGroups(Channels.Next(Channel));
#endif
if (Channel && Channel->GroupSep()) if (Channel && Channel->GroupSep())
currentGroup = Channel; currentGroup = Channel;
} }
else else
{ {
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
if (Channel)
Channel = Channels->Get(Channels->GetNextGroup(Channel->Index()));
#else
if (Channel) if (Channel)
Channel = Channels.Get(Channels.GetNextGroup(Channel->Index())); Channel = Channels.Get(Channels.GetNextGroup(Channel->Index()));
#endif
currentGroup = Channel; currentGroup = Channel;
} }
return Channel; return Channel;
} }
// ******************** cMenuList ****************** // ******************** cChannelList ******************
cMenuList::cMenuList(cItemIterator *Iterator) : iterator(Iterator) cChannelList::cChannelList(cChannelIterator *Iterator) : iterator(Iterator)
{} {}
cMenuList::~cMenuList() cChannelList::~cChannelList()
{ {
delete iterator; delete iterator;
} }
// ******************** cHtmlMenuList ****************** int cChannelList::GetGroupIndex(const cChannel *Group)
const char* cHtmlMenuList::menu = {
int index = 0;
for (int curr = Channels.GetNextGroup(-1); curr >= 0; curr = Channels.GetNextGroup(curr))
{
if (Channels.Get(curr) == Group)
return index;
index++;
}
return -1;
}
const cChannel* cChannelList::GetGroup(int Index)
{
int group = Channels.GetNextGroup(-1);
while (Index-- && group >= 0)
group = Channels.GetNextGroup(group);
return group >= 0 ? Channels.Get(group) : NULL;
}
// ******************** cHtmlChannelList ******************
const char* cHtmlChannelList::menu =
"[<a href=\"/\">Home</a> (<a href=\"all.html\" tvid=\"RED\">no script</a>)] " "[<a href=\"/\">Home</a> (<a href=\"all.html\" tvid=\"RED\">no script</a>)] "
"[<a href=\"tree.html\" tvid=\"GREEN\">Tree View</a>] " "[<a href=\"tree.html\" tvid=\"GREEN\">Tree View</a>] "
"[<a href=\"groups.html\" tvid=\"YELLOW\">Groups</a> (<a href=\"groups.m3u\">Playlist</a> | <a href=\"groups.rss\">RSS</a>)] " "[<a href=\"groups.html\" tvid=\"YELLOW\">Groups</a> (<a href=\"groups.m3u\">Playlist</a>)] "
"[<a href=\"channels.html\" tvid=\"BLUE\">Channels</a> (<a href=\"channels.m3u\">Playlist</a> | <a href=\"channels.rss\">RSS</a>)] " "[<a href=\"channels.html\" tvid=\"BLUE\">Channels</a> (<a href=\"channels.m3u\">Playlist</a>)] ";
"[<a href=\"recordings.html\">Recordings</a> (<a href=\"recordings.m3u\">Playlist</a> | <a href=\"recordings.rss\">RSS</a>)] ";
const char* cHtmlMenuList::css = const char* cHtmlChannelList::css =
"<style type=\"text/css\">\n" "<style type=\"text/css\">\n"
"<!--\n" "<!--\n"
"a:link, a:visited, a:hover, a:active, a:focus { color:#333399; }\n" "a:link, a:visited, a:hover, a:active, a:focus { color:#333399; }\n"
@@ -322,7 +138,7 @@ const char* cHtmlMenuList::css =
"-->\n" "-->\n"
"</style>"; "</style>";
const char* cHtmlMenuList::js = const char* cHtmlChannelList::js =
"<script language=\"JavaScript\">\n" "<script language=\"JavaScript\">\n"
"<!--\n" "<!--\n"
@@ -383,15 +199,13 @@ const char* cHtmlMenuList::js =
"</script>"; "</script>";
std::string cHtmlMenuList::StreamTypeMenu() std::string cHtmlChannelList::StreamTypeMenu()
{ {
std::string typeMenu; std::string typeMenu;
typeMenu += (streamType == stTS ? (std::string) "[TS] " : typeMenu += (streamType == stTS ? (std::string) "[TS] " :
(std::string) "[<a href=\"/TS/" + self + "\">TS</a>] "); (std::string) "[<a href=\"/TS/" + self + "\">TS</a>] ");
#ifdef STREAMDEV_PS
typeMenu += (streamType == stPS ? (std::string) "[PS] " : typeMenu += (streamType == stPS ? (std::string) "[PS] " :
(std::string) "[<a href=\"/PS/" + self + "\">PS</a>] "); (std::string) "[<a href=\"/PS/" + self + "\">PS</a>] ");
#endif
typeMenu += (streamType == stPES ? (std::string) "[PES] " : typeMenu += (streamType == stPES ? (std::string) "[PES] " :
(std::string) "[<a href=\"/PES/" + self + "\">PES</a>] "); (std::string) "[<a href=\"/PES/" + self + "\">PES</a>] ");
typeMenu += (streamType == stES ? (std::string) "[ES] " : typeMenu += (streamType == stES ? (std::string) "[ES] " :
@@ -401,29 +215,27 @@ std::string cHtmlMenuList::StreamTypeMenu()
return typeMenu; return typeMenu;
} }
cHtmlMenuList::cHtmlMenuList(cItemIterator *Iterator, eStreamType StreamType, const char *Self, const char *Rss, const char *GroupTarget): cMenuList(Iterator) cHtmlChannelList::cHtmlChannelList(cChannelIterator *Iterator, eStreamType StreamType, const char *Self, const char *GroupTarget): cChannelList(Iterator)
{ {
streamType = StreamType; streamType = StreamType;
self = strdup(Self); self = strdup(Self);
rss = strdup(Rss);
groupTarget = (GroupTarget && *GroupTarget) ? strdup(GroupTarget) : NULL; groupTarget = (GroupTarget && *GroupTarget) ? strdup(GroupTarget) : NULL;
htmlState = hsRoot; htmlState = hsRoot;
onItem = true; current = NULL;
} }
cHtmlMenuList::~cHtmlMenuList() cHtmlChannelList::~cHtmlChannelList()
{ {
free((void *) self); free((void *) self);
free((void *) rss);
free((void *) groupTarget); free((void *) groupTarget);
} }
bool cHtmlMenuList::HasNext() bool cHtmlChannelList::HasNext()
{ {
return htmlState != hsPageBottom; return htmlState != hsPageBottom;
} }
std::string cHtmlMenuList::Next() std::string cHtmlChannelList::Next()
{ {
switch (htmlState) switch (htmlState)
{ {
@@ -440,39 +252,39 @@ std::string cHtmlMenuList::Next()
htmlState = hsPageTop; htmlState = hsPageTop;
break; break;
case hsPageTop: case hsPageTop:
onItem = NextItem(); current = NextChannel();
htmlState = onItem ? (IsGroup() ? hsGroupTop : hsPlainTop) : hsPageBottom; htmlState = current ? (current->GroupSep() ? hsGroupTop : hsPlainTop) : hsPageBottom;
break; break;
case hsPlainTop: case hsPlainTop:
htmlState = hsPlainItem; htmlState = hsPlainItem;
break; break;
case hsPlainItem: case hsPlainItem:
onItem = NextItem(); current = NextChannel();
htmlState = onItem && !IsGroup() ? hsPlainItem : hsPlainBottom; htmlState = current && !current->GroupSep() ? hsPlainItem : hsPlainBottom;
break; break;
case hsPlainBottom: case hsPlainBottom:
htmlState = onItem ? hsGroupTop : hsPageBottom; htmlState = current ? hsGroupTop : hsPageBottom;
break; break;
case hsGroupTop: case hsGroupTop:
onItem = NextItem(); current = NextChannel();
htmlState = onItem && !IsGroup() ? hsItemsTop : hsGroupBottom; htmlState = current && !current->GroupSep() ? hsItemsTop : hsGroupBottom;
break; break;
case hsItemsTop: case hsItemsTop:
htmlState = hsItem; htmlState = hsItem;
break; break;
case hsItem: case hsItem:
onItem = NextItem(); current = NextChannel();
htmlState = onItem && !IsGroup() ? hsItem : hsItemsBottom; htmlState = current && !current->GroupSep() ? hsItem : hsItemsBottom;
break; break;
case hsItemsBottom: case hsItemsBottom:
htmlState = hsGroupBottom; htmlState = hsGroupBottom;
break; break;
case hsGroupBottom: case hsGroupBottom:
htmlState = onItem ? hsGroupTop : hsPageBottom; htmlState = current ? hsGroupTop : hsPageBottom;
break; break;
case hsPageBottom: case hsPageBottom:
default: default:
esyslog("streamdev-server cHtmlMenuList: invalid call to Next()"); esyslog("streamdev-server cHtmlChannelList: invalid call to Next()");
break; break;
} }
switch (htmlState) switch (htmlState)
@@ -497,100 +309,99 @@ std::string cHtmlMenuList::Next()
} }
} }
std::string cHtmlMenuList::HtmlHead() std::string cHtmlChannelList::HtmlHead()
{
return (std::string) "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"RSS\" href=\"" + rss + "\"/>";
}
std::string cHtmlMenuList::PageTop()
{
return (std::string) "<div class=\"menu\"><div>" + menu + "</div><div>" + StreamTypeMenu() + "</div></div>";
}
std::string cHtmlMenuList::PageBottom()
{ {
return (std::string) ""; return (std::string) "";
} }
std::string cHtmlMenuList::GroupTitle() std::string cHtmlChannelList::PageTop()
{
return (std::string) "<div class=\"menu\"><div>" + menu + "</div><div>" + StreamTypeMenu() + "</div></div>";
}
std::string cHtmlChannelList::PageBottom()
{
return (std::string) "";
}
std::string cHtmlChannelList::GroupTitle()
{ {
if (groupTarget) if (groupTarget)
{ {
return (std::string) "<a href=\"" + groupTarget + "?group=" + (const char*) ItemId() + "\">" + return (std::string) "<a href=\"" + groupTarget + "?group=" +
ItemTitle() + "</a>"; (const char*) itoa(cChannelList::GetGroupIndex(current)) +
"\">" + current->Name() + "</a>";
} }
else else
{ {
return (std::string) ItemTitle(); return (std::string) current->Name();
} }
} }
std::string cHtmlMenuList::ItemText() std::string cHtmlChannelList::ItemText()
{ {
std::string line; std::string line;
std::string suffix; std::string suffix;
switch (streamType) { switch (streamType) {
case stTS: suffix = (std::string) ".ts"; break; case stTS: suffix = (std::string) ".ts"; break;
#ifdef STREAMDEV_PS
case stPS: suffix = (std::string) ".vob"; break; case stPS: suffix = (std::string) ".vob"; break;
#endif
// for Network Media Tank // for Network Media Tank
case stPES: suffix = (std::string) ".vdr"; break; case stPES: suffix = (std::string) ".vdr"; break;
default: suffix = ""; default: suffix = "";
} }
line += (std::string) "<li value=\"" + (const char*) ItemId() + "\">"; line += (std::string) "<li value=\"" + (const char*) itoa(current->Number()) + "\">";
line += (std::string) "<a href=\"" + (const char*) ItemRessource() + suffix + "\""; line += (std::string) "<a href=\"" + (std::string) current->GetChannelID().ToString() + suffix + "\"";
// for Network Media Tank // for Network Media Tank
line += (std::string) " vod "; line += (std::string) " vod ";
if (strlen(ItemId()) < 4) if (current->Number() < 1000)
line += (std::string) " tvid=\"" + (const char*) ItemId() + "\""; line += (std::string) " tvid=\"" + (const char*) itoa(current->Number()) + "\"";
line += (std::string) ">" + ItemTitle() + "</a>"; line += (std::string) ">" + current->Name() + "</a>";
// TS always streams all PIDs int count = 0;
if (streamType != stTS) for (int i = 0; current->Apid(i) != 0; ++i, ++count)
;
for (int i = 0; current->Dpid(i) != 0; ++i, ++count)
;
if (count > 1)
{ {
int index = 1; int index = 1;
const char* lang; for (int i = 0; current->Apid(i) != 0; ++i, ++index) {
std::string pids; line += (std::string) " <a href=\"" + (std::string) current->GetChannelID().ToString() +
for (int i = 0; (lang = Alang(i)) != NULL; ++i, ++index) { "+" + (const char*)itoa(index) + suffix + "\" class=\"apid\" vod>" + current->Alang(i) + "</a>";
pids += (std::string) " <a href=\"" + (const char*) ItemRessource() +
"+" + (const char*)itoa(index) + suffix + "\" class=\"apid\" vod>" + (const char*) lang + "</a>";
} }
for (int i = 0; (lang = Dlang(i)) != NULL; ++i, ++index) { for (int i = 0; current->Dpid(i) != 0; ++i, ++index) {
pids += (std::string) " <a href=\"" + (const char*) ItemRessource() + line += (std::string) " <a href=\"" + (std::string) current->GetChannelID().ToString() +
"+" + (const char*)itoa(index) + suffix + "\" class=\"dpid\" vod>" + (const char*) lang + "</a>"; "+" + (const char*)itoa(index) + suffix + "\" class=\"dpid\" vod>" + current->Dlang(i) + "</a>";
} }
// always show audio PIDs for stES to select audio only
if (index > 2 || streamType == stES)
line += pids;
} }
line += "</li>"; line += "</li>";
return line; return line;
} }
// ******************** cM3uMenuList ****************** // ******************** cM3uChannelList ******************
cM3uMenuList::cM3uMenuList(cItemIterator *Iterator, const char* Base) cM3uChannelList::cM3uChannelList(cChannelIterator *Iterator, const char* Base)
: cMenuList(Iterator), : cChannelList(Iterator),
m_IConv(cCharSetConv::SystemCharacterTable(), "UTF-8") m_IConv(cCharSetConv::SystemCharacterTable(), "UTF-8")
{ {
base = strdup(Base); base = strdup(Base);
m3uState = msFirst; m3uState = msFirst;
} }
cM3uMenuList::~cM3uMenuList() cM3uChannelList::~cM3uChannelList()
{ {
free(base); free(base);
} }
bool cM3uMenuList::HasNext() bool cM3uChannelList::HasNext()
{ {
return m3uState != msLast; return m3uState != msLast;
} }
std::string cM3uMenuList::Next() std::string cM3uChannelList::Next()
{ {
if (m3uState == msFirst) if (m3uState == msFirst)
{ {
@@ -598,83 +409,26 @@ std::string cM3uMenuList::Next()
return "#EXTM3U"; return "#EXTM3U";
} }
if (!NextItem()) const cChannel *channel = NextChannel();
if (!channel)
{ {
m3uState = msLast; m3uState = msLast;
return ""; return "";
} }
std::string name = (std::string) m_IConv.Convert(ItemTitle()); std::string name = (std::string) m_IConv.Convert(channel->Name());
if (IsGroup()) if (channel->GroupSep())
{ {
return (std::string) "#EXTINF:-1," + name + "\r\n" + return (std::string) "#EXTINF:-1," + name + "\r\n" +
base + "group.m3u?group=" + (const char*) ItemId(); base + "group.m3u?group=" +
(const char*) itoa(cChannelList::GetGroupIndex(channel));
} }
else else
{ {
return (std::string) "#EXTINF:-1," + return (std::string) "#EXTINF:-1," +
(const char*) ItemId() + " " + name + "\r\n" + (const char*) itoa(channel->Number()) + " " + name + "\r\n" +
base + (const char*) ItemRessource(); base + (std::string) channel->GetChannelID().ToString();
} }
} }
// ******************** cRssMenuList ******************
cRssMenuList::cRssMenuList(cItemIterator *Iterator, const char *Base, const char *Html)
: cMenuList(Iterator),
m_IConv(cCharSetConv::SystemCharacterTable(), "UTF-8")
{
base = strdup(Base);
html = strdup(Html);
rssState = msFirst;
}
cRssMenuList::~cRssMenuList()
{
free(base);
free(html);
}
bool cRssMenuList::HasNext()
{
return rssState != msLast;
}
std::string cRssMenuList::Next()
{
std::string type_ext;
if (rssState == msFirst)
{
rssState = msContinue;
return (std::string) "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<rss version=\"2.0\">\n\t<channel>\n"
"\t\t<title>VDR</title>\n"
"\t\t<link>" + base + html + "</link>\n"
"\t\t<description>VDR channel list</description>\n"
;
}
if (!NextItem())
{
rssState = msLast;
return "\t</channel>\n</rss>\n";
}
std::string name = (std::string) m_IConv.Convert(ItemTitle());
if (IsGroup())
{
return (std::string) "\t\t<item>\n\t\t\t<title>" +
name + "</title>\n\t\t\t<link>" +
base + "group.rss?group=" + (const char*) ItemId() + "</link>\n\t\t</item>\n";
}
else
{
return (std::string) "\t\t<item>\n\t\t\t<title>" +
(const char*) ItemId() + " " + name + "</title>\n\t\t\t<link>" +
base + (const char*) ItemRessource() + "</link>\n\t\t\t<enclosure url=\"" +
base + (const char*) ItemRessource() + "\" type=\"video/mpeg\" />\n\t\t</item>\n";
}
}

View File

@@ -3,66 +3,19 @@
#include <string> #include <string>
#include "../common.h" #include "../common.h"
#include <vdr/recording.h>
class cChannel; class cChannel;
// ******************** cItemIterator ****************** // ******************** cChannelIterator ******************
class cItemIterator class cChannelIterator
{
public:
virtual bool Next() = 0;
virtual bool IsGroup() const = 0;
virtual const cString ItemId() const = 0;
virtual const char* ItemTitle() const = 0;
virtual const cString ItemRessource() const = 0;
virtual const char* Alang(int i) const = 0;
virtual const char* Dlang(int i) const = 0;
virtual ~cItemIterator() {};
};
class cRecordingsIterator: public cItemIterator
{ {
private: private:
eStreamType streamType; const cChannel *channel;
const cRecording *first;
const cRecording *current;
cThreadLock RecordingsLock;
protected: protected:
virtual const cRecording* NextSuitable(const cRecording *Recording);
public:
virtual bool Next();
virtual bool IsGroup() const { return false; }
virtual const cString ItemId() const { return current ? itoa(current->Index() + 1) : "0"; }
virtual const char* ItemTitle() const { return current ? current->Title() : ""; }
virtual const cString ItemRessource() const;
virtual const char* Alang(int i) const { return NULL; }
virtual const char* Dlang(int i) const { return NULL; }
cRecordingsIterator(eStreamType StreamType);
virtual ~cRecordingsIterator() {};
};
class cChannelIterator: public cItemIterator
{
private:
const cChannel *first;
const cChannel *current;
protected:
virtual const cChannel* FirstChannel();
virtual const cChannel* NextNormal();
virtual const cChannel* NextGroup();
virtual const cChannel* NextChannel(const cChannel *Channel) = 0; virtual const cChannel* NextChannel(const cChannel *Channel) = 0;
static inline const cChannel* SkipFakeGroups(const cChannel *Channel); static inline const cChannel* SkipFakeGroups(const cChannel *Channel);
// Helper which returns the group by its index
static const cChannel* GetGroup(const char* GroupId);
public: public:
virtual bool Next(); const cChannel* Next();
virtual bool IsGroup() const { return current && current->GroupSep(); }
virtual const cString ItemId() const;
virtual const char* ItemTitle() const { return current ? current->Name() : ""; }
virtual const cString ItemRessource() const { return (current ? current->GetChannelID() : tChannelID::InvalidID).ToString(); }
virtual const char* Alang(int i) const { return current && current->Apid(i) ? current->Alang(i) : NULL; }
virtual const char* Dlang(int i) const { return current && current->Dpid(i) ? current->Dlang(i) : NULL; }
cChannelIterator(const cChannel *First); cChannelIterator(const cChannel *First);
virtual ~cChannelIterator() {}; virtual ~cChannelIterator() {};
}; };
@@ -101,7 +54,7 @@ class cListGroup: public cChannelIterator
protected: protected:
virtual const cChannel* NextChannel(const cChannel *Channel); virtual const cChannel* NextChannel(const cChannel *Channel);
public: public:
cListGroup(const char *GroupId); cListGroup(const cChannel *Group);
virtual ~cListGroup() {}; virtual ~cListGroup() {};
}; };
@@ -113,32 +66,31 @@ class cListTree: public cChannelIterator
protected: protected:
virtual const cChannel* NextChannel(const cChannel *Channel); virtual const cChannel* NextChannel(const cChannel *Channel);
public: public:
cListTree(const char *SelectedGroupId); cListTree(const cChannel *SelectedGroup);
virtual ~cListTree() {}; virtual ~cListTree() {};
}; };
// ******************** cMenuList ****************** // ******************** cChannelList ******************
class cMenuList class cChannelList
{ {
private: private:
cItemIterator *iterator; cChannelIterator *iterator;
protected: protected:
bool NextItem() { return iterator->Next(); } const cChannel* NextChannel() { return iterator->Next(); }
bool IsGroup() { return iterator->IsGroup(); }
const cString ItemId() { return iterator->ItemId(); }
const char* ItemTitle() { return iterator->ItemTitle(); }
const cString ItemRessource() { return iterator->ItemRessource(); }
const char* Alang(int i) { return iterator->Alang(i); }
const char* Dlang(int i) { return iterator->Dlang(i); }
public: public:
// Helper which returns the group index
static int GetGroupIndex(const cChannel* Group);
// Helper which returns the group by its index
static const cChannel* GetGroup(int Index);
virtual std::string HttpHeader() { return "HTTP/1.0 200 OK\r\n"; }; virtual std::string HttpHeader() { return "HTTP/1.0 200 OK\r\n"; };
virtual bool HasNext() = 0; virtual bool HasNext() = 0;
virtual std::string Next() = 0; virtual std::string Next() = 0;
cMenuList(cItemIterator *Iterator); cChannelList(cChannelIterator *Iterator);
virtual ~cMenuList(); virtual ~cChannelList();
}; };
class cHtmlMenuList: public cMenuList class cHtmlChannelList: public cChannelList
{ {
private: private:
static const char* menu; static const char* menu;
@@ -152,10 +104,9 @@ class cHtmlMenuList: public cMenuList
hsItemsTop, hsItem, hsItemsBottom hsItemsTop, hsItem, hsItemsBottom
}; };
eHtmlState htmlState; eHtmlState htmlState;
bool onItem; const cChannel *current;
eStreamType streamType; eStreamType streamType;
const char* self; const char* self;
const char* rss;
const char* groupTarget; const char* groupTarget;
std::string StreamTypeMenu(); std::string StreamTypeMenu();
@@ -166,18 +117,18 @@ class cHtmlMenuList: public cMenuList
std::string PageBottom(); std::string PageBottom();
public: public:
virtual std::string HttpHeader() { virtual std::string HttpHeader() {
return cMenuList::HttpHeader() return cChannelList::HttpHeader()
+ "Content-type: text/html; charset=" + "Content-type: text/html; charset="
+ (cCharSetConv::SystemCharacterTable() ? cCharSetConv::SystemCharacterTable() : "UTF-8") + (cCharSetConv::SystemCharacterTable() ? cCharSetConv::SystemCharacterTable() : "UTF-8")
+ "\r\n"; + "\r\n";
} }
virtual bool HasNext(); virtual bool HasNext();
virtual std::string Next(); virtual std::string Next();
cHtmlMenuList(cItemIterator *Iterator, eStreamType StreamType, const char *Self, const char *Rss, const char *GroupTarget); cHtmlChannelList(cChannelIterator *Iterator, eStreamType StreamType, const char *Self, const char *GroupTarget);
virtual ~cHtmlMenuList(); virtual ~cHtmlChannelList();
}; };
class cM3uMenuList: public cMenuList class cM3uChannelList: public cChannelList
{ {
private: private:
char *base; char *base;
@@ -185,41 +136,17 @@ class cM3uMenuList: public cMenuList
eM3uState m3uState; eM3uState m3uState;
cCharSetConv m_IConv; cCharSetConv m_IConv;
public: public:
virtual std::string HttpHeader() { return cMenuList::HttpHeader() + "Content-type: audio/x-mpegurl; charset=UTF-8\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();
cM3uMenuList(cItemIterator *Iterator, const char* Base); cM3uChannelList(cChannelIterator *Iterator, const char* Base);
virtual ~cM3uMenuList(); virtual ~cM3uChannelList();
};
class cRssMenuList: public cMenuList
{
private:
char *base;
char *html;
enum eRssState { msFirst, msContinue, msLast };
eRssState rssState;
cCharSetConv m_IConv;
public:
virtual std::string HttpHeader() { return cMenuList::HttpHeader() + "Content-type: application/rss+xml\r\n"; };
virtual bool HasNext();
virtual std::string Next();
cRssMenuList(cItemIterator *Iterator, const char *Base, const char *Html);
virtual ~cRssMenuList();
}; };
inline const cChannel* cChannelIterator::SkipFakeGroups(const cChannel* Group) inline const cChannel* cChannelIterator::SkipFakeGroups(const cChannel* Group)
{ {
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
#endif
while (Group && Group->GroupSep() && !*Group->Name()) while (Group && Group->GroupSep() && !*Group->Name())
#if APIVERSNUM >= 20300
Group = Channels->Next(Group);
#else
Group = Channels.Next(Group); Group = Channels.Next(Group);
#endif
return Group; return Group;
} }

View File

@@ -5,17 +5,19 @@
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: streamdev\n" "Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n" "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2014-05-05 22:46+0200\n" "POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: 2008-03-30 02:11+0200\n" "PO-Revision-Date: 2008-03-30 02:11+0200\n"
"Last-Translator: Frank Schmirler <vdrdev@schmirler.de>\n" "Last-Translator: Frank Schmirler <vdrdev@schmirler.de>\n"
"Language-Team: German <vdr@linuxtv.org>\n" "Language-Team: <vdr@linuxtv.org>\n"
"Language: de\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-15\n" "Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
msgid "VDR Streaming Server"
msgstr "VDR Streaming Server"
msgid "Streaming active" msgid "Streaming active"
msgstr "Streamen im Gange" msgstr "Streamen im Gange"
@@ -28,20 +30,26 @@ msgstr "Trennen"
msgid "Suspend" msgid "Suspend"
msgstr "Pausieren" msgstr "Pausieren"
msgid "Offer suspend mode"
msgstr "Pausieren anbieten"
msgid "Always suspended"
msgstr "Immer pausiert"
msgid "Never suspended"
msgstr "Nie pausiert"
msgid "Common Settings" msgid "Common Settings"
msgstr "Allgemeines" msgstr "Allgemeines"
msgid "Hide Mainmenu Entry"
msgstr "Hauptmenüeintrag verstecken"
msgid "Start with Live TV suspended"
msgstr "Live-TV beim Start pausieren"
msgid "Maximum Number of Clients" msgid "Maximum Number of Clients"
msgstr "Maximalanzahl an Clients" msgstr "Maximalanzahl an Clients"
msgid "Live TV buffer delay (ms)" msgid "Suspend behaviour"
msgstr "Live-TV Pufferdauer (ms)" msgstr "Pausierverhalten"
msgid "Client may suspend"
msgstr "Client darf pausieren"
msgid "VDR-to-VDR Server" msgid "VDR-to-VDR Server"
msgstr "VDR-zu-VDR Server" msgstr "VDR-zu-VDR Server"
@@ -55,15 +63,6 @@ msgstr "Port des VDR-zu-VDR Servers"
msgid "Bind to IP" msgid "Bind to IP"
msgstr "Binde an IP" msgstr "Binde an IP"
msgid "Legacy Client Priority"
msgstr "Priorität für alte Clients"
msgid "Client may suspend"
msgstr "Client darf pausieren"
msgid "Loop Prevention"
msgstr "Schleifen verhindern"
msgid "HTTP Server" msgid "HTTP Server"
msgstr "HTTP Server" msgstr "HTTP Server"
@@ -73,9 +72,6 @@ msgstr "HTTP Server starten"
msgid "HTTP Server Port" msgid "HTTP Server Port"
msgstr "Port des HTTP Servers" msgstr "Port des HTTP Servers"
msgid "Priority"
msgstr "Priorität"
msgid "HTTP Streamtype" msgid "HTTP Streamtype"
msgstr "HTTP Streamtyp" msgstr "HTTP Streamtyp"
@@ -90,6 +86,3 @@ msgstr "Port des Multicast Clients"
msgid "Multicast Streamtype" msgid "Multicast Streamtype"
msgstr "Multicast Streamtyp" msgstr "Multicast Streamtyp"
msgid "VDR Streaming Server"
msgstr "VDR Streaming Server"

View File

@@ -6,16 +6,18 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: streamdev 0.5.0\n" "Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n" "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2014-05-05 22:46+0200\n" "POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: 2010-06-19 03:58+0100\n" "PO-Revision-Date: 2010-06-19 03:58+0100\n"
"Last-Translator: Javier Bradineras <jbradi@hotmail.com>\n" "Last-Translator: Javier Bradineras <jbradi@hotmail.com>\n"
"Language-Team: Spanish <vdr@linuxtv.org>\n" "Language-Team: <vdr@linuxtv.org>\n"
"Language: es\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-15\n" "Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
msgid "VDR Streaming Server"
msgstr "Servidor de transmisiones del VDR"
msgid "Streaming active" msgid "Streaming active"
msgstr "Trasmisión activa" msgstr "Trasmisión activa"
@@ -28,20 +30,26 @@ msgstr ""
msgid "Suspend" msgid "Suspend"
msgstr "Suspender" 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" msgid "Common Settings"
msgstr "Configuración común" msgstr "Configuración común"
msgid "Hide Mainmenu Entry"
msgstr ""
msgid "Start with Live TV suspended"
msgstr ""
msgid "Maximum Number of Clients" msgid "Maximum Number of Clients"
msgstr "Numero máximo de clientes" msgstr "Numero máximo de clientes"
msgid "Live TV buffer delay (ms)" msgid "Suspend behaviour"
msgstr "" msgstr "Comportamiento de la suspensión"
msgid "Client may suspend"
msgstr "Permitir suspender al cliente"
msgid "VDR-to-VDR Server" msgid "VDR-to-VDR Server"
msgstr "Servidor VDR-a-VDR" msgstr "Servidor VDR-a-VDR"
@@ -55,15 +63,6 @@ msgstr "Puerto del Servidor VDR-a-VDR"
msgid "Bind to IP" msgid "Bind to IP"
msgstr "IP asociada" msgstr "IP asociada"
msgid "Legacy Client Priority"
msgstr ""
msgid "Client may suspend"
msgstr "Permitir suspender al cliente"
msgid "Loop Prevention"
msgstr ""
msgid "HTTP Server" msgid "HTTP Server"
msgstr "Servidor HTTP" msgstr "Servidor HTTP"
@@ -73,9 +72,6 @@ msgstr "Iniciar Servidor HTTP"
msgid "HTTP Server Port" msgid "HTTP Server Port"
msgstr "Puerto del Servidor HTTP" msgstr "Puerto del Servidor HTTP"
msgid "Priority"
msgstr ""
msgid "HTTP Streamtype" msgid "HTTP Streamtype"
msgstr "Tipo de flujo HTTP" msgstr "Tipo de flujo HTTP"
@@ -90,6 +86,3 @@ msgstr "Puerto del Cliente Multicast"
msgid "Multicast Streamtype" msgid "Multicast Streamtype"
msgstr "Tipo de flujo Multicast" msgstr "Tipo de flujo Multicast"
msgid "VDR Streaming Server"
msgstr "Servidor de transmisiones del VDR"

View File

@@ -1,53 +1,61 @@
# VDR streamdev plugin language source file. # VDR streamdev plugin language source file.
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org # 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. # This file is distributed under the same license as the VDR streamdev package.
# Rolf Ahrenberg, 2008- # Rolf Ahrenberg <rahrenbe@cc.hut.fi>, 2008
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: streamdev 0.5.0\n" "Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n" "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2014-05-05 22:46+0200\n" "POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: 2008-03-30 02:11+0200\n" "PO-Revision-Date: 2008-03-30 02:11+0200\n"
"Last-Translator: Rolf Ahrenberg\n" "Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"
"Language-Team: Finnish <vdr@linuxtv.org>\n" "Language-Team: <vdr@linuxtv.org>\n"
"Language: fi\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
msgid "VDR Streaming Server"
msgstr "VDR-suoratoistopalvelin"
msgid "Streaming active" msgid "Streaming active"
msgstr "Suoratoistopalvelin aktiivinen" msgstr "Suoratoistopalvelin aktiivinen"
msgid "Streamdev Connections" msgid "Streamdev Connections"
msgstr "Suoratoistoyhteydet" msgstr ""
msgid "Disconnect" msgid "Disconnect"
msgstr "Katkaise" msgstr ""
msgid "Suspend" msgid "Suspend"
msgstr "Pysäytä" msgstr "Pysäytä"
msgid "Offer suspend mode"
msgstr "tyrkytä"
msgid "Always suspended"
msgstr "aina"
msgid "Never suspended"
msgstr "ei koskaan"
msgid "Common Settings" msgid "Common Settings"
msgstr "Yleiset asetukset" msgstr "Yleiset asetukset"
msgid "Hide Mainmenu Entry"
msgstr "Piilota valinta päävalikosta"
msgid "Start with Live TV suspended"
msgstr "Käynnistä Live-katselu pysäytettynä"
msgid "Maximum Number of Clients" msgid "Maximum Number of Clients"
msgstr "Suurin sallittu asiakkaiden määrä" msgstr "Suurin sallittu asiakkaiden määrä"
msgid "Live TV buffer delay (ms)" msgid "Suspend behaviour"
msgstr "" msgstr "Pysäytystoiminto"
msgid "Client may suspend"
msgstr "Asiakas saa pysäyttää palvelimen"
msgid "VDR-to-VDR Server" msgid "VDR-to-VDR Server"
msgstr "VDR-palvelin" msgstr "VDR-palvelin"
msgid "Start VDR-to-VDR Server" msgid "Start VDR-to-VDR Server"
msgstr "Käynnistä VDR-palvelin" msgstr "Käynnistä VDR-palvelin"
msgid "VDR-to-VDR Server Port" msgid "VDR-to-VDR Server Port"
msgstr "VDR-palvelimen portti" msgstr "VDR-palvelimen portti"
@@ -55,41 +63,26 @@ msgstr "VDR-palvelimen portti"
msgid "Bind to IP" msgid "Bind to IP"
msgstr "Sido osoitteeseen" msgstr "Sido osoitteeseen"
msgid "Legacy Client Priority"
msgstr "Legacy-asiakkaan prioriteetti"
msgid "Client may suspend"
msgstr "Asiakas saa pysäyttää palvelimen"
msgid "Loop Prevention"
msgstr "Estä asiakaslaitesilmukat"
msgid "HTTP Server" msgid "HTTP Server"
msgstr "HTTP-palvelin" msgstr "HTTP-palvelin"
msgid "Start HTTP Server" msgid "Start HTTP Server"
msgstr "Käynnistä HTTP-palvelin" msgstr "Käynnistä HTTP-palvelin"
msgid "HTTP Server Port" msgid "HTTP Server Port"
msgstr "HTTP-palvelimen portti" msgstr "HTTP-palvelimen portti"
msgid "Priority"
msgstr "Prioriteetti"
msgid "HTTP Streamtype" msgid "HTTP Streamtype"
msgstr "HTTP-lähetysmuoto" msgstr "HTTP-lähetysmuoto"
msgid "Multicast Streaming Server" msgid "Multicast Streaming Server"
msgstr "Multicast-suoratoistopalvelin" msgstr "Multicast-suoratoistopalvelin"
msgid "Start IGMP Server" msgid "Start IGMP Server"
msgstr "Käynnistä IGMP-palvelin" msgstr "Käynnistä IGMP-palvelin"
msgid "Multicast Client Port" msgid "Multicast Client Port"
msgstr "Multicast-portti" msgstr "Multicast-portti"
msgid "Multicast Streamtype" msgid "Multicast Streamtype"
msgstr "Multicast-lähetysmuoto" msgstr "Multicast-lähetysmuoto"
msgid "VDR Streaming Server"
msgstr "VDR-suoratoistopalvelin"

View File

@@ -6,16 +6,18 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: streamdev 0.5.0\n" "Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n" "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2014-05-05 22:46+0200\n" "POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: 2008-03-30 02:11+0200\n" "PO-Revision-Date: 2008-03-30 02:11+0200\n"
"Last-Translator: micky979 <micky979@free.fr>\n" "Last-Translator: micky979 <micky979@free.fr>\n"
"Language-Team: French <vdr@linuxtv.org>\n" "Language-Team: <vdr@linuxtv.org>\n"
"Language: fr\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-15\n" "Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
msgid "VDR Streaming Server"
msgstr "Serveur de streaming VDR"
msgid "Streaming active" msgid "Streaming active"
msgstr "Streaming actif" msgstr "Streaming actif"
@@ -28,20 +30,26 @@ msgstr ""
msgid "Suspend" msgid "Suspend"
msgstr "Suspendre" 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" msgid "Common Settings"
msgstr "Paramètres communs" msgstr "Paramètres communs"
msgid "Hide Mainmenu Entry"
msgstr ""
msgid "Start with Live TV suspended"
msgstr ""
msgid "Maximum Number of Clients" msgid "Maximum Number of Clients"
msgstr "Nombre maximun de clients" msgstr "Nombre maximun de clients"
msgid "Live TV buffer delay (ms)" msgid "Suspend behaviour"
msgstr "" msgstr "Suspendre"
msgid "Client may suspend"
msgstr "Le client peut suspendre"
msgid "VDR-to-VDR Server" msgid "VDR-to-VDR Server"
msgstr "VDR-to-VDR Serveur" msgstr "VDR-to-VDR Serveur"
@@ -55,15 +63,6 @@ msgstr "Port du serveur VDR-to-VDR"
msgid "Bind to IP" msgid "Bind to IP"
msgstr "Attacher aux IP" msgstr "Attacher aux IP"
msgid "Legacy Client Priority"
msgstr ""
msgid "Client may suspend"
msgstr "Le client peut suspendre"
msgid "Loop Prevention"
msgstr ""
msgid "HTTP Server" msgid "HTTP Server"
msgstr "Serveur HTTP" msgstr "Serveur HTTP"
@@ -73,9 +72,6 @@ msgstr "D
msgid "HTTP Server Port" msgid "HTTP Server Port"
msgstr "Port du serveur HTTP" msgstr "Port du serveur HTTP"
msgid "Priority"
msgstr ""
msgid "HTTP Streamtype" msgid "HTTP Streamtype"
msgstr "Type de Streaming HTTP" msgstr "Type de Streaming HTTP"
@@ -90,6 +86,3 @@ msgstr ""
msgid "Multicast Streamtype" msgid "Multicast Streamtype"
msgstr "" msgstr ""
msgid "VDR Streaming Server"
msgstr "Serveur de streaming VDR"

View File

@@ -8,42 +8,50 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: streamdev 0.5.0\n" "Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n" "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2014-05-05 22:46+0200\n" "POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: 2012-06-12 19:57+0100\n" "PO-Revision-Date: 2010-06-19 03:58+0100\n"
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n" "Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
"Language-Team: Italian <vdr@linuxtv.org>\n" "Language-Team: <vdr@linuxtv.org>\n"
"Language: it\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
msgid "VDR Streaming Server"
msgstr "Server trasmissione VDR"
msgid "Streaming active" msgid "Streaming active"
msgstr "Trasmissione attiva" msgstr "Trasmissione attiva"
msgid "Streamdev Connections" msgid "Streamdev Connections"
msgstr "Connessioni Streamdev" msgstr ""
msgid "Disconnect" msgid "Disconnect"
msgstr "Disconnetti" msgstr ""
msgid "Suspend" msgid "Suspend"
msgstr "Sospendi" 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" msgid "Common Settings"
msgstr "Impostazioni comuni" msgstr "Impostazioni comuni"
msgid "Hide Mainmenu Entry"
msgstr "Nascondi voce menu principale"
msgid "Start with Live TV suspended"
msgstr ""
msgid "Maximum Number of Clients" msgid "Maximum Number of Clients"
msgstr "Numero massimo di Client" msgstr "Numero massimo di Client"
msgid "Live TV buffer delay (ms)" msgid "Suspend behaviour"
msgstr "" msgstr "Tipo sospensione"
msgid "Client may suspend"
msgstr "Permetti sospensione al Client"
msgid "VDR-to-VDR Server" msgid "VDR-to-VDR Server"
msgstr "Server VDR-a-VDR" msgstr "Server VDR-a-VDR"
@@ -57,15 +65,6 @@ msgstr "Porta Server VDR-a-VDR"
msgid "Bind to IP" msgid "Bind to IP"
msgstr "IP associati" msgstr "IP associati"
msgid "Legacy Client Priority"
msgstr "Priorità nativa client"
msgid "Client may suspend"
msgstr "Permetti sospensione al Client"
msgid "Loop Prevention"
msgstr "Evita ciclo"
msgid "HTTP Server" msgid "HTTP Server"
msgstr "Server HTTP" msgstr "Server HTTP"
@@ -75,9 +74,6 @@ msgstr "Avvia Server HTTP"
msgid "HTTP Server Port" msgid "HTTP Server Port"
msgstr "Porta Server HTTP" msgstr "Porta Server HTTP"
msgid "Priority"
msgstr "Priorità"
msgid "HTTP Streamtype" msgid "HTTP Streamtype"
msgstr "Tipo flusso HTTP" msgstr "Tipo flusso HTTP"
@@ -92,6 +88,3 @@ msgstr "Porta Client Multicast"
msgid "Multicast Streamtype" msgid "Multicast Streamtype"
msgstr "Tipo flusso Multicast" msgstr "Tipo flusso Multicast"
msgid "VDR Streaming Server"
msgstr "Server trasmissione VDR"

View File

@@ -6,16 +6,18 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: streamdev 0.5.0\n" "Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n" "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2014-05-05 22:46+0200\n" "POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: 2009-11-26 21:57+0200\n" "PO-Revision-Date: 2009-11-26 21:57+0200\n"
"Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n" "Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
"Language-Team: Lithuanian <vdr@linuxtv.org>\n" "Language-Team: Lietuvių\n"
"Language: lt\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
msgid "VDR Streaming Server"
msgstr "VDR transliavimo serveris"
msgid "Streaming active" msgid "Streaming active"
msgstr "Transliavimas vyksta" msgstr "Transliavimas vyksta"
@@ -28,20 +30,26 @@ msgstr ""
msgid "Suspend" msgid "Suspend"
msgstr "Pristabdyti" 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" msgid "Common Settings"
msgstr "Bendri nustatymai" msgstr "Bendri nustatymai"
msgid "Hide Mainmenu Entry"
msgstr ""
msgid "Start with Live TV suspended"
msgstr ""
msgid "Maximum Number of Clients" msgid "Maximum Number of Clients"
msgstr "Maksimalus klientų skaičius" msgstr "Maksimalus klientų skaičius"
msgid "Live TV buffer delay (ms)" msgid "Suspend behaviour"
msgstr "" msgstr "Pristabdyti veikimą"
msgid "Client may suspend"
msgstr "Klientas gali pristabdyti"
msgid "VDR-to-VDR Server" msgid "VDR-to-VDR Server"
msgstr "VDR-su-VDR Serveris" msgstr "VDR-su-VDR Serveris"
@@ -55,15 +63,6 @@ msgstr "VDR-su-VDR Serverio portas"
msgid "Bind to IP" msgid "Bind to IP"
msgstr "Pririšti IP" msgstr "Pririšti IP"
msgid "Legacy Client Priority"
msgstr ""
msgid "Client may suspend"
msgstr "Klientas gali pristabdyti"
msgid "Loop Prevention"
msgstr ""
msgid "HTTP Server" msgid "HTTP Server"
msgstr "HTTP Serveris" msgstr "HTTP Serveris"
@@ -73,9 +72,6 @@ msgstr "Paleisti HTTP serverį"
msgid "HTTP Server Port" msgid "HTTP Server Port"
msgstr "HTTP serverio portas" msgstr "HTTP serverio portas"
msgid "Priority"
msgstr ""
msgid "HTTP Streamtype" msgid "HTTP Streamtype"
msgstr "HTTP transliavimo tipas" msgstr "HTTP transliavimo tipas"
@@ -90,6 +86,3 @@ msgstr "Multicast kliento portas"
msgid "Multicast Streamtype" msgid "Multicast Streamtype"
msgstr "Multicast transliavimo tipas" msgstr "Multicast transliavimo tipas"
msgid "VDR Streaming Server"
msgstr "VDR transliavimo serveris"

View File

@@ -1,96 +0,0 @@
# VDR streamdev plugin language source file.
# Copyright (C) 2008 streamdev development team. See
# This file is distributed under the same license as the VDR streamdev package.
# Tomasz Maciej Nowak, 2014
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-streamdev-server 0.6.1-git\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
"POT-Creation-Date: 2014-11-20 14:11+0100\n"
"PO-Revision-Date: 2014-11-24 18:02+0100\n"
"Last-Translator: Tomasz Maciej Nowak <tomek_n@o2.pl>\n"
"Language-Team: Polish <vdr@linuxtv.org>\n"
"Language: pl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-2\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.6.10\n"
msgid "Streaming active"
msgstr "Strumieniowanie aktywne"
msgid "Streamdev Connections"
msgstr "Po³±czenia Streamdev"
msgid "Disconnect"
msgstr "Roz³±cz"
msgid "Suspend"
msgstr "Wstrzymaj"
msgid "Common Settings"
msgstr "Ustawienia ogólne"
msgid "Hide Mainmenu Entry"
msgstr "Ukryj pozycjê w g³ównym menu"
msgid "Start with Live TV suspended"
msgstr "Uruchom ze wstrzyman± telewizj±"
msgid "Maximum Number of Clients"
msgstr "Maksymalna liczba klientów"
msgid "Live TV buffer delay (ms)"
msgstr "Bufor opó¼nieñ telewizji (ms)"
msgid "VDR-to-VDR Server"
msgstr "Serwer VDR do VDR"
msgid "Start VDR-to-VDR Server"
msgstr "Uruchom serwer VDR do VDR"
msgid "VDR-to-VDR Server Port"
msgstr "Port serwera VDR do VDR"
msgid "Bind to IP"
msgstr "Przypisz do adresu"
msgid "Legacy Client Priority"
msgstr "Priorytet dla starego klienta"
msgid "Client may suspend"
msgstr "Klient mo¿e wstrzymaæ"
msgid "Loop Prevention"
msgstr "Zapobieganie pêtli"
msgid "HTTP Server"
msgstr "Serwer HTTP"
msgid "Start HTTP Server"
msgstr "Uruchom serwer HTTP"
msgid "HTTP Server Port"
msgstr "Port serwera HTTP"
msgid "Priority"
msgstr "Priorytet"
msgid "HTTP Streamtype"
msgstr "Typ strumienia HTTP"
msgid "Multicast Streaming Server"
msgstr "Serwer strumienia Multicast"
msgid "Start IGMP Server"
msgstr "Uruchom serwer Multicast"
msgid "Multicast Client Port"
msgstr "Port klienta Multicast"
msgid "Multicast Streamtype"
msgstr "Typ strumienia Multicast"
msgid "VDR Streaming Server"
msgstr "Serwer strumieniuj±cy VDR"

View File

@@ -6,16 +6,18 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: streamdev 0.5.0\n" "Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n" "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2014-05-05 22:46+0200\n" "POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: 2008-06-26 15:36+0100\n" "PO-Revision-Date: 2008-06-26 15:36+0100\n"
"Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n" "Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n"
"Language-Team: Russian <vdr@linuxtv.org>\n" "Language-Team: <vdr@linuxtv.org>\n"
"Language: ru\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-5\n" "Content-Type: text/plain; charset=ISO-8859-5\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
msgid "VDR Streaming Server"
msgstr "VDR Streaming áÕàÒÕà"
msgid "Streaming active" msgid "Streaming active"
msgstr "ÁâàØÜØÝÓ ÐÚâØÒÕÝ" msgstr "ÁâàØÜØÝÓ ÐÚâØÒÕÝ"
@@ -28,20 +30,26 @@ msgstr ""
msgid "Suspend" msgid "Suspend"
msgstr "¾áâÐÝÞÒÚÐ" msgstr "¾áâÐÝÞÒÚÐ"
msgid "Offer suspend mode"
msgstr "¿àÕÔÛÐÓÐâì ÞáâÐÝÞÒÚã"
msgid "Always suspended"
msgstr "²áÕÓÔÐ ÞáâÐÝÞÒÛÕÝ"
msgid "Never suspended"
msgstr "½ØÚÞÓÔÐ ÝÕ ÞáâÐÝÞÒÛÕÝ"
msgid "Common Settings" msgid "Common Settings"
msgstr "½ÐáâàÞÙÚØ" msgstr "½ÐáâàÞÙÚØ"
msgid "Hide Mainmenu Entry"
msgstr ""
msgid "Start with Live TV suspended"
msgstr ""
msgid "Maximum Number of Clients" msgid "Maximum Number of Clients"
msgstr "¼ÐÚá. ÚÞÛØçÕáâÒÞ ÚÛØÕÝâÞÒ" msgstr "¼ÐÚá. ÚÞÛØçÕáâÒÞ ÚÛØÕÝâÞÒ"
msgid "Live TV buffer delay (ms)" msgid "Suspend behaviour"
msgstr "" msgstr "¿ÞÒÕÔÕÝØÕ ÞáâÐÝÞÒÚØ"
msgid "Client may suspend"
msgstr "ºÛØÕÝâ ÜÞÖÕâ ÞáâÐÝÐÒÛØÒÐâì"
msgid "VDR-to-VDR Server" msgid "VDR-to-VDR Server"
msgstr "VDR-to-VDR áÕàÒÕà" msgstr "VDR-to-VDR áÕàÒÕà"
@@ -55,15 +63,6 @@ msgstr "VDR-to-VDR
msgid "Bind to IP" msgid "Bind to IP"
msgstr "¿àØáÞÕÔØÝØâìáï Ú IP" msgstr "¿àØáÞÕÔØÝØâìáï Ú IP"
msgid "Legacy Client Priority"
msgstr ""
msgid "Client may suspend"
msgstr "ºÛØÕÝâ ÜÞÖÕâ ÞáâÐÝÐÒÛØÒÐâì"
msgid "Loop Prevention"
msgstr ""
msgid "HTTP Server" msgid "HTTP Server"
msgstr "HTTP áÕàÒÕà" msgstr "HTTP áÕàÒÕà"
@@ -73,9 +72,6 @@ msgstr "
msgid "HTTP Server Port" msgid "HTTP Server Port"
msgstr "HTTP áÕàÒÕà ¿Þàâ" msgstr "HTTP áÕàÒÕà ¿Þàâ"
msgid "Priority"
msgstr ""
msgid "HTTP Streamtype" msgid "HTTP Streamtype"
msgstr "ÂØß HTTP ßÞâÞÚÐ" msgstr "ÂØß HTTP ßÞâÞÚÐ"
@@ -90,6 +86,3 @@ msgstr ""
msgid "Multicast Streamtype" msgid "Multicast Streamtype"
msgstr "" msgstr ""
msgid "VDR Streaming Server"
msgstr "VDR Streaming áÕàÒÕà"

85
server/po/sk_SK.po Executable file → Normal file
View File

@@ -6,92 +6,85 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: streamdev_SK\n" "Project-Id-Version: streamdev_SK\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n" "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2014-05-05 22:46+0200\n" "POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: 2013-11-22 23:39+0100\n" "PO-Revision-Date: \n"
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n" "Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
"Language-Team: Slovak <hrala.milan@gmail.com>\n" "Language-Team: Slovak <hrala.milan@gmail.com>\n"
"Language: sk\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-2\n" "Content-Type: text/plain; charset=iso-8859-2\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Slovak\n" "X-Poedit-Language: Slovak\n"
"X-Poedit-Country: SLOVAKIA\n" "X-Poedit-Country: SLOVAKIA\n"
msgid "VDR Streaming Server"
msgstr "VDR prúdový server"
msgid "Streaming active" msgid "Streaming active"
msgstr "Streamovanie aktívne" msgstr "streamovanie aktivne"
msgid "Streamdev Connections" msgid "Streamdev Connections"
msgstr "Streamdev spojenia" msgstr ""
msgid "Disconnect" msgid "Disconnect"
msgstr "Odpoji»" msgstr ""
msgid "Suspend" msgid "Suspend"
msgstr "Pozastav" 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" msgid "Common Settings"
msgstr "V¹eobecné nastavenia" msgstr "V¹eobecné nastavenia"
msgid "Hide Mainmenu Entry"
msgstr "Schova» v hlavnom menu"
msgid "Start with Live TV suspended"
msgstr "Pozastavi» Live TV pri ¹tarte"
msgid "Maximum Number of Clients" msgid "Maximum Number of Clients"
msgstr "Maximály poèet klientov" msgstr "Maximály poèet klientov"
msgid "Live TV buffer delay (ms)" msgid "Suspend behaviour"
msgstr "" msgstr "Správanie preru¹enia"
msgid "VDR-to-VDR Server"
msgstr "Prenos z VDR do VDR"
msgid "Start VDR-to-VDR Server"
msgstr "Spusti» prenos z VDR do VDR"
msgid "VDR-to-VDR Server Port"
msgstr "Port servera prenosu z VDR do VDR"
msgid "Bind to IP"
msgstr "Viaza» na IP"
msgid "Legacy Client Priority"
msgstr "Dodatoèná priorita klienta"
msgid "Client may suspend" msgid "Client may suspend"
msgstr "Klient mô¾e server pozastavi»" msgstr "Klient mô¾e pozastavi»"
msgid "Loop Prevention" msgid "VDR-to-VDR Server"
msgstr "Prevencia sluèky" 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" msgid "HTTP Server"
msgstr "HTTP server " msgstr "server HTTP"
msgid "Start HTTP Server" msgid "Start HTTP Server"
msgstr "Spusti» HTTP Server" msgstr "Spusti» HTTP Server"
msgid "HTTP Server Port" msgid "HTTP Server Port"
msgstr "Port HTTP servera" msgstr "Port serveru HTTP"
msgid "Priority"
msgstr "Priorita"
msgid "HTTP Streamtype" msgid "HTTP Streamtype"
msgstr "Typ HTTP streamu" msgstr "typ prúdu HTTP"
msgid "Multicast Streaming Server" msgid "Multicast Streaming Server"
msgstr "Streamovanie Multicastového servera" msgstr "Multicast prúdový server"
msgid "Start IGMP Server" msgid "Start IGMP Server"
msgstr "Spusti» IGMP Server" msgstr "Spusti» IGMP Server"
msgid "Multicast Client Port" msgid "Multicast Client Port"
msgstr "Port Multicast klienta" msgstr "Port klienta Multicast"
msgid "Multicast Streamtype" msgid "Multicast Streamtype"
msgstr "Typ Multicast streamu" msgstr "Multicast typ streamu"
msgid "VDR Streaming Server"
msgstr "VDR server streamovania"

View File

@@ -24,30 +24,27 @@
// for TSPLAY patch detection // for TSPLAY patch detection
#include "vdr/device.h" #include "vdr/device.h"
#undef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600 #define _XOPEN_SOURCE 600
#include <fcntl.h> #include <fcntl.h>
RecPlayer::RecPlayer(const char* FileName) RecPlayer::RecPlayer(cRecording* rec)
{ {
file = NULL; file = NULL;
fileOpen = 0; fileOpen = 0;
lastPosition = 0; lastPosition = 0;
recording = new cRecording(FileName); recording = rec;
for(int i = 1; i < 1000; i++) segments[i] = NULL; for(int i = 1; i < 1000; i++) segments[i] = NULL;
// FIXME find out max file path / name lengths // FIXME find out max file path / name lengths
indexFile = new cIndexFile(recording->FileName(), false, recording->IsPesRecording()); #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!"); if (!indexFile) esyslog("ERROR: Streamdev: Failed to create indexfile!");
scan(); scan();
parser = new cPatPmtParser();
unsigned char buffer[2 * TS_SIZE];
unsigned long l = getBlock(buffer, 0UL, sizeof(buffer));
if (!l || !parser->ParsePatPmt(buffer, (int) l))
esyslog("ERROR: Streamdev: Failed to parse PAT/PMT");
} }
void RecPlayer::scan() void RecPlayer::scan()
@@ -64,18 +61,24 @@ void RecPlayer::scan()
for(i = 1; i < 1000; i++) 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); snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), i);
file = fopen(fileName, "r"); file = fopen(fileName, "r");
if (!file) { if (!file) {
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i); snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
file = fopen(fileName, "r"); file = fopen(fileName, "r");
} }
#endif
if (!file) break; if (!file) break;
segments[i] = new Segment(); segments[i] = new Segment();
segments[i]->start = totalLength; segments[i]->start = totalLength;
fseek(file, 0, SEEK_END); fseek(file, 0, SEEK_END);
totalLength += ftello(file); totalLength += ftell(file);
totalFrames = indexFile->Last(); totalFrames = indexFile->Last();
//log->log("RecPlayer", Log::DEBUG, "File %i found, totalLength now %llu, numFrames = %lu", i, totalLength, totalFrames); //log->log("RecPlayer", Log::DEBUG, "File %i found, totalLength now %llu, numFrames = %lu", i, totalLength, totalFrames);
segments[i]->end = totalLength; segments[i]->end = totalLength;
@@ -91,9 +94,6 @@ RecPlayer::~RecPlayer()
int i = 1; int i = 1;
while(segments[i++]) delete segments[i]; while(segments[i++]) delete segments[i];
if (file) fclose(file); if (file) fclose(file);
delete indexFile;
delete recording;
delete parser;
} }
int RecPlayer::openFile(int index) int RecPlayer::openFile(int index)
@@ -102,6 +102,7 @@ int RecPlayer::openFile(int index)
char fileName[2048]; char fileName[2048];
#if APIVERSNUM >= 10703 || defined(TSPLAY_PATCH_VERSION)
snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), index); snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), index);
isyslog("openFile called for index %i string:%s", index, fileName); isyslog("openFile called for index %i string:%s", index, fileName);
@@ -111,6 +112,7 @@ int RecPlayer::openFile(int index)
fileOpen = index; fileOpen = index;
return 1; return 1;
} }
#endif
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), index); snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), index);
isyslog("openFile called for index %i string:%s", index, fileName); isyslog("openFile called for index %i string:%s", index, fileName);
@@ -176,7 +178,7 @@ unsigned long RecPlayer::getBlock(unsigned char* buffer, uint64_t position, unsi
uint32_t yetToGet = amount; uint32_t yetToGet = amount;
uint32_t got = 0; uint32_t got = 0;
uint32_t getFromThisSegment = 0; uint32_t getFromThisSegment = 0;
uint64_t filePosition; uint32_t filePosition;
while(got < amount) while(got < amount)
{ {
@@ -198,7 +200,7 @@ unsigned long RecPlayer::getBlock(unsigned char* buffer, uint64_t position, unsi
if (fread(&buffer[got], getFromThisSegment, 1, file) != 1) return 0; // umm, big problem. 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 // Tell linux not to bother keeping the data in the FS cache
posix_fadvise(fileno(file), filePosition, getFromThisSegment, POSIX_FADV_DONTNEED); posix_fadvise(file->_fileno, filePosition, getFromThisSegment, POSIX_FADV_DONTNEED);
got += getFromThisSegment; got += getFromThisSegment;
currentPosition += getFromThisSegment; currentPosition += getFromThisSegment;
@@ -219,47 +221,17 @@ cRecording* RecPlayer::getCurrentRecording()
return recording; return recording;
} }
#if VDRVERSNUM < 10732
#define ALIGNED_POS(x) (positionFromFrameNumber(indexFile->GetNextIFrame(x, 1)))
#else
#define ALIGNED_POS(x) (positionFromFrameNumber(indexFile->GetClosestIFrame(x)))
#endif
uint64_t RecPlayer::positionFromResume(int ResumeID)
{
int resumeBackup = Setup.ResumeID;
Setup.ResumeID = ResumeID;
cResumeFile resume(recording->FileName(), recording->IsPesRecording());
Setup.ResumeID = resumeBackup;
return ALIGNED_POS(resume.Read());
}
uint64_t RecPlayer::positionFromMark(int MarkIndex)
{
cMarks marks;
if (marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording()) && marks.Count()) {
cMark *mark = marks.cConfig<cMark>::Get(MarkIndex);
if (mark)
return ALIGNED_POS(mark->Position());
}
return 0;
}
uint64_t RecPlayer::positionFromTime(int Seconds)
{
return ALIGNED_POS(SecondsToFrames(Seconds, recording->FramesPerSecond()));
}
uint64_t RecPlayer::positionFromPercent(int Percent)
{
return ALIGNED_POS(getLengthFrames() * Percent / 100L);
}
uint64_t RecPlayer::positionFromFrameNumber(uint32_t frameNumber) uint64_t RecPlayer::positionFromFrameNumber(uint32_t frameNumber)
{ {
if (!indexFile) return 0; if (!indexFile) return 0;
#if VDRVERSNUM >= 10703 || defined(TSPLAY_PATCH_VERSION)
uint16_t retFileNumber; uint16_t retFileNumber;
off_t retFileOffset; off_t retFileOffset;
#else
uchar retFileNumber;
int retFileOffset;
#endif
if (!indexFile->Get((int)frameNumber, &retFileNumber, &retFileOffset)) if (!indexFile->Get((int)frameNumber, &retFileNumber, &retFileOffset))
{ {
@@ -290,7 +262,7 @@ uint32_t RecPlayer::frameNumberFromPosition(uint64_t position)
if ((position >= segments[segmentNumber]->start) && (position < segments[segmentNumber]->end)) break; if ((position >= segments[segmentNumber]->start) && (position < segments[segmentNumber]->end)) break;
// position is in this block // position is in this block
} }
uint64_t askposition = position - segments[segmentNumber]->start; uint32_t askposition = position - segments[segmentNumber]->start;
return indexFile->Get((int)segmentNumber, askposition); return indexFile->Get((int)segmentNumber, askposition);
} }

View File

@@ -23,7 +23,6 @@
#include <stdio.h> #include <stdio.h>
#include <vdr/recording.h> #include <vdr/recording.h>
#include <vdr/remux.h>
#include "server/streamer.h" #include "server/streamer.h"
@@ -37,7 +36,7 @@ class Segment
class RecPlayer class RecPlayer
{ {
public: public:
RecPlayer(const char* FileName); RecPlayer(cRecording* rec);
~RecPlayer(); ~RecPlayer();
uint64_t getLengthBytes(); uint64_t getLengthBytes();
uint32_t getLengthFrames(); uint32_t getLengthFrames();
@@ -45,12 +44,7 @@ class RecPlayer
int openFile(int index); int openFile(int index);
uint64_t getLastPosition(); uint64_t getLastPosition();
cRecording* getCurrentRecording(); cRecording* getCurrentRecording();
const cPatPmtParser* getPatPmtData() { return parser; }
void scan(); void scan();
uint64_t positionFromResume(int ResumeID);
uint64_t positionFromMark(int MarkIndex);
uint64_t positionFromTime(int Seconds);
uint64_t positionFromPercent(int Percent);
uint64_t positionFromFrameNumber(uint32_t frameNumber); uint64_t positionFromFrameNumber(uint32_t frameNumber);
uint32_t frameNumberFromPosition(uint64_t position); uint32_t frameNumberFromPosition(uint64_t position);
bool getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength); bool getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength);
@@ -58,7 +52,6 @@ class RecPlayer
private: private:
cRecording* recording; cRecording* recording;
cIndexFile* indexFile; cIndexFile* indexFile;
cPatPmtParser* parser;
FILE* file; FILE* file;
int fileOpen; int fileOpen;
Segment* segments[1000]; Segment* segments[1000];

View File

@@ -1,98 +0,0 @@
#include "remux/ts2ps.h"
#include "remux/ts2pes.h"
#include "remux/ts2es.h"
#include "remux/extern.h"
#include <vdr/ringbuffer.h>
#include "server/recstreamer.h"
#include "server/connection.h"
#include "common.h"
using namespace Streamdev;
// --- cStreamdevRecStreamer -------------------------------------------------
cStreamdevRecStreamer::cStreamdevRecStreamer(const cServerConnection *Connection, RecPlayer *RecPlayer, eStreamType StreamType, int64_t StartOffset, const int *Apid, const int *Dpid):
cStreamdevStreamer("streamdev-recstreaming", Connection),
m_RecPlayer(RecPlayer),
m_StartOffset(StartOffset),
m_From(0L)
{
Dprintf("New rec streamer\n");
m_To = (int64_t) m_RecPlayer->getLengthBytes() - StartOffset - 1;
const cPatPmtParser *parser = RecPlayer->getPatPmtData();
const int *Apids = Apid ? Apid : parser->Apids();
const int *Dpids = Dpid ? Dpid : parser->Dpids();
switch (StreamType) {
case stES:
{
int pid = parser->Vpid();
if (Apid && Apid[0])
pid = Apid[0];
else if (Dpid && Dpid[0])
pid = Dpid[0];
SetRemux(new cTS2ESRemux(pid));
}
break;
case stPES:
if (!m_RecPlayer->getCurrentRecording()->IsPesRecording())
SetRemux(new cTS2PESRemux(parser->Vpid(), Apids, Dpids, parser->Spids()));
break;
#ifdef STREAMDEV_PS
case stPS:
SetRemux(new cTS2PSRemux(parser->Vpid(), Apids, Dpids, parser->Spids()));
break;
#endif
case stEXT:
SetRemux(new cExternRemux(Connection, parser, Apids, Dpids));
break;
default:
break;
}
}
cStreamdevRecStreamer::~cStreamdevRecStreamer()
{
Dprintf("Desctructing rec streamer\n");
Stop();
}
int64_t cStreamdevRecStreamer::SetRange(int64_t &From, int64_t &To)
{
int64_t l = (int64_t) GetLength();
if (From < 0L) {
From += l;
if (From < 0L)
From = 0L;
To = l - 1;
}
else {
if (To < 0L)
To += l;
else if (To >= l)
To = l - 1;
if (From > To) {
// invalid range - return whole content
From = 0L;
To = l - 1;
}
}
m_From = From;
m_To = To;
return m_To - m_From + 1;
}
uchar* cStreamdevRecStreamer::GetFromReceiver(int &Count)
{
if (m_From <= m_To) {
Count = (int) m_RecPlayer->getBlock(m_Buffer, m_StartOffset + m_From, sizeof(m_Buffer));
return m_Buffer;
}
return NULL;
}
cString cStreamdevRecStreamer::ToText() const
{
return "REPLAY";
}

View File

@@ -1,34 +0,0 @@
#ifndef VDR_STREAMDEV_RECSTREAMER_H
#define VDR_STREAMDEV_RECSTREAMER_H
#include "common.h"
#include "server/streamer.h"
#include "server/recplayer.h"
#define RECBUFSIZE (174 * TS_SIZE)
// --- cStreamdevRecStreamer -------------------------------------------------
class cStreamdevRecStreamer: public cStreamdevStreamer {
private:
//Streamdev::cTSRemux *m_Remux;
RecPlayer *m_RecPlayer;
int64_t m_StartOffset;
int64_t m_From;
int64_t m_To;
uchar m_Buffer[RECBUFSIZE];
protected:
virtual uchar* GetFromReceiver(int &Count);
virtual void DelFromReceiver(int Count) { m_From += Count; };
public:
virtual bool IsReceiving(void) const { return m_From <= m_To; };
uint64_t GetLength() { return m_RecPlayer->getLengthBytes() - m_StartOffset; }
int64_t SetRange(int64_t &From, int64_t &To);
virtual cString ToText() const;
cStreamdevRecStreamer(const cServerConnection *Connection, RecPlayer *RecPlayer, eStreamType StreamType, int64_t StartOffset = 0L, const int *Apids = NULL, const int *Dpids = NULL);
virtual ~cStreamdevRecStreamer();
};
#endif // VDR_STREAMDEV_RECSTREAMER_H

View File

@@ -177,11 +177,7 @@ void cStreamdevServer::Action(void)
} }
} }
#if APIVERSNUM >= 20300
cList<cServerConnection>& cStreamdevServer::Clients(cThreadLock& Lock)
#else
const cList<cServerConnection>& cStreamdevServer::Clients(cThreadLock& Lock) const cList<cServerConnection>& cStreamdevServer::Clients(cThreadLock& Lock)
#endif
{ {
Lock.Lock(m_Instance); Lock.Lock(m_Instance);
return m_Clients; return m_Clients;

View File

@@ -37,11 +37,7 @@ public:
static void Destruct(void); static void Destruct(void);
static bool Active(void); static bool Active(void);
#if APIVERSNUM >= 20300
static cList<cServerConnection>& Clients(cThreadLock& Lock);
#else
static const cList<cServerConnection>& Clients(cThreadLock& Lock); static const cList<cServerConnection>& Clients(cThreadLock& Lock);
#endif
}; };
inline bool cStreamdevServer::Active(void) inline bool cStreamdevServer::Active(void)

View File

@@ -10,22 +10,16 @@
cStreamdevServerSetup StreamdevServerSetup; cStreamdevServerSetup StreamdevServerSetup;
cStreamdevServerSetup::cStreamdevServerSetup(void) { cStreamdevServerSetup::cStreamdevServerSetup(void) {
HideMenuEntry = false;
MaxClients = 5; MaxClients = 5;
StartSuspended = ssAuto;
LiveBufferMs = 0;
StartVTPServer = true; StartVTPServer = true;
VTPServerPort = 2004; VTPServerPort = 2004;
VTPPriority = 0;
LoopPrevention = false;
StartHTTPServer = true; StartHTTPServer = true;
HTTPServerPort = 3000; HTTPServerPort = 3000;
HTTPPriority = 0;
HTTPStreamType = stTS; HTTPStreamType = stTS;
StartIGMPServer = false; StartIGMPServer = false;
IGMPClientPort = 1234; IGMPClientPort = 1234;
IGMPPriority = 0;
IGMPStreamType = stTS; IGMPStreamType = stTS;
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");
@@ -33,25 +27,19 @@ cStreamdevServerSetup::cStreamdevServerSetup(void) {
} }
bool cStreamdevServerSetup::SetupParse(const char *Name, const char *Value) { bool cStreamdevServerSetup::SetupParse(const char *Name, const char *Value) {
if (strcmp(Name, "HideMenuEntry") == 0) HideMenuEntry = atoi(Value); if (strcmp(Name, "MaxClients") == 0) MaxClients = atoi(Value);
else if (strcmp(Name, "MaxClients") == 0) MaxClients = atoi(Value);
else if (strcmp(Name, "StartSuspended") == 0) StartSuspended = atoi(Value);
else if (strcmp(Name, "LiveBufferMs") == 0) LiveBufferMs = atoi(Value);
else if (strcmp(Name, "StartServer") == 0) StartVTPServer = atoi(Value); else if (strcmp(Name, "StartServer") == 0) StartVTPServer = atoi(Value);
else if (strcmp(Name, "ServerPort") == 0) VTPServerPort = atoi(Value); else if (strcmp(Name, "ServerPort") == 0) VTPServerPort = atoi(Value);
else if (strcmp(Name, "VTPPriority") == 0) VTPPriority = atoi(Value);
else if (strcmp(Name, "VTPBindIP") == 0) strcpy(VTPBindIP, Value); else if (strcmp(Name, "VTPBindIP") == 0) strcpy(VTPBindIP, Value);
else if (strcmp(Name, "LoopPrevention") == 0) LoopPrevention = atoi(Value);
else if (strcmp(Name, "StartHTTPServer") == 0) StartHTTPServer = atoi(Value); else if (strcmp(Name, "StartHTTPServer") == 0) StartHTTPServer = atoi(Value);
else if (strcmp(Name, "HTTPServerPort") == 0) HTTPServerPort = atoi(Value); else if (strcmp(Name, "HTTPServerPort") == 0) HTTPServerPort = atoi(Value);
else if (strcmp(Name, "HTTPPriority") == 0) HTTPPriority = 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, "StartIGMPServer") == 0) StartIGMPServer = atoi(Value);
else if (strcmp(Name, "IGMPClientPort") == 0) IGMPClientPort = atoi(Value); else if (strcmp(Name, "IGMPClientPort") == 0) IGMPClientPort = atoi(Value);
else if (strcmp(Name, "IGMPPriority") == 0) IGMPPriority = atoi(Value);
else if (strcmp(Name, "IGMPStreamType") == 0) IGMPStreamType = atoi(Value); else if (strcmp(Name, "IGMPStreamType") == 0) IGMPStreamType = atoi(Value);
else if (strcmp(Name, "IGMPBindIP") == 0) strcpy(IGMPBindIP, Value); else if (strcmp(Name, "IGMPBindIP") == 0) strcpy(IGMPBindIP, 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;
@@ -65,6 +53,12 @@ const char* cStreamdevServerMenuSetupPage::StreamTypes[st_Count - 1] = {
"EXT" "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;
@@ -75,44 +69,34 @@ cStreamdevServerMenuSetupPage::~cStreamdevServerMenuSetupPage() {
} }
void cStreamdevServerMenuSetupPage::Set(void) { void cStreamdevServerMenuSetupPage::Set(void) {
static const char *StartSuspendedItems[ss_Count] = static const char* modes[sm_Count];
{ for (int i = 0; i < sm_Count; i++)
trVDR("no"), modes[i] = tr(SuspendModes[i]);
trVDR("yes"),
trVDR("auto")
};
int current = Current(); int current = Current();
Clear(); Clear();
AddCategory (tr("Common Settings")); AddCategory (tr("Common Settings"));
Add(new cMenuEditBoolItem(tr("Hide Mainmenu Entry"), &m_NewSetup.HideMenuEntry));
Add(new cMenuEditStraItem(tr("Start with Live TV suspended"), &m_NewSetup.StartSuspended, ss_Count, StartSuspendedItems));
Add(new cMenuEditIntItem (tr("Maximum Number of Clients"), &m_NewSetup.MaxClients, 0, 100)); Add(new cMenuEditIntItem (tr("Maximum Number of Clients"), &m_NewSetup.MaxClients, 0, 100));
Add(new cMenuEditIntItem (tr("Live TV buffer delay (ms)"), &m_NewSetup.LiveBufferMs, 0, 1500));
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")); AddCategory (tr("VDR-to-VDR Server"));
Add(new cMenuEditBoolItem(tr("Start VDR-to-VDR Server"), &m_NewSetup.StartVTPServer)); 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 cMenuEditIntItem (tr("VDR-to-VDR Server Port"), &m_NewSetup.VTPServerPort, 0, 65535));
Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.VTPBindIP)); Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.VTPBindIP));
Add(new cMenuEditIntItem (tr("Legacy Client Priority"), &m_NewSetup.VTPPriority, MINPRIORITY, MAXPRIORITY));
Add(new cMenuEditBoolItem(tr("Client may suspend"), &m_NewSetup.AllowSuspend));
if (cPluginManager::CallFirstService(LOOP_PREVENTION_SERVICE))
Add(new cMenuEditBoolItem(tr("Loop Prevention"), &m_NewSetup.LoopPrevention));
AddCategory (tr("HTTP Server")); AddCategory (tr("HTTP Server"));
Add(new cMenuEditBoolItem(tr("Start HTTP Server"), &m_NewSetup.StartHTTPServer)); Add(new cMenuEditBoolItem(tr("Start HTTP Server"), &m_NewSetup.StartHTTPServer));
Add(new cMenuEditIntItem (tr("HTTP Server Port"), &m_NewSetup.HTTPServerPort, 0, 65535)); Add(new cMenuEditIntItem (tr("HTTP Server Port"), &m_NewSetup.HTTPServerPort, 0, 65535));
Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.HTTPBindIP));
Add(new cMenuEditIntItem (tr("Priority"), &m_NewSetup.HTTPPriority, MINPRIORITY, MAXPRIORITY));
Add(new cMenuEditStraItem(tr("HTTP Streamtype"), &m_NewSetup.HTTPStreamType, st_Count - 1, StreamTypes)); 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")); AddCategory (tr("Multicast Streaming Server"));
Add(new cMenuEditBoolItem(tr("Start IGMP Server"), &m_NewSetup.StartIGMPServer)); Add(new cMenuEditBoolItem(tr("Start IGMP Server"), &m_NewSetup.StartIGMPServer));
Add(new cMenuEditIntItem (tr("Multicast Client Port"), &m_NewSetup.IGMPClientPort, 0, 65535)); Add(new cMenuEditIntItem (tr("Multicast Client Port"), &m_NewSetup.IGMPClientPort, 0, 65535));
Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.IGMPBindIP));
Add(new cMenuEditIntItem (tr("Priority"), &m_NewSetup.IGMPPriority, MINPRIORITY, MAXPRIORITY));
Add(new cMenuEditStraItem(tr("Multicast Streamtype"), &m_NewSetup.IGMPStreamType, st_Count - 1, StreamTypes)); 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)); SetCurrent(Get(current));
Display(); Display();
} }
@@ -142,25 +126,19 @@ void cStreamdevServerMenuSetupPage::Store(void) {
cStreamdevServer::Destruct(); cStreamdevServer::Destruct();
} }
SetupStore("HideMenuEntry", m_NewSetup.HideMenuEntry);
SetupStore("MaxClients", m_NewSetup.MaxClients); SetupStore("MaxClients", m_NewSetup.MaxClients);
SetupStore("StartSuspended", m_NewSetup.StartSuspended);
SetupStore("LiveBufferMs", m_NewSetup.LiveBufferMs);
SetupStore("StartServer", m_NewSetup.StartVTPServer); SetupStore("StartServer", m_NewSetup.StartVTPServer);
SetupStore("ServerPort", m_NewSetup.VTPServerPort); SetupStore("ServerPort", m_NewSetup.VTPServerPort);
SetupStore("VTPBindIP", m_NewSetup.VTPBindIP); SetupStore("VTPBindIP", m_NewSetup.VTPBindIP);
SetupStore("VTPPriority", m_NewSetup.VTPPriority);
SetupStore("LoopPrevention", m_NewSetup.LoopPrevention);
SetupStore("StartHTTPServer", m_NewSetup.StartHTTPServer); SetupStore("StartHTTPServer", m_NewSetup.StartHTTPServer);
SetupStore("HTTPServerPort", m_NewSetup.HTTPServerPort); SetupStore("HTTPServerPort", m_NewSetup.HTTPServerPort);
SetupStore("HTTPBindIP", m_NewSetup.HTTPBindIP);
SetupStore("HTTPPriority", m_NewSetup.HTTPPriority);
SetupStore("HTTPStreamType", m_NewSetup.HTTPStreamType); SetupStore("HTTPStreamType", m_NewSetup.HTTPStreamType);
SetupStore("HTTPBindIP", m_NewSetup.HTTPBindIP);
SetupStore("StartIGMPServer", m_NewSetup.StartIGMPServer); SetupStore("StartIGMPServer", m_NewSetup.StartIGMPServer);
SetupStore("IGMPClientPort", m_NewSetup.IGMPClientPort); SetupStore("IGMPClientPort", m_NewSetup.IGMPClientPort);
SetupStore("IGMPBindIP", m_NewSetup.IGMPBindIP);
SetupStore("IGMPPriority", m_NewSetup.IGMPPriority);
SetupStore("IGMPStreamType", m_NewSetup.IGMPStreamType); SetupStore("IGMPStreamType", m_NewSetup.IGMPStreamType);
SetupStore("IGMPBindIP", m_NewSetup.IGMPBindIP);
SetupStore("SuspendMode", m_NewSetup.SuspendMode);
SetupStore("AllowSuspend", m_NewSetup.AllowSuspend); SetupStore("AllowSuspend", m_NewSetup.AllowSuspend);
StreamdevServerSetup = m_NewSetup; StreamdevServerSetup = m_NewSetup;
@@ -168,3 +146,11 @@ void cStreamdevServerMenuSetupPage::Store(void) {
if (restart) if (restart)
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

@@ -7,38 +7,25 @@
#include "common.h" #include "common.h"
enum eStartSuspended {
ssNo,
ssYes,
ssAuto,
ss_Count
};
struct cStreamdevServerSetup { struct cStreamdevServerSetup {
cStreamdevServerSetup(void); cStreamdevServerSetup(void);
bool SetupParse(const char *Name, const char *Value); bool SetupParse(const char *Name, const char *Value);
int HideMenuEntry;
int MaxClients; int MaxClients;
int StartSuspended;
int LiveBufferMs;
int StartVTPServer; int StartVTPServer;
int VTPServerPort; int VTPServerPort;
char VTPBindIP[20]; char VTPBindIP[20];
int VTPPriority;
int AllowSuspend;
int LoopPrevention;
int StartHTTPServer; int StartHTTPServer;
int HTTPServerPort; int HTTPServerPort;
int HTTPPriority;
int HTTPStreamType; int HTTPStreamType;
char HTTPBindIP[20]; char HTTPBindIP[20];
int StartIGMPServer; int StartIGMPServer;
int IGMPClientPort; int IGMPClientPort;
int IGMPPriority;
int IGMPStreamType; int IGMPStreamType;
char IGMPBindIP[20]; char IGMPBindIP[20];
int SuspendMode;
int AllowSuspend;
}; };
extern cStreamdevServerSetup StreamdevServerSetup; extern cStreamdevServerSetup StreamdevServerSetup;
@@ -46,12 +33,14 @@ extern cStreamdevServerSetup StreamdevServerSetup;
class cStreamdevServerMenuSetupPage: public cMenuSetupPage { class cStreamdevServerMenuSetupPage: public cMenuSetupPage {
private: private:
static const char* StreamTypes[]; static const char* StreamTypes[];
static const char* SuspendModes[];
cStreamdevServerSetup m_NewSetup; cStreamdevServerSetup m_NewSetup;
void AddCategory(const char *Title); void AddCategory(const char *Title);
void Set(); 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

@@ -12,44 +12,15 @@
#include "server/menu.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"
#if !defined(APIVERSNUM) || APIVERSNUM < 10725 #if !defined(APIVERSNUM) || APIVERSNUM < 10516
#error "VDR-1.7.25 or greater required to compile server! Use 'make client' to compile client only." #error "VDR-1.5.16 API version or greater is required!"
#endif #endif
cList<cMainThreadHookSubscriber> cMainThreadHookSubscriber::m_Subscribers;
cMutex cMainThreadHookSubscriber::m_Mutex;
#if APIVERSNUM >= 20300
cList<cMainThreadHookSubscriber>& cMainThreadHookSubscriber::Subscribers(cMutexLock& Lock)
#else
const cList<cMainThreadHookSubscriber>& cMainThreadHookSubscriber::Subscribers(cMutexLock& Lock)
#endif
{
Lock.Lock(&m_Mutex);
return m_Subscribers;
}
cMainThreadHookSubscriber::cMainThreadHookSubscriber()
{
m_Mutex.Lock();
m_Subscribers.Add(this);
m_Mutex.Unlock();
}
cMainThreadHookSubscriber::~cMainThreadHookSubscriber()
{
m_Mutex.Lock();
m_Subscribers.Del(this, false);
m_Mutex.Unlock();
}
const char *cPluginStreamdevServer::DESCRIPTION = trNOOP("VDR Streaming Server"); const char *cPluginStreamdevServer::DESCRIPTION = trNOOP("VDR Streaming Server");
cPluginStreamdevServer::cPluginStreamdevServer(void) cPluginStreamdevServer::cPluginStreamdevServer(void)
{ {
m_Suspend = false;
} }
cPluginStreamdevServer::~cPluginStreamdevServer() cPluginStreamdevServer::~cPluginStreamdevServer()
@@ -127,9 +98,6 @@ bool cPluginStreamdevServer::Start(void)
cStreamdevServer::Initialize(); cStreamdevServer::Initialize();
m_Suspend = StreamdevServerSetup.StartSuspended == ssAuto ?
!cDevice::PrimaryDevice()->HasDecoder() :
StreamdevServerSetup.StartSuspended;
return true; return true;
} }
@@ -151,7 +119,7 @@ cString cPluginStreamdevServer::Active(void)
const char *cPluginStreamdevServer::MainMenuEntry(void) const char *cPluginStreamdevServer::MainMenuEntry(void)
{ {
return !StreamdevServerSetup.HideMenuEntry ? tr("Streamdev Connections") : NULL; return tr("Streamdev Connections");
} }
cOsdObject *cPluginStreamdevServer::MainMenuAction(void) cOsdObject *cPluginStreamdevServer::MainMenuAction(void)
@@ -161,18 +129,9 @@ cOsdObject *cPluginStreamdevServer::MainMenuAction(void)
void cPluginStreamdevServer::MainThreadHook(void) void cPluginStreamdevServer::MainThreadHook(void)
{ {
if (m_Suspend) { cThreadLock lock;
cControl::Launch(new cSuspendCtl); const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
m_Suspend = false; for (cServerConnection *s = clients.First(); s; s = clients.Next(s))
}
cMutexLock lock;
#if APIVERSNUM >= 20300
cList<cMainThreadHookSubscriber>& subs = cMainThreadHookSubscriber::Subscribers(lock);
#else
const cList<cMainThreadHookSubscriber>& subs = cMainThreadHookSubscriber::Subscribers(lock);
#endif
for (cMainThreadHookSubscriber *s = subs.First(); s; s = subs.Next(s))
s->MainThreadHook(); s->MainThreadHook();
} }
@@ -186,90 +145,4 @@ bool cPluginStreamdevServer::SetupParse(const char *Name, const char *Value)
return StreamdevServerSetup.SetupParse(Name, Value); return StreamdevServerSetup.SetupParse(Name, Value);
} }
const char **cPluginStreamdevServer::SVDRPHelpPages(void)
{
static const char *HelpPages[]=
{
"LSTC\n"
" List connected clients\n",
"DISC client_id\n"
" Disconnect a client\n",
NULL
};
return HelpPages;
}
cString cPluginStreamdevServer::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
{
cString reply = NULL;
if (!strcasecmp(Command, "LSTC"))
{
reply = "";
cThreadLock lock;
#if APIVERSNUM >= 20300
cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
#else
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
#endif
cServerConnection *s = clients.First();
if (!s)
{
reply = "no client connected";
ReplyCode = 550;
} else
{
for (; s; s = clients.Next(s))
{
reply = cString::sprintf("%s%p: %s\n", (const char*) reply, s, (const char *) s->ToText());
}
ReplyCode = 250;
}
} else if (!strcasecmp(Command, "DISC"))
{
void *client = NULL;
if (sscanf(Option, "%p", &client) != 1 || !client)
{
reply = "invalid client handle";
ReplyCode = 501;
} else
{
cThreadLock lock;
#if APIVERSNUM >= 20300
cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
#else
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
#endif
cServerConnection *s = clients.First();
for (; s && s != client; s = clients.Next(s));
if (!s)
{
reply = "client not found";
ReplyCode = 501;
} else
{
s->Close();
reply = "client disconnected";
ReplyCode = 250;
}
}
}
return reply;
}
bool cPluginStreamdevServer::Service(const char *Id, void *Data)
{
if (strcmp(Id, "StreamdevServer::ClientCount-v1.0") == 0) {
if (Data) {
int *count = (int *) Data;
cThreadLock lock;
*count = cStreamdevServer::Clients(lock).Count();
}
return true;
}
return false;
}
VDRPLUGINCREATOR(cPluginStreamdevServer); // Don't touch this! VDRPLUGINCREATOR(cPluginStreamdevServer); // Don't touch this!

View File

@@ -7,30 +7,11 @@
#include "common.h" #include "common.h"
#include <vdr/tools.h>
#include <vdr/plugin.h> #include <vdr/plugin.h>
class cMainThreadHookSubscriber: public cListObject {
private:
static cList<cMainThreadHookSubscriber> m_Subscribers;
static cMutex m_Mutex;
public:
#if APIVERSNUM >= 20300
static cList<cMainThreadHookSubscriber>& Subscribers(cMutexLock& Lock);
#else
static const cList<cMainThreadHookSubscriber>& Subscribers(cMutexLock& Lock);
#endif
virtual void MainThreadHook() = 0;
cMainThreadHookSubscriber();
virtual ~cMainThreadHookSubscriber();
};
class cPluginStreamdevServer : public cPlugin { class cPluginStreamdevServer : public cPlugin {
private: private:
static const char *DESCRIPTION; static const char *DESCRIPTION;
bool m_Suspend;
public: public:
cPluginStreamdevServer(void); cPluginStreamdevServer(void);
@@ -48,9 +29,6 @@ public:
virtual void MainThreadHook(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);
virtual const char **SVDRPHelpPages(void);
virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
virtual bool Service(const char *Id, void *Data = NULL);
}; };
#endif // VDR_STREAMDEVSERVER_H #endif // VDR_STREAMDEVSERVER_H

View File

@@ -8,6 +8,8 @@
#include <unistd.h> #include <unistd.h>
#include "server/streamer.h" #include "server/streamer.h"
#include "server/suspend.h"
#include "server/setup.h"
#include "tools/socket.h" #include "tools/socket.h"
#include "tools/select.h" #include "tools/select.h"
#include "common.h" #include "common.h"
@@ -45,18 +47,15 @@ void cStreamdevWriter::Action(void)
int count, offset = 0; int count, offset = 0;
int timeout = 0; int timeout = 0;
#if APIVERSNUM >= 10705
SetPriority(-3); SetPriority(-3);
#endif
sel.Clear(); sel.Clear();
sel.Add(*m_Socket, true); sel.Add(*m_Socket, true);
while (Running()) { while (Running()) {
if (block == NULL) { if (block == NULL) {
block = m_Streamer->Get(count); block = m_Streamer->Get(count);
offset = 0; offset = 0;
// still no data - are we done?
if (block == NULL && !m_Streamer->IsReceiving() && timeout++ > 20) {
esyslog("streamdev-server: streamer done - writer exiting");
break;
}
} }
if (block != NULL) { if (block != NULL) {
@@ -103,82 +102,72 @@ void cStreamdevWriter::Action(void)
} }
} }
} }
m_Socket->Close();
Dprintf("Max. Transmit Blocksize was: %d\n", max); Dprintf("Max. Transmit Blocksize was: %d\n", max);
} }
// --- cRemuxDummy ------------------------------------------------------------
class cRemuxDummy: public Streamdev::cTSRemux {
private:
cStreamdevBuffer m_Buffer;
public:
cRemuxDummy();
virtual int Put(const uchar *Data, int Count) { return m_Buffer.Put(Data, Count); }
virtual uchar *Get(int& Count) { return m_Buffer.Get(Count); }
virtual void Del(int Count) { return m_Buffer.Del(Count); }
};
cRemuxDummy::cRemuxDummy(): m_Buffer(WRITERBUFSIZE, TS_SIZE * 2)
{
m_Buffer.SetTimeouts(100, 100);
}
// --- cStreamdevStreamer ----------------------------------------------------- // --- cStreamdevStreamer -----------------------------------------------------
cStreamdevStreamer::cStreamdevStreamer(const char *Name, const cServerConnection *Connection): cStreamdevStreamer::cStreamdevStreamer(const char *Name, const cServerConnection *Connection):
cThread(Name), cThread(Name),
m_Connection(Connection), m_Connection(Connection),
m_Remux(new cRemuxDummy()), m_Writer(NULL),
m_Writer(NULL) m_RingBuffer(new cStreamdevBuffer(STREAMERBUFSIZE, TS_SIZE * 2,
true, "streamdev-streamer")),
m_SendBuffer(new cStreamdevBuffer(WRITERBUFSIZE, TS_SIZE * 2))
{ {
m_RingBuffer->SetTimeouts(0, 100);
m_SendBuffer->SetTimeouts(100, 100);
} }
cStreamdevStreamer::~cStreamdevStreamer() cStreamdevStreamer::~cStreamdevStreamer()
{ {
Dprintf("Desctructing streamer\n"); Dprintf("Desctructing streamer\n");
delete m_Remux; delete m_RingBuffer;
delete m_SendBuffer;
} }
void cStreamdevStreamer::Start(cTBSocket *Socket) void cStreamdevStreamer::Start(cTBSocket *Socket)
{ {
Dprintf("start writer\n");
m_Writer = new cStreamdevWriter(Socket, this);
m_Writer->Start();
if (!Active()) {
Dprintf("start streamer\n"); Dprintf("start streamer\n");
m_Writer = new cStreamdevWriter(Socket, this);
Attach();
}
void cStreamdevStreamer::Activate(bool On)
{
if (On && !Active()) {
Dprintf("activate streamer\n");
m_Writer->Start();
cThread::Start(); cThread::Start();
} }
Attach();
} }
void cStreamdevStreamer::Stop(void) void cStreamdevStreamer::Stop(void)
{ {
Detach();
if (Running()) { if (Running()) {
Dprintf("stop streamer\n"); Dprintf("stopping streamer\n");
Cancel(3); Cancel(3);
} }
Dprintf("stop writer\n"); if (m_Writer) {
Detach();
DELETENULL(m_Writer); DELETENULL(m_Writer);
}
} }
void cStreamdevStreamer::Action(void) void cStreamdevStreamer::Action(void)
{ {
#if APIVERSNUM >= 10705
SetPriority(-3); SetPriority(-3);
#endif
while (Running()) { while (Running()) {
int got; int got;
uchar *block = GetFromReceiver(got); uchar *block = m_RingBuffer->Get(got);
if (block) { if (block) {
int count = Put(block, got); int count = Put(block, got);
if (count) if (count)
DelFromReceiver(count); m_RingBuffer->Del(count);
} }
} }
} }
int cStreamdevStreamer::Put(const uchar *Data, int Count) {
return m_Remux->Put(Data, Count);
}

View File

@@ -9,8 +9,6 @@
#include <vdr/ringbuffer.h> #include <vdr/ringbuffer.h>
#include <vdr/tools.h> #include <vdr/tools.h>
#include "remux/tsremux.h"
class cTBSocket; class cTBSocket;
class cStreamdevStreamer; class cStreamdevStreamer;
class cServerConnection; class cServerConnection;
@@ -19,6 +17,7 @@ class cServerConnection;
#define TS_SIZE 188 #define TS_SIZE 188
#endif #endif
#define STREAMERBUFSIZE (20000 * TS_SIZE)
#define WRITERBUFSIZE (20000 * TS_SIZE) #define WRITERBUFSIZE (20000 * TS_SIZE)
// --- cStreamdevBuffer ------------------------------------------------------- // --- cStreamdevBuffer -------------------------------------------------------
@@ -67,18 +66,14 @@ public:
class cStreamdevStreamer: public cThread { class cStreamdevStreamer: public cThread {
private: private:
const cServerConnection *m_Connection; const cServerConnection *m_Connection;
Streamdev::cTSRemux *m_Remux;
cStreamdevWriter *m_Writer; cStreamdevWriter *m_Writer;
cStreamdevBuffer *m_RingBuffer;
cStreamdevBuffer *m_SendBuffer; cStreamdevBuffer *m_SendBuffer;
protected: protected:
virtual uchar* GetFromReceiver(int &Count) = 0;
virtual void DelFromReceiver(int Count) = 0;
virtual int Put(const uchar *Data, int Count);
virtual void Action(void); virtual void Action(void);
bool IsRunning(void) const { return m_Writer; } bool IsRunning(void) const { return m_Writer; }
void SetRemux(Streamdev::cTSRemux *Remux) { delete m_Remux; m_Remux = Remux; }
public: public:
cStreamdevStreamer(const char *Name, const cServerConnection *Connection = NULL); cStreamdevStreamer(const char *Name, const cServerConnection *Connection = NULL);
@@ -88,16 +83,18 @@ public:
virtual void Start(cTBSocket *Socket); virtual void Start(cTBSocket *Socket);
virtual void Stop(void); virtual void Stop(void);
virtual bool IsReceiving(void) const = 0;
bool Abort(void); bool Abort(void);
uchar *Get(int &Count) { return m_Remux->Get(Count); } void Activate(bool On);
void Del(int Count) { m_Remux->Del(Count); } int Receive(uchar *Data, int Length) { return m_RingBuffer->PutTS(Data, Length); }
void ReportOverflow(int Bytes) { m_RingBuffer->ReportOverflow(Bytes); }
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 void Del(int Count) { m_SendBuffer->Del(Count); }
virtual void Detach(void) {} virtual void Detach(void) {}
virtual void Attach(void) {} virtual void Attach(void) {}
virtual cString ToText() const { return ""; };
}; };
inline bool cStreamdevStreamer::Abort(void) inline bool cStreamdevStreamer::Abort(void)

View File

@@ -39,7 +39,7 @@ void cSuspendLive::Action(void) {
bool cSuspendCtl::m_Active = false; bool cSuspendCtl::m_Active = false;
cSuspendCtl::cSuspendCtl(void): cSuspendCtl::cSuspendCtl(void):
cControl(m_Suspend = new cSuspendLive, true) { cControl(m_Suspend = new cSuspendLive) {
m_Active = true; m_Active = true;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,4 @@
#include "tools/socket.h" #include "tools/socket.h"
#include "tools/select.h"
#include <vdr/tools.h> #include <vdr/tools.h>
#include <string.h> #include <string.h>
@@ -28,7 +27,7 @@ cTBSocket::~cTBSocket() {
if (IsOpen()) Close(); if (IsOpen()) Close();
} }
bool cTBSocket::Connect(const std::string &Host, unsigned int Port, unsigned int TimeoutMs) { bool cTBSocket::Connect(const std::string &Host, unsigned int Port) {
socklen_t len; socklen_t len;
int socket; int socket;
@@ -46,37 +45,14 @@ bool cTBSocket::Connect(const std::string &Host, unsigned int Port, unsigned int
return false; return false;
} }
if (TimeoutMs > 0 && ::fcntl(socket, F_SETFL, O_NONBLOCK) == -1) {
::close(socket);
return false;
}
m_RemoteAddr.sin_family = AF_INET; m_RemoteAddr.sin_family = AF_INET;
m_RemoteAddr.sin_port = htons(Port); m_RemoteAddr.sin_port = htons(Port);
m_RemoteAddr.sin_addr.s_addr = inet_addr(Host.c_str()); m_RemoteAddr.sin_addr.s_addr = inet_addr(Host.c_str());
if (::connect(socket, (struct sockaddr*)&m_RemoteAddr, sizeof(m_RemoteAddr)) == -1) { if (::connect(socket, (struct sockaddr*)&m_RemoteAddr,
if (TimeoutMs > 0 && errno == EINPROGRESS) { sizeof(m_RemoteAddr)) == -1) {
int so_error;
socklen_t len = sizeof(so_error);
cTBSelect select;
select.Add(socket);
if (select.Select(TimeoutMs) == -1 ||
::getsockopt(socket, SOL_SOCKET, SO_ERROR, &so_error, &len) == -1) {
::close(socket); ::close(socket);
return false; return false;
} }
if (so_error) {
errno = so_error;
::close(socket);
return false;
}
}
else {
::close(socket);
return false;
}
}
if (m_Type == SOCK_STREAM) { if (m_Type == SOCK_STREAM) {
len = sizeof(struct sockaddr_in); len = sizeof(struct sockaddr_in);

View File

@@ -32,13 +32,11 @@ public:
Reimplemented for TCP/IPv4 sockets. */ Reimplemented for TCP/IPv4 sockets. */
virtual ssize_t SysWrite(const void *Buffer, size_t Length) const; virtual ssize_t SysWrite(const void *Buffer, size_t Length) const;
/* Connect() tries to connect an available local socket within TimeoutMs /* Connect() tries to connect an available local socket to the port given
milliseconds to the port given by Port of the target host given by by Port of the target host given by Host in numbers-and-dots notation
Host in numbers-and-dots notation (i.e. "212.43.45.21"). A TimeoutMs (i.e. "212.43.45.21"). Returns true if the connection attempt was
of 0 will disable non-blocking IO for the connect call. Returns true successful and false otherwise, setting errno appropriately. */
if the connection attempt was successful and false otherwise, setting virtual bool Connect(const std::string &Host, uint Port);
errno appropriately. */
virtual bool Connect(const std::string &Host, uint Port, uint TimeoutMs = 0);
/* Shutdown() shuts down one or both ends of a socket. If called with How /* Shutdown() shuts down one or both ends of a socket. If called with How
set to SHUT_RD, further reads on this socket will be denied. If called set to SHUT_RD, further reads on this socket will be denied. If called
@@ -62,7 +60,7 @@ public:
/* Accept() returns a newly created cTBSocket, which is connected to the /* Accept() returns a newly created cTBSocket, which is connected to the
first connection request on the queue of pending connections of a first connection request on the queue of pending connections of a
listening socket. If no connection request was pending, or if any other listening socket. If no connection request was pending, or if any other
error occurred, the resulting cTBSocket is closed. */ error occured, the resulting cTBSocket is closed. */
virtual cTBSocket Accept(void) const; virtual cTBSocket Accept(void) const;
/* Accept() extracts the first connection request on the queue of pending /* Accept() extracts the first connection request on the queue of pending

View File

@@ -57,20 +57,15 @@ ssize_t cTBSource::Write(const void *Buffer, size_t Length) {
bool cTBSource::TimedWrite(const void *Buffer, size_t Length, uint TimeoutMs) { bool cTBSource::TimedWrite(const void *Buffer, size_t Length, uint TimeoutMs) {
cTBSelect sel; cTBSelect sel;
int ms, offs; int ms, offs;
cTimeMs starttime;
cTimeMs starttime;
ms = TimeoutMs;
offs = 0; offs = 0;
sel.Clear(); sel.Clear();
sel.Add(m_Filed, true); sel.Add(m_Filed, true);
while (Length > 0) { while (Length > 0) {
int b; int b;
ms = TimeoutMs - starttime.Elapsed();
if (ms <= 0) {
errno = ETIMEDOUT;
return false;
}
if (sel.Select(ms) == -1) if (sel.Select(ms) == -1)
return false; return false;
@@ -81,6 +76,11 @@ bool cTBSource::TimedWrite(const void *Buffer, size_t Length, uint TimeoutMs) {
Length -= b; Length -= b;
} }
ms = TimeoutMs - starttime.Elapsed();
if (ms <= 0) {
errno = ETIMEDOUT;
return false;
}
} }
return true; return true;
} }

View File

@@ -54,7 +54,7 @@ public:
/* Close() resets the source to the uninitialized state (IsOpen() == false) /* Close() resets the source to the uninitialized state (IsOpen() == false)
and must be called by any derivations after really closing the source. and must be called by any derivations after really closing the source.
Returns true on success and false on error, setting errno appropriately. Returns true on success and false on error, setting errno appropriately.
The object is in closed state afterwards, even if an error occurred. */ The object is in closed state afterwards, even if an error occured. */
virtual bool Close(void); virtual bool Close(void);
/* Read() reads at most Length bytes into the storage pointed to by Buffer, /* Read() reads at most Length bytes into the storage pointed to by Buffer,