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 suggesting to change the URL path from EXTERN to EXT
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
for providing vdr-incompletesections.diff
@@ -119,10 +116,9 @@ Jori Hamalainen
for extensive testing while making stream compatible to Network Media Tank
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 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
for fixing Min/MaxPriority parsing
@@ -202,60 +198,3 @@ Ville Skytt
for restricting VTP command RENR to liemikuutio patch < 1.32
for fixing memory and filedescriptor leaks in libdvbmpeg
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
---------------------------------------
- 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ä)
- properly shutdown IGMP timeout handler thread when the plugin is stopped.
Fixes occasional segfaults on VDR exit.

View File

@@ -1,44 +1,57 @@
#
# Makefile for a Video Disk Recorder plugin
#
# $Id: $
# 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.
# $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:
# Use package data if installed...otherwise assume we're under the VDR source directory:
PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
LIBDIR = $(call PKGCFG,libdir)
LOCDIR = $(call PKGCFG,locdir)
PLGCFG = $(call PKGCFG,plgcfg)
#
TMPDIR ?= /tmp
VDRDIR = ../../..
LIBDIR = ../../lib
TMPDIR = /tmp
### The compiler options:
### The version number of VDR (taken from VDR's "config.h"):
export CFLAGS = $(call PKGCFG,cflags)
export CXXFLAGS = $(call PKGCFG,cxxflags)
### The version number of VDR's plugin API:
APIVERSION = $(call PKGCFG,apiversion)
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:
-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
VDRDIR := $(shell cd $(VDRDIR) >/dev/null 2>&1 && pwd)
LIBDIR := $(shell cd $(LIBDIR) >/dev/null 2>&1 && pwd)
LOCDIR := $(shell cd $(LOCDIR) >/dev/null 2>&1 && pwd)
export
unexport PLUGIN
@@ -50,7 +63,6 @@ PACKAGE = vdr-$(ARCHIVE)
### Includes and Defines (add further entries here):
INCLUDES += -I$(VDRDIR)/include -I..
export INCLUDES
DEFINES += -D_GNU_SOURCE
@@ -63,7 +75,7 @@ endif
### The main target:
.PHONY: all client server install install-client install-server dist clean
.PHONY: all client server dist clean
all: client server
### Targets:
@@ -71,28 +83,20 @@ all: client server
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
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)
install: install-client install-server
dist: clean
@-rm -rf $(TMPDIR)/$(ARCHIVE)
@mkdir $(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)
@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:
------------------
This version is not compatible to VDR releases older than 1.7.25. Use one of
the streamdev-0.5.x releases for older versions.
This version is not compatible to VDR releases older than 1.5.9. Take one of
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:
--------------
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
tar xvfz vdr-streamdev-0.5.0.tgz
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,
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:
--------------------
(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
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
of currently connected clients. Use the "red" key to terminate a connection.
Note that depending on connection type and client, the client might re-connect
sooner or later.
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
image is displayed instead. This would allow a low priority client to switch
to a different transponder. Enable "Client may suspend" in the server setup
to allow VDR clients to suspend live TV remotely.
The parameter "Suspend behaviour" allows you to specify how the server should
react in case the client requests a channel that would require switching the
primary device (i.e. disrupt live-tv). If set to "Offer suspend mode", you
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
the server. The "auto" option will suspend live TV if there's no device with
an MPEG decoder available which is typically the case on a headless server.
NOTE: Precedence is mainly an issue on One-Card-Systems, since with multiple
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.
NOTE: This mainly applies to One-Card-Systems, since with multiple cards there
is no need to switch transponders on the primary interface, if on 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.
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)
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)
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
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
to see the server's current live TV channel.
In addition, you can specify the desired stream type as a path to the channel.
will request the channel by unique channel id. In addition, you can specify
the desired stream type as a path to the channel.
http://hostname:3000/TS/3
http://hostname:3000/PES/S19.2E-0-12480-898
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
format explicitly if you want to listen to radio channels. Play them back i.e.
with mpg123.
Possible values are 'PES', 'TS', 'PS', 'ES' and 'EXT'. You need to specify
the ES format explicitly if you want to listen to radio channels. Play them
back i.e. with mpg123.
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
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
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.
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.
Binding an IGMP socket is a privileged operation, so you must start VDR as root.
If you pass the -u option to VDR, it will drop almost all priviledges before
streamdev is even loaded. Apply vdr-cap_net_raw.diff to keep VDR from dropping
the CAP_NET_RAW capability required to bind the IGMP socket. The patch is part
of streamdev's source distribution. Check the patches subdirectory. There's no
need to patch VDR if it is kept running as root (not recommended).
The multicast server is disabled by default. Enter the streamdev-server setup
menu to enable it and - IMPORTANT - bind the multicast server to the IP of your
VDR server's LAN ethernet card. The multicast server will refuse to start with
the default bind 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
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
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:
----------------------------
@@ -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
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 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
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
@@ -389,83 +354,66 @@ type "127001<OK>" on your remote. If you want to enter "192.168.1.12", type
"1921681<Right>12<OK>".
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
by default, because it wouldn't make much sense to start the client without
specifying a server anyway. Activate the client by setting "Simultaneously used
Devices" to at least 1. Streamdev-client will allocate as many VDR devices as
you configure here. Each of these devices opens one connection to the server
and becomes associated with one of the server's devices (typically a DVB card)
on demand.
address of the remote VDR-to-VDR server to connect to. Activate the client by
setting "Start Client" to yes. It is disabled by default, because it wouldn't
make much sense to start the client without specifying a server anyway. The
client is activated after you push the OK button, so there's no need to restart
VDR. Deactivation on-the-fly is not possible, so in order to deactivate the
client, you will have to restart VDR. However requests to switch channels will
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
during an active transfer. This makes it possible to switch languages, receive
additional channels on the same transponder and use plugins that use 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.
additional channels (for recording on the client) and use plugins that use
receivers themselves (like osdteletext).
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.
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
regional programs like NDR), then you need to enable "Filter Streaming" to
correctly receive them. You also need to set in VDRs DVB setup the option
"Update channels" to at least "PIDs only" (or "names and PIDs") for this
to work.
With maximum 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".
"Filter streaming" uses internally a socketpair(2) to copy meta data to
VDR. This socketpair may require larger than default buffering. If
you see a mesage like the following in syslog,
If you are running at least VDR 1.7.0, you can also configure the "Broadcast
Systems / Cost" of the streamdev-client device. On a pure streamdev-client only
system it doesn't matter what you configure here. But if your client is equipped
with a DVB card, you should read on. VDR always prefers the cheapest device
in terms of supported broadcast systems and modulations. A DVB-S2 card supports
two broadcast systems (DVB-S and DVB-S2). From VDR 1.7.15 on, the supported
modulations are counted as well (QPSK, QAM32/64/128/256, VSB8/16, TURBO_FEC).
So for a DVB-S2 card which does QPSK you'll get a total cost of three. A DVB-C
card (one broadcast system) which can do QAM32,QAM64,QAM128,QAM256 would give
you a total of five. Check your log for "frontend ... provides ... with ..."
messages to find out the cost of your DVB cards. Then pick a suitable value for
streamdev-client. With equal costs, VDR will usually prefer the DVB card and
take streamdev for recordings. If streamdev's costs are higher, live TV will
use your DVB card until a recordings kicks in. Then the recording will take the
DVB card and live TV will be shifted to streamdev (you'll notice a short
interruption of live TV).
cStreamdevFilter::PutSection(Pid:18 Tid: 64): Dropped 2995 bytes, max queue: 328640
then you should increase the streamdev client "FilterSockBufSize" value. A
good value is 3072000. You will need to first configure your linux to
permit such a large buffer size:
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):
Note that streamdev-client acts similar to a DVB card. It is possible to receive
multiple channels simultaneously, but only from the same transponder. Just add
additional instances of streamdev-client and you will be able to receive as many
transponders at a time. The same trick allows a client to receive channels from
different servers. To create an additional instance, copy the streamdev-client
binary to a different name (e.g. streamdev-client2):
cd VDRPLUGINLIBDIR
cp libvdr-streamdev-client.so.1.X.X libvdr-streamdev-client2.so.1.X.X
@@ -571,10 +519,14 @@ The script should perform the following steps (pseudocode):
6. Known Problems:
------------------
* In VDR before 1.7.30 viewing encrypted channels is an issue as 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:
* There have been reports that channel switching with VDR 1.5.x/1.6.x clients
sometimes fails. Current version includes a workaround which seems to work, but
YMMV ;)
* Viewing encrypted channels became an issue with VDR's new CAM handling code.
Streamdev doesn't provide a (dummy) CAM, so out of the box, VDR won't ever try
to receive encrypted channels from streamdev. Pick one of the following
solutions to work around the problem:
1. Force VDR to use streamdev. Open the channels menu on the client (or edit its
channels.conf if you know how to do this) and set the CA field of all channels
@@ -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
(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
"patches/vdr-1.6.0-1.7.29-ignore_missing_cam.diff" to your client VDR.
Intcamdevices is the clean solution, but it modifies the VDR API. So you will
need to recompile all of your plugins. The ignore_missing_cam patch is trivial,
no need to recompile other plugins. However it is not suitable for clients with
a DVB card of their own.
2. Apply either patch "patches/vdr-1.6.0-intcamdevices.patch" or patch
"patches/vdr-1.6.0-ignore_missing_cam.diff" to your client VDR. Intcamdevices
is the clean solution, but it modifies the VDR API. So you will need to
recompile all of your plugins. The ignore_missing_cam patch is trivial, no need
to recompile other plugins. However it is not suitable for clients with a DVB
card of their own.

View File

@@ -1,18 +1,14 @@
#
# 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.
# 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
### The name of the shared object file:
SOFILE = libvdr-$(PLUGIN).so
### Includes and Defines (add further entries here):
DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
@@ -26,7 +22,8 @@ CLIENTOBJS = $(PLUGIN).o \
### The main target:
all: $(SOFILE) i18n
.PHONY: all i18n dist clean
all: libvdr-$(PLUGIN).so i18n
### Implicit rules:
@@ -37,47 +34,51 @@ all: $(SOFILE) i18n
MAKEDEP = $(CXX) -MM -MG
DEPFILE = .dependencies
$(DEPFILE): Makefile
@$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(CLIENTOBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) > $@
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(CLIENTOBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) > $@
-include $(DEPFILE)
### Internationalization (I18N):
PODIR = po
LOCALEDIR = $(VDRDIR)/locale
I18Npo = $(wildcard $(PODIR)/*.po)
I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file))))
I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
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 --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)
msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $<
msgmerge -U --no-wrap --no-location --backup=none -q $@ $<
@touch $@
$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
install -D -m644 $< $@
$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
@mkdir -p $(dir $@)
cp $< $@
.PHONY: i18n
i18n: $(I18Nmo) $(I18Npot)
install-i18n: $(I18Nmsgs)
i18n: $(I18Nmsgs)
### Targets:
$(SOFILE): $(CLIENTOBJS) $(COMMONOBJS) ../tools/sockettools.a
$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $^ -o $@
libvdr-$(PLUGIN).so: $(CLIENTOBJS) $(COMMONOBJS) ../tools/sockettools.a
install-lib: $(SOFILE)
install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION)
%.so:
$(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:
@-rm -f $(PODIR)/*.mo $(PODIR)/*.pot
@-rm -f $(COMMONOBJS) $(CLIENTOBJS) $(DEPFILE) *.so *.tgz core* *~
@-rm -f $(COMMONOBJS) $(CLIENTOBJS) $(DEPFILE) $(PODIR)/*.mo $(PODIR)/*.pot *.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;
#ifndef LIVEPRIORITY
#define LIVEPRIORITY 0
#endif
#ifndef TRANSFERPRIORITY
#define TRANSFERPRIORITY -1
#endif
#define VIDEOBUFSIZE MEGABYTE(3)
const cChannel *cStreamdevDevice::m_DenyChannel = NULL;
cStreamdevDevice *cStreamdevDevice::m_Device = NULL;
cStreamdevDevice::cStreamdevDevice(void) {
m_Disabled = false;
m_ClientSocket = new cClientSocket();
m_Channel = NULL;
m_TSBuffer = NULL;
m_Channel = NULL;
m_TSBuffer = NULL;
m_Filters = new cStreamdevFilters(m_ClientSocket);
m_Filters = new cStreamdevFilters;
StartSectionHandler();
isyslog("streamdev-client: got device number %d", CardIndex() + 1);
m_Device = this;
m_Pids = 0;
m_Priority = -1;
m_DvrClosed = true;
}
cStreamdevDevice::~cStreamdevDevice() {
Dprintf("Device gets destructed\n");
Lock();
m_Device = NULL;
m_Filters->SetConnection(-1);
m_ClientSocket->Quit();
m_ClientSocket->Reset();
ClientSocket.Quit();
ClientSocket.Reset();
Unlock();
Cancel(3);
@@ -58,7 +52,6 @@ cStreamdevDevice::~cStreamdevDevice() {
StopSectionHandler();
DELETENULL(m_Filters);
DELETENULL(m_TSBuffer);
delete m_ClientSocket;
}
#if APIVERSNUM >= 10700
@@ -77,29 +70,27 @@ bool cStreamdevDevice::ProvidesTransponder(const cChannel *Channel) const
return true;
}
#if APIVERSNUM >= 10722
bool cStreamdevDevice::IsTunedToTransponder(const cChannel *Channel) const
#else
bool cStreamdevDevice::IsTunedToTransponder(const cChannel *Channel)
#endif
{
return m_ClientSocket->DataSocket(siLive) != NULL &&
m_Channel != NULL &&
Channel->Transponder() == m_Channel->Transponder();
}
const cChannel *cStreamdevDevice::GetCurrentlyTunedTransponder(void) const {
if (m_ClientSocket->DataSocket(siLive) != NULL)
return m_Channel;
return NULL;
bool res = false;
if (ClientSocket.DataSocket(siLive) != NULL
&& TRANSPONDER(Channel, m_Channel)
&& Channel->Ca() == CA_FTA
&& m_Channel->Ca() == CA_FTA)
res = true;
return res;
}
bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
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;
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)
{
@@ -114,41 +105,12 @@ bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
return false;
}
int newPrio = Priority;
if (Priority == LIVEPRIORITY) {
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;
else
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 (ClientSocket.DataSocket(siLive) != NULL
&& TRANSPONDER(Channel, m_Channel))
res = true;
else {
res = prio && ClientSocket.ProvidesChannel(Channel, Priority);
ndr = true;
}
if (NeedsDetachReceivers)
@@ -159,93 +121,139 @@ bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
bool cStreamdevDevice::SetChannelDevice(const cChannel *Channel,
bool LiveView) {
bool res;
Dprintf("SetChannelDevice Channel: %s, LiveView: %s\n", Channel->Name(),
LiveView ? "true" : "false");
LOCK_THREAD;
m_UpdatePriority = ClientSocket.SupportsPrio();
if (LiveView)
return false;
if (Receiving() && IsTunedToTransponder(Channel) && (
Channel->Ca() < CA_ENCRYPTED_MIN ||
(Channel->Vpid() && HasPid(Channel->Vpid())) ||
(Channel->Apid(0) && HasPid(Channel->Apid(0))))) {
res = true;
}
else {
DetachAllReceivers();
m_Channel = Channel;
// Old servers delete cStreamdevLiveStreamer in ABRT.
// Delete it now or it will happen after we tuned to new channel
if (m_ClientSocket->ServerVersion() < 100)
CloseDvr();
res = m_ClientSocket->SetChannelDevice(m_Channel);
}
Dprintf("setchanneldevice res=%d\n", res);
return res;
if (ClientSocket.DataSocket(siLive) != NULL
&& TRANSPONDER(Channel, m_Channel)
&& Channel->Ca() == CA_FTA
&& m_Channel->Ca() == CA_FTA)
return true;
DetachAllReceivers();
m_Channel = Channel;
bool r = ClientSocket.SetChannelDevice(m_Channel);
Dprintf("setchanneldevice r=%d\n", r);
return r;
}
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;
m_UpdatePriority = ClientSocket.SupportsPrio();
if (On && !m_TSBuffer) {
Dprintf("SetPid: no data connection -> OpenDvr()");
OpenDvrInt();
}
bool res = true;
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;
if (m_Pids < 0)
m_Pids = 0;
if(m_Pids < 1 && m_DvrClosed) {
Dprintf("SetPid: 0 pids left -> CloseDvr()");
CloseDvrInt();
}
}
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) {
Dprintf("OpenDvr\n");
LOCK_THREAD;
CloseDvr();
if (m_ClientSocket->CreateDataConnection(siLive)) {
m_TSBuffer = new cTSBuffer(*m_ClientSocket->DataSocket(siLive), MEGABYTE(2), CardIndex() + 1);
}
else {
esyslog("cStreamdevDevice::OpenDvr(): DVR connection FAILED");
}
return m_TSBuffer != NULL;
m_DvrClosed = false;
return OpenDvrInt();
}
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) {
Dprintf("CloseDvr\n");
LOCK_THREAD;
m_ClientSocket->CloseDvr();
DELETENULL(m_TSBuffer);
m_DvrClosed = true;
CloseDvrInt();
}
bool cStreamdevDevice::GetTSPacket(uchar *&Data) {
if (m_TSBuffer) {
if (m_TSBuffer && m_Device) {
Data = m_TSBuffer->Get();
#if 1 // TODO: this should be fixed in vdr cTSBuffer
// simple disconnect detection
static int m_TSFails = 0;
if (!Data) {
LOCK_THREAD;
if(!m_ClientSocket->DataSocket(siLive)) {
if(!ClientSocket.DataSocket(siLive)) {
return false; // triggers CloseDvr() + OpenDvr() in cDevice
}
cPoller Poller(*m_ClientSocket->DataSocket(siLive));
cPoller Poller(*ClientSocket.DataSocket(siLive));
errno = 0;
if (Poller.Poll() && !errno) {
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);
m_TSFails++;
if (m_TSFails > 10) {
isyslog("cStreamdevDevice::GetTSPacket(): disconnected");
m_Pids = 0;
CloseDvr();
CloseDvrInt();
m_TSFails = 0;
return false;
}
@@ -267,89 +275,67 @@ int cStreamdevDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask) {
return -1;
if (!m_ClientSocket->DataSocket(siLiveFilter)) {
if (m_ClientSocket->CreateDataConnection(siLiveFilter)) {
m_Filters->SetConnection(*m_ClientSocket->DataSocket(siLiveFilter));
if (!ClientSocket.DataSocket(siLiveFilter)) {
if (ClientSocket.CreateDataConnection(siLiveFilter)) {
m_Filters->SetConnection(*ClientSocket.DataSocket(siLiveFilter));
} else {
isyslog("cStreamdevDevice::OpenFilter: connect failed: %m");
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 -1;
}
void cStreamdevDevice::CloseFilter(int Handle) {
if(m_Filters)
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);
bool cStreamdevDevice::Init(void) {
if (m_Device == NULL && StreamdevClientSetup.StartClient)
new cStreamdevDevice;
return true;
}
void cStreamdevDevice::UpdatePriority(bool SwitchingChannels) const {
if (!m_Disabled) {
//LOCK_THREAD;
const_cast<cStreamdevDevice*>(this)->Lock();
if (m_ClientSocket->SupportsPrio() && m_ClientSocket->DataSocket(siLive)) {
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);
bool cStreamdevDevice::ReInit(void) {
if(m_Device) {
m_Device->Lock();
m_Device->m_Filters->SetConnection(-1);
m_Device->m_Pids = 0;
}
ClientSocket.Quit();
ClientSocket.Reset();
if (m_Device != NULL) {
//DELETENULL(m_Device->m_TSBuffer);
m_Device->Unlock();
}
return StreamdevClientSetup.StartClient ? Init() : true;
}
void cStreamdevDevice::UpdatePriority(void) {
if (m_Device) {
m_Device->Lock();
if (m_Device->m_UpdatePriority && ClientSocket.DataSocket(siLive)) {
int Priority = m_Device->Priority();
if (m_Device == cDevice::ActualDevice() && Priority < Setup.PrimaryLimit)
Priority = Setup.PrimaryLimit;
if (m_Device->m_Priority != Priority && ClientSocket.SetPriority(Priority))
m_Device->m_Priority = Priority;
}
const_cast<cStreamdevDevice*>(this)->Unlock();
m_Device->Unlock();
}
}
cString cStreamdevDevice::DeviceName(void) const {
return StreamdevClientSetup.RemoteIp;
}
cString cStreamdevDevice::DeviceType(void) const {
static int dev = -1;
static cString devType("STRDev");
int d = -1;
if (m_ClientSocket->DataSocket(siLive) != NULL)
m_ClientSocket->GetSignal(NULL, NULL, &d);
if (d != dev) {
dev = d;
devType = d < 0 ? "STRDev" : *cString::sprintf("STRD%2d", d);
}
return devType;
}
int cStreamdevDevice::SignalStrength(void) const {
int strength = -1;
if (m_ClientSocket->DataSocket(siLive) != NULL)
m_ClientSocket->GetSignal(&strength, NULL, NULL);
if (ClientSocket.DataSocket(siLive) != NULL)
ClientSocket.GetSignal(&strength, NULL);
return strength;
}
int cStreamdevDevice::SignalQuality(void) const {
int quality = -1;
if (m_ClientSocket->DataSocket(siLive) != NULL)
m_ClientSocket->GetSignal(NULL, &quality, NULL);
if (ClientSocket.DataSocket(siLive) != NULL)
ClientSocket.GetSignal(NULL, &quality);
return quality;
}

View File

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

View File

@@ -6,28 +6,20 @@
#include "client/socket.h"
#include "tools/select.h"
#include "common.h"
#include <sys/ioctl.h>
#include <string.h>
#include <vdr/device.h>
#define PID_MASK_HI 0x1F
// --- cStreamdevFilter ------------------------------------------------------
static int FilterSockBufSize_warn = 0;
class cStreamdevFilter: public cListObject {
private:
uchar m_Buffer[8192];
uchar m_Buffer[4096];
int m_Used;
int m_Pipe[2];
u_short m_Pid;
u_char m_Tid;
u_char m_Mask;
#ifdef TIOCOUTQ
unsigned long m_maxq;
unsigned long m_flushed;
#endif
public:
cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask);
@@ -37,6 +29,7 @@ public:
bool PutSection(const uchar *Data, int Length, bool Pusi);
int ReadPipe(void) const { return m_Pipe[0]; }
bool IsClosed(void);
void Reset(void);
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_Mask = Mask;
m_Pipe[0] = m_Pipe[1] = -1;
#ifdef TIOCOUTQ
m_flushed = 0;
m_maxq = 0;
#endif
#ifdef SOCK_SEQPACKET
// 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");
}
// Set buffer for socketpair. During certain situations, such as startup, channel/transponder
// 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 ||
else if(fcntl(m_Pipe[0], 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");
}
@@ -117,12 +67,11 @@ cStreamdevFilter::cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask) {
cStreamdevFilter::~cStreamdevFilter() {
Dprintf("~cStreamdevFilter %p\n", this);
if (m_Pipe[0] >= 0) {
close(m_Pipe[0]);
}
if (m_Pipe[1] >= 0) {
// ownership of handle m_Pipe[0] has been transferred to VDR section handler
//if (m_Pipe[0] >= 0)
// close(m_Pipe[0]);
if (m_Pipe[1] >= 0)
close(m_Pipe[1]);
}
}
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;
if (m_Used == length) {
m_Used = 0;
#ifdef TIOCOUTQ
// If we can determine the queue size of the socket,
// we flush rather then let the socket drop random packets.
// This ensures that we have more contiguous set of packets
// 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;
if (write(m_Pipe[1], m_Buffer, length) < 0) {
if(errno == EAGAIN || errno == EWOULDBLOCK)
dsyslog("cStreamdevFilter::PutSection socket overflow, "
"Pid %4d Tid %3d", m_Pid, m_Tid);
} 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));
else
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;
}
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(cClientSocket *ClientSocket):
cStreamdevFilters::cStreamdevFilters(void):
cThread("streamdev-client: sections assembler") {
m_ClientSocket = ClientSocket;
m_TSBuffer = NULL;
}
@@ -216,27 +154,43 @@ cStreamdevFilters::~cStreamdevFilters() {
}
int cStreamdevFilters::OpenFilter(u_short Pid, u_char Tid, u_char Mask) {
CarbageCollect();
cStreamdevFilter *f = new cStreamdevFilter(Pid, Tid, Mask);
int fh = f->ReadPipe();
LOCK_THREAD;
Lock();
Add(f);
Unlock();
return fh;
}
void cStreamdevFilters::CloseFilter(int Handle) {
void cStreamdevFilters::CarbageCollect(void) {
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)) {
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);
return;
cStreamdevFilter *next = Prev(fi);
Del(fi);
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)
@@ -244,8 +198,9 @@ bool cStreamdevFilters::ReActivateFilters(void)
LOCK_THREAD;
bool res = true;
CarbageCollect();
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");
}
return res;
@@ -296,7 +251,7 @@ void cStreamdevFilters::Action(void) {
Dprintf("FATAL ERROR: %m\n");
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);
// Filter was closed.
// - need to check remaining filters for another match
@@ -306,7 +261,7 @@ void cStreamdevFilters::Action(void) {
} else {
#if 1 // TODO: this should be fixed in vdr cTSBuffer
// Check disconnection
int fd = *m_ClientSocket->DataSocket(siLiveFilter);
int fd = *ClientSocket.DataSocket(siLiveFilter);
if(fd < 0)
break;
cPoller Poller(fd);
@@ -318,7 +273,7 @@ void cStreamdevFilters::Action(void) {
++fails;
if (fails >= 10) {
esyslog("cStreamdevFilters::Action(): stream disconnected ?");
m_ClientSocket->CloseDataConnection(siLiveFilter);
ClientSocket.CloseDataConnection(siLiveFilter);
break;
}
} else {

View File

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

View File

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

View File

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

View File

@@ -1,62 +1,50 @@
# VDR streamdev plugin language source file.
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
# This file is distributed under the same license as the VDR streamdev package.
# Rolf Ahrenberg, 2008-
# Rolf Ahrenberg <rahrenbe@cc.hut.fi>, 2008
#
msgid ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
"POT-Creation-Date: 2015-01-16 22:32+0100\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
"Last-Translator: Rolf Ahrenberg\n"
"Language-Team: Finnish <vdr@linuxtv.org>\n"
"Language: fi\n"
"Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"
"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "Hide Mainmenu Entry"
msgstr "Piilota valinta päävalikosta"
msgid "VTP Streaming Client"
msgstr "VTP-suoratoistoasiakas"
msgid "Simultaneously used Devices"
msgstr "Yhtäaikaiset laitteet"
msgid "Suspend Server"
msgstr "Pysäytä palvelin"
msgid "Server is suspended"
msgstr "Palvelin on pysäytetty"
msgid "Couldn't suspend Server!"
msgstr "Palvelinta ei onnistuttu pysäyttämään!"
msgid "Hide Mainmenu Entry"
msgstr "Piilota valinta päävalikosta"
msgid "Start Client"
msgstr "Käynnistä VDR-asiakas"
msgid "Remote IP"
msgstr "Etäkoneen IP-osoite"
msgstr "Etäkoneen IP-osoite"
msgid "Remote Port"
msgstr "Etäkoneen portti"
msgid "Timeout (s)"
msgstr "Yhteyden aikakatkaisu (s)"
msgstr "Etäkoneen portti"
msgid "Filter Streaming"
msgstr "Suodatetun tiedon suoratoisto"
msgid "Filter SockBufSize"
msgstr ""
msgid "Live TV Priority"
msgstr "Live-katselun prioriteetti"
msgid "Minimum Priority"
msgstr "Pienin prioriteetti"
msgid "Maximum Priority"
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 ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
"POT-Creation-Date: 2015-01-16 22:32+0100\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
"Last-Translator: micky979 <micky979@free.fr>\n"
"Language-Team: French <vdr@linuxtv.org>\n"
"Language: fr\n"
"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "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"
msgstr "Client de streaming VTP"
@@ -60,3 +26,25 @@ msgstr "Le serveur est suspendu"
msgid "Couldn't suspend Server!"
msgstr "Impossible de suspendre le serveur!"
msgid "Hide Mainmenu Entry"
msgstr "Masquer dans le menu principal"
msgid "Start Client"
msgstr "Démarrage du client"
msgid "Remote IP"
msgstr "Adresse IP du serveur"
msgid "Remote Port"
msgstr "Port du serveur"
msgid "Filter Streaming"
msgstr "Filtre streaming"
msgid "Minimum Priority"
msgstr ""
msgid "Maximum Priority"
msgstr ""

View File

@@ -8,49 +8,15 @@
msgid ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
"POT-Creation-Date: 2015-01-16 22:32+0100\n"
"PO-Revision-Date: 2012-06-10 20:34+0100\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
"Language-Team: Italian <vdr@linuxtv.org>\n"
"Language: it\n"
"Language-Team: <vdr@linuxtv.org>\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"
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"
msgstr "Client trasmissione VTP"
@@ -62,3 +28,25 @@ msgstr "Server sospeso"
msgid "Couldn't suspend Server!"
msgstr "Impossibile sospendere il server!"
msgid "Hide Mainmenu Entry"
msgstr "Nascondi voce menu principale"
msgid "Start Client"
msgstr "Avvia Client"
msgid "Remote IP"
msgstr "Indirizzo IP del Server"
msgid "Remote Port"
msgstr "Porta Server Remoto"
msgid "Filter Streaming"
msgstr "Filtra trasmissione"
msgid "Minimum Priority"
msgstr "Priorità minima"
msgid "Maximum Priority"
msgstr "Priorità massima"

View File

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

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

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

@@ -6,23 +6,34 @@
msgid ""
msgstr ""
"Project-Id-Version: streamdev_SK\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
"POT-Creation-Date: 2015-01-16 22:32+0100\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
"PO-Revision-Date: \n"
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
"Language-Team: Slovak <hrala.milan@gmail.com>\n"
"Language: sk\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-2\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Slovak\n"
"X-Poedit-Country: SLOVAKIA\n"
msgid "VTP Streaming Client"
msgstr "VTP prúdový klient"
msgid "Suspend Server"
msgstr "Server pozastavený"
msgid "Server is suspended"
msgstr "Server je doèasne preru¹ený"
msgid "Couldn't suspend Server!"
msgstr "Nepodarilo sa pozastavi» Server!"
msgid "Hide Mainmenu Entry"
msgstr "Schova» polo¾ku v hlavnom menu"
msgid "Simultaneously used Devices"
msgstr "Súbe¾ne pou¾íva» zariadenia"
msgid "Start Client"
msgstr "Spusti» Klienta"
msgid "Remote IP"
msgstr "Vzdialená IP"
@@ -30,35 +41,12 @@ msgstr "Vzdialen
msgid "Remote Port"
msgstr "Vzdialený port"
msgid "Timeout (s)"
msgstr "Èasový limit (s)"
msgid "Filter Streaming"
msgstr "Filtrova» dátový prúd"
msgid "Filter SockBufSize"
msgstr ""
msgid "Live TV Priority"
msgstr "Priorita ¾ivého vysielania"
msgstr "filtrova» prúdy"
msgid "Minimum Priority"
msgstr "Minimálna priorita"
msgstr "minimálna priorita"
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 "client/setup.h"
#include "client/streamdev-client.h"
#ifndef MINPRIORITY
#define MINPRIORITY -MAXPRIORITY
#endif
#include "client/device.h"
cStreamdevClientSetup StreamdevClientSetup;
cStreamdevClientSetup::cStreamdevClientSetup(void) {
StartClient = false;
RemotePort = 2004;
Timeout = 2;
StreamFilters = false;
HideMenuEntry = false;
LivePriority = 0;
MinPriority = MINPRIORITY;
MinPriority = -1;
MaxPriority = MAXPRIORITY;
#if APIVERSNUM >= 10700
NumProvidedSystems = 1;
#endif
strcpy(RemoteIp, "");
FilterSockBufSize = 0;
}
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);
}
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, "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, "MaxPriority") == 0) MaxPriority = atoi(Value);
else if (strcmp(Name, "FilterSockBufSize") == 0) FilterSockBufSize = atoi(Value);
#if APIVERSNUM >= 10700
else if (strcmp(Name, "NumProvidedSystems") == 0) NumProvidedSystems = atoi(Value);
#endif
@@ -52,21 +42,16 @@ bool cStreamdevClientSetup::SetupParse(const char *Name, const char *Value) {
return true;
}
cStreamdevClientMenuSetupPage::cStreamdevClientMenuSetupPage(cPluginStreamdevClient *Plugin) {
m_Plugin = Plugin;
cStreamdevClientMenuSetupPage::cStreamdevClientMenuSetupPage(void) {
m_NewSetup = StreamdevClientSetup;
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 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));
if(m_NewSetup.StreamFilters)
Add(new cMenuEditIntItem (tr("Filter SockBufSize"), &m_NewSetup.FilterSockBufSize, 0, 8192000));
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));
Add(new cMenuEditIntItem (tr("Minimum Priority"), &m_NewSetup.MinPriority, -1, MAXPRIORITY));
Add(new cMenuEditIntItem (tr("Maximum Priority"), &m_NewSetup.MaxPriority, -1, MAXPRIORITY));
#if APIVERSNUM >= 10715
Add(new cMenuEditIntItem (tr("Broadcast Systems / Cost"), &m_NewSetup.NumProvidedSystems, 1, 15));
#elif APIVERSNUM >= 10700
@@ -79,25 +64,27 @@ cStreamdevClientMenuSetupPage::~cStreamdevClientMenuSetupPage() {
}
void cStreamdevClientMenuSetupPage::Store(void) {
if (m_NewSetup.StartClient != StreamdevClientSetup.StartClient) {
if (m_NewSetup.StartClient)
cStreamdevDevice::Init();
}
SetupStore("StartClient", m_NewSetup.StartClient);
if (strcmp(m_NewSetup.RemoteIp, "") == 0)
SetupStore("RemoteIp", "-none-");
else
SetupStore("RemoteIp", m_NewSetup.RemoteIp);
SetupStore("RemotePort", m_NewSetup.RemotePort);
SetupStore("Timeout", m_NewSetup.Timeout);
SetupStore("StreamFilters", m_NewSetup.StreamFilters);
SetupStore("HideMenuEntry", m_NewSetup.HideMenuEntry);
SetupStore("LivePriority", m_NewSetup.LivePriority);
SetupStore("MinPriority", m_NewSetup.MinPriority);
SetupStore("MaxPriority", m_NewSetup.MaxPriority);
#if APIVERSNUM >= 10700
SetupStore("NumProvidedSystems", m_NewSetup.NumProvidedSystems);
#endif
SetupStore("FilterSockBufSize", m_NewSetup.FilterSockBufSize);
StreamdevClientSetup = m_NewSetup;
m_Plugin->Initialize();
cStreamdevDevice::ReInit();
}

View File

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

View File

@@ -11,24 +11,19 @@
#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/setup.h"
#include "common.h"
cClientSocket ClientSocket;
cClientSocket::cClientSocket(void)
{
memset(m_DataSockets, 0, sizeof(cTBSocket*) * si_Count);
m_ServerVersion = 0;
m_Priority = -100;
m_Prio = false;
m_Abort = false;
m_LastSignalUpdate = 0;
m_LastSignalStrength = -1;
m_LastSignalQuality = -1;
m_LastDev = -1;
Reset();
}
@@ -40,62 +35,53 @@ cClientSocket::~cClientSocket()
void cClientSocket::Reset(void)
{
for (int it = 0; it < si_Count; ++it)
DELETENULL(m_DataSockets[it]);
m_Priority = -100;
for (int it = 0; it < si_Count; ++it) {
if (m_DataSockets[it] != NULL)
DELETENULL(m_DataSockets[it]);
}
}
cTBSocket *cClientSocket::DataSocket(eSocketId Id) const {
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;
std::string buffer;
if (Send(Command) && Receive(Command, &code, &buffer)) {
if (code == Expected)
return true;
errno = 0;
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";
Dprintf("OUT: |%s|\n", Command.c_str());
errno = 0;
if (!TimedWrite(pkt.c_str(), pkt.size(), WRITE_TIMEOUT_MS)) {
esyslog("ERROR: streamdev-client: Failed sending command '%s' to %s:%d: %s",
Command.c_str(), RemoteIp().c_str(), RemotePort(), strerror(errno));
cTimeMs starttime;
if (!TimedWrite(pkt.c_str(), pkt.size(), TimeoutMs)) {
esyslog("Streamdev: Lost connection to %s:%d: %s", RemoteIp().c_str(), RemotePort(),
strerror(errno));
Close();
return false;
}
uint64_t elapsed = starttime.Elapsed();
if (Expected != 0) { // XXX+ What if elapsed > TimeoutMs?
TimeoutMs -= elapsed;
return Expect(Expected, NULL, TimeoutMs);
}
return true;
}
#define TIMEOUT_MS 1000
bool cClientSocket::Receive(const std::string &Command, uint *Code, std::string *Result, uint TimeoutMs) {
bool cClientSocket::Expect(uint Expected, std::string *Result, uint TimeoutMs) {
char *endptr;
int bufcount;
do
{
errno = 0;
bufcount = ReadUntil(m_Buffer, sizeof(m_Buffer) - 1, "\012", TimeoutMs < TIMEOUT_MS ? TimeoutMs : TIMEOUT_MS);
if (bufcount == -1) {
if (m_Abort)
return false;
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();
return false;
}
TimeoutMs -= TIMEOUT_MS;
}
} while (bufcount == -1);
bool res;
errno = 0;
if ((bufcount = ReadUntil(m_Buffer, sizeof(m_Buffer) - 1, "\012", TimeoutMs)) == -1) {
esyslog("Streamdev: Lost connection to %s:%d: %s", RemoteIp().c_str(), RemotePort(),
strerror(errno));
Close();
return false;
}
if (m_Buffer[bufcount - 1] == '\015')
--bufcount;
m_Buffer[bufcount] = '\0';
@@ -103,9 +89,9 @@ bool cClientSocket::Receive(const std::string &Command, uint *Code, std::string
if (Result != NULL)
*Result = m_Buffer;
if (Code != NULL)
*Code = strtoul(m_Buffer, NULL, 10);
return true;
res = strtoul(m_Buffer, &endptr, 10) == Expected;
return res;
}
bool cClientSocket::CheckConnection(void) {
@@ -114,22 +100,25 @@ bool cClientSocket::CheckConnection(void) {
if (IsOpen()) {
cTBSelect select;
Dprintf("connection open\n");
// XXX+ check if connection is still alive (is there a better way?)
// 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)
select.Add(*this, false);
int res;
if ((res = select.Select(0)) == 0) {
Dprintf("select said nothing happened\n");
return true;
}
Dprintf("closing connection (res was %d)\n", res);
Dprintf("closing connection (res was %d)", res);
Close();
}
if (!Connect(StreamdevClientSetup.RemoteIp, StreamdevClientSetup.RemotePort, StreamdevClientSetup.Timeout * 1000)){
if (!Connect(StreamdevClientSetup.RemoteIp, StreamdevClientSetup.RemotePort)){
static time_t lastTime = 0;
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,
StreamdevClientSetup.RemotePort, strerror(errno));
lastTime = time(NULL);
@@ -137,51 +126,34 @@ bool cClientSocket::CheckConnection(void) {
return false;
}
uint code = 0;
std::string buffer;
if (!Receive("<connect>", &code, &buffer)) {
Close();
return false;
}
if (code != 220) {
esyslog("ERROR: streamdev-client: Didn't receive greeting from %s:%d: %s",
RemoteIp().c_str(), RemotePort(), buffer.c_str());
if (!Expect(220)) {
if (errno == 0)
esyslog("ERROR: Streamdev: Didn't receive greeting from %s:%d",
RemoteIp().c_str(), RemotePort());
Close();
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)) {
Close();
return false;
}
const char *Filters = "";
if(Command("CAPS FILTERS", 220))
Filters = ",FILTERS";
const char *Prio = "";
if(Command("CAPS PRIO", 220)) {
Prio = ",PRIO";
m_Prio = true;
}
isyslog("streamdev-client: Connected to server %s:%d using capabilities TSPIDS%s%s",
RemoteIp().c_str(), RemotePort(), Filters, Prio);
if (!Command("CAPS TSPIDS", 220)) {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't negotiate capabilities on %s:%d",
RemoteIp().c_str(), RemotePort());
Close();
return false;
}
else {
if(!Command("VERS 1.0", 220)) {
Close();
return false;
}
const char *Filters = "";
if(Command("CAPS FILTERS", 220))
Filters = ",FILTERS";
const char *Prio = "";
if(Command("CAPS PRIO", 220)) {
Prio = ",PRIO";
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;
}
@@ -192,19 +164,17 @@ bool cClientSocket::ProvidesChannel(const cChannel *Channel, int Priority) {
std::string command = (std::string)"PROV " + (const char*)itoa(Priority) + " "
+ (const char*)Channel->GetChannelID().ToString();
if (!Send(command))
if (!Command(command))
return false;
uint code;
std::string buffer;
if (!Receive(command, &code, &buffer))
return false;
if (code != 220 && code != 560) {
esyslog("streamdev-client: Unexpected reply to '%s' from %s:%d: %s",
command.c_str(), RemoteIp().c_str(), RemotePort(), buffer.c_str());
if (!Expect(220, &buffer)) {
if (buffer.substr(0, 3) != "560" && errno == 0)
esyslog("ERROR: Streamdev: Couldn't check if %s:%d provides channel %s",
RemoteIp().c_str(), RemotePort(), Channel->Name());
return false;
}
return code == 220;
return true;
}
bool cClientSocket::CreateDataConnection(eSocketId Id) {
@@ -216,7 +186,7 @@ bool cClientSocket::CreateDataConnection(eSocketId Id) {
DELETENULL(m_DataSockets[Id]);
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));
return false;
}
@@ -231,8 +201,13 @@ bool cClientSocket::CreateDataConnection(eSocketId Id) {
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;
}
/* The server SHOULD do the following:
* - get PORT command
@@ -242,7 +217,7 @@ bool cClientSocket::CreateDataConnection(eSocketId Id) {
m_DataSockets[Id] = new cTBSocket;
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 ? "" : ": ",
errno == 0 ? "" : strerror(errno));
DELETENULL(m_DataSockets[Id]);
@@ -253,12 +228,18 @@ bool cClientSocket::CreateDataConnection(eSocketId Id) {
}
bool cClientSocket::CloseDataConnection(eSocketId Id) {
//if (!CheckConnection()) return false;
CMD_LOCK;
if(Id == siLive || Id == siLiveFilter)
if (m_DataSockets[Id] != NULL) {
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]);
}
return true;
@@ -271,41 +252,41 @@ bool cClientSocket::SetChannelDevice(const cChannel *Channel) {
std::string command = (std::string)"TUNE "
+ (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;
}
m_LastSignalUpdate = 0;
return true;
}
bool cClientSocket::SetPriority(int Priority) {
if (Priority == m_Priority)
return true;
if (!CheckConnection()) return false;
CMD_LOCK;
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;
m_Priority = Priority;
}
return true;
}
bool cClientSocket::GetSignal(int *SignalStrength, int *SignalQuality, int *Dev) {
bool cClientSocket::GetSignal(int *SignalStrength, int *SignalQuality) {
if (!CheckConnection()) return -1;
CMD_LOCK;
if (m_LastSignalUpdate != time(NULL)) {
uint code = 0;
std::string buffer;
std::string command("SGNL");
if (!Send(command) || !Receive(command, &code, &buffer) || code != 220
|| sscanf(buffer.c_str(), "%*d %d %d:%d", &m_LastDev, &m_LastSignalStrength, &m_LastSignalQuality) != 3) {
m_LastDev = -1;
if (!Command("SGNL") || !Expect(220, &buffer)
|| sscanf(buffer.c_str(), "%*d %*d %d:%d", &m_LastSignalStrength, &m_LastSignalQuality) != 2) {
m_LastSignalStrength = -1;
m_LastSignalQuality = -1;
}
@@ -315,8 +296,6 @@ bool cClientSocket::GetSignal(int *SignalStrength, int *SignalQuality, int *Dev)
*SignalStrength = m_LastSignalStrength;
if (SignalQuality)
*SignalQuality = m_LastSignalQuality;
if (Dev)
*Dev = m_LastDev;
return 0;
}
@@ -326,7 +305,13 @@ bool cClientSocket::SetPid(int Pid, bool On) {
CMD_LOCK;
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) {
@@ -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)
+ " " + (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) {
@@ -346,20 +337,27 @@ bool cClientSocket::CloseDvr(void) {
if (m_DataSockets[siLive] != NULL) {
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;
}
DELETENULL(m_DataSockets[siLive]);
}
return true;
}
bool cClientSocket::Quit(void) {
m_Abort = true;
if (!IsOpen()) return false;
bool res;
CMD_LOCK;
std::string command("QUIT");
bool res = Send(command) && Receive(command, NULL, NULL, QUIT_TIMEOUT_MS);
if (!CheckConnection()) return false;
if (!(res = Command("QUIT", 221))) {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't quit command connection to %s:%d",
RemoteIp().c_str(), RemotePort());
}
Close();
return res;
}
@@ -369,5 +367,10 @@ bool cClientSocket::SuspendServer(void) {
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 "common.h"
#include "client/setup.h"
#include <string>
@@ -21,27 +20,23 @@ private:
cTBSocket *m_DataSockets[si_Count];
cMutex m_Mutex;
char m_Buffer[BUFSIZ + 1]; // various uses
unsigned int m_ServerVersion;
bool m_Prio; // server supports command PRIO
int m_Priority; // current device priority
bool m_Abort; // quit command pending
time_t m_LastSignalUpdate;
int m_LastSignalStrength;
int m_LastSignalQuality;
int m_LastDev;
protected:
/* Send Command, and return true if the command results in Expected.
Returns false on failure. */
bool Command(const std::string &Command, uint Expected);
Returns false on failure, setting errno appropriately if it has been
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. */
bool Send(const std::string &Command);
/* Fetch results from an ongoing Command. The status code and the
buffer holding the server's response are stored in Code and Result
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);
/* Fetch results from an ongoing Command called with Expected == 0. Returns
true if the response has the code Expected, returning an internal buffer
in the array pointer pointed to by Result. Returns false on failure,
setting errno appropriately if it has been a system failure. */
bool Expect(uint Expected, std::string *Result = NULL, uint TimeoutMs = 1500);
public:
cClientSocket(void);
@@ -55,12 +50,10 @@ public:
bool CloseDataConnection(eSocketId Id);
bool SetChannelDevice(const cChannel *Channel);
bool SupportsPrio() { return m_Prio; }
unsigned int ServerVersion() { return m_ServerVersion; }
int Priority() const { return m_Priority; }
bool SetPriority(int Priority);
bool SetPid(int Pid, 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 SuspendServer(void);
bool Quit(void);
@@ -68,4 +61,6 @@ public:
cTBSocket *DataSocket(eSocketId Id) const;
};
extern class cClientSocket ClientSocket;
#endif // VDR_STREAMDEV_CLIENT_CONNECTION_H

View File

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

View File

@@ -9,25 +9,20 @@
#include <vdr/plugin.h>
#define STREAMDEV_MAXDEVICES 8
class cStreamdevDevice;
class cPluginStreamdevClient : public cPlugin {
private:
static const char *DESCRIPTION;
cStreamdevDevice *m_Devices[STREAMDEV_MAXDEVICES];
static const char *DESCRIPTION;
public:
cPluginStreamdevClient(void);
virtual ~cPluginStreamdevClient();
virtual const char *Version(void) { return VERSION; }
virtual const char *Description(void);
virtual bool Initialize(void);
virtual bool Start(void);
virtual const char *MainMenuEntry(void);
virtual cOsdObject *MainMenuAction(void);
virtual cMenuSetupPage *SetupMenu(void);
virtual bool SetupParse(const char *Name, const char *Value);
virtual bool Service(const char *Id, void *Data = NULL);
virtual void MainThreadHook(void);
};

View File

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

View File

@@ -17,23 +17,20 @@
#include "tools/socket.h"
#ifdef DEBUG
#include <stdio.h>
#include <time.h>
#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);\
}
# include <stdio.h>
# define Dprintf(x...) fprintf(stderr, x)
#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
#define MAXPARSEBUFFER KILOBYTE(16)
/* Service ID for loop prevention */
#define LOOP_PREVENTION_SERVICE "StreamdevLoopPrevention"
/* Check if a channel is a radio station. */
#define ISRADIO(x) ((x)->Vpid()==0||(x)->Vpid()==1||(x)->Vpid()==0x1fff)
@@ -49,6 +46,13 @@ enum eStreamType {
st_Count
};
enum eSuspendMode {
smOffer,
smAlways,
smNever,
sm_Count
};
enum eSocketId {
siLive,
siReplay,

View File

@@ -298,6 +298,7 @@ void write_pes(int fd, pes_packet *p){
}
static unsigned int find_length(int f){
uint64_t p = 0;
uint64_t start = 0;
uint64_t q = 0;
int found = 0;
@@ -308,7 +309,7 @@ static unsigned int find_length(int f){
start -=2;
lseek(f,start,SEEK_SET);
while ( neof > 0 && !found ){
lseek(f,0,SEEK_CUR);
p = lseek(f,0,SEEK_CUR);
neof = save_read(f,&sync4,4);
if (sync4[0] == 0x00 && sync4[1] == 0x00 && sync4[2] == 0x01) {
switch ( sync4[3] ) {
@@ -557,7 +558,7 @@ int read_pes(int f, pes_packet *p){
while (neof > 0 && !found) {
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 (sync4[0] == 0x00 && sync4[1] == 0x00 && sync4[2] == 0x01) {
p->stream_id = sync4[3];
@@ -1333,7 +1334,7 @@ void tfilter(trans *p)
{
int l,c;
int tpid;
uint8_t flags;
uint8_t flag,flags;
uint8_t adapt_length = 0;
uint8_t cpid[2];
@@ -1349,6 +1350,7 @@ void tfilter(trans *p)
tpid);
}
flag = cpid[0];
flags = p->packet[3];
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 p = 0;
uint32_t pts = 0;
uint32_t dts = 0;
int stuff = 0;
int length = *vlength;
long diff = 0;
@@ -786,6 +787,7 @@ int write_video_pes( Remux *rem, uint8_t *buf, int *vlength)
if (add < 0) return -1;
pos += add;
rem->vpts_old = rem->vpts;
dts = rem->vdts;
rem->vpts = rem->vpts_list[0].PTS;
rem->vdts = rem->vpts_list[0].dts;
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/streamer.h"
#include <vdr/channels.h>
#include <vdr/remux.h>
#include <vdr/tools.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -26,7 +25,7 @@ protected:
virtual void Action(void);
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();
void Put(const uchar *Data, int Count);
@@ -35,7 +34,7 @@ public:
} // 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_Active(false),
m_Process(-1),
@@ -74,24 +73,17 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
#define ADDENV(x...) if (asprintf(&env[i++], x) < 0) i--
// add channel ID, name and pids to environment
if (Channel) {
ADDENV("REMUX_CHANNEL_ID=%s", *Channel->GetChannelID().ToString());
ADDENV("REMUX_CHANNEL_NAME=%s", Channel->Name());
ADDENV("REMUX_VTYPE=%d", Channel->Vtype());
if (Channel->Vpid())
ADDENV("REMUX_VPID=%d", Channel->Vpid());
if (Channel->Ppid() != Channel->Vpid())
ADDENV("REMUX_PPID=%d", Channel->Ppid());
if (Channel->Tpid())
ADDENV("REMUX_TPID=%d", Channel->Tpid());
}
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());
}
ADDENV("REMUX_CHANNEL_ID=%s", *Channel->GetChannelID().ToString());
ADDENV("REMUX_CHANNEL_NAME=%s", Channel->Name());
#if APIVERSNUM >= 10701
ADDENV("REMUX_VTYPE=%d", Channel->Vtype());
#endif
if (Channel->Vpid())
ADDENV("REMUX_VPID=%d", Channel->Vpid());
if (Channel->Ppid() != Channel->Vpid())
ADDENV("REMUX_PPID=%d", Channel->Ppid());
if (Channel->Tpid())
ADDENV("REMUX_TPID=%d", Channel->Tpid());
std::string buffer;
if (Apids && *Apids) {
@@ -102,16 +94,9 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
buffer.clear();
for (const int *pid = Apids; *pid; pid++) {
int j;
if (Channel) {
for (j = 0; Channel->Apid(j) && Channel->Apid(j) != *pid; j++)
;
(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) ? " " : "");
}
for (j = 0; Channel->Apid(j) && Channel->Apid(j) != *pid; j++)
;
(buffer += Channel->Alang(j)) += (*(pid + 1) ? " " : "");
}
ADDENV("REMUX_ALANG=%s", buffer.c_str());
}
@@ -125,21 +110,14 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
buffer.clear();
for (const int *pid = Dpids; *pid; pid++) {
int j;
if (Channel) {
for (j = 0; Channel->Dpid(j) && Channel->Dpid(j) != *pid; j++)
;
(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) ? " " : "");
}
for (j = 0; Channel->Dpid(j) && Channel->Dpid(j) != *pid; j++)
;
(buffer += Channel->Dlang(j)) += (*(pid + 1) ? " " : "");
}
ADDENV("REMUX_DLANG=%s", buffer.c_str());
}
if (Channel && Channel->Spid(0)) {
if (Channel->Spid(0)) {
buffer.clear();
for (const int *pid = Channel->Spids(); *pid; pid++)
(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) ? " " : "");
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) {
// add vars for a CGI like interface
@@ -303,7 +270,7 @@ void cTSExt::Action(void)
dsyslog("streamdev-server: buffer full while reading from externremux");
if (result == -1) {
if (errno != EINTR && errno != EAGAIN) {
if (errno != EINTR) {
LOG_ERROR_STR("read failed");
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):
m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE)),
m_Remux(new cTSExt(m_ResultBuffer, Connection, Channel, NULL, 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(new cRingBufferLinear(WRITERBUFSIZE, TS_SIZE * 2)),
m_Remux(new cTSExt(m_ResultBuffer, Connection, Channel, Apids, Dpids))
{
m_ResultBuffer->SetTimeouts(500, 100);
}

View File

@@ -6,7 +6,6 @@
#include <string>
class cChannel;
class cPatPmtParser;
class cServerConnection;
namespace Streamdev {
@@ -20,7 +19,6 @@ private:
public:
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();
int Put(const uchar *Data, int Count);

View File

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

View File

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

View File

@@ -1,18 +1,14 @@
#
# 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.
# 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
### The name of the shared object file:
SOFILE = libvdr-$(PLUGIN).so
### Includes and Defines (add further entries here):
DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
@@ -26,12 +22,13 @@ SERVEROBJS = $(PLUGIN).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 \
streamer.o livestreamer.o livefilter.o recplayer.o \
menu.o suspend.o setup.o
### The main target:
all: $(SOFILE) i18n
.PHONY: all i18n clean
all: libvdr-$(PLUGIN).so i18n
### Implicit rules:
@@ -42,48 +39,44 @@ all: $(SOFILE) i18n
MAKEDEP = $(CXX) -MM -MG
DEPFILE = .dependencies
$(DEPFILE): Makefile
@$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(SERVEROBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) > $@
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(SERVEROBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) > $@
-include $(DEPFILE)
### Internationalization (I18N):
PODIR = po
LOCALEDIR = $(VDRDIR)/locale
I18Npo = $(wildcard $(PODIR)/*.po)
I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file))))
I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
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 --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)
msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $<
msgmerge -U --no-wrap --no-location --backup=none -q $@ $<
@touch $@
$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
install -D -m644 $< $@
$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
@mkdir -p $(dir $@)
cp $< $@
.PHONY: i18n
i18n: $(I18Nmo) $(I18Npot)
install-i18n: $(I18Nmsgs)
i18n: $(I18Nmsgs)
### Targets:
$(SOFILE): $(SERVEROBJS) $(COMMONOBJS) \
libvdr-$(PLUGIN).so: $(SERVEROBJS) $(COMMONOBJS) \
../tools/sockettools.a ../remux/remux.a ../libdvbmpeg/libdvbmpegtools.a
$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $^ -o $@
install-lib: $(SOFILE)
install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION)
install: install-lib install-i18n
%.so:
$(CXX) $(CXXFLAGS) -shared $^ -o $@
@cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION)
clean:
@-rm -f $(PODIR)/*.mo $(PODIR)/*.pot
@-rm -f $(COMMONOBJS) $(SERVEROBJS) $(DEPFILE) *.so *.tgz core* *~
@-rm -f $(COMMONOBJS) $(SERVEROBJS) $(DEPFILE) $(PODIR)/*.mo $(PODIR)/*.pot *.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
#endif
{
cMulticastGroup *group = m_Groups.First();
while (group && group->group != Group)
@@ -121,12 +117,7 @@ bool cComponentIGMP::Initialize(void)
{
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))
#endif
{
if (channel->GroupSep())
continue;
@@ -155,12 +146,7 @@ void cComponentIGMP::Destruct(void)
Cancel(-1);
m_CondWait.Signal();
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))
#endif
{
if (channel->GroupSep())
continue;
@@ -446,18 +432,9 @@ cServerConnection* cComponentIGMP::IGMPStartMulticast(cMulticastGroup* Group)
in_addr_t g = ntohl(Group->group);
if (g > MULTICAST_PRIV_MIN && g <= MULTICAST_PRIV_MAX) {
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);
#endif
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
#if APIVERSNUM >= 20300
const cServerConnection *s = clients.First();
#else
cServerConnection *s = clients.First();
#endif
while (s) {
if (s->RemoteIpAddr() == Group->group)
break;
@@ -476,11 +453,7 @@ cServerConnection* cComponentIGMP::IGMPStartMulticast(cMulticastGroup* Group)
void cComponentIGMP::IGMPStopMulticast(cMulticastGroup* Group)
{
cThreadLock lock;
#if APIVERSNUM >= 20300
cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
#else
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
#endif
for (cServerConnection *s = clients.First(); s; s = clients.Next(s)) {
if (s->RemoteIpAddr() == Group->group)
s->Close();

View File

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

View File

@@ -9,10 +9,59 @@
#include <vdr/tools.h>
#include <vdr/thread.h>
#include <vdr/transfer.h>
#include <string.h>
#include <stdarg.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):
cTBSocket(Type),
m_Protocol(Protocol),
@@ -20,14 +69,14 @@ cServerConnection::cServerConnection(const char *Protocol, int Type):
m_Pending(false),
m_ReadBytes(0),
m_WriteBytes(0),
m_WriteIndex(0),
m_Streamer(NULL)
m_WriteIndex(0)
{
m_SwitchLive = new cSwitchLive();
}
cServerConnection::~cServerConnection()
{
delete(m_Streamer);
delete m_SwitchLive;
}
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)) {
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())
channel = Channels.GetByNumber(temp);
#endif
} else {
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
channel = Channels->GetByChannelID(tChannelID::FromString(string));
#else
channel = Channels.GetByChannelID(tChannelID::FromString(string));
#endif
if (channel == NULL) {
int i = 1;
#if APIVERSNUM >= 20300
while ((channel = Channels->GetByNumber(i, 1)) != NULL) {
#else
while ((channel = Channels.GetByNumber(i, 1)) != NULL) {
#endif
if (String == channel->Name())
break;
@@ -212,7 +244,176 @@ bool cServerConnection::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 "common.h"
#include "server/streamer.h"
#include <map>
#include <string>
typedef std::map<std::string,std::string> tStrStrMap;
typedef std::pair<std::string,std::string> tStrStr;
class cChannel;
class cDevice;
class cSwitchLive;
/* Basic capabilities of a straight text-based protocol, most functions
virtual to support more complicated protocols */
@@ -34,10 +34,19 @@ private:
uint m_WriteBytes;
uint m_WriteIndex;
cStreamdevStreamer *m_Streamer;
cSwitchLive *m_SwitchLive;
tStrStrMap m_Headers;
/* Check if a device would be available for transfering the given
channel. This call has no side effects except for temporarily
detaching this connection's receivers. */
cDevice *CheckDevice(const cChannel *Channel, int Priority, bool LiveView, const cDevice *AvoidDevice = NULL);
/* Test if device is in use as the transfer mode receiver device
or a FF card, displaying live TV from internal tuner */
static bool UsedByLiveTV(cDevice *device);
protected:
/* Will be called when a command terminated by a newline has been
received */
@@ -54,12 +63,6 @@ protected:
/* Add a request header */
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);
public:
@@ -103,13 +106,27 @@ public:
/* Close the socket */
virtual bool Close(void);
/* Will retrieve an unused device for transmitting data. Receivers have
already been attached from the device if necessary. Use the returned
cDevice in a following call to StartTransfer */
cDevice *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 Detach(void) = 0;
virtual void Attach(void) = 0;
/* This connections protocol name */
virtual const char* Protocol(void) const { return m_Protocol; }
/* Text description of stream */
virtual cString ToText(char Delimiter = ' ') const;
/* Representation in menu */
virtual cString ToText(void) const;
/* std::map with additional information */
const tStrStrMap& Headers(void) const { return m_Headers; }

View File

@@ -3,15 +3,6 @@
*/
#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/menuHTTP.h"
@@ -21,12 +12,10 @@
cConnectionHTTP::cConnectionHTTP(void):
cServerConnection("HTTP"),
m_Status(hsRequest),
m_StreamType((eStreamType)StreamdevServerSetup.HTTPStreamType),
m_LiveStreamer(NULL),
m_Channel(NULL),
m_RecPlayer(NULL),
m_ReplayPos(0),
m_ReplayFakeRange(false),
m_MenuList(NULL)
m_StreamType((eStreamType)StreamdevServerSetup.HTTPStreamType),
m_ChannelList(NULL)
{
Dprintf("constructor hsRequest\n");
m_Apid[0] = m_Apid[1] = 0;
@@ -35,9 +24,7 @@ cConnectionHTTP::cConnectionHTTP(void):
cConnectionHTTP::~cConnectionHTTP()
{
SetStreamer(NULL);
delete m_RecPlayer;
delete m_MenuList;
delete m_LiveStreamer;
}
bool cConnectionHTTP::CanAuthenticate(void)
@@ -61,24 +48,9 @@ bool cConnectionHTTP::Command(char *Cmd)
*v = 0;
SetHeader("REQUEST_METHOD", Cmd);
q = strchr(p, '?');
if (q) {
if (q)
*q = 0;
SetHeader("QUERY_STRING", q + 1);
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("QUERY_STRING", q ? ++q : "");
SetHeader("PATH_INFO", p);
m_Status = hsHeaders;
return true;
@@ -161,260 +133,97 @@ bool cConnectionHTTP::ProcessRequest(void)
}
if (!authOk) {
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_pathinfo = Headers().find(PATH_INFO);
if (it_method == Headers().end() || it_pathinfo == Headers().end()) {
// should never happen
esyslog("streamdev-server connectionHTTP: Missing method or pathinfo");
} else if (it_method->second.compare("GET") == 0 && ProcessURI(it_pathinfo->second)) {
if (m_MenuList)
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) {
if (cStreamdevLiveStreamer::ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority)) {
cStreamdevLiveStreamer* liveStreamer = new cStreamdevLiveStreamer(this, m_Channel, StreamdevServerSetup.HTTPPriority, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
if (liveStreamer->GetDevice()) {
SetStreamer(liveStreamer);
cDevice *device = NULL;
if (ProvidesChannel(m_Channel, 0))
device = GetDevice(m_Channel, 0);
if (device != NULL) {
device->SwitchChannel(m_Channel, false);
m_LiveStreamer = new cStreamdevLiveStreamer(0, this);
if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL)) {
m_LiveStreamer->SetDevice(device);
if (!SetDSCP())
LOG_ERROR_STR("unable to set DSCP sockopt");
if (m_StreamType == stEXT) {
return Respond("HTTP/1.0 200 OK");
} else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) {
return 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)) {
return HttpResponse(200, false, "audio/mpeg");
return Respond("HTTP/1.0 200 OK")
&& Respond("Content-Type: audio/mpeg")
&& Respond("");
} else {
return HttpResponse(200, false, "video/mpeg");
return Respond("HTTP/1.0 200 OK")
&& Respond("Content-Type: video/mpeg")
&& Respond("");
}
}
SetStreamer(NULL);
delete liveStreamer;
DELETENULL(m_LiveStreamer);
}
return HttpResponse(503, true);
}
else if (m_RecPlayer != NULL) {
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");
DeferClose();
return Respond("HTTP/1.0 409 Channel not available")
&& Respond("");
}
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)) {
if (m_MenuList) {
DeferClose();
return Respond("%s", true, m_MenuList->HttpHeader().c_str());
}
DeferClose();
if (m_ChannelList)
return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
else if (m_Channel != NULL) {
if (cStreamdevLiveStreamer::ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority)) {
if (ProvidesChannel(m_Channel, 0)) {
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);
SetStreamer(liveStreamer);
return Respond("HTTP/1.0 200 OK");
// TODO
return Respond("HTTP/1.0 200 OK")
&& Respond("");
} 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)) {
return HttpResponse(200, true, "audio/mpeg");
return Respond("HTTP/1.0 200 OK")
&& Respond("Content-Type: audio/mpeg")
&& Respond("");
} else {
return HttpResponse(200, true, "video/mpeg");
return Respond("HTTP/1.0 200 OK")
&& Respond("Content-Type: video/mpeg")
&& Respond("");
}
}
return HttpResponse(503, true);
}
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");
return Respond("HTTP/1.0 409 Channel not available")
&& Respond("");
}
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();
switch (Code)
{
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;
DeferClose();
return Respond("HTTP/1.0 400 Bad Request")
&& Respond("");
}
void cConnectionHTTP::Flushed(void)
@@ -422,21 +231,21 @@ void cConnectionHTTP::Flushed(void)
if (m_Status != hsBody)
return;
if (m_MenuList) {
if (m_MenuList->HasNext()) {
if (!Respond("%s", true, m_MenuList->Next().c_str()))
if (m_ChannelList) {
if (m_ChannelList->HasNext()) {
if (!Respond("%s", true, m_ChannelList->Next().c_str()))
DeferClose();
}
else {
DELETENULL(m_MenuList);
DELETENULL(m_ChannelList);
m_Status = hsFinished;
DeferClose();
}
return;
}
else if (Streamer()) {
else if (m_Channel != NULL) {
Dprintf("streamer start\n");
Streamer()->Start(this);
m_LiveStreamer->Start(this);
m_Status = hsFinished;
}
else {
@@ -446,61 +255,57 @@ 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;
cItemIterator *iterator = NULL;
// keys for Headers() hash
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) {
tStrStrMap::const_iterator it = m_Params.find(GROUP);
iterator = new cListTree(it == m_Params.end() ? NULL : it->second.c_str());
const cChannel* c = NULL;
size_t groupIndex = query.find("group=");
if (groupIndex != std::string::npos)
c = cChannelList::GetGroup(atoi(query.c_str() + groupIndex + 6));
iterator = new cListTree(c);
groupTarget = Filebase + Fileext;
} else if (Filebase.compare("groups") == 0) {
iterator = new cListGroups();
groupTarget = (std::string) "group" + Fileext;
} else if (Filebase.compare("group") == 0) {
tStrStrMap::const_iterator it = m_Params.find(GROUP);
iterator = new cListGroup(it == m_Params.end() ? NULL : it->second.c_str());
const cChannel* c = NULL;
size_t groupIndex = query.find("group=");
if (groupIndex != std::string::npos)
c = cChannelList::GetGroup(atoi(query.c_str() + groupIndex + 6));
iterator = new cListGroup(c);
} else if (Filebase.compare("channels") == 0) {
iterator = new cListChannels();
} else if (Filebase.compare("all") == 0 ||
(Filebase.empty() && Fileext.empty())) {
iterator = new cListAll();
} else if (Filebase.compare("recordings") == 0) {
iterator = new cRecordingsIterator(m_StreamType);
}
if (iterator) {
// assemble base url: http://host/path/
std::string base;
const static std::string HOST("HTTP_HOST");
tStrStrMap::const_iterator it = Headers().find(HOST);
if (it != Headers().end())
base = "http://" + it->second + "/";
else
base = (std::string) "http://" + LocalIp() + ":" +
(const char*) itoa(StreamdevServerSetup.HTTPServerPort) + "/";
base += Path;
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());
if (!query.empty())
self += '?' + query;
return new cHtmlChannelList(iterator, m_StreamType, self.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());
std::string base;
tStrStrMap::const_iterator it = Headers().find(HOST);
if (it != Headers().end())
base = "http://" + it->second + "/";
else
base = (std::string) "http://" + LocalIp() + ":" +
(const char*) itoa(StreamdevServerSetup.HTTPServerPort) + "/";
base += Path;
return new cM3uChannelList(iterator, base.c_str());
} else {
delete iterator;
}
@@ -508,93 +313,6 @@ cMenuList* cConnectionHTTP::MenuListFromString(const std::string& Path, const st
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)
{
std::string filespec, fileext;
@@ -602,24 +320,11 @@ bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
if (file_pos != std::string::npos) {
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
filespec = PathInfo.substr(file_pos + 1, ext_pos - file_pos - 1);
if (ext_pos != std::string::npos)
// file extension including leading .
fileext = PathInfo.substr(ext_pos);
}
if (fileext.length() > 5) {
//probably not an extension
@@ -630,12 +335,10 @@ bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
// Streamtype with leading / stripped off
std::string type = PathInfo.substr(1, PathInfo.find_first_of("/;", 1) - 1);
const char* pType = type.c_str();
if (strcasecmp(pType, "PES") == 0) {
m_StreamType = stPES;
#ifdef STREAMDEV_PS
} else if (strcasecmp(pType, "PS") == 0) {
if (strcasecmp(pType, "PS") == 0) {
m_StreamType = stPS;
#endif
} else if (strcasecmp(pType, "PES") == 0) {
m_StreamType = stPES;
} else if (strcasecmp(pType, "TS") == 0) {
m_StreamType = stTS;
} 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());
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");
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) {
Dprintf("Channel found. Apid/Dpid is %d/%d\n", m_Apid[0], m_Dpid[0]);
return true;
@@ -659,8 +359,8 @@ bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
return false;
}
cString cConnectionHTTP::ToText(char Delimiter) const
cString cConnectionHTTP::ToText() const
{
cString str = cServerConnection::ToText(Delimiter);
return Streamer() ? cString::sprintf("%s%c%s", *str, Delimiter, *Streamer()->ToText()) : str;
cString str = cServerConnection::ToText();
return m_LiveStreamer ? cString::sprintf("%s\t%s", *str, *m_LiveStreamer->ToText()) : str;
}

View File

@@ -7,13 +7,13 @@
#include "connection.h"
#include "server/livestreamer.h"
#include "server/recstreamer.h"
#include <map>
#include <tools/select.h>
class cChannel;
class cMenuList;
class cStreamdevLiveStreamer;
class cChannelList;
class cConnectionHTTP: public cServerConnection {
private:
@@ -26,32 +26,17 @@ private:
std::string m_Authorization;
eHTTPStatus m_Status;
tStrStrMap m_Params;
eStreamType m_StreamType;
// job: transfer
cStreamdevLiveStreamer *m_LiveStreamer;
const cChannel *m_Channel;
int m_Apid[2];
int m_Dpid[2];
// job: replay
RecPlayer *m_RecPlayer;
int64_t m_ReplayPos;
bool m_ReplayFakeRange;
eStreamType m_StreamType;
// job: listing
cMenuList *m_MenuList;
cMenuList* MenuListFromString(const std::string &PathInfo, const std::string &Filebase, const std::string &Fileext) const;
RecPlayer* RecPlayerFromString(const char* FileBase, const char* FileExt);
cChannelList *m_ChannelList;
cChannelList* ChannelListFromString(const std::string &PathInfo, const std::string &Filebase, const std::string &Fileext) const;
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:
bool ProcessRequest(void);
@@ -59,7 +44,10 @@ public:
cConnectionHTTP(void);
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);
@@ -71,7 +59,7 @@ public:
inline bool cConnectionHTTP::Abort(void) const
{
return !IsOpen() || (Streamer() && Streamer()->Abort());
return !IsOpen() || (m_LiveStreamer && m_LiveStreamer->Abort());
}
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,7 @@
#include "server/recplayer.h"
class cTBSocket;
class cStreamdevLiveStreamer;
class cStreamdevFilterStreamer;
class cLSTEHandler;
class cLSTCHandler;
@@ -19,6 +20,7 @@ class cConnectionVTP: public cServerConnection {
private:
cTBSocket *m_LiveSocket;
cStreamdevLiveStreamer *m_LiveStreamer;
cTBSocket *m_FilterSocket;
cStreamdevFilterStreamer *m_FilterStreamer;
cTBSocket *m_RecSocket;
@@ -26,9 +28,7 @@ private:
char *m_LastCommand;
eStreamType m_StreamType;
unsigned int m_ClientVersion;
bool m_FiltersSupport;
bool m_LoopPrevention;
RecPlayer *m_RecPlayer;
// Priority is only known in PROV command
@@ -53,7 +53,7 @@ public:
virtual void Welcome(void);
virtual void Reject(void);
virtual cString ToText(char Delimiter = ' ') const;
virtual cString ToText() const;
virtual bool Abort(void) const;
virtual void Detach(void);
@@ -61,7 +61,6 @@ public:
virtual bool Command(char *Cmd);
bool CmdCAPS(char *Opts);
bool CmdVERS(char *Opts);
bool CmdPROV(char *Opts);
bool CmdPORT(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 $
*/
#include <vdr/filter.h>
#include "server/livefilter.h"
#include "server/streamer.h"
#include "common.h"
#ifndef TS_SYNC_BYTE
# define TS_SYNC_BYTE 0x47
#endif
#define FILTERBUFSIZE (1000 * TS_SIZE)
// --- 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;
cStreamdevLiveFilter::cStreamdevLiveFilter(cStreamdevStreamer *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)
{
uchar buffer[TS_SIZE];
@@ -66,86 +32,8 @@ void cStreamdevLiveFilter::Process(u_short Pid, u_char Tid, const u_char *Data,
length -= chunk;
pos += chunk;
m_Streamer->Receive(buffer);
int p = m_Streamer->Put(buffer, TS_SIZE);
if (p != TS_SIZE)
m_Streamer->ReportOverflow(TS_SIZE - p);
}
}
// --- 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)
m_ReceiveBuffer->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
#define VDR_STREAMEV_LIVEFILTER_H
#include "server/streamer.h"
#include <vdr/config.h>
class cDevice;
class cStreamdevLiveFilter;
#include <vdr/filter.h>
class cStreamdevFilterStreamer: public cStreamdevStreamer {
class cStreamdevStreamer;
class cStreamdevLiveFilter: public cFilter {
private:
cDevice *m_Device;
cStreamdevLiveFilter *m_Filter;
cStreamdevBuffer *m_ReceiveBuffer;
cStreamdevStreamer *m_Streamer;
protected:
virtual uchar* GetFromReceiver(int &Count) { return m_ReceiveBuffer->Get(Count); }
virtual void DelFromReceiver(int Count) { m_ReceiveBuffer->Del(Count); }
virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
public:
cStreamdevFilterStreamer();
virtual ~cStreamdevFilterStreamer();
cStreamdevLiveFilter(cStreamdevStreamer *Streamer);
void SetDevice(cDevice *Device);
bool SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On);
virtual bool IsReceiving(void) const;
void Receive(uchar *Data);
virtual void Attach(void);
virtual void Detach(void);
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);
}
};
#endif // VDR_STREAMEV_LIVEFILTER_H

View File

@@ -8,47 +8,45 @@
#include "remux/ts2es.h"
#include "remux/extern.h"
#include <vdr/transfer.h>
#include <vdr/ringbuffer.h>
#include "server/livestreamer.h"
#include "server/setup.h"
#include "server/livefilter.h"
#include "common.h"
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 -------------------------------------------------
class cStreamdevLiveReceiver: public cReceiver {
friend class cStreamdevStreamer;
private:
cStreamdevLiveStreamer *m_Streamer;
cStreamdevStreamer *m_Streamer;
protected:
#if APIVERSNUM >= 20300
virtual void Receive(const uchar *Data, int Length);
#else
virtual void Activate(bool On);
virtual void Receive(uchar *Data, int Length);
#endif
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();
};
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),
#else
cReceiver(Channel->GetChannelID(), Priority, 0, Pids),
#endif
m_Streamer(Streamer)
{
#if APIVERSNUM >= 10712
// clears all PIDs but channel remains set
SetPids(NULL);
AddPids(Pids);
#endif
}
cStreamdevLiveReceiver::~cStreamdevLiveReceiver()
@@ -57,12 +55,16 @@ cStreamdevLiveReceiver::~cStreamdevLiveReceiver()
Detach();
}
#if APIVERSNUM >= 20300
void cStreamdevLiveReceiver::Receive(const uchar *Data, int Length) {
#else
void cStreamdevLiveReceiver::Receive(uchar *Data, int Length) {
#endif
m_Streamer->Receive(Data, Length);
int p = 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 ----------------------------------------------------
@@ -258,12 +260,7 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
SI::PAT::Association assoc;
for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); ) {
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());
#endif
if (Channel && (Channel == m_Channel)) {
int prevPmtPid = pmtPid;
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(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),
m_Priority(Priority),
m_NumPids(0),
m_Channel(Channel),
m_StreamType(stTSPIDS),
m_Channel(NULL),
m_Device(NULL),
m_Receiver(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()
{
Dprintf("Desctructing Live streamer\n");
Stop();
DELETENULL(m_PatFilter);
if(m_PatFilter) {
Detach();
DELETENULL(m_PatFilter);
}
DELETENULL(m_Receiver);
delete m_ReceiveBuffer;
delete m_Remux;
}
bool cStreamdevLiveStreamer::HasPid(int Pid)
@@ -456,21 +446,17 @@ bool cStreamdevLiveStreamer::SetPids(int Pid, const int *Pids1, const int *Pids2
void cStreamdevLiveStreamer::SetPriority(int Priority)
{
m_Priority = Priority;
#if VDRVERSNUM >= 20104
cThreadLock ThreadLock(m_Device);
if (m_Receiver)
m_Receiver->SetPriority(Priority);
else
#endif
StartReceiver();
StartReceiver();
}
void cStreamdevLiveStreamer::GetSignal(int *DevNum, int *Strength, int *Quality) const
{
if (m_Device) {
*DevNum = m_Device->DeviceNumber() + 1;
#if APIVERSNUM >= 10719
*Strength = m_Device->SignalStrength();
*Quality = m_Device->SignalQuality();
#endif
}
}
@@ -482,19 +468,13 @@ cString cStreamdevLiveStreamer::ToText() const
return cString("");
}
bool cStreamdevLiveStreamer::IsReceiving(void) const
void cStreamdevLiveStreamer::StartReceiver(void)
{
cThreadLock ThreadLock(m_Device);
return m_Receiver && m_Receiver->IsAttached();
}
void cStreamdevLiveStreamer::StartReceiver(bool Force)
{
if (m_NumPids > 0 || Force) {
if (m_NumPids > 0) {
Dprintf("Creating Receiver to respect changed pids\n");
cReceiver *current = m_Receiver;
cThreadLock ThreadLock(m_Device);
m_Receiver = new cStreamdevLiveReceiver(this, m_Channel, m_Priority, m_Pids);
cThreadLock ThreadLock(m_Device);
if (IsRunning())
Attach();
delete current;
@@ -503,15 +483,17 @@ void cStreamdevLiveStreamer::StartReceiver(bool Force)
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");
//printf("ca pid: %d\n", Channel->Ca());
m_Channel = Channel;
m_StreamType = StreamType;
const int *Apids = Apid ? Apid : m_Channel->Apids();
const int *Dpids = Dpid ? Dpid : m_Channel->Dpids();
switch (StreamType) {
switch (m_StreamType) {
case stES:
{
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];
else if (Dpid && Dpid[0])
pid = Dpid[0];
SetRemux(new cTS2ESRemux(pid));
m_Remux = new cTS2ESRemux(pid);
return SetPids(pid);
}
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());
#ifdef STREAMDEV_PS
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());
#endif
case stEXT:
SetRemux(new cExternRemux(Connection(), m_Channel, Apids, Dpids));
m_Remux = new cExternRemux(Connection(), m_Channel, Apids, Dpids);
// fall through
case stTS:
// This should never happen, but ...
@@ -552,40 +532,12 @@ bool cStreamdevLiveStreamer::SetChannel(eStreamType StreamType, const int* Apid,
case stTSPIDS:
Dprintf("pid streaming mode\n");
// No PIDs requested yet. Start receiver anyway to occupy device
StartReceiver(true);
return true;
default:
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)
{
// insert si data
@@ -593,12 +545,34 @@ int cStreamdevLiveStreamer::Put(const uchar *Data, int Count)
int siCount;
uchar *siData = m_PatFilter->Get(siCount);
if (siData) {
siCount = cStreamdevStreamer::Put(siData, siCount);
if (m_Remux)
siCount = m_Remux->Put(siData, siCount);
else
siCount = cStreamdevStreamer::Put(siData, siCount);
if (siCount)
m_PatFilter->Del(siCount);
}
}
return cStreamdevStreamer::Put(Data, Count);
if (m_Remux)
return m_Remux->Put(Data, Count);
else
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)
@@ -606,8 +580,7 @@ void cStreamdevLiveStreamer::Attach(void)
Dprintf("cStreamdevLiveStreamer::Attach()\n");
if (m_Device) {
if (m_Receiver) {
if (m_Receiver->IsAttached())
m_Device->Detach(m_Receiver);
m_Device->Detach(m_Receiver);
m_Device->AttachReceiver(m_Receiver);
}
if (m_PatFilter) {
@@ -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 result;
@@ -739,3 +617,105 @@ std::string cStreamdevLiveStreamer::Report(void)
result += "\n";
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
#include <vdr/config.h>
#include <vdr/status.h>
#include <vdr/receiver.h>
#include "server/streamer.h"
#include "server/streamdev-server.h"
#include "common.h"
#define LIVEBUFSIZE (20000 * TS_SIZE)
namespace Streamdev {
class cTSRemux;
}
class cStreamdevPatFilter;
class cStreamdevLiveReceiver;
// --- cStreamdevLiveStreamer -------------------------------------------------
class cStreamdevLiveStreamer: public cStreamdevStreamer, public cMainThreadHookSubscriber
#if VDRVERSNUM >= 20104
, public cStatus
#endif
{
class cStreamdevLiveStreamer: public cStreamdevStreamer {
private:
int m_Priority;
int m_Pids[MAXRECEIVEPIDS + 1];
int m_NumPids;
int m_Caids[MAXCAIDS + 1];
eStreamType m_StreamType;
const cChannel *m_Channel;
cDevice *m_Device;
cStreamdevLiveReceiver *m_Receiver;
cStreamdevBuffer *m_ReceiveBuffer;
cStreamdevPatFilter *m_PatFilter;
bool m_SwitchLive;
Streamdev::cTSRemux *m_Remux;
void StartReceiver(bool Force = false);
void StartReceiver(void);
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:
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();
void SetDevice(cDevice *Device) { m_Device = Device; }
bool SetPid(int Pid, bool On);
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 GetSignal(int *DevNum, int *Strength, int *Quality) const;
virtual cString ToText() const;
cString ToText() const;
#if APIVERSNUM >= 20300
void Receive(const uchar *Data, int Length);
#else
void Receive(uchar *Data, int Length);
#endif
virtual bool IsReceiving(void) const;
virtual int Put(const uchar *Data, int Count);
virtual uchar *Get(int &Count);
virtual void Del(int Count);
virtual void Attach(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:
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

View File

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

View File

@@ -1,244 +1,58 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <vdr/channels.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(const cChannel *First)
{
first = First;
current = NULL;
}
cChannelIterator::cChannelIterator(const cChannel *First): channel(First)
{}
bool cChannelIterator::Next()
const cChannel* cChannelIterator::Next()
{
if (first)
{
current = first;
first = NULL;
}
else
current = NextChannel(current);
const cChannel *current = channel;
channel = NextChannel(channel);
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(): cChannelIterator(FirstChannel())
cListAll::cListAll(): cChannelIterator(Channels.First())
{}
const cChannel* cListAll::NextChannel(const cChannel *Channel)
{
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
if (Channel)
Channel = SkipFakeGroups(Channels->Next(Channel));
#else
if (Channel)
Channel = SkipFakeGroups(Channels.Next(Channel));
#endif
return Channel;
}
//**************************** cListChannels **************
cListChannels::cListChannels(): cChannelIterator(NextNormal())
cListChannels::cListChannels(): cChannelIterator(Channels.Get(Channels.GetNextNormal(-1)))
{}
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)
Channel = Channels.Get(Channels.GetNextNormal(Channel->Index()));
#endif
return Channel;
}
// ********************* cListGroups ****************
cListGroups::cListGroups(): cChannelIterator(NextGroup())
cListGroups::cListGroups(): cChannelIterator(Channels.Get(Channels.GetNextGroup(-1)))
{}
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)
Channel = Channels.Get(Channels.GetNextGroup(Channel->Index()));
#endif
return Channel;
}
//
// ********************* 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)
{
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
if (Channel)
Channel = SkipFakeGroups(Channels->Next(Channel));
#else
if (Channel)
Channel = SkipFakeGroups(Channels.Next(Channel));
#endif
return Channel && !Channel->GroupSep() ? Channel : NULL;
}
@@ -248,65 +62,67 @@ const cChannel* cListGroup::NextChannel(const cChannel *Channel)
}
//
// ********************* cListTree ****************
cListTree::cListTree(const char *SelectedGroupId): cChannelIterator(NextGroup())
cListTree::cListTree(const cChannel *SelectedGroup): cChannelIterator(Channels.Get(Channels.GetNextGroup(-1)))
{
selectedGroup = GetGroup(SelectedGroupId);
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
currentGroup = Channels->Get(Channels->GetNextGroup(-1));
#else
selectedGroup = SelectedGroup;
currentGroup = Channels.Get(Channels.GetNextGroup(-1));
#endif
}
const cChannel* cListTree::NextChannel(const cChannel *Channel)
{
if (currentGroup == selectedGroup)
{
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
if (Channel)
Channel = SkipFakeGroups(Channels->Next(Channel));
#else
if (Channel)
Channel = SkipFakeGroups(Channels.Next(Channel));
#endif
if (Channel && Channel->GroupSep())
currentGroup = Channel;
}
else
{
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
if (Channel)
Channel = Channels->Get(Channels->GetNextGroup(Channel->Index()));
#else
if (Channel)
Channel = Channels.Get(Channels.GetNextGroup(Channel->Index()));
#endif
currentGroup = Channel;
}
return Channel;
}
// ******************** cMenuList ******************
cMenuList::cMenuList(cItemIterator *Iterator) : iterator(Iterator)
// ******************** cChannelList ******************
cChannelList::cChannelList(cChannelIterator *Iterator) : iterator(Iterator)
{}
cMenuList::~cMenuList()
cChannelList::~cChannelList()
{
delete iterator;
}
// ******************** cHtmlMenuList ******************
const char* cHtmlMenuList::menu =
int cChannelList::GetGroupIndex(const cChannel *Group)
{
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=\"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=\"channels.html\" tvid=\"BLUE\">Channels</a> (<a href=\"channels.m3u\">Playlist</a> | <a href=\"channels.rss\">RSS</a>)] "
"[<a href=\"recordings.html\">Recordings</a> (<a href=\"recordings.m3u\">Playlist</a> | <a href=\"recordings.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>)] ";
const char* cHtmlMenuList::css =
const char* cHtmlChannelList::css =
"<style type=\"text/css\">\n"
"<!--\n"
"a:link, a:visited, a:hover, a:active, a:focus { color:#333399; }\n"
@@ -322,7 +138,7 @@ const char* cHtmlMenuList::css =
"-->\n"
"</style>";
const char* cHtmlMenuList::js =
const char* cHtmlChannelList::js =
"<script language=\"JavaScript\">\n"
"<!--\n"
@@ -383,15 +199,13 @@ const char* cHtmlMenuList::js =
"</script>";
std::string cHtmlMenuList::StreamTypeMenu()
std::string cHtmlChannelList::StreamTypeMenu()
{
std::string typeMenu;
typeMenu += (streamType == stTS ? (std::string) "[TS] " :
(std::string) "[<a href=\"/TS/" + self + "\">TS</a>] ");
#ifdef STREAMDEV_PS
typeMenu += (streamType == stPS ? (std::string) "[PS] " :
(std::string) "[<a href=\"/PS/" + self + "\">PS</a>] ");
#endif
typeMenu += (streamType == stPES ? (std::string) "[PES] " :
(std::string) "[<a href=\"/PES/" + self + "\">PES</a>] ");
typeMenu += (streamType == stES ? (std::string) "[ES] " :
@@ -401,29 +215,27 @@ std::string cHtmlMenuList::StreamTypeMenu()
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;
self = strdup(Self);
rss = strdup(Rss);
groupTarget = (GroupTarget && *GroupTarget) ? strdup(GroupTarget) : NULL;
htmlState = hsRoot;
onItem = true;
current = NULL;
}
cHtmlMenuList::~cHtmlMenuList()
cHtmlChannelList::~cHtmlChannelList()
{
free((void *) self);
free((void *) rss);
free((void *) groupTarget);
}
bool cHtmlMenuList::HasNext()
bool cHtmlChannelList::HasNext()
{
return htmlState != hsPageBottom;
}
std::string cHtmlMenuList::Next()
std::string cHtmlChannelList::Next()
{
switch (htmlState)
{
@@ -440,39 +252,39 @@ std::string cHtmlMenuList::Next()
htmlState = hsPageTop;
break;
case hsPageTop:
onItem = NextItem();
htmlState = onItem ? (IsGroup() ? hsGroupTop : hsPlainTop) : hsPageBottom;
current = NextChannel();
htmlState = current ? (current->GroupSep() ? hsGroupTop : hsPlainTop) : hsPageBottom;
break;
case hsPlainTop:
htmlState = hsPlainItem;
break;
case hsPlainItem:
onItem = NextItem();
htmlState = onItem && !IsGroup() ? hsPlainItem : hsPlainBottom;
current = NextChannel();
htmlState = current && !current->GroupSep() ? hsPlainItem : hsPlainBottom;
break;
case hsPlainBottom:
htmlState = onItem ? hsGroupTop : hsPageBottom;
htmlState = current ? hsGroupTop : hsPageBottom;
break;
case hsGroupTop:
onItem = NextItem();
htmlState = onItem && !IsGroup() ? hsItemsTop : hsGroupBottom;
current = NextChannel();
htmlState = current && !current->GroupSep() ? hsItemsTop : hsGroupBottom;
break;
case hsItemsTop:
htmlState = hsItem;
break;
case hsItem:
onItem = NextItem();
htmlState = onItem && !IsGroup() ? hsItem : hsItemsBottom;
current = NextChannel();
htmlState = current && !current->GroupSep() ? hsItem : hsItemsBottom;
break;
case hsItemsBottom:
htmlState = hsGroupBottom;
break;
case hsGroupBottom:
htmlState = onItem ? hsGroupTop : hsPageBottom;
htmlState = current ? hsGroupTop : hsPageBottom;
break;
case hsPageBottom:
default:
esyslog("streamdev-server cHtmlMenuList: invalid call to Next()");
esyslog("streamdev-server cHtmlChannelList: invalid call to Next()");
break;
}
switch (htmlState)
@@ -497,100 +309,99 @@ std::string cHtmlMenuList::Next()
}
}
std::string cHtmlMenuList::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()
std::string cHtmlChannelList::HtmlHead()
{
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)
{
return (std::string) "<a href=\"" + groupTarget + "?group=" + (const char*) ItemId() + "\">" +
ItemTitle() + "</a>";
return (std::string) "<a href=\"" + groupTarget + "?group=" +
(const char*) itoa(cChannelList::GetGroupIndex(current)) +
"\">" + current->Name() + "</a>";
}
else
{
return (std::string) ItemTitle();
return (std::string) current->Name();
}
}
std::string cHtmlMenuList::ItemText()
std::string cHtmlChannelList::ItemText()
{
std::string line;
std::string suffix;
switch (streamType) {
case stTS: suffix = (std::string) ".ts"; break;
#ifdef STREAMDEV_PS
case stPS: suffix = (std::string) ".vob"; break;
#endif
// for Network Media Tank
case stPES: suffix = (std::string) ".vdr"; break;
default: suffix = "";
}
line += (std::string) "<li value=\"" + (const char*) ItemId() + "\">";
line += (std::string) "<a href=\"" + (const char*) ItemRessource() + suffix + "\"";
line += (std::string) "<li value=\"" + (const char*) itoa(current->Number()) + "\">";
line += (std::string) "<a href=\"" + (std::string) current->GetChannelID().ToString() + suffix + "\"";
// for Network Media Tank
line += (std::string) " vod ";
if (strlen(ItemId()) < 4)
line += (std::string) " tvid=\"" + (const char*) ItemId() + "\"";
if (current->Number() < 1000)
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
if (streamType != stTS)
int count = 0;
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;
const char* lang;
std::string pids;
for (int i = 0; (lang = Alang(i)) != NULL; ++i, ++index) {
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) {
pids += (std::string) " <a href=\"" + (const char*) ItemRessource() +
"+" + (const char*)itoa(index) + suffix + "\" class=\"dpid\" vod>" + (const char*) lang + "</a>";
}
// always show audio PIDs for stES to select audio only
if (index > 2 || streamType == stES)
line += pids;
for (int i = 0; current->Apid(i) != 0; ++i, ++index) {
line += (std::string) " <a href=\"" + (std::string) current->GetChannelID().ToString() +
"+" + (const char*)itoa(index) + suffix + "\" class=\"apid\" vod>" + current->Alang(i) + "</a>";
}
for (int i = 0; current->Dpid(i) != 0; ++i, ++index) {
line += (std::string) " <a href=\"" + (std::string) current->GetChannelID().ToString() +
"+" + (const char*)itoa(index) + suffix + "\" class=\"dpid\" vod>" + current->Dlang(i) + "</a>";
}
}
line += "</li>";
return line;
}
// ******************** cM3uMenuList ******************
cM3uMenuList::cM3uMenuList(cItemIterator *Iterator, const char* Base)
: cMenuList(Iterator),
// ******************** cM3uChannelList ******************
cM3uChannelList::cM3uChannelList(cChannelIterator *Iterator, const char* Base)
: cChannelList(Iterator),
m_IConv(cCharSetConv::SystemCharacterTable(), "UTF-8")
{
base = strdup(Base);
m3uState = msFirst;
}
cM3uMenuList::~cM3uMenuList()
cM3uChannelList::~cM3uChannelList()
{
free(base);
}
bool cM3uMenuList::HasNext()
bool cM3uChannelList::HasNext()
{
return m3uState != msLast;
}
std::string cM3uMenuList::Next()
std::string cM3uChannelList::Next()
{
if (m3uState == msFirst)
{
@@ -598,83 +409,26 @@ std::string cM3uMenuList::Next()
return "#EXTM3U";
}
if (!NextItem())
const cChannel *channel = NextChannel();
if (!channel)
{
m3uState = msLast;
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" +
base + "group.m3u?group=" + (const char*) ItemId();
base + "group.m3u?group=" +
(const char*) itoa(cChannelList::GetGroupIndex(channel));
}
else
{
return (std::string) "#EXTINF:-1," +
(const char*) ItemId() + " " + name + "\r\n" +
base + (const char*) ItemRessource();
(const char*) itoa(channel->Number()) + " " + name + "\r\n" +
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 "../common.h"
#include <vdr/recording.h>
class cChannel;
// ******************** cItemIterator ******************
class cItemIterator
{
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
// ******************** cChannelIterator ******************
class cChannelIterator
{
private:
eStreamType streamType;
const cRecording *first;
const cRecording *current;
cThreadLock RecordingsLock;
const cChannel *channel;
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;
static inline const cChannel* SkipFakeGroups(const cChannel *Channel);
// Helper which returns the group by its index
static const cChannel* GetGroup(const char* GroupId);
public:
virtual bool 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; }
const cChannel* Next();
cChannelIterator(const cChannel *First);
virtual ~cChannelIterator() {};
};
@@ -101,7 +54,7 @@ class cListGroup: public cChannelIterator
protected:
virtual const cChannel* NextChannel(const cChannel *Channel);
public:
cListGroup(const char *GroupId);
cListGroup(const cChannel *Group);
virtual ~cListGroup() {};
};
@@ -113,32 +66,31 @@ class cListTree: public cChannelIterator
protected:
virtual const cChannel* NextChannel(const cChannel *Channel);
public:
cListTree(const char *SelectedGroupId);
cListTree(const cChannel *SelectedGroup);
virtual ~cListTree() {};
};
// ******************** cMenuList ******************
class cMenuList
// ******************** cChannelList ******************
class cChannelList
{
private:
cItemIterator *iterator;
cChannelIterator *iterator;
protected:
bool NextItem() { 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); }
const cChannel* NextChannel() { return iterator->Next(); }
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 bool HasNext() = 0;
virtual std::string Next() = 0;
cMenuList(cItemIterator *Iterator);
virtual ~cMenuList();
cChannelList(cChannelIterator *Iterator);
virtual ~cChannelList();
};
class cHtmlMenuList: public cMenuList
class cHtmlChannelList: public cChannelList
{
private:
static const char* menu;
@@ -152,10 +104,9 @@ class cHtmlMenuList: public cMenuList
hsItemsTop, hsItem, hsItemsBottom
};
eHtmlState htmlState;
bool onItem;
const cChannel *current;
eStreamType streamType;
const char* self;
const char* rss;
const char* groupTarget;
std::string StreamTypeMenu();
@@ -166,18 +117,18 @@ class cHtmlMenuList: public cMenuList
std::string PageBottom();
public:
virtual std::string HttpHeader() {
return cMenuList::HttpHeader()
return cChannelList::HttpHeader()
+ "Content-type: text/html; charset="
+ (cCharSetConv::SystemCharacterTable() ? cCharSetConv::SystemCharacterTable() : "UTF-8")
+ "\r\n";
}
virtual bool HasNext();
virtual std::string Next();
cHtmlMenuList(cItemIterator *Iterator, eStreamType StreamType, const char *Self, const char *Rss, const char *GroupTarget);
virtual ~cHtmlMenuList();
cHtmlChannelList(cChannelIterator *Iterator, eStreamType StreamType, const char *Self, const char *GroupTarget);
virtual ~cHtmlChannelList();
};
class cM3uMenuList: public cMenuList
class cM3uChannelList: public cChannelList
{
private:
char *base;
@@ -185,41 +136,17 @@ class cM3uMenuList: public cMenuList
eM3uState m3uState;
cCharSetConv m_IConv;
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 std::string Next();
cM3uMenuList(cItemIterator *Iterator, const char* Base);
virtual ~cM3uMenuList();
};
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();
cM3uChannelList(cChannelIterator *Iterator, const char* Base);
virtual ~cM3uChannelList();
};
inline const cChannel* cChannelIterator::SkipFakeGroups(const cChannel* Group)
{
#if APIVERSNUM >= 20300
LOCK_CHANNELS_READ;
#endif
while (Group && Group->GroupSep() && !*Group->Name())
#if APIVERSNUM >= 20300
Group = Channels->Next(Group);
#else
Group = Channels.Next(Group);
#endif
return Group;
}

View File

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

View File

@@ -6,16 +6,18 @@
msgid ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
"Last-Translator: Javier Bradineras <jbradi@hotmail.com>\n"
"Language-Team: Spanish <vdr@linuxtv.org>\n"
"Language: es\n"
"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "VDR Streaming Server"
msgstr "Servidor de transmisiones del VDR"
msgid "Streaming active"
msgstr "Trasmisión activa"
@@ -28,20 +30,26 @@ msgstr ""
msgid "Suspend"
msgstr "Suspender"
msgid "Offer suspend mode"
msgstr "Ofrecer modo de suspensión"
msgid "Always suspended"
msgstr "Siempre suspendido"
msgid "Never suspended"
msgstr "Nunca suspendido"
msgid "Common Settings"
msgstr "Configuración común"
msgid "Hide Mainmenu Entry"
msgstr ""
msgid "Start with Live TV suspended"
msgstr ""
msgid "Maximum Number of Clients"
msgstr "Numero máximo de clientes"
msgid "Live TV buffer delay (ms)"
msgstr ""
msgid "Suspend behaviour"
msgstr "Comportamiento de la suspensión"
msgid "Client may suspend"
msgstr "Permitir suspender al cliente"
msgid "VDR-to-VDR Server"
msgstr "Servidor VDR-a-VDR"
@@ -55,15 +63,6 @@ msgstr "Puerto del Servidor VDR-a-VDR"
msgid "Bind to IP"
msgstr "IP asociada"
msgid "Legacy Client Priority"
msgstr ""
msgid "Client may suspend"
msgstr "Permitir suspender al cliente"
msgid "Loop Prevention"
msgstr ""
msgid "HTTP Server"
msgstr "Servidor HTTP"
@@ -73,9 +72,6 @@ msgstr "Iniciar Servidor HTTP"
msgid "HTTP Server Port"
msgstr "Puerto del Servidor HTTP"
msgid "Priority"
msgstr ""
msgid "HTTP Streamtype"
msgstr "Tipo de flujo HTTP"
@@ -90,6 +86,3 @@ msgstr "Puerto del Cliente Multicast"
msgid "Multicast Streamtype"
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.
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
# This file is distributed under the same license as the VDR streamdev package.
# Rolf Ahrenberg, 2008-
# Rolf Ahrenberg <rahrenbe@cc.hut.fi>, 2008
#
msgid ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
"Last-Translator: Rolf Ahrenberg\n"
"Language-Team: Finnish <vdr@linuxtv.org>\n"
"Language: fi\n"
"Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"
"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "VDR Streaming Server"
msgstr "VDR-suoratoistopalvelin"
msgid "Streaming active"
msgstr "Suoratoistopalvelin aktiivinen"
msgid "Streamdev Connections"
msgstr "Suoratoistoyhteydet"
msgstr ""
msgid "Disconnect"
msgstr "Katkaise"
msgstr ""
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"
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"
msgstr "Suurin sallittu asiakkaiden määrä"
msgstr "Suurin sallittu asiakkaiden määrä"
msgid "Live TV buffer delay (ms)"
msgstr ""
msgid "Suspend behaviour"
msgstr "Pysäytystoiminto"
msgid "Client may suspend"
msgstr "Asiakas saa pysäyttää palvelimen"
msgid "VDR-to-VDR Server"
msgstr "VDR-palvelin"
msgid "Start VDR-to-VDR Server"
msgstr "Käynnistä VDR-palvelin"
msgstr "Käynnistä VDR-palvelin"
msgid "VDR-to-VDR Server Port"
msgstr "VDR-palvelimen portti"
@@ -55,41 +63,26 @@ msgstr "VDR-palvelimen portti"
msgid "Bind to IP"
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"
msgstr "HTTP-palvelin"
msgid "Start HTTP Server"
msgstr "Käynnistä HTTP-palvelin"
msgstr "Käynnistä HTTP-palvelin"
msgid "HTTP Server Port"
msgstr "HTTP-palvelimen portti"
msgid "Priority"
msgstr "Prioriteetti"
msgid "HTTP Streamtype"
msgstr "HTTP-lähetysmuoto"
msgstr "HTTP-lähetysmuoto"
msgid "Multicast Streaming Server"
msgstr "Multicast-suoratoistopalvelin"
msgid "Start IGMP Server"
msgstr "Käynnistä IGMP-palvelin"
msgstr "Käynnistä IGMP-palvelin"
msgid "Multicast Client Port"
msgstr "Multicast-portti"
msgid "Multicast Streamtype"
msgstr "Multicast-lähetysmuoto"
msgid "VDR Streaming Server"
msgstr "VDR-suoratoistopalvelin"
msgstr "Multicast-lähetysmuoto"

View File

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

View File

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

View File

@@ -6,16 +6,18 @@
msgid ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: 2009-11-26 21:57+0200\n"
"Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
"Language-Team: Lithuanian <vdr@linuxtv.org>\n"
"Language: lt\n"
"Language-Team: Lietuvių\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "VDR Streaming Server"
msgstr "VDR transliavimo serveris"
msgid "Streaming active"
msgstr "Transliavimas vyksta"
@@ -28,20 +30,26 @@ msgstr ""
msgid "Suspend"
msgstr "Pristabdyti"
msgid "Offer suspend mode"
msgstr "Klausti dėl sustabdymo"
msgid "Always suspended"
msgstr "Visada stabdyti"
msgid "Never suspended"
msgstr "Niekada nestabdyti"
msgid "Common Settings"
msgstr "Bendri nustatymai"
msgid "Hide Mainmenu Entry"
msgstr ""
msgid "Start with Live TV suspended"
msgstr ""
msgid "Maximum Number of Clients"
msgstr "Maksimalus klientų skaičius"
msgid "Live TV buffer delay (ms)"
msgstr ""
msgid "Suspend behaviour"
msgstr "Pristabdyti veikimą"
msgid "Client may suspend"
msgstr "Klientas gali pristabdyti"
msgid "VDR-to-VDR Server"
msgstr "VDR-su-VDR Serveris"
@@ -55,15 +63,6 @@ msgstr "VDR-su-VDR Serverio portas"
msgid "Bind to 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"
msgstr "HTTP Serveris"
@@ -73,9 +72,6 @@ msgstr "Paleisti HTTP serverį"
msgid "HTTP Server Port"
msgstr "HTTP serverio portas"
msgid "Priority"
msgstr ""
msgid "HTTP Streamtype"
msgstr "HTTP transliavimo tipas"
@@ -90,6 +86,3 @@ msgstr "Multicast kliento portas"
msgid "Multicast Streamtype"
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 ""
msgstr ""
"Project-Id-Version: streamdev 0.5.0\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: 2008-06-26 15:36+0100\n"
"Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n"
"Language-Team: Russian <vdr@linuxtv.org>\n"
"Language: ru\n"
"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-5\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "VDR Streaming Server"
msgstr "VDR Streaming áÕàÒÕà"
msgid "Streaming active"
msgstr "ÁâàØÜØÝÓ ÐÚâØÒÕÝ"
@@ -28,20 +30,26 @@ msgstr ""
msgid "Suspend"
msgstr "¾áâÐÝÞÒÚÐ"
msgid "Offer suspend mode"
msgstr "¿àÕÔÛÐÓÐâì ÞáâÐÝÞÒÚã"
msgid "Always suspended"
msgstr "²áÕÓÔÐ ÞáâÐÝÞÒÛÕÝ"
msgid "Never suspended"
msgstr "½ØÚÞÓÔÐ ÝÕ ÞáâÐÝÞÒÛÕÝ"
msgid "Common Settings"
msgstr "½ÐáâàÞÙÚØ"
msgid "Hide Mainmenu Entry"
msgstr ""
msgid "Start with Live TV suspended"
msgstr ""
msgid "Maximum Number of Clients"
msgstr "¼ÐÚá. ÚÞÛØçÕáâÒÞ ÚÛØÕÝâÞÒ"
msgid "Live TV buffer delay (ms)"
msgstr ""
msgid "Suspend behaviour"
msgstr "¿ÞÒÕÔÕÝØÕ ÞáâÐÝÞÒÚØ"
msgid "Client may suspend"
msgstr "ºÛØÕÝâ ÜÞÖÕâ ÞáâÐÝÐÒÛØÒÐâì"
msgid "VDR-to-VDR Server"
msgstr "VDR-to-VDR áÕàÒÕà"
@@ -55,15 +63,6 @@ msgstr "VDR-to-VDR
msgid "Bind to IP"
msgstr "¿àØáÞÕÔØÝØâìáï Ú IP"
msgid "Legacy Client Priority"
msgstr ""
msgid "Client may suspend"
msgstr "ºÛØÕÝâ ÜÞÖÕâ ÞáâÐÝÐÒÛØÒÐâì"
msgid "Loop Prevention"
msgstr ""
msgid "HTTP Server"
msgstr "HTTP áÕàÒÕà"
@@ -73,9 +72,6 @@ msgstr "
msgid "HTTP Server Port"
msgstr "HTTP áÕàÒÕà ¿Þàâ"
msgid "Priority"
msgstr ""
msgid "HTTP Streamtype"
msgstr "ÂØß HTTP ßÞâÞÚÐ"
@@ -90,6 +86,3 @@ msgstr ""
msgid "Multicast Streamtype"
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 ""
msgstr ""
"Project-Id-Version: streamdev_SK\n"
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
"PO-Revision-Date: 2013-11-22 23:39+0100\n"
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
"PO-Revision-Date: \n"
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
"Language-Team: Slovak <hrala.milan@gmail.com>\n"
"Language: sk\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-2\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Slovak\n"
"X-Poedit-Country: SLOVAKIA\n"
msgid "VDR Streaming Server"
msgstr "VDR prúdový server"
msgid "Streaming active"
msgstr "Streamovanie aktívne"
msgstr "streamovanie aktivne"
msgid "Streamdev Connections"
msgstr "Streamdev spojenia"
msgstr ""
msgid "Disconnect"
msgstr "Odpoji»"
msgstr ""
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"
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"
msgstr "Maximály poèet klientov"
msgid "Live TV buffer delay (ms)"
msgstr ""
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 "Suspend behaviour"
msgstr "Správanie preru¹enia"
msgid "Client may suspend"
msgstr "Klient mô¾e server pozastavi»"
msgstr "Klient mô¾e pozastavi»"
msgid "Loop Prevention"
msgstr "Prevencia sluèky"
msgid "VDR-to-VDR Server"
msgstr "VDR-do-VDR server"
msgid "Start VDR-to-VDR Server"
msgstr "Spusti» VDR-do-VDR Server"
msgid "VDR-to-VDR Server Port"
msgstr "Port serveru pre VDR-do-VDR"
msgid "Bind to IP"
msgstr "viaza» na IP"
msgid "HTTP Server"
msgstr "HTTP server "
msgstr "server HTTP"
msgid "Start HTTP Server"
msgstr "Spusti» HTTP Server"
msgid "HTTP Server Port"
msgstr "Port HTTP servera"
msgid "Priority"
msgstr "Priorita"
msgstr "Port serveru HTTP"
msgid "HTTP Streamtype"
msgstr "Typ HTTP streamu"
msgstr "typ prúdu HTTP"
msgid "Multicast Streaming Server"
msgstr "Streamovanie Multicastového servera"
msgstr "Multicast prúdový server"
msgid "Start IGMP Server"
msgstr "Spusti» IGMP Server"
msgid "Multicast Client Port"
msgstr "Port Multicast klienta"
msgstr "Port klienta Multicast"
msgid "Multicast Streamtype"
msgstr "Typ Multicast streamu"
msgid "VDR Streaming Server"
msgstr "VDR server streamovania"
msgstr "Multicast typ streamu"

View File

@@ -24,30 +24,27 @@
// for TSPLAY patch detection
#include "vdr/device.h"
#undef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600
#include <fcntl.h>
RecPlayer::RecPlayer(const char* FileName)
RecPlayer::RecPlayer(cRecording* rec)
{
file = NULL;
fileOpen = 0;
lastPosition = 0;
recording = new cRecording(FileName);
recording = rec;
for(int i = 1; i < 1000; i++) segments[i] = NULL;
// 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!");
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()
@@ -64,18 +61,24 @@ void RecPlayer::scan()
for(i = 1; i < 1000; i++)
{
#if APIVERSNUM < 10703 || !defined(TSPLAY_PATCH_VERSION)
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
//log->log("RecPlayer", Log::DEBUG, "FILENAME: %s", fileName);
file = fopen(fileName, "r");
#else
snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), i);
file = fopen(fileName, "r");
if (!file) {
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
file = fopen(fileName, "r");
}
#endif
if (!file) break;
segments[i] = new Segment();
segments[i]->start = totalLength;
fseek(file, 0, SEEK_END);
totalLength += ftello(file);
totalLength += ftell(file);
totalFrames = indexFile->Last();
//log->log("RecPlayer", Log::DEBUG, "File %i found, totalLength now %llu, numFrames = %lu", i, totalLength, totalFrames);
segments[i]->end = totalLength;
@@ -91,9 +94,6 @@ RecPlayer::~RecPlayer()
int i = 1;
while(segments[i++]) delete segments[i];
if (file) fclose(file);
delete indexFile;
delete recording;
delete parser;
}
int RecPlayer::openFile(int index)
@@ -102,6 +102,7 @@ int RecPlayer::openFile(int index)
char fileName[2048];
#if APIVERSNUM >= 10703 || defined(TSPLAY_PATCH_VERSION)
snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), index);
isyslog("openFile called for index %i string:%s", index, fileName);
@@ -111,6 +112,7 @@ int RecPlayer::openFile(int index)
fileOpen = index;
return 1;
}
#endif
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), index);
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 got = 0;
uint32_t getFromThisSegment = 0;
uint64_t filePosition;
uint32_t filePosition;
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.
// 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;
currentPosition += getFromThisSegment;
@@ -219,47 +221,17 @@ cRecording* RecPlayer::getCurrentRecording()
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)
{
if (!indexFile) return 0;
#if VDRVERSNUM >= 10703 || defined(TSPLAY_PATCH_VERSION)
uint16_t retFileNumber;
off_t retFileOffset;
#else
uchar retFileNumber;
int retFileOffset;
#endif
if (!indexFile->Get((int)frameNumber, &retFileNumber, &retFileOffset))
{
@@ -290,7 +262,7 @@ uint32_t RecPlayer::frameNumberFromPosition(uint64_t position)
if ((position >= segments[segmentNumber]->start) && (position < segments[segmentNumber]->end)) break;
// 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);
}

View File

@@ -23,7 +23,6 @@
#include <stdio.h>
#include <vdr/recording.h>
#include <vdr/remux.h>
#include "server/streamer.h"
@@ -37,7 +36,7 @@ class Segment
class RecPlayer
{
public:
RecPlayer(const char* FileName);
RecPlayer(cRecording* rec);
~RecPlayer();
uint64_t getLengthBytes();
uint32_t getLengthFrames();
@@ -45,12 +44,7 @@ class RecPlayer
int openFile(int index);
uint64_t getLastPosition();
cRecording* getCurrentRecording();
const cPatPmtParser* getPatPmtData() { return parser; }
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);
uint32_t frameNumberFromPosition(uint64_t position);
bool getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength);
@@ -58,7 +52,6 @@ class RecPlayer
private:
cRecording* recording;
cIndexFile* indexFile;
cPatPmtParser* parser;
FILE* file;
int fileOpen;
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)
#endif
{
Lock.Lock(m_Instance);
return m_Clients;

View File

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

View File

@@ -10,22 +10,16 @@
cStreamdevServerSetup StreamdevServerSetup;
cStreamdevServerSetup::cStreamdevServerSetup(void) {
HideMenuEntry = false;
MaxClients = 5;
StartSuspended = ssAuto;
LiveBufferMs = 0;
StartVTPServer = true;
VTPServerPort = 2004;
VTPPriority = 0;
LoopPrevention = false;
StartHTTPServer = true;
HTTPServerPort = 3000;
HTTPPriority = 0;
HTTPStreamType = stTS;
StartIGMPServer = false;
IGMPClientPort = 1234;
IGMPPriority = 0;
IGMPStreamType = stTS;
SuspendMode = smAlways;
AllowSuspend = false;
strcpy(VTPBindIP, "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) {
if (strcmp(Name, "HideMenuEntry") == 0) HideMenuEntry = 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);
if (strcmp(Name, "MaxClients") == 0) MaxClients = atoi(Value);
else if (strcmp(Name, "StartServer") == 0) StartVTPServer = 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, "LoopPrevention") == 0) LoopPrevention = atoi(Value);
else if (strcmp(Name, "StartHTTPServer") == 0) StartHTTPServer = 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, "HTTPBindIP") == 0) strcpy(HTTPBindIP, Value);
else if (strcmp(Name, "StartIGMPServer") == 0) StartIGMPServer = atoi(Value);
else if (strcmp(Name, "IGMPClientPort") == 0) IGMPClientPort = atoi(Value);
else if (strcmp(Name, "IGMPPriority") == 0) IGMPPriority = atoi(Value);
else if (strcmp(Name, "IGMPStreamType") == 0) IGMPStreamType = atoi(Value);
else if (strcmp(Name, "IGMPBindIP") == 0) strcpy(IGMPBindIP, Value);
else if (strcmp(Name, "SuspendMode") == 0) SuspendMode = atoi(Value);
else if (strcmp(Name, "AllowSuspend") == 0) AllowSuspend = atoi(Value);
else return false;
return true;
@@ -65,6 +53,12 @@ const char* cStreamdevServerMenuSetupPage::StreamTypes[st_Count - 1] = {
"EXT"
};
const char* cStreamdevServerMenuSetupPage::SuspendModes[sm_Count] = {
trNOOP("Offer suspend mode"),
trNOOP("Always suspended"),
trNOOP("Never suspended")
};
cStreamdevServerMenuSetupPage::cStreamdevServerMenuSetupPage(void) {
m_NewSetup = StreamdevServerSetup;
@@ -75,44 +69,34 @@ cStreamdevServerMenuSetupPage::~cStreamdevServerMenuSetupPage() {
}
void cStreamdevServerMenuSetupPage::Set(void) {
static const char *StartSuspendedItems[ss_Count] =
{
trVDR("no"),
trVDR("yes"),
trVDR("auto")
};
static const char* modes[sm_Count];
for (int i = 0; i < sm_Count; i++)
modes[i] = tr(SuspendModes[i]);
int current = Current();
Clear();
AddCategory (tr("Common Settings"));
Add(new 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("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"));
Add(new cMenuEditBoolItem(tr("Start VDR-to-VDR Server"), &m_NewSetup.StartVTPServer));
Add(new cMenuEditIntItem (tr("VDR-to-VDR Server Port"), &m_NewSetup.VTPServerPort, 0, 65535));
Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.VTPBindIP));
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"));
Add(new cMenuEditBoolItem(tr("Start HTTP Server"), &m_NewSetup.StartHTTPServer));
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 cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.HTTPBindIP));
AddCategory (tr("Multicast Streaming Server"));
Add(new cMenuEditBoolItem(tr("Start IGMP Server"), &m_NewSetup.StartIGMPServer));
Add(new cMenuEditIntItem (tr("Multicast Client Port"), &m_NewSetup.IGMPClientPort, 0, 65535));
Add(new 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 cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.IGMPBindIP));
SetCurrent(Get(current));
Display();
}
@@ -142,25 +126,19 @@ void cStreamdevServerMenuSetupPage::Store(void) {
cStreamdevServer::Destruct();
}
SetupStore("HideMenuEntry", m_NewSetup.HideMenuEntry);
SetupStore("MaxClients", m_NewSetup.MaxClients);
SetupStore("StartSuspended", m_NewSetup.StartSuspended);
SetupStore("LiveBufferMs", m_NewSetup.LiveBufferMs);
SetupStore("StartServer", m_NewSetup.StartVTPServer);
SetupStore("ServerPort", m_NewSetup.VTPServerPort);
SetupStore("VTPBindIP", m_NewSetup.VTPBindIP);
SetupStore("VTPPriority", m_NewSetup.VTPPriority);
SetupStore("LoopPrevention", m_NewSetup.LoopPrevention);
SetupStore("StartHTTPServer", m_NewSetup.StartHTTPServer);
SetupStore("HTTPServerPort", m_NewSetup.HTTPServerPort);
SetupStore("HTTPBindIP", m_NewSetup.HTTPBindIP);
SetupStore("HTTPPriority", m_NewSetup.HTTPPriority);
SetupStore("HTTPStreamType", m_NewSetup.HTTPStreamType);
SetupStore("HTTPBindIP", m_NewSetup.HTTPBindIP);
SetupStore("StartIGMPServer", m_NewSetup.StartIGMPServer);
SetupStore("IGMPClientPort", m_NewSetup.IGMPClientPort);
SetupStore("IGMPBindIP", m_NewSetup.IGMPBindIP);
SetupStore("IGMPPriority", m_NewSetup.IGMPPriority);
SetupStore("IGMPStreamType", m_NewSetup.IGMPStreamType);
SetupStore("IGMPBindIP", m_NewSetup.IGMPBindIP);
SetupStore("SuspendMode", m_NewSetup.SuspendMode);
SetupStore("AllowSuspend", m_NewSetup.AllowSuspend);
StreamdevServerSetup = m_NewSetup;
@@ -168,3 +146,11 @@ void cStreamdevServerMenuSetupPage::Store(void) {
if (restart)
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"
enum eStartSuspended {
ssNo,
ssYes,
ssAuto,
ss_Count
};
struct cStreamdevServerSetup {
cStreamdevServerSetup(void);
bool SetupParse(const char *Name, const char *Value);
int HideMenuEntry;
int MaxClients;
int StartSuspended;
int LiveBufferMs;
int StartVTPServer;
int VTPServerPort;
char VTPBindIP[20];
int VTPPriority;
int AllowSuspend;
int LoopPrevention;
int StartHTTPServer;
int HTTPServerPort;
int HTTPPriority;
int HTTPStreamType;
char HTTPBindIP[20];
int StartIGMPServer;
int IGMPClientPort;
int IGMPPriority;
int IGMPStreamType;
char IGMPBindIP[20];
int SuspendMode;
int AllowSuspend;
};
extern cStreamdevServerSetup StreamdevServerSetup;
@@ -46,12 +33,14 @@ extern cStreamdevServerSetup StreamdevServerSetup;
class cStreamdevServerMenuSetupPage: public cMenuSetupPage {
private:
static const char* StreamTypes[];
static const char* SuspendModes[];
cStreamdevServerSetup m_NewSetup;
void AddCategory(const char *Title);
void Set();
protected:
virtual void Store(void);
virtual eOSState ProcessKey(eKeys Key);
public:
cStreamdevServerMenuSetupPage(void);

View File

@@ -12,44 +12,15 @@
#include "server/menu.h"
#include "server/setup.h"
#include "server/server.h"
#include "server/suspend.h"
#if !defined(APIVERSNUM) || APIVERSNUM < 10725
#error "VDR-1.7.25 or greater required to compile server! Use 'make client' to compile client only."
#if !defined(APIVERSNUM) || APIVERSNUM < 10516
#error "VDR-1.5.16 API version or greater is required!"
#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");
cPluginStreamdevServer::cPluginStreamdevServer(void)
{
m_Suspend = false;
}
cPluginStreamdevServer::~cPluginStreamdevServer()
@@ -127,9 +98,6 @@ bool cPluginStreamdevServer::Start(void)
cStreamdevServer::Initialize();
m_Suspend = StreamdevServerSetup.StartSuspended == ssAuto ?
!cDevice::PrimaryDevice()->HasDecoder() :
StreamdevServerSetup.StartSuspended;
return true;
}
@@ -151,7 +119,7 @@ cString cPluginStreamdevServer::Active(void)
const char *cPluginStreamdevServer::MainMenuEntry(void)
{
return !StreamdevServerSetup.HideMenuEntry ? tr("Streamdev Connections") : NULL;
return tr("Streamdev Connections");
}
cOsdObject *cPluginStreamdevServer::MainMenuAction(void)
@@ -161,18 +129,9 @@ cOsdObject *cPluginStreamdevServer::MainMenuAction(void)
void cPluginStreamdevServer::MainThreadHook(void)
{
if (m_Suspend) {
cControl::Launch(new cSuspendCtl);
m_Suspend = false;
}
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))
cThreadLock lock;
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
for (cServerConnection *s = clients.First(); s; s = clients.Next(s))
s->MainThreadHook();
}
@@ -186,90 +145,4 @@ bool cPluginStreamdevServer::SetupParse(const char *Name, const char *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!

View File

@@ -7,30 +7,11 @@
#include "common.h"
#include <vdr/tools.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 {
private:
static const char *DESCRIPTION;
bool m_Suspend;
public:
cPluginStreamdevServer(void);
@@ -48,9 +29,6 @@ public:
virtual void MainThreadHook(void);
virtual cMenuSetupPage *SetupMenu(void);
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

View File

@@ -8,6 +8,8 @@
#include <unistd.h>
#include "server/streamer.h"
#include "server/suspend.h"
#include "server/setup.h"
#include "tools/socket.h"
#include "tools/select.h"
#include "common.h"
@@ -45,18 +47,15 @@ void cStreamdevWriter::Action(void)
int count, offset = 0;
int timeout = 0;
#if APIVERSNUM >= 10705
SetPriority(-3);
#endif
sel.Clear();
sel.Add(*m_Socket, true);
while (Running()) {
if (block == NULL) {
block = m_Streamer->Get(count);
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) {
@@ -103,82 +102,72 @@ void cStreamdevWriter::Action(void)
}
}
}
m_Socket->Close();
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(const char *Name, const cServerConnection *Connection):
cThread(Name),
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()
{
Dprintf("Desctructing streamer\n");
delete m_Remux;
delete m_RingBuffer;
delete m_SendBuffer;
}
void cStreamdevStreamer::Start(cTBSocket *Socket)
{
Dprintf("start writer\n");
Dprintf("start streamer\n");
m_Writer = new cStreamdevWriter(Socket, this);
m_Writer->Start();
if (!Active()) {
Dprintf("start streamer\n");
Attach();
}
void cStreamdevStreamer::Activate(bool On)
{
if (On && !Active()) {
Dprintf("activate streamer\n");
m_Writer->Start();
cThread::Start();
}
Attach();
}
void cStreamdevStreamer::Stop(void)
{
Detach();
if (Running()) {
Dprintf("stop streamer\n");
Dprintf("stopping streamer\n");
Cancel(3);
}
Dprintf("stop writer\n");
DELETENULL(m_Writer);
if (m_Writer) {
Detach();
DELETENULL(m_Writer);
}
}
void cStreamdevStreamer::Action(void)
{
#if APIVERSNUM >= 10705
SetPriority(-3);
#endif
while (Running()) {
int got;
uchar *block = GetFromReceiver(got);
uchar *block = m_RingBuffer->Get(got);
if (block) {
int count = Put(block, got);
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/tools.h>
#include "remux/tsremux.h"
class cTBSocket;
class cStreamdevStreamer;
class cServerConnection;
@@ -19,6 +17,7 @@ class cServerConnection;
#define TS_SIZE 188
#endif
#define STREAMERBUFSIZE (20000 * TS_SIZE)
#define WRITERBUFSIZE (20000 * TS_SIZE)
// --- cStreamdevBuffer -------------------------------------------------------
@@ -67,18 +66,14 @@ public:
class cStreamdevStreamer: public cThread {
private:
const cServerConnection *m_Connection;
Streamdev::cTSRemux *m_Remux;
cStreamdevWriter *m_Writer;
cStreamdevBuffer *m_SendBuffer;
cStreamdevWriter *m_Writer;
cStreamdevBuffer *m_RingBuffer;
cStreamdevBuffer *m_SendBuffer;
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);
bool IsRunning(void) const { return m_Writer; }
void SetRemux(Streamdev::cTSRemux *Remux) { delete m_Remux; m_Remux = Remux; }
public:
cStreamdevStreamer(const char *Name, const cServerConnection *Connection = NULL);
@@ -88,16 +83,18 @@ public:
virtual void Start(cTBSocket *Socket);
virtual void Stop(void);
virtual bool IsReceiving(void) const = 0;
bool Abort(void);
uchar *Get(int &Count) { return m_Remux->Get(Count); }
void Del(int Count) { m_Remux->Del(Count); }
void Activate(bool On);
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 Attach(void) {}
virtual cString ToText() const { return ""; };
};
inline bool cStreamdevStreamer::Abort(void)

View File

@@ -39,7 +39,7 @@ void cSuspendLive::Action(void) {
bool cSuspendCtl::m_Active = false;
cSuspendCtl::cSuspendCtl(void):
cControl(m_Suspend = new cSuspendLive, true) {
cControl(m_Suspend = new cSuspendLive) {
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/select.h"
#include <vdr/tools.h>
#include <string.h>
@@ -28,7 +27,7 @@ cTBSocket::~cTBSocket() {
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;
int socket;
@@ -46,36 +45,13 @@ bool cTBSocket::Connect(const std::string &Host, unsigned int Port, unsigned int
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_port = htons(Port);
m_RemoteAddr.sin_addr.s_addr = inet_addr(Host.c_str());
if (::connect(socket, (struct sockaddr*)&m_RemoteAddr, sizeof(m_RemoteAddr)) == -1) {
if (TimeoutMs > 0 && errno == EINPROGRESS) {
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);
return false;
}
if (so_error) {
errno = so_error;
::close(socket);
return false;
}
}
else {
::close(socket);
return false;
}
if (::connect(socket, (struct sockaddr*)&m_RemoteAddr,
sizeof(m_RemoteAddr)) == -1) {
::close(socket);
return false;
}
if (m_Type == SOCK_STREAM) {

View File

@@ -32,13 +32,11 @@ public:
Reimplemented for TCP/IPv4 sockets. */
virtual ssize_t SysWrite(const void *Buffer, size_t Length) const;
/* Connect() tries to connect an available local socket within TimeoutMs
milliseconds to the port given by Port of the target host given by
Host in numbers-and-dots notation (i.e. "212.43.45.21"). A TimeoutMs
of 0 will disable non-blocking IO for the connect call. Returns true
if the connection attempt was successful and false otherwise, setting
errno appropriately. */
virtual bool Connect(const std::string &Host, uint Port, uint TimeoutMs = 0);
/* Connect() tries to connect an available local socket to the port given
by Port of the target host given by Host in numbers-and-dots notation
(i.e. "212.43.45.21"). Returns true if the connection attempt was
successful and false otherwise, setting errno appropriately. */
virtual bool Connect(const std::string &Host, uint Port);
/* 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
@@ -62,7 +60,7 @@ public:
/* Accept() returns a newly created cTBSocket, which is connected to the
first connection request on the queue of pending connections of a
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;
/* 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) {
cTBSelect sel;
int ms, offs;
cTimeMs starttime;
cTimeMs starttime;
ms = TimeoutMs;
offs = 0;
sel.Clear();
sel.Add(m_Filed, true);
while (Length > 0) {
int b;
ms = TimeoutMs - starttime.Elapsed();
if (ms <= 0) {
errno = ETIMEDOUT;
return false;
}
if (sel.Select(ms) == -1)
return false;
@@ -81,6 +76,11 @@ bool cTBSource::TimedWrite(const void *Buffer, size_t Length, uint TimeoutMs) {
Length -= b;
}
ms = TimeoutMs - starttime.Elapsed();
if (ms <= 0) {
errno = ETIMEDOUT;
return false;
}
}
return true;
}

View File

@@ -54,7 +54,7 @@ public:
/* Close() resets the source to the uninitialized state (IsOpen() == false)
and must be called by any derivations after really closing the source.
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);
/* Read() reads at most Length bytes into the storage pointed to by Buffer,