Compare commits
113 Commits
servermenu
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2a9b979d3 | ||
|
|
644078220b | ||
|
|
95256a52cf | ||
|
|
b84b7d858c | ||
|
|
674bb5b331 | ||
|
|
d66c635a80 | ||
|
|
fc52e920ad | ||
|
|
84c6f6b6f3 | ||
|
|
3e06c59196 | ||
|
|
b33d2631df | ||
|
|
657c8bc49c | ||
|
|
5f5fb7953e | ||
|
|
7b17f7725c | ||
|
|
1ee2049c4d | ||
|
|
99b223c55f | ||
|
|
7df7185e1a | ||
|
|
dd556ee7fd | ||
|
|
58f0348578 | ||
|
|
e83c9d92aa | ||
|
|
520adaf3da | ||
|
|
71c26e7455 | ||
|
|
e8629b5ec6 | ||
|
|
2d919997a8 | ||
|
|
703dffa0cb | ||
|
|
bdee8c1923 | ||
|
|
83262870d5 | ||
|
|
888cf0a2f8 | ||
|
|
1dc1423429 | ||
|
|
5a173b0b21 | ||
|
|
3e9e7f7de6 | ||
|
|
62fa951c61 | ||
|
|
bfbf19decc | ||
|
|
e555017565 | ||
|
|
7be0c81a81 | ||
|
|
2cdf160648 | ||
|
|
54440cb080 | ||
|
|
40704cdcbc | ||
|
|
5fcd6eca69 | ||
|
|
a4a774c6ce | ||
|
|
c18f7d47e7 | ||
|
|
1439b016b3 | ||
|
|
f194ca2074 | ||
|
|
1d4a7e06b4 | ||
|
|
458a21a62a | ||
|
|
69b654d539 | ||
|
|
5e5070edc0 | ||
|
|
dfc8339c9e | ||
|
|
a9c2adb565 | ||
|
|
8c5859ed4a | ||
|
|
e1ba17ca21 | ||
|
|
d3df5d07a1 | ||
|
|
c92de13d06 | ||
|
|
d7652d89ca | ||
|
|
b25e53c867 | ||
|
|
329129d9c1 | ||
|
|
75ee90a974 | ||
|
|
08198729ac | ||
|
|
10db11acd9 | ||
|
|
f58086a83a | ||
|
|
d3dd72072c | ||
|
|
9bbb74b7fd | ||
|
|
176df8341d | ||
|
|
138580f284 | ||
|
|
525edc1ccf | ||
|
|
9135cde712 | ||
|
|
0cf406ed3a | ||
|
|
1866716471 | ||
|
|
50d249c62e | ||
|
|
0fb7076192 | ||
|
|
f5da0ea1fc | ||
|
|
0677f48329 | ||
|
|
eaf9321c4c | ||
|
|
83e9f3250f | ||
|
|
c267b585fd | ||
|
|
be9da74958 | ||
|
|
e7bcc9349c | ||
|
|
b614fa0ec3 | ||
|
|
84db6323a6 | ||
|
|
281105f0c7 | ||
|
|
80e40d4260 | ||
|
|
5cfa16c402 | ||
|
|
af48d11b18 | ||
|
|
744dc6792c | ||
|
|
16f8c75918 | ||
|
|
fffd5aef4f | ||
|
|
6389c5fd90 | ||
|
|
6a47e20435 | ||
|
|
12b48591be | ||
|
|
ab26b4770a | ||
|
|
00b7318a7b | ||
|
|
c3ac597623 | ||
|
|
ae634538f8 | ||
|
|
783b261bcb | ||
|
|
316ac3344d | ||
|
|
8719007f5a | ||
|
|
2e8aefd2fe | ||
|
|
a1797719de | ||
|
|
5a3c535778 | ||
|
|
173d2cbb7a | ||
|
|
83b05a6292 | ||
|
|
a63f7247cb | ||
|
|
26af4459d8 | ||
|
|
3da6ae734e | ||
|
|
c1dc1453c5 | ||
|
|
a047fc7d32 | ||
|
|
229e8fbfff | ||
|
|
ba7c61fb39 | ||
|
|
6a971b9145 | ||
|
|
3440072e7e | ||
|
|
59c6558ce3 | ||
|
|
d93ca82bd1 | ||
|
|
ffb8707118 | ||
|
|
afe255aa0b |
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# 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
|
||||||
63
CONTRIBUTORS
63
CONTRIBUTORS
@@ -39,6 +39,9 @@ Rolf Ahrenberg
|
|||||||
for requesting replacement of asprintf calls
|
for requesting replacement of asprintf calls
|
||||||
for suggesting to change the URL path from EXTERN to EXT
|
for suggesting to change the URL path from EXTERN to EXT
|
||||||
for suggesting increased thread priorities for cStreamdevWriter/Streamer
|
for suggesting increased thread priorities for cStreamdevWriter/Streamer
|
||||||
|
for adding "Hide mainmenu entry" option
|
||||||
|
for polishing po file headers
|
||||||
|
for adding the special meaning "show current channel" to channel 0
|
||||||
|
|
||||||
Rantanen Teemu
|
Rantanen Teemu
|
||||||
for providing vdr-incompletesections.diff
|
for providing vdr-incompletesections.diff
|
||||||
@@ -116,9 +119,10 @@ Jori Hamalainen
|
|||||||
for extensive testing while making stream compatible to Network Media Tank
|
for extensive testing while making stream compatible to Network Media Tank
|
||||||
for adding Network Media Tank browser support to HTML pages
|
for adding Network Media Tank browser support to HTML pages
|
||||||
|
|
||||||
owagner
|
Oliver Wagner
|
||||||
for pointing out a problem with the encrypted channel switching fix
|
for pointing out a problem with the encrypted channel switching fix
|
||||||
for suggesting use of SO_KEEPALIVE socket option to detect dead sockets
|
for suggesting use of SO_KEEPALIVE socket option to detect dead sockets
|
||||||
|
for making cStatus::ChannelChange re-tune only if CA IDs changed
|
||||||
|
|
||||||
Joachim König-Baltes
|
Joachim König-Baltes
|
||||||
for fixing Min/MaxPriority parsing
|
for fixing Min/MaxPriority parsing
|
||||||
@@ -198,3 +202,60 @@ Ville Skytt
|
|||||||
for restricting VTP command RENR to liemikuutio patch < 1.32
|
for restricting VTP command RENR to liemikuutio patch < 1.32
|
||||||
for fixing memory and filedescriptor leaks in libdvbmpeg
|
for fixing memory and filedescriptor leaks in libdvbmpeg
|
||||||
for code cleanup and optimization
|
for code cleanup and optimization
|
||||||
|
for correcting typos
|
||||||
|
|
||||||
|
Methodus
|
||||||
|
for suggesting to use HTTP code 503 for unavailable channels
|
||||||
|
|
||||||
|
Uwe
|
||||||
|
for reporting a compiler error in client/device.c with VDR < 1.7.22
|
||||||
|
|
||||||
|
Chris Tallon
|
||||||
|
for his kind permission to use VOMP's recplayer for replaying recordings
|
||||||
|
|
||||||
|
macmenot
|
||||||
|
for adapting Makefiles to VDR 1.7.36+
|
||||||
|
|
||||||
|
thomasjfox
|
||||||
|
for fixing cSuspendCtl preventing idle shutdown
|
||||||
|
|
||||||
|
hivdr
|
||||||
|
for adding the pos= parameter for replaying recordings from a certain position
|
||||||
|
for suggesting to add the HTTP "Server" header
|
||||||
|
|
||||||
|
hummel99
|
||||||
|
for reporting and helping to debug channel switch issues with priority > 0
|
||||||
|
for reporting a race condition when switching the server's LiveTV device
|
||||||
|
|
||||||
|
Henrik Niehaus
|
||||||
|
for fixing replay of large TS files on 32-bit systems
|
||||||
|
|
||||||
|
Guy Martin
|
||||||
|
for adding SVDRP commands to list and disconnect clients
|
||||||
|
|
||||||
|
Martin1234
|
||||||
|
for suggesting a service call, returning the number of clients
|
||||||
|
for implementing GetCurrentlyTunedTransponder() on client
|
||||||
|
|
||||||
|
Toerless Eckert
|
||||||
|
for converting suspend.dat into proper PES format
|
||||||
|
for investigating and fixing problems caused by filter streaming
|
||||||
|
for fixing TimedWrite() so it doesn't fail after a slow but successful write
|
||||||
|
for suggesting to double the size of client's filter buffer
|
||||||
|
|
||||||
|
Tomasz Maciej Nowak
|
||||||
|
for providing Polish language texts
|
||||||
|
|
||||||
|
Christopher Reimer
|
||||||
|
for providing an initial compatibility patch for VDR 2.3.1
|
||||||
|
|
||||||
|
Matthias Senzel
|
||||||
|
for refining the compatibility patch for VDR 2.3.1
|
||||||
|
|
||||||
|
David Binderman
|
||||||
|
for fixing an lseek error check in libdvbmpeg
|
||||||
|
|
||||||
|
Jasmin J
|
||||||
|
for fixing some warnings in libdvbmpeg
|
||||||
|
for adding .gitignore
|
||||||
|
for fixing compilation for VDR 2.3.7
|
||||||
|
|||||||
128
HISTORY
128
HISTORY
@@ -1,6 +1,134 @@
|
|||||||
VDR Plugin 'streamdev' Revision History
|
VDR Plugin 'streamdev' Revision History
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
|
- fixed compilation for VDR 2.3.7 (thanks to Jasmin J)
|
||||||
|
- added .gitignore (thanks to Jasmin J)
|
||||||
|
- fixed some warnings in libdvbmpeg (thanks to Jasmin J)
|
||||||
|
- fixed lseek error check in libdvbmpeg (thanks to David Binderman)
|
||||||
|
- server compatibility with VDR 2.3.1 (thanks to Christopher Reimer and
|
||||||
|
Matthias Senzel)
|
||||||
|
- client compatibility with VDR 2.3.1
|
||||||
|
- use cReceiver::SetPriority(...) in VDR 2.1.4+
|
||||||
|
- doubled size of client's filter buffer (suggested by Toerless Eckert)
|
||||||
|
- make sure TimedWrite(...) doesn't return failure after a slow but successful
|
||||||
|
write operation (thanks to Toerless Eckert)
|
||||||
|
- fixed problems related to VTP filter streaming like ringbuffer overflows,
|
||||||
|
stuttering or aborting video stream (thanks to Toerless Eckert)
|
||||||
|
- added Polish translation (thanks to Tomasz Maciej Nowak)
|
||||||
|
- converted suspend.dat into proper PES format (thanks to Toerless Eckert)
|
||||||
|
- implemented GetCurrentlyTunedTransponder() on client (thanks to Martin1234)
|
||||||
|
- added service call returning the number of clients (suggested by Martin1234)
|
||||||
|
- added SVDRP commands to list and disconnect clients (thanks to Guy Martin)
|
||||||
|
- fixed recplayer issues with large TS files (>4GB)
|
||||||
|
- Don't abort externremux when internal read buffer is empty
|
||||||
|
- Implemented remuxing of recordings
|
||||||
|
- Make ChannelChange retune only if CA IDs changed (thanks to Oliver Wagner)
|
||||||
|
- Implemented VDR 2.1.4 cStatus::ChannelChange(...)
|
||||||
|
- Call detach only if receiver is attached
|
||||||
|
- Try changing to other device when receiver got detached
|
||||||
|
- In TSPIDS mode, create and attach receiver with empty pid list to occupy dev
|
||||||
|
- Restructured server classes
|
||||||
|
- New option for server side live TV buffer to prevent buffer underruns
|
||||||
|
|
||||||
|
2013-11-28: Version 0.6.1
|
||||||
|
|
||||||
|
- Updated Slovak translation (thanks to Milan Hrala)
|
||||||
|
- Updated Finnish translation (thanks to Rolf Ahrenberg)
|
||||||
|
- Disabled PS remuxer which is said to produce anything but PS
|
||||||
|
- The patches intcamdevices and ignore_missing_cam are no longer required
|
||||||
|
on VDR >= 1.7.30. The localchannelprovide patch became obsolete with VDR
|
||||||
|
1.7.21.
|
||||||
|
- Added option to suspend live TV when the server starts
|
||||||
|
- Set device occupied when streamdev switches away LiveTV on the server, to
|
||||||
|
reduce the risk that the VDR main loop immediately switches back, resulting
|
||||||
|
in a black screen on the client (reported by hummel99)
|
||||||
|
- Fixed channel switch issues with priority > 0 (reported by hummel99)
|
||||||
|
- Removed noisy debug messages
|
||||||
|
- Fixed HTTP menu destruction
|
||||||
|
- API change of VDR 2.1.2
|
||||||
|
- Fixed priority handling, messed up when adding multi-device support
|
||||||
|
- Added HTTP "Server" header (suggested by hivdr)
|
||||||
|
- Ignore dummy file extensions (.ts, .vob, .vdr) when parsing HTTP URIs
|
||||||
|
- Select start position for replaying a recording by parameter pos=. Supported
|
||||||
|
values are resume, mark.#, time.#, frame.# or a plain # representing a
|
||||||
|
percentage if < 100 or a byte position otherwise (thanks to hivdr)
|
||||||
|
- Start cSuspendCtl hidden or it will prevent idle shutdown (thanks to
|
||||||
|
thomasjfox)
|
||||||
|
- Fixed recordings menu inode numbers: ino_t is a long long on some systems
|
||||||
|
- Updated Slovak translation (thanks to Milan Hrala)
|
||||||
|
- Adapted Makefiles to VDR 1.7.36+ (thanks to macmenot). Old makefiles have
|
||||||
|
been renamed to Makefile-1.7.33.
|
||||||
|
- API changes of VDR 1.7.38 (thanks to mal@vdr-developer)
|
||||||
|
- Added simple recordings menu in HTTP server
|
||||||
|
- Restructured menuHTTP classes
|
||||||
|
- Added RSS format for HTTP menus
|
||||||
|
- Recordings can now also be selected by struct stat "st_dev:st_ino.rec"
|
||||||
|
- Implemented multi-device support for streamdev client (suggested by johns)
|
||||||
|
- Basic support for HTTP streaming of recordings
|
||||||
|
- Close writer when streamer is finished
|
||||||
|
- Don't abort VTP connection if filter stream is broken
|
||||||
|
- Restructured cStreamdevStreamer: Moved inbound buffer into actual subclass.
|
||||||
|
- In cStreamdevStreamer dropped Activate(bool) and moved its code into Start().
|
||||||
|
- Moved cStreamdevFilterStreamer to livefilter.[hc]
|
||||||
|
- Return HTTP/1.1 compliant response headers plus some always useful headers
|
||||||
|
- Return HTTP URL parameters ending with ".dlna.org" as response headers
|
||||||
|
- Store HTTP URL parameters in a map
|
||||||
|
- Support HTTP HEAD requests with external remuxer
|
||||||
|
- Fixed always using priority 0 for HTTP HEAD requests
|
||||||
|
- Start writer right after creating it
|
||||||
|
- Corrected typos (thanks to Ville Skyttä)
|
||||||
|
- Fixed compiler error in client/device.c with VDR < 1.7.22 (reported by
|
||||||
|
Uwe@vdrportal)
|
||||||
|
- Updated Italian translation (thanks to Diego Pierotto)
|
||||||
|
- Added DeviceName() and DeviceType() to client device. The server IP and the
|
||||||
|
number of the device used on the server are returned respectively.
|
||||||
|
|
||||||
|
2012-05-29: Version 0.6.0
|
||||||
|
|
||||||
|
- Reimplemented some client device methods
|
||||||
|
- Proper fix for "client sends ABRT after TUNE". Obsoletes many hacks in client
|
||||||
|
- Added CLOCK_MONOTONIC timestamp and thread id to Dprintf
|
||||||
|
- Silenced warning (thanks to Rolf Ahrenberg)
|
||||||
|
- Updated Finnish translation (thanks to Rolf Ahrenberg)
|
||||||
|
- Replaced server-side suspend modes with priority based precedence handling
|
||||||
|
- Client-side priority handling for VDR >= 1.7.25 and servers running VTP > 1.0
|
||||||
|
- Introduced VTP protocol version numbering for easier compatibility handling
|
||||||
|
between different client and server versions. The server includes the protocol
|
||||||
|
version in its greeting string, the client reports its version with the new
|
||||||
|
command "VERS".
|
||||||
|
- Dropped compatibility of streamdev-server with VDR < 1.7.25
|
||||||
|
|
||||||
|
2012-05-12: Version 0.5.2
|
||||||
|
|
||||||
|
- Use fileno() to retrieve the fd from a FILE structure (submitted by an
|
||||||
|
anonymous user)
|
||||||
|
- New special meaning "show current channel" when channel 0 is requested.
|
||||||
|
Applies to HTTP streaming only (thanks to Rolf Ahrenberg)
|
||||||
|
- Fixed ProvidesChannel() on client always returning true since the new timeout
|
||||||
|
option has been added.
|
||||||
|
- Updated Finnish translation (thanks to Rolf Ahrenberg)
|
||||||
|
- With VDR 1.7.25 priorities down to -99 will be used. Please update
|
||||||
|
"Minimum Priority" in streamdev-client setup.
|
||||||
|
- Use the new streamdev-client setup option "Live TV Priority" to control
|
||||||
|
precedence among multiple clients. The VDR option "Primary Limit" which
|
||||||
|
has previouly been used for this purpose has been dropped in VDR 1.7.25.
|
||||||
|
- Timout for network operations now configurable in streamdev-client setup
|
||||||
|
- Added timeout to Connect()
|
||||||
|
- Report the server-side HTTP status "503 Service unavailable" instead of
|
||||||
|
the client-side error "409 Conflict" when a channel is unavailable
|
||||||
|
(suggested by Methodus)
|
||||||
|
- Update of po headers and Finnish translation (thanks to Rolf Ahrenberg)
|
||||||
|
- support for non-cycle-free setups (e.g. where two VDRs mutually share
|
||||||
|
their DVB cards through streamdev-client/-server). Must be enabled in
|
||||||
|
streamdev-server setup. Obsoletes recursion patches.
|
||||||
|
- API change of VDR 1.7.22
|
||||||
|
- VDR 1.7.22 obsoletes cap_net_raw patch. Added cap_net_raw patch for VDR
|
||||||
|
1.7.5 - 1.7.21.
|
||||||
|
- Update and UTF-8 conversion of Finnish po files (thanks to Rolf Ahrenberg)
|
||||||
|
- Added "Hide mainmenu entry" option on server (thanks to Rolf Ahrenberg)
|
||||||
|
- Added server menu with list of clients. Connections can be terminated
|
||||||
|
with the "red" key. The former main menu action of suspending live TV
|
||||||
|
moved to the "blue" key.
|
||||||
- code cleanup and optimization (thanks to Ville Skyttä)
|
- code cleanup and optimization (thanks to Ville Skyttä)
|
||||||
- properly shutdown IGMP timeout handler thread when the plugin is stopped.
|
- properly shutdown IGMP timeout handler thread when the plugin is stopped.
|
||||||
Fixes occasional segfaults on VDR exit.
|
Fixes occasional segfaults on VDR exit.
|
||||||
|
|||||||
70
Makefile
70
Makefile
@@ -1,57 +1,44 @@
|
|||||||
#
|
#
|
||||||
# Makefile for a Video Disk Recorder plugin
|
# Makefile for a Video Disk Recorder plugin
|
||||||
#
|
#
|
||||||
# $Id: Makefile,v 1.23 2010/08/02 10:36:59 schmirl Exp $
|
# $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.
|
||||||
|
|
||||||
# The main source file name.
|
|
||||||
#
|
|
||||||
PLUGIN = streamdev
|
PLUGIN = streamdev
|
||||||
|
|
||||||
### The C/C++ compiler and options:
|
|
||||||
|
|
||||||
CC ?= gcc
|
|
||||||
CFLAGS ?= -g -O2 -Wall
|
|
||||||
|
|
||||||
CXX ?= g++
|
|
||||||
CXXFLAGS ?= -g -O2 -Wall -Woverloaded-virtual -Wno-parentheses
|
|
||||||
|
|
||||||
### The version number of this plugin (taken from the main source file):
|
### The version number of this plugin (taken from the main source file):
|
||||||
|
|
||||||
VERSION = $(shell grep 'const char \*VERSION *=' common.c | awk '{ print $$5 }' | sed -e 's/[";]//g')
|
VERSION = $(shell grep 'const char \*VERSION *=' common.c | awk '{ print $$5 }' | sed -e 's/[";]//g')
|
||||||
|
|
||||||
### The directory environment:
|
### The directory environment:
|
||||||
|
|
||||||
VDRDIR = ../../..
|
# Use package data if installed...otherwise assume we're under the VDR source directory:
|
||||||
LIBDIR = ../../lib
|
PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
|
||||||
TMPDIR = /tmp
|
LIBDIR = $(call PKGCFG,libdir)
|
||||||
|
LOCDIR = $(call PKGCFG,locdir)
|
||||||
|
PLGCFG = $(call PKGCFG,plgcfg)
|
||||||
|
#
|
||||||
|
TMPDIR ?= /tmp
|
||||||
|
|
||||||
### The version number of VDR (taken from VDR's "config.h"):
|
### The compiler options:
|
||||||
|
|
||||||
APIVERSION = $(shell grep 'define APIVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
|
export CFLAGS = $(call PKGCFG,cflags)
|
||||||
APIVERSNUM = $(shell grep 'define APIVERSNUM ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
|
export CXXFLAGS = $(call PKGCFG,cxxflags)
|
||||||
TSPLAYVERSNUM = $(shell grep 'define TSPLAY_PATCH_VERSION ' $(VDRDIR)/device.h | awk '{ print $$3 }')
|
|
||||||
|
### The version number of VDR's plugin API:
|
||||||
|
|
||||||
|
APIVERSION = $(call PKGCFG,apiversion)
|
||||||
|
|
||||||
### Allow user defined options to overwrite defaults:
|
### Allow user defined options to overwrite defaults:
|
||||||
|
|
||||||
ifeq ($(shell test $(APIVERSNUM) -ge 10713; echo $$?),0)
|
-include $(PLGCFG)
|
||||||
include $(VDRDIR)/Make.global
|
|
||||||
else
|
|
||||||
ifeq ($(shell test $(APIVERSNUM) -ge 10704 -o -n "$(TSPLAYVERSNUM)" ; echo $$?),0)
|
|
||||||
DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE
|
|
||||||
CFLAGS += -fPIC
|
|
||||||
CXXFLAGS += -fPIC
|
|
||||||
else
|
|
||||||
CFLAGS += -fPIC
|
|
||||||
CXXFLAGS += -fPIC
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
-include $(VDRDIR)/Make.config
|
|
||||||
|
|
||||||
### export all vars for sub-makes, using absolute paths
|
### export all vars for sub-makes, using absolute paths
|
||||||
|
|
||||||
VDRDIR := $(shell cd $(VDRDIR) >/dev/null 2>&1 && pwd)
|
|
||||||
LIBDIR := $(shell cd $(LIBDIR) >/dev/null 2>&1 && pwd)
|
LIBDIR := $(shell cd $(LIBDIR) >/dev/null 2>&1 && pwd)
|
||||||
|
LOCDIR := $(shell cd $(LOCDIR) >/dev/null 2>&1 && pwd)
|
||||||
export
|
export
|
||||||
unexport PLUGIN
|
unexport PLUGIN
|
||||||
|
|
||||||
@@ -63,6 +50,7 @@ PACKAGE = vdr-$(ARCHIVE)
|
|||||||
### Includes and Defines (add further entries here):
|
### Includes and Defines (add further entries here):
|
||||||
|
|
||||||
INCLUDES += -I$(VDRDIR)/include -I..
|
INCLUDES += -I$(VDRDIR)/include -I..
|
||||||
|
export INCLUDES
|
||||||
|
|
||||||
DEFINES += -D_GNU_SOURCE
|
DEFINES += -D_GNU_SOURCE
|
||||||
|
|
||||||
@@ -75,7 +63,7 @@ endif
|
|||||||
|
|
||||||
### The main target:
|
### The main target:
|
||||||
|
|
||||||
.PHONY: all client server dist clean
|
.PHONY: all client server install install-client install-server dist clean
|
||||||
all: client server
|
all: client server
|
||||||
|
|
||||||
### Targets:
|
### Targets:
|
||||||
@@ -83,20 +71,28 @@ all: client server
|
|||||||
client:
|
client:
|
||||||
$(MAKE) -C ./tools
|
$(MAKE) -C ./tools
|
||||||
$(MAKE) -C ./client
|
$(MAKE) -C ./client
|
||||||
# installs to $(LIBDIR)/libvdr-streamdev-client.so.$(APIVERSION)
|
|
||||||
|
|
||||||
server:
|
server:
|
||||||
$(MAKE) -C ./tools
|
$(MAKE) -C ./tools
|
||||||
$(MAKE) -C ./libdvbmpeg
|
$(MAKE) -C ./libdvbmpeg
|
||||||
$(MAKE) -C ./remux
|
$(MAKE) -C ./remux
|
||||||
$(MAKE) -C ./server
|
$(MAKE) -C ./server
|
||||||
|
|
||||||
|
install-client: client
|
||||||
|
$(MAKE) -C ./client install
|
||||||
|
# installs to $(LIBDIR)/libvdr-streamdev-client.so.$(APIVERSION)
|
||||||
|
|
||||||
|
install-server: server
|
||||||
|
$(MAKE) -C ./server install
|
||||||
# installs to $(LIBDIR)/libvdr-streamdev-server.so.$(APIVERSION)
|
# installs to $(LIBDIR)/libvdr-streamdev-server.so.$(APIVERSION)
|
||||||
|
|
||||||
|
install: install-client install-server
|
||||||
|
|
||||||
dist: clean
|
dist: clean
|
||||||
@-rm -rf $(TMPDIR)/$(ARCHIVE)
|
@-rm -rf $(TMPDIR)/$(ARCHIVE)
|
||||||
@mkdir $(TMPDIR)/$(ARCHIVE)
|
@mkdir $(TMPDIR)/$(ARCHIVE)
|
||||||
@cp -a * $(TMPDIR)/$(ARCHIVE)
|
@cp -a * $(TMPDIR)/$(ARCHIVE)
|
||||||
@tar czf $(PACKAGE).tgz --exclude CVS -C $(TMPDIR) $(ARCHIVE)
|
@tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
|
||||||
@-rm -rf $(TMPDIR)/$(ARCHIVE)
|
@-rm -rf $(TMPDIR)/$(ARCHIVE)
|
||||||
@echo Distribution package created as $(PACKAGE).tgz
|
@echo Distribution package created as $(PACKAGE).tgz
|
||||||
|
|
||||||
|
|||||||
108
Makefile-1.7.33
Normal file
108
Makefile-1.7.33
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
#
|
||||||
|
# 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
252
README
@@ -101,13 +101,17 @@ as otherwise -r will be passed to VDR and not to streamdev.
|
|||||||
2.1 Compatibility:
|
2.1 Compatibility:
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
This version is not compatible to VDR releases older than 1.5.9. Take one of
|
This version is not compatible to VDR releases older than 1.7.25. Use one of
|
||||||
the streamdev-0.4.x releases if you are running at least VDR 1.4.x. For older
|
the streamdev-0.5.x releases for older versions.
|
||||||
VDRs you will probably need one of the streamdev-0.3.x releases.
|
|
||||||
|
|
||||||
2.2 Compiling:
|
2.2 Compiling:
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
The Makefiles are for VDR 1.7.36 and above. For VDR 1.7.33 and below, please
|
||||||
|
replace the Makefiles in the main directory and in the client/ and server/
|
||||||
|
subdirectories with the corresponding Makefile-1.7.33 files. With VDR 1.7.34 and
|
||||||
|
1.7.35 YMMV ;)
|
||||||
|
|
||||||
cd vdr-1.X.X/PLUGINS/src
|
cd vdr-1.X.X/PLUGINS/src
|
||||||
tar xvfz vdr-streamdev-0.5.0.tgz
|
tar xvfz vdr-streamdev-0.5.0.tgz
|
||||||
ln -s streamdev-0.5.0 streamdev
|
ln -s streamdev-0.5.0 streamdev
|
||||||
@@ -130,6 +134,24 @@ If you are updating streamdev from an earlier release, you might have to
|
|||||||
perform some additional steps. Check which version you've been running before,
|
perform some additional steps. Check which version you've been running before,
|
||||||
then read below for the necessary changes.
|
then read below for the necessary changes.
|
||||||
|
|
||||||
|
* Priorities:
|
||||||
|
-------------
|
||||||
|
(Affected: 0.5.x and older)
|
||||||
|
|
||||||
|
The server-side setting "Suspend behaviour" has been dropped in 0.6.0 in favour
|
||||||
|
of priority based precedence. A priority of 0 and above means that clients
|
||||||
|
have precedence. A negative priority gives precedence to local live TV on the
|
||||||
|
server. So if "Suspend behaviour" was previously set to "Client may suspend" or
|
||||||
|
"Never suspended", you will have to configure a negative priority. If the
|
||||||
|
"Suspend behaviour" was set to "Always suspended", the default values should do.
|
||||||
|
|
||||||
|
Configure the desired priorities for HTTP and IGMP Multicast streaming in the
|
||||||
|
settings of streamdev-server. If you haven't updated all your streamdev-clients
|
||||||
|
to at least 0.5.2, configure "Legacy Client Priority", too.
|
||||||
|
|
||||||
|
In streamdev-client, you should set "Minimum Priority" to -99. Adjust "Live TV
|
||||||
|
Priority" if necessary.
|
||||||
|
|
||||||
* Location of files:
|
* Location of files:
|
||||||
--------------------
|
--------------------
|
||||||
(Affected: 0.3.x, 0.4.x, 0.4.0pre, 0.5.0pre)
|
(Affected: 0.3.x, 0.4.x, 0.4.0pre, 0.5.0pre)
|
||||||
@@ -175,25 +197,41 @@ Start the server core itself by specifying -Pstreamdev-server on your VDR
|
|||||||
commandline. To use the client core, specify -Pstreamdev-client. Both parts
|
commandline. To use the client core, specify -Pstreamdev-client. Both parts
|
||||||
can run in one VDR instance, if necessary.
|
can run in one VDR instance, if necessary.
|
||||||
|
|
||||||
The parameter "Suspend behaviour" allows you to specify how the server should
|
Precedence between multiple clients and between client and server is controlled
|
||||||
react in case the client requests a channel that would require switching the
|
with priorities. For HTTP and IGMP Multicast, the priority is configured in
|
||||||
primary device (i.e. disrupt live-tv). If set to "Offer suspend mode", you will
|
streamdev-server's setup menu. A negative priority gives precedence to local
|
||||||
have a new entry in the main menu. Activating that will put the server into
|
live TV on the server. Zero and positive values give precedence to the client.
|
||||||
"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).
|
|
||||||
|
|
||||||
NOTE: This mainly applies to One-Card-Systems, since with multiple cards there
|
The priority for VDR clients watching live TV is configured in the plugin setup
|
||||||
is no need to switch transponders on the primary interface, if the secondary
|
of streamdev-client. For other client tasks (e.g. recording a client side timer)
|
||||||
can stream a given channel (i.e. if it is not blocked by a recording). If both
|
the same priority as on the client is used. With the parameter "Legacy client
|
||||||
cards are in use (i.e. when something is recorded, or by multiple clients),
|
Priority" in streamdev-server's setup menu you can configure the priority for
|
||||||
this applies to Multiple-Card-Systems as well.
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
3.1 Usage HTTP server:
|
3.1 Usage HTTP server:
|
||||||
----------------------
|
----------------------
|
||||||
@@ -207,7 +245,6 @@ has been requested in the URL (see below). The supported stream types are:
|
|||||||
|
|
||||||
TS Transport Stream (i.e. a dump from the device)
|
TS Transport Stream (i.e. a dump from the device)
|
||||||
PES Packetized Elemetary Stream (VDR's native recording format)
|
PES Packetized Elemetary Stream (VDR's native recording format)
|
||||||
PS Program Stream (SVCD, DVD like stream)
|
|
||||||
ES Elementary Stream (only Video, if available, otherwise only Audio)
|
ES Elementary Stream (only Video, if available, otherwise only Audio)
|
||||||
EXT Pass stream through external script (e.g. for converting with mencoder)
|
EXT Pass stream through external script (e.g. for converting with mencoder)
|
||||||
|
|
||||||
@@ -225,16 +262,18 @@ streams directly like this:
|
|||||||
http://hostname:3000/S19.2E-0-12480-898
|
http://hostname:3000/S19.2E-0-12480-898
|
||||||
|
|
||||||
The first one will deliver a channel by number on the server, the second one
|
The first one will deliver a channel by number on the server, the second one
|
||||||
will request the channel by unique channel id. In addition, you can specify
|
will request the channel by unique channel id. Use the special channel number 0
|
||||||
the desired stream type as a path to the channel.
|
to see the server's current live TV channel.
|
||||||
|
|
||||||
|
In addition, you can specify the desired stream type as a path to the channel.
|
||||||
|
|
||||||
http://hostname:3000/TS/3
|
http://hostname:3000/TS/3
|
||||||
http://hostname:3000/PES/S19.2E-0-12480-898
|
http://hostname:3000/PES/S19.2E-0-12480-898
|
||||||
|
|
||||||
The first one would deliver the stream in TS, the second one in PES format.
|
The first one would deliver the stream in TS, the second one in PES format.
|
||||||
Possible values are 'PES', 'TS', 'PS', 'ES' and 'EXT'. You need to specify
|
Possible values are 'PES', 'TS', 'ES' and 'EXT'. You need to specify the ES
|
||||||
the ES format explicitly if you want to listen to radio channels. Play them
|
format explicitly if you want to listen to radio channels. Play them back i.e.
|
||||||
back i.e. with mpg123.
|
with mpg123.
|
||||||
|
|
||||||
mpg123 http://hostname:3000/ES/200
|
mpg123 http://hostname:3000/ES/200
|
||||||
|
|
||||||
@@ -280,16 +319,11 @@ reserved according to RFC-2365).
|
|||||||
|
|
||||||
Before you can use streamdev's multicast server, you might need to patch VDR.
|
Before you can use streamdev's multicast server, you might need to patch VDR.
|
||||||
Binding an IGMP socket is a privileged operation, so you must start VDR as root.
|
Binding an IGMP socket is a privileged operation, so you must start VDR as root.
|
||||||
If you pass the -u option to VDR, it will drop almost all priviledges before
|
|
||||||
streamdev is even loaded. Apply vdr-cap_net_raw.diff to keep VDR from dropping
|
|
||||||
the CAP_NET_RAW capability required to bind the IGMP socket. The patch is part
|
|
||||||
of streamdev's source distribution. Check the patches subdirectory. There's no
|
|
||||||
need to patch VDR if it is kept running as root (not recommended).
|
|
||||||
|
|
||||||
The multicast server is disabled by default. Enter the streamdev-server setup
|
The multicast server is disabled by default. Enter the streamdev-server setup
|
||||||
menu to enable it and - IMPORTANT - bind the multicast server to the IP of your
|
menu to enable it and - IMPORTANT - bind the multicast server to the IP of your
|
||||||
VDR server's LAN ethernet card. The multicast server will refuse to start with
|
VDR server's LAN ethernet card. The multicast server will refuse to start with
|
||||||
the default bind adresse "0.0.0.0".
|
the default bind address "0.0.0.0".
|
||||||
|
|
||||||
Now edit your streamdevhosts.conf. To allow streaming of all channels, it must
|
Now edit your streamdevhosts.conf. To allow streaming of all channels, it must
|
||||||
contain "239.255.0.0/16". Note that you cannot limit connections by client IP
|
contain "239.255.0.0/16". Note that you cannot limit connections by client IP
|
||||||
@@ -329,6 +363,13 @@ port where you want the server to listen for incoming connections. The server
|
|||||||
will be activated when you push the OK button inside the setup menu, so there's
|
will be activated when you push the OK button inside the setup menu, so there's
|
||||||
no need to restart VDR.
|
no need to restart VDR.
|
||||||
|
|
||||||
|
If both, streamdev-client and streamdev-server are installed, the additional
|
||||||
|
option "Loop prevention" will show up in the streamdev-server setup. If enabled,
|
||||||
|
streamdev-client won't be considered when streamdev-server is looking for a
|
||||||
|
device which is able to receive some channel. This is required if two or more
|
||||||
|
VDRs mutually share their DVB devices through streamdev. Otherwise you would
|
||||||
|
end up in a loop.
|
||||||
|
|
||||||
3.4 Usage VDR-to-VDR client:
|
3.4 Usage VDR-to-VDR client:
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
@@ -337,9 +378,9 @@ setup parameter "Hide Mainmenu Entry" you can hide this menu item if you don't
|
|||||||
need it. "Suspend Server" is only useful if the server runs in "Offer suspend
|
need it. "Suspend Server" is only useful if the server runs in "Offer suspend
|
||||||
mode" with "Client may suspend" enabled.
|
mode" with "Client may suspend" enabled.
|
||||||
|
|
||||||
The parameter "Remote IP" uses an IP-Adress-Editor, where you can just enter
|
The parameter "Remote IP" uses an IP-Address-Editor, where you can just enter
|
||||||
the IP number with the number keys on your remote. After three digits (or if
|
the IP number with the number keys on your remote. After three digits (or if
|
||||||
the next digit would result in an invalid IP adress, or if the first digit is
|
the next digit would result in an invalid IP address, or if the first digit is
|
||||||
0), the current position jumps to the next one. You can change positions with
|
0), the current position jumps to the next one. You can change positions with
|
||||||
the left and right buttons, and you can cycle the current position using up
|
the left and right buttons, and you can cycle the current position using up
|
||||||
and down. To confirm the entered address, press OK. So, if you want to enter
|
and down. To confirm the entered address, press OK. So, if you want to enter
|
||||||
@@ -348,66 +389,83 @@ type "127001<OK>" on your remote. If you want to enter "192.168.1.12", type
|
|||||||
"1921681<Right>12<OK>".
|
"1921681<Right>12<OK>".
|
||||||
|
|
||||||
The parameters "Remote IP" and "Remote Port" in the client's setup specify the
|
The parameters "Remote IP" and "Remote Port" in the client's setup specify the
|
||||||
address of the remote VDR-to-VDR server to connect to. Activate the client by
|
address of the remote VDR-to-VDR server to connect to. The client is disabled
|
||||||
setting "Start Client" to yes. It is disabled by default, because it wouldn't
|
by default, because it wouldn't make much sense to start the client without
|
||||||
make much sense to start the client without specifying a server anyway. The
|
specifying a server anyway. Activate the client by setting "Simultaneously used
|
||||||
client is activated after you push the OK button, so there's no need to restart
|
Devices" to at least 1. Streamdev-client will allocate as many VDR devices as
|
||||||
VDR. Deactivation on-the-fly is not possible, so in order to deactivate the
|
you configure here. Each of these devices opens one connection to the server
|
||||||
client, you will have to restart VDR. However requests to switch channels will
|
and becomes associated with one of the server's devices (typically a DVB card)
|
||||||
be refused by streamdev-client once it has been deactivated. All other settings
|
on demand.
|
||||||
can be changed without restarting VDR.
|
|
||||||
|
|
||||||
The client will try to connect to the server (in case it isn't yet) whenever
|
|
||||||
a remote channel is requested. Just activate the client and switch to a
|
|
||||||
channel that's not available by local devices. If anything goes wrong with the
|
|
||||||
connection between the two, you will see it in the logfile instantly. If you
|
|
||||||
now switch the client to a channel which isn't covered by it's own local
|
|
||||||
devices, it will ask the server for it. If the server can (currently) receive
|
|
||||||
that channel, the client will show it until you switch again, or until the
|
|
||||||
server needs that card (if no other is free) for a recording on a different
|
|
||||||
transponder.
|
|
||||||
|
|
||||||
Only the needed PIDs are transferred, and additional PIDs can be turned on
|
Only the needed PIDs are transferred, and additional PIDs can be turned on
|
||||||
during an active transfer. This makes it possible to switch languages, receive
|
during an active transfer. This makes it possible to switch languages, receive
|
||||||
additional channels (for recording on the client) and use plugins that use
|
additional channels on the same transponder and use plugins that use receivers
|
||||||
receivers themselves (like osdteletext).
|
themselves (like osdteletext).
|
||||||
|
|
||||||
|
So for viewing live TV a single device is sufficient. But if the client needs
|
||||||
|
to receive channels from different transponders simultaneously (e.g. for PiP or
|
||||||
|
client side recordings) a second device becomes necessary.
|
||||||
|
|
||||||
|
To allocate additional devices, just increase the number and push the OK button.
|
||||||
|
There's no need to restart VDR. Deleting VDR devices on-the-fly is not possible.
|
||||||
|
However requests to switch channels will be refused by redundant devices.
|
||||||
|
|
||||||
|
The default timeout of 2 seconds for network operations should be sufficient in
|
||||||
|
most cases. Increase "Timeout" if you get frequent timeout errors in the log.
|
||||||
|
|
||||||
With "Filter Streaming" enabled, the client will receive meta information like
|
With "Filter Streaming" enabled, the client will receive meta information like
|
||||||
EPG data and service information, just as if the client had its own DVB card.
|
EPG data and service information, just as if the client had its own DVB card.
|
||||||
Link channels and even a client-side EPG scan have been reported to work.
|
Link channels and even a client-side EPG scan have been reported to work.
|
||||||
|
|
||||||
With maximum and minimum priority you can keep VDR from considering streamdev
|
If you have TV programs with dynamically changing PIDs (such as some german
|
||||||
in certain cases. If for instance you have a streamdev client with its own DVB
|
regional programs like NDR), then you need to enable "Filter Streaming" to
|
||||||
card, VDR might use streamdev for recording. If this is not what you want, you
|
correctly receive them. You also need to set in VDRs DVB setup the option
|
||||||
could set the maximum priority to 0. As recordings usually have a much higher
|
"Update channels" to at least "PIDs only" (or "names and PIDs") for this
|
||||||
priority (default 50), streamdev is now no longer used for recordings. The two
|
to work.
|
||||||
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".
|
|
||||||
|
|
||||||
If you are running at least VDR 1.7.0, you can also configure the "Broadcast
|
"Filter streaming" uses internally a socketpair(2) to copy meta data to
|
||||||
Systems / Cost" of the streamdev-client device. On a pure streamdev-client only
|
VDR. This socketpair may require larger than default buffering. If
|
||||||
system it doesn't matter what you configure here. But if your client is equipped
|
you see a mesage like the following in syslog,
|
||||||
with a DVB card, you should read on. VDR always prefers the cheapest device
|
|
||||||
in terms of supported broadcast systems and modulations. A DVB-S2 card supports
|
|
||||||
two broadcast systems (DVB-S and DVB-S2). From VDR 1.7.15 on, the supported
|
|
||||||
modulations are counted as well (QPSK, QAM32/64/128/256, VSB8/16, TURBO_FEC).
|
|
||||||
So for a DVB-S2 card which does QPSK you'll get a total cost of three. A DVB-C
|
|
||||||
card (one broadcast system) which can do QAM32,QAM64,QAM128,QAM256 would give
|
|
||||||
you a total of five. Check your log for "frontend ... provides ... with ..."
|
|
||||||
messages to find out the cost of your DVB cards. Then pick a suitable value for
|
|
||||||
streamdev-client. With equal costs, VDR will usually prefer the DVB card and
|
|
||||||
take streamdev for recordings. If streamdev's costs are higher, live TV will
|
|
||||||
use your DVB card until a recordings kicks in. Then the recording will take the
|
|
||||||
DVB card and live TV will be shifted to streamdev (you'll notice a short
|
|
||||||
interruption of live TV).
|
|
||||||
|
|
||||||
Note that streamdev-client acts similar to a DVB card. It is possible to receive
|
cStreamdevFilter::PutSection(Pid:18 Tid: 64): Dropped 2995 bytes, max queue: 328640
|
||||||
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
|
then you should increase the streamdev client "FilterSockBufSize" value. A
|
||||||
transponders at a time. The same trick allows a client to receive channels from
|
good value is 3072000. You will need to first configure your linux to
|
||||||
different servers. To create an additional instance, copy the streamdev-client
|
permit such a large buffer size:
|
||||||
binary to a different name (e.g. streamdev-client2):
|
|
||||||
|
sysctl net.core.wmem_max=3072000
|
||||||
|
|
||||||
|
The precedence among multiple client VDRs receiving live TV from the same
|
||||||
|
server is controlled with "Live TV Priority".
|
||||||
|
|
||||||
|
With "Maximum Priority" and "Minimum Priority" you can keep VDR from considering
|
||||||
|
streamdev in certain cases. If for instance you have a streamdev client with its
|
||||||
|
own DVB card, VDR might use streamdev for recording. If this is not what you
|
||||||
|
want, you could set the maximum priority to 0. As recordings usually have a much
|
||||||
|
higher priority (default 50), streamdev is now no longer used for recordings.
|
||||||
|
The two parameters define the inclusive range of priorities for which streamdev
|
||||||
|
will accept to tune. Setting the minimum priority to a higher value than the
|
||||||
|
maximum, you will get two ranges: "up to maximum" and "minimum and above".
|
||||||
|
|
||||||
|
You can also configure the "Broadcast Systems / Cost" of the streamdev-client
|
||||||
|
device. On a pure streamdev-client only system it doesn't matter what you
|
||||||
|
configure here. But if your client is equipped with a DVB card, you should read
|
||||||
|
on. VDR always prefers the cheapest device in terms of supported broadcast
|
||||||
|
systems and modulations. A DVB-S2 card supports two broadcast systems (DVB-S and
|
||||||
|
DVB-S2). The supported modulations are counted as well (QPSK, QAM32/64/128/256,
|
||||||
|
VSB8/16, TURBO_FEC). So for a DVB-S2 card which does QPSK you'll get a total
|
||||||
|
cost of three. A DVB-C card (one broadcast system) which can do QAM32, QAM64,
|
||||||
|
QAM128, QAM256 would give you a total of five. Check your log for "frontend ...
|
||||||
|
provides ... with ..." messages to find out the cost of your DVB cards. Then
|
||||||
|
pick a suitable value for streamdev-client. With equal costs, VDR will usually
|
||||||
|
prefer the DVB card and take streamdev for recordings. If streamdev's costs are
|
||||||
|
higher, live TV will use your DVB card until a recordings kicks in. Then the
|
||||||
|
recording will take the DVB card and live TV will be shifted to streamdev
|
||||||
|
(you'll notice a short interruption of live TV).
|
||||||
|
|
||||||
|
To receive channels from multiple servers, create additional instances of the
|
||||||
|
streamdev-client plugin. Simply copy (don't link!) the binary to a different
|
||||||
|
name (e.g. streamdev-client2):
|
||||||
|
|
||||||
cd VDRPLUGINLIBDIR
|
cd VDRPLUGINLIBDIR
|
||||||
cp libvdr-streamdev-client.so.1.X.X libvdr-streamdev-client2.so.1.X.X
|
cp libvdr-streamdev-client.so.1.X.X libvdr-streamdev-client2.so.1.X.X
|
||||||
@@ -513,14 +571,10 @@ The script should perform the following steps (pseudocode):
|
|||||||
6. Known Problems:
|
6. Known Problems:
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
* There have been reports that channel switching with VDR 1.5.x/1.6.x clients
|
* In VDR before 1.7.30 viewing encrypted channels is an issue as Streamdev
|
||||||
sometimes fails. Current version includes a workaround which seems to work, but
|
doesn't provide a (dummy) CAM. So out of the box, VDR won't ever try to receive
|
||||||
YMMV ;)
|
encrypted channels from streamdev. Pick one of the following solutions to work
|
||||||
|
around the problem:
|
||||||
* Viewing encrypted channels became an issue with VDR's new CAM handling code.
|
|
||||||
Streamdev doesn't provide a (dummy) CAM, so out of the box, VDR won't ever try
|
|
||||||
to receive encrypted channels from streamdev. Pick one of the following
|
|
||||||
solutions to work around the problem:
|
|
||||||
|
|
||||||
1. Force VDR to use streamdev. Open the channels menu on the client (or edit its
|
1. Force VDR to use streamdev. Open the channels menu on the client (or edit its
|
||||||
channels.conf if you know how to do this) and set the CA field of all channels
|
channels.conf if you know how to do this) and set the CA field of all channels
|
||||||
@@ -530,9 +584,9 @@ up. So please consider the logs for the correct value. Remember to fill in
|
|||||||
hexadecimal values if you are using an editor to modify your channels.conf
|
hexadecimal values if you are using an editor to modify your channels.conf
|
||||||
(number 10 becomes an "a", number 11 a "b", ...).
|
(number 10 becomes an "a", number 11 a "b", ...).
|
||||||
|
|
||||||
2. Apply either patch "patches/vdr-1.6.0-intcamdevices.patch" or patch
|
2. Apply either patch "patches/vdr-1.6.0-1.7.29-intcamdevices.patch" or patch
|
||||||
"patches/vdr-1.6.0-ignore_missing_cam.diff" to your client VDR. Intcamdevices
|
"patches/vdr-1.6.0-1.7.29-ignore_missing_cam.diff" to your client VDR.
|
||||||
is the clean solution, but it modifies the VDR API. So you will need to
|
Intcamdevices is the clean solution, but it modifies the VDR API. So you will
|
||||||
recompile all of your plugins. The ignore_missing_cam patch is trivial, no need
|
need to recompile all of your plugins. The ignore_missing_cam patch is trivial,
|
||||||
to recompile other plugins. However it is not suitable for clients with a DVB
|
no need to recompile other plugins. However it is not suitable for clients with
|
||||||
card of their own.
|
a DVB card of their own.
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
#
|
#
|
||||||
# Makefile for a Video Disk Recorder plugin
|
# Makefile for a Video Disk Recorder plugin
|
||||||
#
|
#
|
||||||
# $Id: Makefile,v 1.2 2010/07/19 13:49:25 schmirl Exp $
|
# $Id: $
|
||||||
|
|
||||||
# The official name of this plugin.
|
# The official name of this plugin.
|
||||||
# This name will be used in the '-P...' option of VDR to load the plugin.
|
# This name will be used in the '-P...' option of VDR to load the plugin.
|
||||||
# By default the main source file also carries this name.
|
# By default the main source file also carries this name.
|
||||||
#
|
|
||||||
PLUGIN = streamdev-client
|
PLUGIN = streamdev-client
|
||||||
|
|
||||||
|
### The name of the shared object file:
|
||||||
|
|
||||||
|
SOFILE = libvdr-$(PLUGIN).so
|
||||||
|
|
||||||
### Includes and Defines (add further entries here):
|
### Includes and Defines (add further entries here):
|
||||||
|
|
||||||
DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
|
DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
|
||||||
@@ -22,8 +26,7 @@ CLIENTOBJS = $(PLUGIN).o \
|
|||||||
|
|
||||||
### The main target:
|
### The main target:
|
||||||
|
|
||||||
.PHONY: all i18n dist clean
|
all: $(SOFILE) i18n
|
||||||
all: libvdr-$(PLUGIN).so i18n
|
|
||||||
|
|
||||||
### Implicit rules:
|
### Implicit rules:
|
||||||
|
|
||||||
@@ -34,51 +37,47 @@ all: libvdr-$(PLUGIN).so i18n
|
|||||||
|
|
||||||
MAKEDEP = $(CXX) -MM -MG
|
MAKEDEP = $(CXX) -MM -MG
|
||||||
DEPFILE = .dependencies
|
DEPFILE = .dependencies
|
||||||
|
|
||||||
$(DEPFILE): Makefile
|
$(DEPFILE): Makefile
|
||||||
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(CLIENTOBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) > $@
|
@$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(CLIENTOBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) > $@
|
||||||
|
|
||||||
-include $(DEPFILE)
|
-include $(DEPFILE)
|
||||||
|
|
||||||
### Internationalization (I18N):
|
### Internationalization (I18N):
|
||||||
|
|
||||||
PODIR = po
|
PODIR = po
|
||||||
LOCALEDIR = $(VDRDIR)/locale
|
|
||||||
I18Npo = $(wildcard $(PODIR)/*.po)
|
I18Npo = $(wildcard $(PODIR)/*.po)
|
||||||
I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
|
I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file))))
|
||||||
|
I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
|
||||||
I18Npot = $(PODIR)/$(PLUGIN).pot
|
I18Npot = $(PODIR)/$(PLUGIN).pot
|
||||||
|
|
||||||
%.mo: %.po
|
%.mo: %.po
|
||||||
msgfmt -c -o $@ $<
|
msgfmt -c -o $@ $<
|
||||||
|
|
||||||
$(I18Npot): $(CLIENTOBJS:%.o=%.c)
|
$(I18Npot): $(CLIENTOBJS:%.o=%.c)
|
||||||
xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --msgid-bugs-address='<http://www.vdr-developer.org/mantisbt/>' -o $@ $^
|
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 $^`
|
||||||
|
|
||||||
%.po: $(I18Npot)
|
%.po: $(I18Npot)
|
||||||
msgmerge -U --no-wrap --no-location --backup=none -q $@ $<
|
msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $<
|
||||||
@touch $@
|
@touch $@
|
||||||
|
|
||||||
$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
|
$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
|
||||||
@mkdir -p $(dir $@)
|
install -D -m644 $< $@
|
||||||
cp $< $@
|
|
||||||
|
|
||||||
i18n: $(I18Nmsgs)
|
.PHONY: i18n
|
||||||
|
i18n: $(I18Nmo) $(I18Npot)
|
||||||
|
|
||||||
|
install-i18n: $(I18Nmsgs)
|
||||||
|
|
||||||
### Targets:
|
### Targets:
|
||||||
|
|
||||||
libvdr-$(PLUGIN).so: $(CLIENTOBJS) $(COMMONOBJS) ../tools/sockettools.a
|
$(SOFILE): $(CLIENTOBJS) $(COMMONOBJS) ../tools/sockettools.a
|
||||||
|
$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $^ -o $@
|
||||||
|
|
||||||
%.so:
|
install-lib: $(SOFILE)
|
||||||
$(CXX) $(CXXFLAGS) -shared $^ -o $@
|
install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION)
|
||||||
@cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION)
|
|
||||||
|
|
||||||
dist: clean
|
install: install-lib install-i18n
|
||||||
@-rm -rf $(TMPDIR)/$(ARCHIVE)
|
|
||||||
@mkdir $(TMPDIR)/$(ARCHIVE)
|
|
||||||
@cp -a * $(TMPDIR)/$(ARCHIVE)
|
|
||||||
@tar czf $(PACKAGE).tgz --exclude CVS -C $(TMPDIR) $(ARCHIVE)
|
|
||||||
@-rm -rf $(TMPDIR)/$(ARCHIVE)
|
|
||||||
@echo Distribution package created as $(PACKAGE).tgz
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@-rm -f $(COMMONOBJS) $(CLIENTOBJS) $(DEPFILE) $(PODIR)/*.mo $(PODIR)/*.pot *.so *.tgz core* *~
|
@-rm -f $(PODIR)/*.mo $(PODIR)/*.pot
|
||||||
|
@-rm -f $(COMMONOBJS) $(CLIENTOBJS) $(DEPFILE) *.so *.tgz core* *~
|
||||||
|
|||||||
84
client/Makefile-1.7.33
Normal file
84
client/Makefile-1.7.33
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#
|
||||||
|
# Makefile for a Video Disk Recorder plugin
|
||||||
|
#
|
||||||
|
# $Id: Makefile,v 1.2 2010/07/19 13:49:25 schmirl Exp $
|
||||||
|
|
||||||
|
# The official name of this plugin.
|
||||||
|
# This name will be used in the '-P...' option of VDR to load the plugin.
|
||||||
|
# By default the main source file also carries this name.
|
||||||
|
#
|
||||||
|
PLUGIN = streamdev-client
|
||||||
|
|
||||||
|
### Includes and Defines (add further entries here):
|
||||||
|
|
||||||
|
DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
|
||||||
|
|
||||||
|
### The object files (add further files here):
|
||||||
|
|
||||||
|
COMMONOBJS = ../common.o
|
||||||
|
|
||||||
|
CLIENTOBJS = $(PLUGIN).o \
|
||||||
|
device.o filter.o setup.o socket.o
|
||||||
|
|
||||||
|
### The main target:
|
||||||
|
|
||||||
|
.PHONY: all i18n dist clean
|
||||||
|
all: libvdr-$(PLUGIN).so i18n
|
||||||
|
|
||||||
|
### Implicit rules:
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
|
||||||
|
|
||||||
|
### Dependencies:
|
||||||
|
|
||||||
|
MAKEDEP = $(CXX) -MM -MG
|
||||||
|
DEPFILE = .dependencies
|
||||||
|
|
||||||
|
$(DEPFILE): Makefile
|
||||||
|
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(CLIENTOBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) > $@
|
||||||
|
|
||||||
|
-include $(DEPFILE)
|
||||||
|
|
||||||
|
### Internationalization (I18N):
|
||||||
|
|
||||||
|
PODIR = po
|
||||||
|
LOCALEDIR = $(VDRDIR)/locale
|
||||||
|
I18Npo = $(wildcard $(PODIR)/*.po)
|
||||||
|
I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
|
||||||
|
I18Npot = $(PODIR)/$(PLUGIN).pot
|
||||||
|
|
||||||
|
%.mo: %.po
|
||||||
|
msgfmt -c -o $@ $<
|
||||||
|
|
||||||
|
$(I18Npot): $(CLIENTOBJS:%.o=%.c)
|
||||||
|
xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --msgid-bugs-address='<http://www.vdr-developer.org/mantisbt/>' -o $@ $^
|
||||||
|
|
||||||
|
%.po: $(I18Npot)
|
||||||
|
msgmerge -U --no-wrap --no-location --backup=none -q $@ $<
|
||||||
|
@touch $@
|
||||||
|
|
||||||
|
$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
cp $< $@
|
||||||
|
|
||||||
|
i18n: $(I18Nmsgs)
|
||||||
|
|
||||||
|
### Targets:
|
||||||
|
|
||||||
|
libvdr-$(PLUGIN).so: $(CLIENTOBJS) $(COMMONOBJS) ../tools/sockettools.a
|
||||||
|
|
||||||
|
%.so:
|
||||||
|
$(CXX) $(CXXFLAGS) -shared $^ -o $@
|
||||||
|
@cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION)
|
||||||
|
|
||||||
|
dist: clean
|
||||||
|
@-rm -rf $(TMPDIR)/$(ARCHIVE)
|
||||||
|
@mkdir $(TMPDIR)/$(ARCHIVE)
|
||||||
|
@cp -a * $(TMPDIR)/$(ARCHIVE)
|
||||||
|
@tar czf $(PACKAGE).tgz --exclude CVS -C $(TMPDIR) $(ARCHIVE)
|
||||||
|
@-rm -rf $(TMPDIR)/$(ARCHIVE)
|
||||||
|
@echo Distribution package created as $(PACKAGE).tgz
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@-rm -f $(COMMONOBJS) $(CLIENTOBJS) $(DEPFILE) $(PODIR)/*.mo $(PODIR)/*.pot *.so *.tgz core* *~
|
||||||
286
client/device.c
286
client/device.c
@@ -19,32 +19,38 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
#ifndef LIVEPRIORITY
|
||||||
|
#define LIVEPRIORITY 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TRANSFERPRIORITY
|
||||||
|
#define TRANSFERPRIORITY -1
|
||||||
|
#endif
|
||||||
|
|
||||||
#define VIDEOBUFSIZE MEGABYTE(3)
|
#define VIDEOBUFSIZE MEGABYTE(3)
|
||||||
|
|
||||||
cStreamdevDevice *cStreamdevDevice::m_Device = NULL;
|
const cChannel *cStreamdevDevice::m_DenyChannel = NULL;
|
||||||
|
|
||||||
cStreamdevDevice::cStreamdevDevice(void) {
|
cStreamdevDevice::cStreamdevDevice(void) {
|
||||||
|
m_Disabled = false;
|
||||||
|
m_ClientSocket = new cClientSocket();
|
||||||
m_Channel = NULL;
|
m_Channel = NULL;
|
||||||
m_TSBuffer = NULL;
|
m_TSBuffer = NULL;
|
||||||
|
|
||||||
m_Filters = new cStreamdevFilters;
|
m_Filters = new cStreamdevFilters(m_ClientSocket);
|
||||||
StartSectionHandler();
|
StartSectionHandler();
|
||||||
isyslog("streamdev-client: got device number %d", CardIndex() + 1);
|
isyslog("streamdev-client: got device number %d", CardIndex() + 1);
|
||||||
|
|
||||||
m_Device = this;
|
|
||||||
m_Pids = 0;
|
m_Pids = 0;
|
||||||
m_Priority = -1;
|
|
||||||
m_DvrClosed = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cStreamdevDevice::~cStreamdevDevice() {
|
cStreamdevDevice::~cStreamdevDevice() {
|
||||||
Dprintf("Device gets destructed\n");
|
Dprintf("Device gets destructed\n");
|
||||||
|
|
||||||
Lock();
|
Lock();
|
||||||
m_Device = NULL;
|
|
||||||
m_Filters->SetConnection(-1);
|
m_Filters->SetConnection(-1);
|
||||||
ClientSocket.Quit();
|
m_ClientSocket->Quit();
|
||||||
ClientSocket.Reset();
|
m_ClientSocket->Reset();
|
||||||
Unlock();
|
Unlock();
|
||||||
|
|
||||||
Cancel(3);
|
Cancel(3);
|
||||||
@@ -52,6 +58,7 @@ cStreamdevDevice::~cStreamdevDevice() {
|
|||||||
StopSectionHandler();
|
StopSectionHandler();
|
||||||
DELETENULL(m_Filters);
|
DELETENULL(m_Filters);
|
||||||
DELETENULL(m_TSBuffer);
|
DELETENULL(m_TSBuffer);
|
||||||
|
delete m_ClientSocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if APIVERSNUM >= 10700
|
#if APIVERSNUM >= 10700
|
||||||
@@ -70,27 +77,29 @@ bool cStreamdevDevice::ProvidesTransponder(const cChannel *Channel) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if APIVERSNUM >= 10722
|
||||||
|
bool cStreamdevDevice::IsTunedToTransponder(const cChannel *Channel) const
|
||||||
|
#else
|
||||||
bool cStreamdevDevice::IsTunedToTransponder(const cChannel *Channel)
|
bool cStreamdevDevice::IsTunedToTransponder(const cChannel *Channel)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
bool res = false;
|
return m_ClientSocket->DataSocket(siLive) != NULL &&
|
||||||
if (ClientSocket.DataSocket(siLive) != NULL
|
m_Channel != NULL &&
|
||||||
&& TRANSPONDER(Channel, m_Channel)
|
Channel->Transponder() == m_Channel->Transponder();
|
||||||
&& Channel->Ca() == CA_FTA
|
}
|
||||||
&& m_Channel->Ca() == CA_FTA)
|
|
||||||
res = true;
|
const cChannel *cStreamdevDevice::GetCurrentlyTunedTransponder(void) const {
|
||||||
return res;
|
if (m_ClientSocket->DataSocket(siLive) != NULL)
|
||||||
|
return m_Channel;
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
|
bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
|
||||||
bool *NeedsDetachReceivers) const {
|
bool *NeedsDetachReceivers) const {
|
||||||
bool res = false;
|
if (m_Disabled || Channel == m_DenyChannel)
|
||||||
bool prio = Priority < 0 || Priority > this->Priority();
|
|
||||||
bool ndr = false;
|
|
||||||
|
|
||||||
if (!StreamdevClientSetup.StartClient)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Dprintf("ProvidesChannel, Channel=%s, Prio=%d\n", Channel->Name(), Priority);
|
Dprintf("ProvidesChannel, Channel=%s, Priority=%d, SocketPrio=%d\n", Channel->Name(), Priority, m_ClientSocket->Priority());
|
||||||
|
|
||||||
if (StreamdevClientSetup.MinPriority <= StreamdevClientSetup.MaxPriority)
|
if (StreamdevClientSetup.MinPriority <= StreamdevClientSetup.MaxPriority)
|
||||||
{
|
{
|
||||||
@@ -105,13 +114,42 @@ bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ClientSocket.DataSocket(siLive) != NULL
|
int newPrio = Priority;
|
||||||
&& TRANSPONDER(Channel, m_Channel))
|
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;
|
res = true;
|
||||||
else {
|
else
|
||||||
res = prio && ClientSocket.ProvidesChannel(Channel, Priority);
|
|
||||||
ndr = true;
|
ndr = true;
|
||||||
}
|
}
|
||||||
|
else if (prio) {
|
||||||
|
if (Priority == LIVEPRIORITY && m_ClientSocket->ServerVersion() >= 100)
|
||||||
|
UpdatePriority(true);
|
||||||
|
|
||||||
|
res = m_ClientSocket->ProvidesChannel(Channel, newPrio);
|
||||||
|
ndr = Receiving();
|
||||||
|
|
||||||
|
if (m_ClientSocket->ServerVersion() >= 100)
|
||||||
|
UpdatePriority(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (NeedsDetachReceivers)
|
if (NeedsDetachReceivers)
|
||||||
*NeedsDetachReceivers = ndr;
|
*NeedsDetachReceivers = ndr;
|
||||||
@@ -121,139 +159,93 @@ bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
|
|||||||
|
|
||||||
bool cStreamdevDevice::SetChannelDevice(const cChannel *Channel,
|
bool cStreamdevDevice::SetChannelDevice(const cChannel *Channel,
|
||||||
bool LiveView) {
|
bool LiveView) {
|
||||||
|
bool res;
|
||||||
Dprintf("SetChannelDevice Channel: %s, LiveView: %s\n", Channel->Name(),
|
Dprintf("SetChannelDevice Channel: %s, LiveView: %s\n", Channel->Name(),
|
||||||
LiveView ? "true" : "false");
|
LiveView ? "true" : "false");
|
||||||
LOCK_THREAD;
|
LOCK_THREAD;
|
||||||
|
|
||||||
m_UpdatePriority = ClientSocket.SupportsPrio();
|
|
||||||
|
|
||||||
if (LiveView)
|
if (LiveView)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (ClientSocket.DataSocket(siLive) != NULL
|
if (Receiving() && IsTunedToTransponder(Channel) && (
|
||||||
&& TRANSPONDER(Channel, m_Channel)
|
Channel->Ca() < CA_ENCRYPTED_MIN ||
|
||||||
&& Channel->Ca() == CA_FTA
|
(Channel->Vpid() && HasPid(Channel->Vpid())) ||
|
||||||
&& m_Channel->Ca() == CA_FTA)
|
(Channel->Apid(0) && HasPid(Channel->Apid(0))))) {
|
||||||
return true;
|
res = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
DetachAllReceivers();
|
DetachAllReceivers();
|
||||||
m_Channel = Channel;
|
m_Channel = Channel;
|
||||||
bool r = ClientSocket.SetChannelDevice(m_Channel);
|
// Old servers delete cStreamdevLiveStreamer in ABRT.
|
||||||
Dprintf("setchanneldevice r=%d\n", r);
|
// Delete it now or it will happen after we tuned to new channel
|
||||||
return r;
|
if (m_ClientSocket->ServerVersion() < 100)
|
||||||
|
CloseDvr();
|
||||||
|
res = m_ClientSocket->SetChannelDevice(m_Channel);
|
||||||
|
}
|
||||||
|
Dprintf("setchanneldevice res=%d\n", res);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cStreamdevDevice::SetPid(cPidHandle *Handle, int Type, bool On) {
|
bool cStreamdevDevice::SetPid(cPidHandle *Handle, int Type, bool On) {
|
||||||
Dprintf("SetPid, Pid=%d, Type=%d, On=%d, used=%d\n", Handle->pid, Type, On,
|
Dprintf("SetPid, Pid=%d, Type=%d, On=%d, used=%d\n", Handle->pid, Type, On, Handle->used);
|
||||||
Handle->used);
|
|
||||||
LOCK_THREAD;
|
LOCK_THREAD;
|
||||||
|
|
||||||
m_UpdatePriority = ClientSocket.SupportsPrio();
|
|
||||||
|
|
||||||
if (On && !m_TSBuffer) {
|
|
||||||
Dprintf("SetPid: no data connection -> OpenDvr()");
|
|
||||||
OpenDvrInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool res = true;
|
bool res = true;
|
||||||
if (Handle->pid && (On || !Handle->used)) {
|
if (Handle->pid && (On || !Handle->used)) {
|
||||||
res = ClientSocket.SetPid(Handle->pid, On);
|
res = m_ClientSocket->SetPid(Handle->pid, On);
|
||||||
|
|
||||||
m_Pids += (!res) ? 0 : On ? 1 : -1;
|
m_Pids += (!res) ? 0 : On ? 1 : -1;
|
||||||
if (m_Pids < 0)
|
if (m_Pids < 0)
|
||||||
m_Pids = 0;
|
m_Pids = 0;
|
||||||
|
|
||||||
if(m_Pids < 1 && m_DvrClosed) {
|
|
||||||
Dprintf("SetPid: 0 pids left -> CloseDvr()");
|
|
||||||
CloseDvrInt();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cStreamdevDevice::OpenDvrInt(void) {
|
|
||||||
Dprintf("OpenDvrInt\n");
|
|
||||||
LOCK_THREAD;
|
|
||||||
|
|
||||||
CloseDvrInt();
|
|
||||||
if (m_TSBuffer) {
|
|
||||||
Dprintf("cStreamdevDevice::OpenDvrInt(): DVR connection already open\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Dprintf("cStreamdevDevice::OpenDvrInt(): Connecting ...\n");
|
|
||||||
if (ClientSocket.CreateDataConnection(siLive)) {
|
|
||||||
m_TSBuffer = new cTSBuffer(*ClientSocket.DataSocket(siLive), MEGABYTE(2), CardIndex() + 1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
esyslog("cStreamdevDevice::OpenDvrInt(): DVR connection FAILED");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cStreamdevDevice::OpenDvr(void) {
|
bool cStreamdevDevice::OpenDvr(void) {
|
||||||
Dprintf("OpenDvr\n");
|
Dprintf("OpenDvr\n");
|
||||||
LOCK_THREAD;
|
LOCK_THREAD;
|
||||||
|
|
||||||
m_DvrClosed = false;
|
CloseDvr();
|
||||||
return OpenDvrInt();
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cStreamdevDevice::CloseDvrInt(void) {
|
|
||||||
Dprintf("CloseDvrInt\n");
|
|
||||||
LOCK_THREAD;
|
|
||||||
|
|
||||||
if (ClientSocket.CheckConnection()) {
|
|
||||||
if (!m_DvrClosed) {
|
|
||||||
Dprintf("cStreamdevDevice::CloseDvrInt(): m_DvrClosed=false -> not closing yet\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (m_Pids > 0) {
|
|
||||||
Dprintf("cStreamdevDevice::CloseDvrInt(): %d active pids -> not closing yet\n", m_Pids);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Dprintf("cStreamdevDevice::CloseDvrInt(): Control connection gone !\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
Dprintf("cStreamdevDevice::CloseDvrInt(): Closing DVR connection\n");
|
|
||||||
// Hack for VDR 1.5.x clients (sometimes sending ABRT after TUNE)
|
|
||||||
// TODO: Find a clean solution to fix this
|
|
||||||
ClientSocket.SetChannelDevice(m_Channel);
|
|
||||||
ClientSocket.CloseDvr();
|
|
||||||
DELETENULL(m_TSBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cStreamdevDevice::CloseDvr(void) {
|
void cStreamdevDevice::CloseDvr(void) {
|
||||||
Dprintf("CloseDvr\n");
|
Dprintf("CloseDvr\n");
|
||||||
LOCK_THREAD;
|
LOCK_THREAD;
|
||||||
|
|
||||||
m_DvrClosed = true;
|
m_ClientSocket->CloseDvr();
|
||||||
CloseDvrInt();
|
DELETENULL(m_TSBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cStreamdevDevice::GetTSPacket(uchar *&Data) {
|
bool cStreamdevDevice::GetTSPacket(uchar *&Data) {
|
||||||
if (m_TSBuffer && m_Device) {
|
if (m_TSBuffer) {
|
||||||
Data = m_TSBuffer->Get();
|
Data = m_TSBuffer->Get();
|
||||||
#if 1 // TODO: this should be fixed in vdr cTSBuffer
|
#if 1 // TODO: this should be fixed in vdr cTSBuffer
|
||||||
// simple disconnect detection
|
// simple disconnect detection
|
||||||
static int m_TSFails = 0;
|
static int m_TSFails = 0;
|
||||||
if (!Data) {
|
if (!Data) {
|
||||||
LOCK_THREAD;
|
LOCK_THREAD;
|
||||||
if(!ClientSocket.DataSocket(siLive)) {
|
if(!m_ClientSocket->DataSocket(siLive)) {
|
||||||
return false; // triggers CloseDvr() + OpenDvr() in cDevice
|
return false; // triggers CloseDvr() + OpenDvr() in cDevice
|
||||||
}
|
}
|
||||||
cPoller Poller(*ClientSocket.DataSocket(siLive));
|
cPoller Poller(*m_ClientSocket->DataSocket(siLive));
|
||||||
errno = 0;
|
errno = 0;
|
||||||
if (Poller.Poll() && !errno) {
|
if (Poller.Poll() && !errno) {
|
||||||
char tmp[1];
|
char tmp[1];
|
||||||
if (recv(*ClientSocket.DataSocket(siLive), tmp, 1, MSG_PEEK) == 0 && !errno) {
|
if (recv(*m_ClientSocket->DataSocket(siLive), tmp, 1, MSG_PEEK) == 0 && !errno) {
|
||||||
esyslog("cStreamDevice::GetTSPacket: GetChecked: NOTHING (%d)", m_TSFails);
|
esyslog("cStreamDevice::GetTSPacket: GetChecked: NOTHING (%d)", m_TSFails);
|
||||||
m_TSFails++;
|
m_TSFails++;
|
||||||
if (m_TSFails > 10) {
|
if (m_TSFails > 10) {
|
||||||
isyslog("cStreamdevDevice::GetTSPacket(): disconnected");
|
isyslog("cStreamdevDevice::GetTSPacket(): disconnected");
|
||||||
m_Pids = 0;
|
m_Pids = 0;
|
||||||
CloseDvrInt();
|
CloseDvr();
|
||||||
m_TSFails = 0;
|
m_TSFails = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -275,67 +267,89 @@ int cStreamdevDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask) {
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
|
||||||
if (!ClientSocket.DataSocket(siLiveFilter)) {
|
if (!m_ClientSocket->DataSocket(siLiveFilter)) {
|
||||||
if (ClientSocket.CreateDataConnection(siLiveFilter)) {
|
if (m_ClientSocket->CreateDataConnection(siLiveFilter)) {
|
||||||
m_Filters->SetConnection(*ClientSocket.DataSocket(siLiveFilter));
|
m_Filters->SetConnection(*m_ClientSocket->DataSocket(siLiveFilter));
|
||||||
} else {
|
} else {
|
||||||
isyslog("cStreamdevDevice::OpenFilter: connect failed: %m");
|
isyslog("cStreamdevDevice::OpenFilter: connect failed: %m");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ClientSocket.SetFilter(Pid, Tid, Mask, true))
|
if (m_ClientSocket->SetFilter(Pid, Tid, Mask, true))
|
||||||
return m_Filters->OpenFilter(Pid, Tid, Mask);
|
return m_Filters->OpenFilter(Pid, Tid, Mask);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cStreamdevDevice::Init(void) {
|
void cStreamdevDevice::CloseFilter(int Handle) {
|
||||||
if (m_Device == NULL && StreamdevClientSetup.StartClient)
|
|
||||||
new cStreamdevDevice;
|
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);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cStreamdevDevice::ReInit(void) {
|
void cStreamdevDevice::UpdatePriority(bool SwitchingChannels) const {
|
||||||
if(m_Device) {
|
if (!m_Disabled) {
|
||||||
m_Device->Lock();
|
//LOCK_THREAD;
|
||||||
m_Device->m_Filters->SetConnection(-1);
|
const_cast<cStreamdevDevice*>(this)->Lock();
|
||||||
m_Device->m_Pids = 0;
|
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;
|
||||||
}
|
}
|
||||||
ClientSocket.Quit();
|
m_ClientSocket->SetPriority(Priority);
|
||||||
ClientSocket.Reset();
|
}
|
||||||
if (m_Device != NULL) {
|
const_cast<cStreamdevDevice*>(this)->Unlock();
|
||||||
//DELETENULL(m_Device->m_TSBuffer);
|
|
||||||
m_Device->Unlock();
|
|
||||||
}
|
}
|
||||||
return StreamdevClientSetup.StartClient ? Init() : true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cStreamdevDevice::UpdatePriority(void) {
|
cString cStreamdevDevice::DeviceName(void) const {
|
||||||
if (m_Device) {
|
return StreamdevClientSetup.RemoteIp;
|
||||||
m_Device->Lock();
|
|
||||||
if (m_Device->m_UpdatePriority && ClientSocket.DataSocket(siLive)) {
|
|
||||||
int Priority = m_Device->Priority();
|
|
||||||
if (m_Device == cDevice::ActualDevice() && Priority < Setup.PrimaryLimit)
|
|
||||||
Priority = Setup.PrimaryLimit;
|
|
||||||
if (m_Device->m_Priority != Priority && ClientSocket.SetPriority(Priority))
|
|
||||||
m_Device->m_Priority = Priority;
|
|
||||||
}
|
}
|
||||||
m_Device->Unlock();
|
|
||||||
|
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 cStreamdevDevice::SignalStrength(void) const {
|
||||||
int strength = -1;
|
int strength = -1;
|
||||||
if (ClientSocket.DataSocket(siLive) != NULL)
|
if (m_ClientSocket->DataSocket(siLive) != NULL)
|
||||||
ClientSocket.GetSignal(&strength, NULL);
|
m_ClientSocket->GetSignal(&strength, NULL, NULL);
|
||||||
return strength;
|
return strength;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cStreamdevDevice::SignalQuality(void) const {
|
int cStreamdevDevice::SignalQuality(void) const {
|
||||||
int quality = -1;
|
int quality = -1;
|
||||||
if (ClientSocket.DataSocket(siLive) != NULL)
|
if (m_ClientSocket->DataSocket(siLive) != NULL)
|
||||||
ClientSocket.GetSignal(NULL, &quality);
|
m_ClientSocket->GetSignal(NULL, &quality, NULL);
|
||||||
return quality;
|
return quality;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,22 +17,22 @@ class cTBString;
|
|||||||
class cStreamdevDevice: public cDevice {
|
class cStreamdevDevice: public cDevice {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool m_Disabled;
|
||||||
|
cClientSocket *m_ClientSocket;
|
||||||
const cChannel *m_Channel;
|
const cChannel *m_Channel;
|
||||||
cTSBuffer *m_TSBuffer;
|
cTSBuffer *m_TSBuffer;
|
||||||
cStreamdevFilters *m_Filters;
|
cStreamdevFilters *m_Filters;
|
||||||
int m_Pids;
|
int m_Pids;
|
||||||
int m_Priority;
|
|
||||||
bool m_UpdatePriority;
|
|
||||||
bool m_DvrClosed;
|
|
||||||
|
|
||||||
static cStreamdevDevice *m_Device;
|
static const cChannel *m_DenyChannel;
|
||||||
|
|
||||||
bool OpenDvrInt(void);
|
|
||||||
void CloseDvrInt(void);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
|
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
|
||||||
|
#if APIVERSNUM >= 10738
|
||||||
|
virtual bool HasLock(int TimeoutMs) const
|
||||||
|
#else
|
||||||
virtual bool HasLock(int TimeoutMs)
|
virtual bool HasLock(int TimeoutMs)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
//printf("HasLock is %d\n", (ClientSocket.DataSocket(siLive) != NULL));
|
//printf("HasLock is %d\n", (ClientSocket.DataSocket(siLive) != NULL));
|
||||||
//return ClientSocket.DataSocket(siLive) != NULL;
|
//return ClientSocket.DataSocket(siLive) != NULL;
|
||||||
@@ -45,6 +45,7 @@ protected:
|
|||||||
virtual bool GetTSPacket(uchar *&Data);
|
virtual bool GetTSPacket(uchar *&Data);
|
||||||
|
|
||||||
virtual int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
|
virtual int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
|
||||||
|
virtual void CloseFilter(int Handle);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cStreamdevDevice(void);
|
cStreamdevDevice(void);
|
||||||
@@ -61,15 +62,21 @@ public:
|
|||||||
#if APIVERSNUM >= 10719
|
#if APIVERSNUM >= 10719
|
||||||
virtual bool AvoidRecording(void) const { return true; }
|
virtual bool AvoidRecording(void) const { return true; }
|
||||||
#endif
|
#endif
|
||||||
|
#if APIVERSNUM >= 10722
|
||||||
|
virtual bool IsTunedToTransponder(const cChannel *Channel) const;
|
||||||
|
#else
|
||||||
virtual bool IsTunedToTransponder(const cChannel *Channel);
|
virtual bool IsTunedToTransponder(const cChannel *Channel);
|
||||||
|
#endif
|
||||||
|
virtual const cChannel *GetCurrentlyTunedTransponder(void) const;
|
||||||
|
virtual cString DeviceName(void) const;
|
||||||
|
virtual cString DeviceType(void) const;
|
||||||
virtual int SignalStrength(void) const;
|
virtual int SignalStrength(void) const;
|
||||||
virtual int SignalQuality(void) const;
|
virtual int SignalQuality(void) const;
|
||||||
|
|
||||||
static void UpdatePriority(void);
|
bool ReInit(bool Disable);
|
||||||
static bool Init(void);
|
void UpdatePriority(bool SwitchingChannels = false) const;
|
||||||
static bool ReInit(void);
|
bool SuspendServer() { return m_ClientSocket->SuspendServer(); }
|
||||||
|
static void DenyChannel(const cChannel *Channel) { m_DenyChannel = Channel; }
|
||||||
static cStreamdevDevice *GetDevice(void) { return m_Device; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VDR_STREAMDEV_DEVICE_H
|
#endif // VDR_STREAMDEV_DEVICE_H
|
||||||
|
|||||||
167
client/filter.c
167
client/filter.c
@@ -6,20 +6,28 @@
|
|||||||
#include "client/socket.h"
|
#include "client/socket.h"
|
||||||
#include "tools/select.h"
|
#include "tools/select.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include <vdr/device.h>
|
#include <vdr/device.h>
|
||||||
|
|
||||||
#define PID_MASK_HI 0x1F
|
#define PID_MASK_HI 0x1F
|
||||||
// --- cStreamdevFilter ------------------------------------------------------
|
// --- cStreamdevFilter ------------------------------------------------------
|
||||||
|
|
||||||
|
static int FilterSockBufSize_warn = 0;
|
||||||
|
|
||||||
class cStreamdevFilter: public cListObject {
|
class cStreamdevFilter: public cListObject {
|
||||||
private:
|
private:
|
||||||
uchar m_Buffer[4096];
|
uchar m_Buffer[8192];
|
||||||
int m_Used;
|
int m_Used;
|
||||||
int m_Pipe[2];
|
int m_Pipe[2];
|
||||||
u_short m_Pid;
|
u_short m_Pid;
|
||||||
u_char m_Tid;
|
u_char m_Tid;
|
||||||
u_char m_Mask;
|
u_char m_Mask;
|
||||||
|
#ifdef TIOCOUTQ
|
||||||
|
unsigned long m_maxq;
|
||||||
|
unsigned long m_flushed;
|
||||||
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask);
|
cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask);
|
||||||
@@ -29,7 +37,6 @@ public:
|
|||||||
bool PutSection(const uchar *Data, int Length, bool Pusi);
|
bool PutSection(const uchar *Data, int Length, bool Pusi);
|
||||||
int ReadPipe(void) const { return m_Pipe[0]; }
|
int ReadPipe(void) const { return m_Pipe[0]; }
|
||||||
|
|
||||||
bool IsClosed(void);
|
|
||||||
void Reset(void);
|
void Reset(void);
|
||||||
|
|
||||||
u_short Pid(void) const { return m_Pid; }
|
u_short Pid(void) const { return m_Pid; }
|
||||||
@@ -47,6 +54,10 @@ cStreamdevFilter::cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask) {
|
|||||||
m_Tid = Tid;
|
m_Tid = Tid;
|
||||||
m_Mask = Mask;
|
m_Mask = Mask;
|
||||||
m_Pipe[0] = m_Pipe[1] = -1;
|
m_Pipe[0] = m_Pipe[1] = -1;
|
||||||
|
#ifdef TIOCOUTQ
|
||||||
|
m_flushed = 0;
|
||||||
|
m_maxq = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef SOCK_SEQPACKET
|
#ifdef SOCK_SEQPACKET
|
||||||
// SOCK_SEQPACKET (since kernel 2.6.4)
|
// SOCK_SEQPACKET (since kernel 2.6.4)
|
||||||
@@ -58,7 +69,46 @@ cStreamdevFilter::cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask) {
|
|||||||
esyslog("streamdev-client: couldn't open section filter socket: %m");
|
esyslog("streamdev-client: couldn't open section filter socket: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(fcntl(m_Pipe[0], F_SETFL, O_NONBLOCK) != 0 ||
|
// 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 ||
|
||||||
fcntl(m_Pipe[1], F_SETFL, O_NONBLOCK) != 0) {
|
fcntl(m_Pipe[1], F_SETFL, O_NONBLOCK) != 0) {
|
||||||
esyslog("streamdev-client: couldn't set section filter socket to non-blocking mode: %m");
|
esyslog("streamdev-client: couldn't set section filter socket to non-blocking mode: %m");
|
||||||
}
|
}
|
||||||
@@ -67,12 +117,13 @@ cStreamdevFilter::cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask) {
|
|||||||
cStreamdevFilter::~cStreamdevFilter() {
|
cStreamdevFilter::~cStreamdevFilter() {
|
||||||
Dprintf("~cStreamdevFilter %p\n", this);
|
Dprintf("~cStreamdevFilter %p\n", this);
|
||||||
|
|
||||||
// ownership of handle m_Pipe[0] has been transferred to VDR section handler
|
if (m_Pipe[0] >= 0) {
|
||||||
//if (m_Pipe[0] >= 0)
|
close(m_Pipe[0]);
|
||||||
// close(m_Pipe[0]);
|
}
|
||||||
if (m_Pipe[1] >= 0)
|
if (m_Pipe[1] >= 0) {
|
||||||
close(m_Pipe[1]);
|
close(m_Pipe[1]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool cStreamdevFilter::PutSection(const uchar *Data, int Length, bool Pusi) {
|
bool cStreamdevFilter::PutSection(const uchar *Data, int Length, bool Pusi) {
|
||||||
|
|
||||||
@@ -94,13 +145,42 @@ bool cStreamdevFilter::PutSection(const uchar *Data, int Length, bool Pusi) {
|
|||||||
int length = (((m_Buffer[1] & 0x0F) << 8) | m_Buffer[2]) + 3;
|
int length = (((m_Buffer[1] & 0x0F) << 8) | m_Buffer[2]) + 3;
|
||||||
if (m_Used == length) {
|
if (m_Used == length) {
|
||||||
m_Used = 0;
|
m_Used = 0;
|
||||||
if (write(m_Pipe[1], m_Buffer, length) < 0) {
|
#ifdef TIOCOUTQ
|
||||||
if(errno == EAGAIN || errno == EWOULDBLOCK)
|
// If we can determine the queue size of the socket,
|
||||||
dsyslog("cStreamdevFilter::PutSection socket overflow, "
|
// we flush rather then let the socket drop random packets.
|
||||||
"Pid %4d Tid %3d", m_Pid, m_Tid);
|
// 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;
|
||||||
|
|
||||||
else
|
} else {
|
||||||
|
m_flushed += length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!m_flushed)
|
||||||
|
#endif
|
||||||
|
if(write(m_Pipe[1], m_Buffer, length) < 0) {
|
||||||
|
if(errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||||
|
dsyslog("cStreamdevFilter::PutSection(Pid:%d Tid: %d): error: %s",
|
||||||
|
m_Pid, m_Tid, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
|
} else {
|
||||||
|
#ifdef TIOCOUTQ
|
||||||
|
m_flushed += length;
|
||||||
|
#else
|
||||||
|
dsyslog("cStreamdevFilter::PutSection(Pid:%d Tid: %d): "
|
||||||
|
"Dropping packet %ld bytes (queue overflow)",
|
||||||
|
m_Pid, m_Tid, length);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,29 +203,11 @@ void cStreamdevFilter::Reset(void) {
|
|||||||
m_Used = 0;
|
m_Used = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cStreamdevFilter::IsClosed(void) {
|
|
||||||
char m_Buffer[3] = {0,0,0}; /* tid 0, 0 bytes */
|
|
||||||
|
|
||||||
// Test if pipe/socket has been closed by writing empty section
|
|
||||||
if (write(m_Pipe[1], m_Buffer, 3) < 0 &&
|
|
||||||
errno != EAGAIN &&
|
|
||||||
errno != EWOULDBLOCK) {
|
|
||||||
|
|
||||||
if (errno != ECONNREFUSED &&
|
|
||||||
errno != ECONNRESET &&
|
|
||||||
errno != EPIPE)
|
|
||||||
esyslog("cStreamdevFilter::IsClosed failed: %m");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- cStreamdevFilters -----------------------------------------------------
|
// --- cStreamdevFilters -----------------------------------------------------
|
||||||
|
|
||||||
cStreamdevFilters::cStreamdevFilters(void):
|
cStreamdevFilters::cStreamdevFilters(cClientSocket *ClientSocket):
|
||||||
cThread("streamdev-client: sections assembler") {
|
cThread("streamdev-client: sections assembler") {
|
||||||
|
m_ClientSocket = ClientSocket;
|
||||||
m_TSBuffer = NULL;
|
m_TSBuffer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,43 +216,27 @@ cStreamdevFilters::~cStreamdevFilters() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int cStreamdevFilters::OpenFilter(u_short Pid, u_char Tid, u_char Mask) {
|
int cStreamdevFilters::OpenFilter(u_short Pid, u_char Tid, u_char Mask) {
|
||||||
CarbageCollect();
|
|
||||||
|
|
||||||
cStreamdevFilter *f = new cStreamdevFilter(Pid, Tid, Mask);
|
cStreamdevFilter *f = new cStreamdevFilter(Pid, Tid, Mask);
|
||||||
int fh = f->ReadPipe();
|
int fh = f->ReadPipe();
|
||||||
|
|
||||||
Lock();
|
LOCK_THREAD;
|
||||||
Add(f);
|
Add(f);
|
||||||
Unlock();
|
|
||||||
|
|
||||||
return fh;
|
return fh;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cStreamdevFilters::CarbageCollect(void) {
|
void cStreamdevFilters::CloseFilter(int Handle) {
|
||||||
LOCK_THREAD;
|
LOCK_THREAD;
|
||||||
for (cStreamdevFilter *fi = First(); fi;) {
|
|
||||||
if (fi->IsClosed()) {
|
|
||||||
if (errno == ECONNREFUSED ||
|
|
||||||
errno == ECONNRESET ||
|
|
||||||
errno == EPIPE) {
|
|
||||||
ClientSocket.SetFilter(fi->Pid(), fi->Tid(), fi->Mask(), false);
|
|
||||||
Dprintf("cStreamdevFilters::CarbageCollector: filter closed: Pid %4d, Tid %3d, Mask %2x (%d filters left)",
|
|
||||||
(int)fi->Pid(), (int)fi->Tid(), fi->Mask(), Count()-1);
|
|
||||||
|
|
||||||
cStreamdevFilter *next = Prev(fi);
|
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);
|
Del(fi);
|
||||||
fi = next ? Next(next) : First();
|
return;
|
||||||
} else {
|
|
||||||
esyslog("cStreamdevFilters::CarbageCollector() error: "
|
|
||||||
"Pid %4d, Tid %3d, Mask %2x (%d filters left) failed",
|
|
||||||
(int)fi->Pid(), (int)fi->Tid(), fi->Mask(), Count()-1);
|
|
||||||
LOG_ERROR;
|
|
||||||
fi = Next(fi);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fi = Next(fi);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
esyslog("cStreamdevFilters::CloseFilter(%d): failed (%d filters left)\n", Handle, Count()-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cStreamdevFilters::ReActivateFilters(void)
|
bool cStreamdevFilters::ReActivateFilters(void)
|
||||||
@@ -198,9 +244,8 @@ bool cStreamdevFilters::ReActivateFilters(void)
|
|||||||
LOCK_THREAD;
|
LOCK_THREAD;
|
||||||
|
|
||||||
bool res = true;
|
bool res = true;
|
||||||
CarbageCollect();
|
|
||||||
for (cStreamdevFilter *fi = First(); fi; fi = Next(fi)) {
|
for (cStreamdevFilter *fi = First(); fi; fi = Next(fi)) {
|
||||||
res = ClientSocket.SetFilter(fi->Pid(), fi->Tid(), fi->Mask(), true) && res;
|
res = m_ClientSocket->SetFilter(fi->Pid(), fi->Tid(), fi->Mask(), true) && res;
|
||||||
Dprintf("ReActivateFilters(%d, %d, %d) -> %s", fi->Pid(), fi->Tid(), fi->Mask(), res ? "Ok" :"FAIL");
|
Dprintf("ReActivateFilters(%d, %d, %d) -> %s", fi->Pid(), fi->Tid(), fi->Mask(), res ? "Ok" :"FAIL");
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
@@ -251,7 +296,7 @@ void cStreamdevFilters::Action(void) {
|
|||||||
Dprintf("FATAL ERROR: %m\n");
|
Dprintf("FATAL ERROR: %m\n");
|
||||||
esyslog("streamdev-client: couldn't send section packet: %m");
|
esyslog("streamdev-client: couldn't send section packet: %m");
|
||||||
}
|
}
|
||||||
ClientSocket.SetFilter(f->Pid(), f->Tid(), f->Mask(), false);
|
m_ClientSocket->SetFilter(f->Pid(), f->Tid(), f->Mask(), false);
|
||||||
Del(f);
|
Del(f);
|
||||||
// Filter was closed.
|
// Filter was closed.
|
||||||
// - need to check remaining filters for another match
|
// - need to check remaining filters for another match
|
||||||
@@ -261,7 +306,7 @@ void cStreamdevFilters::Action(void) {
|
|||||||
} else {
|
} else {
|
||||||
#if 1 // TODO: this should be fixed in vdr cTSBuffer
|
#if 1 // TODO: this should be fixed in vdr cTSBuffer
|
||||||
// Check disconnection
|
// Check disconnection
|
||||||
int fd = *ClientSocket.DataSocket(siLiveFilter);
|
int fd = *m_ClientSocket->DataSocket(siLiveFilter);
|
||||||
if(fd < 0)
|
if(fd < 0)
|
||||||
break;
|
break;
|
||||||
cPoller Poller(fd);
|
cPoller Poller(fd);
|
||||||
@@ -273,7 +318,7 @@ void cStreamdevFilters::Action(void) {
|
|||||||
++fails;
|
++fails;
|
||||||
if (fails >= 10) {
|
if (fails >= 10) {
|
||||||
esyslog("cStreamdevFilters::Action(): stream disconnected ?");
|
esyslog("cStreamdevFilters::Action(): stream disconnected ?");
|
||||||
ClientSocket.CloseDataConnection(siLiveFilter);
|
m_ClientSocket->CloseDataConnection(siLiveFilter);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -11,23 +11,24 @@
|
|||||||
|
|
||||||
class cTSBuffer;
|
class cTSBuffer;
|
||||||
class cStreamdevFilter;
|
class cStreamdevFilter;
|
||||||
|
class cClientSocket;
|
||||||
|
|
||||||
class cStreamdevFilters: public cList<cStreamdevFilter>, public cThread {
|
class cStreamdevFilters: public cList<cStreamdevFilter>, public cThread {
|
||||||
private:
|
private:
|
||||||
|
cClientSocket *m_ClientSocket;
|
||||||
cTSBuffer *m_TSBuffer;
|
cTSBuffer *m_TSBuffer;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void Action(void);
|
virtual void Action(void);
|
||||||
void CarbageCollect(void);
|
|
||||||
|
|
||||||
bool ReActivateFilters(void);
|
bool ReActivateFilters(void);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cStreamdevFilters(void);
|
cStreamdevFilters(cClientSocket *ClientSocket);
|
||||||
virtual ~cStreamdevFilters();
|
virtual ~cStreamdevFilters();
|
||||||
|
|
||||||
void SetConnection(int Handle);
|
void SetConnection(int Handle);
|
||||||
int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
|
int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
|
||||||
|
void CloseFilter(int Handle);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VDR_STREAMDEV_FILTER_H
|
#endif // VDR_STREAMDEV_FILTER_H
|
||||||
|
|||||||
@@ -6,15 +6,49 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: streamdev 0.5.0\n"
|
"Project-Id-Version: streamdev 0.5.0\n"
|
||||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||||
"POT-Creation-Date: 2011-02-16 08:49+0100\n"
|
"POT-Creation-Date: 2014-10-20 22:57+0200\n"
|
||||||
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
||||||
"Last-Translator: Frank Schmirler <vdrdev@schmirler.de>\n"
|
"Last-Translator: Frank Schmirler <vdrdev@schmirler.de>\n"
|
||||||
"Language-Team: <vdr@linuxtv.org>\n"
|
"Language-Team: German <vdr@linuxtv.org>\n"
|
||||||
|
"Language: de\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
msgid "Hide Mainmenu Entry"
|
||||||
|
msgstr "Hauptmenüeintrag verstecken"
|
||||||
|
|
||||||
|
msgid "Simultaneously used Devices"
|
||||||
|
msgstr "Gleichzeitig genutzte DVB-Karten"
|
||||||
|
|
||||||
|
msgid "Remote IP"
|
||||||
|
msgstr "IP der Gegenseite"
|
||||||
|
|
||||||
|
msgid "Remote Port"
|
||||||
|
msgstr "Port der Gegenseite"
|
||||||
|
|
||||||
|
msgid "Timeout (s)"
|
||||||
|
msgstr "Timeout (s)"
|
||||||
|
|
||||||
|
msgid "Filter Streaming"
|
||||||
|
msgstr "Filter-Daten streamen"
|
||||||
|
|
||||||
|
msgid "Filter SockBufSize"
|
||||||
|
msgstr "Filter Socket Puffergröße"
|
||||||
|
|
||||||
|
msgid "Live TV Priority"
|
||||||
|
msgstr "Live TV Priorität"
|
||||||
|
|
||||||
|
msgid "Minimum Priority"
|
||||||
|
msgstr "Minimale Priorität"
|
||||||
|
|
||||||
|
msgid "Maximum Priority"
|
||||||
|
msgstr "Maximale Priorität"
|
||||||
|
|
||||||
|
msgid "Broadcast Systems / Cost"
|
||||||
|
msgstr "Empfangssysteme / Kosten"
|
||||||
|
|
||||||
msgid "VTP Streaming Client"
|
msgid "VTP Streaming Client"
|
||||||
msgstr "VTP Streaming Client"
|
msgstr "VTP Streaming Client"
|
||||||
|
|
||||||
@@ -26,27 +60,3 @@ msgstr "Server ist pausiert"
|
|||||||
|
|
||||||
msgid "Couldn't suspend Server!"
|
msgid "Couldn't suspend Server!"
|
||||||
msgstr "Konnte Server nicht pausieren!"
|
msgstr "Konnte Server nicht pausieren!"
|
||||||
|
|
||||||
msgid "Hide Mainmenu Entry"
|
|
||||||
msgstr "Hauptmenüeintrag verstecken"
|
|
||||||
|
|
||||||
msgid "Start Client"
|
|
||||||
msgstr "Client starten"
|
|
||||||
|
|
||||||
msgid "Remote IP"
|
|
||||||
msgstr "IP der Gegenseite"
|
|
||||||
|
|
||||||
msgid "Remote Port"
|
|
||||||
msgstr "Port der Gegenseite"
|
|
||||||
|
|
||||||
msgid "Filter Streaming"
|
|
||||||
msgstr "Filter-Daten streamen"
|
|
||||||
|
|
||||||
msgid "Minimum Priority"
|
|
||||||
msgstr "Minimale Priorität"
|
|
||||||
|
|
||||||
msgid "Maximum Priority"
|
|
||||||
msgstr "Maximale Priorität"
|
|
||||||
|
|
||||||
msgid "Broadcast Systems / Cost"
|
|
||||||
msgstr "Empfangssysteme / Kosten"
|
|
||||||
|
|||||||
@@ -6,15 +6,49 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: streamdev 0.5.0\n"
|
"Project-Id-Version: streamdev 0.5.0\n"
|
||||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
"POT-Creation-Date: 2015-01-16 22:32+0100\n"
|
||||||
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
|
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
|
||||||
"Last-Translator: Javier Bradineras <jbradi@hotmail.com>\n"
|
"Last-Translator: Javier Bradineras <jbradi@hotmail.com>\n"
|
||||||
"Language-Team: <vdr@linuxtv.org>\n"
|
"Language-Team: Spanish <vdr@linuxtv.org>\n"
|
||||||
|
"Language: es\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
msgid "Hide Mainmenu Entry"
|
||||||
|
msgstr "Ocultar entrada en menú principal"
|
||||||
|
|
||||||
|
msgid "Simultaneously used Devices"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Remote IP"
|
||||||
|
msgstr "Indicar IP del Servidor"
|
||||||
|
|
||||||
|
msgid "Remote Port"
|
||||||
|
msgstr "Indicar puerto remoto del Servidor"
|
||||||
|
|
||||||
|
msgid "Timeout (s)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Filter Streaming"
|
||||||
|
msgstr "Filtrar transmisión"
|
||||||
|
|
||||||
|
msgid "Filter SockBufSize"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Live TV Priority"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Minimum Priority"
|
||||||
|
msgstr "Prioridad mínima"
|
||||||
|
|
||||||
|
msgid "Maximum Priority"
|
||||||
|
msgstr "Prioridad máxima"
|
||||||
|
|
||||||
|
msgid "Broadcast Systems / Cost"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "VTP Streaming Client"
|
msgid "VTP Streaming Client"
|
||||||
msgstr "Cliente trasmisión VTP"
|
msgstr "Cliente trasmisión VTP"
|
||||||
|
|
||||||
@@ -26,26 +60,3 @@ msgstr "Servidor en suspensi
|
|||||||
|
|
||||||
msgid "Couldn't suspend Server!"
|
msgid "Couldn't suspend Server!"
|
||||||
msgstr "Imposible suspender el servidor!"
|
msgstr "Imposible suspender el servidor!"
|
||||||
|
|
||||||
msgid "Hide Mainmenu Entry"
|
|
||||||
msgstr "Ocultar entrada en menú principal"
|
|
||||||
|
|
||||||
msgid "Start Client"
|
|
||||||
msgstr "Iniciar Cliente"
|
|
||||||
|
|
||||||
msgid "Remote IP"
|
|
||||||
msgstr "Indicar IP del Servidor"
|
|
||||||
|
|
||||||
msgid "Remote Port"
|
|
||||||
msgstr "Indicar puerto remoto del Servidor"
|
|
||||||
|
|
||||||
msgid "Filter Streaming"
|
|
||||||
msgstr "Filtrar transmisión"
|
|
||||||
|
|
||||||
msgid "Minimum Priority"
|
|
||||||
msgstr "Prioridad mínima"
|
|
||||||
|
|
||||||
msgid "Maximum Priority"
|
|
||||||
msgstr "Prioridad máxima"
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,50 +1,62 @@
|
|||||||
# VDR streamdev plugin language source file.
|
# VDR streamdev plugin language source file.
|
||||||
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
||||||
# This file is distributed under the same license as the VDR streamdev package.
|
# This file is distributed under the same license as the VDR streamdev package.
|
||||||
# Rolf Ahrenberg <rahrenbe@cc.hut.fi>, 2008
|
# Rolf Ahrenberg, 2008-
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: streamdev 0.5.0\n"
|
"Project-Id-Version: streamdev 0.5.0\n"
|
||||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
"POT-Creation-Date: 2015-01-16 22:32+0100\n"
|
||||||
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
||||||
"Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"
|
"Last-Translator: Rolf Ahrenberg\n"
|
||||||
"Language-Team: <vdr@linuxtv.org>\n"
|
"Language-Team: Finnish <vdr@linuxtv.org>\n"
|
||||||
|
"Language: fi\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
msgid "VTP Streaming Client"
|
|
||||||
msgstr "VTP-suoratoistoasiakas"
|
|
||||||
|
|
||||||
msgid "Suspend Server"
|
|
||||||
msgstr "Pysäytä palvelin"
|
|
||||||
|
|
||||||
msgid "Server is suspended"
|
|
||||||
msgstr "Palvelin on pysäytetty"
|
|
||||||
|
|
||||||
msgid "Couldn't suspend Server!"
|
|
||||||
msgstr "Palvelinta ei onnistuttu pysäyttämään!"
|
|
||||||
|
|
||||||
msgid "Hide Mainmenu Entry"
|
msgid "Hide Mainmenu Entry"
|
||||||
msgstr "Piilota valinta päävalikosta"
|
msgstr "Piilota valinta päävalikosta"
|
||||||
|
|
||||||
msgid "Start Client"
|
msgid "Simultaneously used Devices"
|
||||||
msgstr "Käynnistä VDR-asiakas"
|
msgstr "Yhtäaikaiset laitteet"
|
||||||
|
|
||||||
msgid "Remote IP"
|
msgid "Remote IP"
|
||||||
msgstr "Etäkoneen IP-osoite"
|
msgstr "Etäkoneen IP-osoite"
|
||||||
|
|
||||||
msgid "Remote Port"
|
msgid "Remote Port"
|
||||||
msgstr "Etäkoneen portti"
|
msgstr "Etäkoneen portti"
|
||||||
|
|
||||||
|
msgid "Timeout (s)"
|
||||||
|
msgstr "Yhteyden aikakatkaisu (s)"
|
||||||
|
|
||||||
msgid "Filter Streaming"
|
msgid "Filter Streaming"
|
||||||
msgstr "Suodatetun tiedon suoratoisto"
|
msgstr "Suodatetun tiedon suoratoisto"
|
||||||
|
|
||||||
|
msgid "Filter SockBufSize"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Live TV Priority"
|
||||||
|
msgstr "Live-katselun prioriteetti"
|
||||||
|
|
||||||
msgid "Minimum Priority"
|
msgid "Minimum Priority"
|
||||||
msgstr "Pienin prioriteetti"
|
msgstr "Pienin prioriteetti"
|
||||||
|
|
||||||
msgid "Maximum Priority"
|
msgid "Maximum Priority"
|
||||||
msgstr "Suurin prioriteetti"
|
msgstr "Suurin prioriteetti"
|
||||||
|
|
||||||
|
msgid "Broadcast Systems / Cost"
|
||||||
|
msgstr "Lähetysjärjestelmien suhdeluku"
|
||||||
|
|
||||||
|
msgid "VTP Streaming Client"
|
||||||
|
msgstr "VTP-suoratoistoasiakas"
|
||||||
|
|
||||||
|
msgid "Suspend Server"
|
||||||
|
msgstr "Pysäytä palvelin"
|
||||||
|
|
||||||
|
msgid "Server is suspended"
|
||||||
|
msgstr "Palvelin on pysäytetty"
|
||||||
|
|
||||||
|
msgid "Couldn't suspend Server!"
|
||||||
|
msgstr "Palvelinta ei onnistuttu pysäyttämään!"
|
||||||
|
|||||||
@@ -6,15 +6,49 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: streamdev 0.5.0\n"
|
"Project-Id-Version: streamdev 0.5.0\n"
|
||||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
"POT-Creation-Date: 2015-01-16 22:32+0100\n"
|
||||||
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
||||||
"Last-Translator: micky979 <micky979@free.fr>\n"
|
"Last-Translator: micky979 <micky979@free.fr>\n"
|
||||||
"Language-Team: <vdr@linuxtv.org>\n"
|
"Language-Team: French <vdr@linuxtv.org>\n"
|
||||||
|
"Language: fr\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
msgid "Hide Mainmenu Entry"
|
||||||
|
msgstr "Masquer dans le menu principal"
|
||||||
|
|
||||||
|
msgid "Simultaneously used Devices"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Remote IP"
|
||||||
|
msgstr "Adresse IP du serveur"
|
||||||
|
|
||||||
|
msgid "Remote Port"
|
||||||
|
msgstr "Port du serveur"
|
||||||
|
|
||||||
|
msgid "Timeout (s)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Filter Streaming"
|
||||||
|
msgstr "Filtre streaming"
|
||||||
|
|
||||||
|
msgid "Filter SockBufSize"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Live TV Priority"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Minimum Priority"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Maximum Priority"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Broadcast Systems / Cost"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "VTP Streaming Client"
|
msgid "VTP Streaming Client"
|
||||||
msgstr "Client de streaming VTP"
|
msgstr "Client de streaming VTP"
|
||||||
|
|
||||||
@@ -26,25 +60,3 @@ msgstr "Le serveur est suspendu"
|
|||||||
|
|
||||||
msgid "Couldn't suspend Server!"
|
msgid "Couldn't suspend Server!"
|
||||||
msgstr "Impossible de suspendre le serveur!"
|
msgstr "Impossible de suspendre le serveur!"
|
||||||
|
|
||||||
msgid "Hide Mainmenu Entry"
|
|
||||||
msgstr "Masquer dans le menu principal"
|
|
||||||
|
|
||||||
msgid "Start Client"
|
|
||||||
msgstr "Démarrage du client"
|
|
||||||
|
|
||||||
msgid "Remote IP"
|
|
||||||
msgstr "Adresse IP du serveur"
|
|
||||||
|
|
||||||
msgid "Remote Port"
|
|
||||||
msgstr "Port du serveur"
|
|
||||||
|
|
||||||
msgid "Filter Streaming"
|
|
||||||
msgstr "Filtre streaming"
|
|
||||||
|
|
||||||
msgid "Minimum Priority"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Maximum Priority"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,49 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: streamdev 0.5.0\n"
|
"Project-Id-Version: streamdev 0.5.0\n"
|
||||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
"POT-Creation-Date: 2015-01-16 22:32+0100\n"
|
||||||
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
|
"PO-Revision-Date: 2012-06-10 20:34+0100\n"
|
||||||
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
|
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
|
||||||
"Language-Team: <vdr@linuxtv.org>\n"
|
"Language-Team: Italian <vdr@linuxtv.org>\n"
|
||||||
|
"Language: it\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
msgid "Hide Mainmenu Entry"
|
||||||
|
msgstr "Nascondi voce menu principale"
|
||||||
|
|
||||||
|
msgid "Simultaneously used Devices"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Remote IP"
|
||||||
|
msgstr "Indirizzo IP del Server"
|
||||||
|
|
||||||
|
msgid "Remote Port"
|
||||||
|
msgstr "Porta Server Remoto"
|
||||||
|
|
||||||
|
msgid "Timeout (s)"
|
||||||
|
msgstr "Scadenza (s)"
|
||||||
|
|
||||||
|
msgid "Filter Streaming"
|
||||||
|
msgstr "Filtra trasmissione"
|
||||||
|
|
||||||
|
msgid "Filter SockBufSize"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Live TV Priority"
|
||||||
|
msgstr "Priorità TV dal vivo"
|
||||||
|
|
||||||
|
msgid "Minimum Priority"
|
||||||
|
msgstr "Priorità minima"
|
||||||
|
|
||||||
|
msgid "Maximum Priority"
|
||||||
|
msgstr "Priorità massima"
|
||||||
|
|
||||||
|
msgid "Broadcast Systems / Cost"
|
||||||
|
msgstr "Costo / sistemi trasmissione"
|
||||||
|
|
||||||
msgid "VTP Streaming Client"
|
msgid "VTP Streaming Client"
|
||||||
msgstr "Client trasmissione VTP"
|
msgstr "Client trasmissione VTP"
|
||||||
|
|
||||||
@@ -28,25 +62,3 @@ msgstr "Server sospeso"
|
|||||||
|
|
||||||
msgid "Couldn't suspend Server!"
|
msgid "Couldn't suspend Server!"
|
||||||
msgstr "Impossibile sospendere il server!"
|
msgstr "Impossibile sospendere il server!"
|
||||||
|
|
||||||
msgid "Hide Mainmenu Entry"
|
|
||||||
msgstr "Nascondi voce menu principale"
|
|
||||||
|
|
||||||
msgid "Start Client"
|
|
||||||
msgstr "Avvia Client"
|
|
||||||
|
|
||||||
msgid "Remote IP"
|
|
||||||
msgstr "Indirizzo IP del Server"
|
|
||||||
|
|
||||||
msgid "Remote Port"
|
|
||||||
msgstr "Porta Server Remoto"
|
|
||||||
|
|
||||||
msgid "Filter Streaming"
|
|
||||||
msgstr "Filtra trasmissione"
|
|
||||||
|
|
||||||
msgid "Minimum Priority"
|
|
||||||
msgstr "Priorità minima"
|
|
||||||
|
|
||||||
msgid "Maximum Priority"
|
|
||||||
msgstr "Priorità massima"
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,15 +6,49 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: streamdev 0.5.0\n"
|
"Project-Id-Version: streamdev 0.5.0\n"
|
||||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
"POT-Creation-Date: 2015-01-16 22:32+0100\n"
|
||||||
"PO-Revision-Date: 2009-11-26 21:57+0200\n"
|
"PO-Revision-Date: 2009-11-26 21:57+0200\n"
|
||||||
"Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
|
"Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
|
||||||
"Language-Team: Lietuvių\n"
|
"Language-Team: Lithuanian <vdr@linuxtv.org>\n"
|
||||||
|
"Language: lt\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
msgid "Hide Mainmenu Entry"
|
||||||
|
msgstr "Paslėpti pagrindinio meniu įrašą"
|
||||||
|
|
||||||
|
msgid "Simultaneously used Devices"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Remote IP"
|
||||||
|
msgstr "Nuotolinis IP adresas"
|
||||||
|
|
||||||
|
msgid "Remote Port"
|
||||||
|
msgstr "Nuotolinis portas"
|
||||||
|
|
||||||
|
msgid "Timeout (s)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Filter Streaming"
|
||||||
|
msgstr "Filtruoti transliavimą"
|
||||||
|
|
||||||
|
msgid "Filter SockBufSize"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Live TV Priority"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Minimum Priority"
|
||||||
|
msgstr "Minimalus prioritetas"
|
||||||
|
|
||||||
|
msgid "Maximum Priority"
|
||||||
|
msgstr "Maksimalus prioritetas"
|
||||||
|
|
||||||
|
msgid "Broadcast Systems / Cost"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "VTP Streaming Client"
|
msgid "VTP Streaming Client"
|
||||||
msgstr "VTP transliavimo standartas"
|
msgstr "VTP transliavimo standartas"
|
||||||
|
|
||||||
@@ -26,25 +60,3 @@ msgstr "Serveris sustabdytas"
|
|||||||
|
|
||||||
msgid "Couldn't suspend Server!"
|
msgid "Couldn't suspend Server!"
|
||||||
msgstr "Negali sustabdyti serverio!"
|
msgstr "Negali sustabdyti serverio!"
|
||||||
|
|
||||||
msgid "Hide Mainmenu Entry"
|
|
||||||
msgstr "Paslėpti pagrindinio meniu įrašą"
|
|
||||||
|
|
||||||
msgid "Start Client"
|
|
||||||
msgstr "Paleisti klientą"
|
|
||||||
|
|
||||||
msgid "Remote IP"
|
|
||||||
msgstr "Nuotolinis IP adresas"
|
|
||||||
|
|
||||||
msgid "Remote Port"
|
|
||||||
msgstr "Nuotolinis portas"
|
|
||||||
|
|
||||||
msgid "Filter Streaming"
|
|
||||||
msgstr "Filtruoti transliavimą"
|
|
||||||
|
|
||||||
msgid "Minimum Priority"
|
|
||||||
msgstr "Minimalus prioritetas"
|
|
||||||
|
|
||||||
msgid "Maximum Priority"
|
|
||||||
msgstr "Maksimalus prioritetas"
|
|
||||||
|
|
||||||
|
|||||||
63
client/po/pl_PL.po
Normal file
63
client/po/pl_PL.po
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# 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!"
|
||||||
@@ -6,15 +6,49 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: streamdev 0.5.0\n"
|
"Project-Id-Version: streamdev 0.5.0\n"
|
||||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
"POT-Creation-Date: 2015-01-16 22:32+0100\n"
|
||||||
"PO-Revision-Date: 2008-06-26 15:36+0100\n"
|
"PO-Revision-Date: 2008-06-26 15:36+0100\n"
|
||||||
"Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n"
|
"Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n"
|
||||||
"Language-Team: <vdr@linuxtv.org>\n"
|
"Language-Team: Russian <vdr@linuxtv.org>\n"
|
||||||
|
"Language: ru\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=ISO-8859-5\n"
|
"Content-Type: text/plain; charset=ISO-8859-5\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
msgid "Hide Mainmenu Entry"
|
||||||
|
msgstr "ÁßàïâÐâì Ò ÓÛÐÒÝÞÜ ÜÕÝî"
|
||||||
|
|
||||||
|
msgid "Simultaneously used Devices"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Remote IP"
|
||||||
|
msgstr "ÃÔÐÛÕÝÝëÙ IP"
|
||||||
|
|
||||||
|
msgid "Remote Port"
|
||||||
|
msgstr "ÃÔÐÛÕÝÝëÙ ßÞàâ"
|
||||||
|
|
||||||
|
msgid "Timeout (s)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Filter Streaming"
|
||||||
|
msgstr "ÄØÛìâà ßÞâÞÚÐ"
|
||||||
|
|
||||||
|
msgid "Filter SockBufSize"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Live TV Priority"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Minimum Priority"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Maximum Priority"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Broadcast Systems / Cost"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "VTP Streaming Client"
|
msgid "VTP Streaming Client"
|
||||||
msgstr "VTP Streaming ÚÛØÕÝâ"
|
msgstr "VTP Streaming ÚÛØÕÝâ"
|
||||||
|
|
||||||
@@ -26,25 +60,3 @@ msgstr "
|
|||||||
|
|
||||||
msgid "Couldn't suspend Server!"
|
msgid "Couldn't suspend Server!"
|
||||||
msgstr "ÝÕ ÜÞÓã ÞáâÐÝÞÒØâì áÕàÒÕà"
|
msgstr "ÝÕ ÜÞÓã ÞáâÐÝÞÒØâì áÕàÒÕà"
|
||||||
|
|
||||||
msgid "Hide Mainmenu Entry"
|
|
||||||
msgstr "ÁßàïâÐâì Ò ÓÛÐÒÝÞÜ ÜÕÝî"
|
|
||||||
|
|
||||||
msgid "Start Client"
|
|
||||||
msgstr "ÁâÐàâ ÚÛØÕÝâÐ"
|
|
||||||
|
|
||||||
msgid "Remote IP"
|
|
||||||
msgstr "ÃÔÐÛÕÝÝëÙ IP"
|
|
||||||
|
|
||||||
msgid "Remote Port"
|
|
||||||
msgstr "ÃÔÐÛÕÝÝëÙ ßÞàâ"
|
|
||||||
|
|
||||||
msgid "Filter Streaming"
|
|
||||||
msgstr "ÄØÛìâà ßÞâÞÚÐ"
|
|
||||||
|
|
||||||
msgid "Minimum Priority"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Maximum Priority"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
|
|||||||
50
client/po/sk_SK.po
Normal file → Executable file
50
client/po/sk_SK.po
Normal file → Executable file
@@ -6,34 +6,23 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: streamdev_SK\n"
|
"Project-Id-Version: streamdev_SK\n"
|
||||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
"POT-Creation-Date: 2015-01-16 22:32+0100\n"
|
||||||
"PO-Revision-Date: \n"
|
"PO-Revision-Date: \n"
|
||||||
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
|
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
|
||||||
"Language-Team: Slovak <hrala.milan@gmail.com>\n"
|
"Language-Team: Slovak <hrala.milan@gmail.com>\n"
|
||||||
|
"Language: sk\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=iso-8859-2\n"
|
"Content-Type: text/plain; charset=iso-8859-2\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Poedit-Language: Slovak\n"
|
"X-Poedit-Language: Slovak\n"
|
||||||
"X-Poedit-Country: SLOVAKIA\n"
|
"X-Poedit-Country: SLOVAKIA\n"
|
||||||
|
|
||||||
msgid "VTP Streaming Client"
|
|
||||||
msgstr "VTP prúdový klient"
|
|
||||||
|
|
||||||
msgid "Suspend Server"
|
|
||||||
msgstr "Server pozastavený"
|
|
||||||
|
|
||||||
msgid "Server is suspended"
|
|
||||||
msgstr "Server je doèasne preru¹ený"
|
|
||||||
|
|
||||||
msgid "Couldn't suspend Server!"
|
|
||||||
msgstr "Nepodarilo sa pozastavi» Server!"
|
|
||||||
|
|
||||||
msgid "Hide Mainmenu Entry"
|
msgid "Hide Mainmenu Entry"
|
||||||
msgstr "Schova» polo¾ku v hlavnom menu"
|
msgstr "Schova» polo¾ku v hlavnom menu"
|
||||||
|
|
||||||
msgid "Start Client"
|
msgid "Simultaneously used Devices"
|
||||||
msgstr "Spusti» Klienta"
|
msgstr "Súbe¾ne pou¾íva» zariadenia"
|
||||||
|
|
||||||
msgid "Remote IP"
|
msgid "Remote IP"
|
||||||
msgstr "Vzdialená IP"
|
msgstr "Vzdialená IP"
|
||||||
@@ -41,12 +30,35 @@ msgstr "Vzdialen
|
|||||||
msgid "Remote Port"
|
msgid "Remote Port"
|
||||||
msgstr "Vzdialený port"
|
msgstr "Vzdialený port"
|
||||||
|
|
||||||
|
msgid "Timeout (s)"
|
||||||
|
msgstr "Èasový limit (s)"
|
||||||
|
|
||||||
msgid "Filter Streaming"
|
msgid "Filter Streaming"
|
||||||
msgstr "filtrova» prúdy"
|
msgstr "Filtrova» dátový prúd"
|
||||||
|
|
||||||
|
msgid "Filter SockBufSize"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Live TV Priority"
|
||||||
|
msgstr "Priorita ¾ivého vysielania"
|
||||||
|
|
||||||
msgid "Minimum Priority"
|
msgid "Minimum Priority"
|
||||||
msgstr "minimálna priorita"
|
msgstr "Minimálna priorita"
|
||||||
|
|
||||||
msgid "Maximum Priority"
|
msgid "Maximum Priority"
|
||||||
msgstr "maximálna priorita"
|
msgstr "Maximálna priorita"
|
||||||
|
|
||||||
|
msgid "Broadcast Systems / Cost"
|
||||||
|
msgstr "Systémy vysielania / Hodnota"
|
||||||
|
|
||||||
|
msgid "VTP Streaming Client"
|
||||||
|
msgstr "VDR klient streamovania"
|
||||||
|
|
||||||
|
msgid "Suspend Server"
|
||||||
|
msgstr "Pozastavi» server"
|
||||||
|
|
||||||
|
msgid "Server is suspended"
|
||||||
|
msgstr "Server je doèasne pozastavený"
|
||||||
|
|
||||||
|
msgid "Couldn't suspend Server!"
|
||||||
|
msgstr "Server sa nepodarilo pozastavi»!"
|
||||||
|
|||||||
@@ -5,21 +5,28 @@
|
|||||||
#include <vdr/menuitems.h>
|
#include <vdr/menuitems.h>
|
||||||
|
|
||||||
#include "client/setup.h"
|
#include "client/setup.h"
|
||||||
#include "client/device.h"
|
#include "client/streamdev-client.h"
|
||||||
|
|
||||||
|
#ifndef MINPRIORITY
|
||||||
|
#define MINPRIORITY -MAXPRIORITY
|
||||||
|
#endif
|
||||||
|
|
||||||
cStreamdevClientSetup StreamdevClientSetup;
|
cStreamdevClientSetup StreamdevClientSetup;
|
||||||
|
|
||||||
cStreamdevClientSetup::cStreamdevClientSetup(void) {
|
cStreamdevClientSetup::cStreamdevClientSetup(void) {
|
||||||
StartClient = false;
|
StartClient = false;
|
||||||
RemotePort = 2004;
|
RemotePort = 2004;
|
||||||
|
Timeout = 2;
|
||||||
StreamFilters = false;
|
StreamFilters = false;
|
||||||
HideMenuEntry = false;
|
HideMenuEntry = false;
|
||||||
MinPriority = -1;
|
LivePriority = 0;
|
||||||
|
MinPriority = MINPRIORITY;
|
||||||
MaxPriority = MAXPRIORITY;
|
MaxPriority = MAXPRIORITY;
|
||||||
#if APIVERSNUM >= 10700
|
#if APIVERSNUM >= 10700
|
||||||
NumProvidedSystems = 1;
|
NumProvidedSystems = 1;
|
||||||
#endif
|
#endif
|
||||||
strcpy(RemoteIp, "");
|
strcpy(RemoteIp, "");
|
||||||
|
FilterSockBufSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cStreamdevClientSetup::SetupParse(const char *Name, const char *Value) {
|
bool cStreamdevClientSetup::SetupParse(const char *Name, const char *Value) {
|
||||||
@@ -31,10 +38,13 @@ bool cStreamdevClientSetup::SetupParse(const char *Name, const char *Value) {
|
|||||||
strcpy(RemoteIp, Value);
|
strcpy(RemoteIp, Value);
|
||||||
}
|
}
|
||||||
else if (strcmp(Name, "RemotePort") == 0) RemotePort = atoi(Value);
|
else if (strcmp(Name, "RemotePort") == 0) RemotePort = atoi(Value);
|
||||||
|
else if (strcmp(Name, "Timeout") == 0) Timeout = atoi(Value);
|
||||||
else if (strcmp(Name, "StreamFilters") == 0) StreamFilters = atoi(Value);
|
else if (strcmp(Name, "StreamFilters") == 0) StreamFilters = atoi(Value);
|
||||||
else if (strcmp(Name, "HideMenuEntry") == 0) HideMenuEntry = atoi(Value);
|
else if (strcmp(Name, "HideMenuEntry") == 0) HideMenuEntry = atoi(Value);
|
||||||
|
else if (strcmp(Name, "LivePriority") == 0) LivePriority = atoi(Value);
|
||||||
else if (strcmp(Name, "MinPriority") == 0) MinPriority = atoi(Value);
|
else if (strcmp(Name, "MinPriority") == 0) MinPriority = atoi(Value);
|
||||||
else if (strcmp(Name, "MaxPriority") == 0) MaxPriority = atoi(Value);
|
else if (strcmp(Name, "MaxPriority") == 0) MaxPriority = atoi(Value);
|
||||||
|
else if (strcmp(Name, "FilterSockBufSize") == 0) FilterSockBufSize = atoi(Value);
|
||||||
#if APIVERSNUM >= 10700
|
#if APIVERSNUM >= 10700
|
||||||
else if (strcmp(Name, "NumProvidedSystems") == 0) NumProvidedSystems = atoi(Value);
|
else if (strcmp(Name, "NumProvidedSystems") == 0) NumProvidedSystems = atoi(Value);
|
||||||
#endif
|
#endif
|
||||||
@@ -42,16 +52,21 @@ bool cStreamdevClientSetup::SetupParse(const char *Name, const char *Value) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
cStreamdevClientMenuSetupPage::cStreamdevClientMenuSetupPage(void) {
|
cStreamdevClientMenuSetupPage::cStreamdevClientMenuSetupPage(cPluginStreamdevClient *Plugin) {
|
||||||
|
m_Plugin = Plugin;
|
||||||
m_NewSetup = StreamdevClientSetup;
|
m_NewSetup = StreamdevClientSetup;
|
||||||
|
|
||||||
Add(new cMenuEditBoolItem(tr("Hide Mainmenu Entry"), &m_NewSetup.HideMenuEntry));
|
Add(new cMenuEditBoolItem(tr("Hide Mainmenu Entry"), &m_NewSetup.HideMenuEntry));
|
||||||
Add(new cMenuEditBoolItem(tr("Start Client"), &m_NewSetup.StartClient));
|
Add(new cMenuEditIntItem (tr("Simultaneously used Devices"), &m_NewSetup.StartClient, 0, STREAMDEV_MAXDEVICES));
|
||||||
Add(new cMenuEditIpItem (tr("Remote IP"), m_NewSetup.RemoteIp));
|
Add(new cMenuEditIpItem (tr("Remote IP"), m_NewSetup.RemoteIp));
|
||||||
Add(new cMenuEditIntItem (tr("Remote Port"), &m_NewSetup.RemotePort, 0, 65535));
|
Add(new cMenuEditIntItem (tr("Remote Port"), &m_NewSetup.RemotePort, 0, 65535));
|
||||||
|
Add(new cMenuEditIntItem (tr("Timeout (s)"), &m_NewSetup.Timeout, 1, 15));
|
||||||
Add(new cMenuEditBoolItem(tr("Filter Streaming"), &m_NewSetup.StreamFilters));
|
Add(new cMenuEditBoolItem(tr("Filter Streaming"), &m_NewSetup.StreamFilters));
|
||||||
Add(new cMenuEditIntItem (tr("Minimum Priority"), &m_NewSetup.MinPriority, -1, MAXPRIORITY));
|
if(m_NewSetup.StreamFilters)
|
||||||
Add(new cMenuEditIntItem (tr("Maximum Priority"), &m_NewSetup.MaxPriority, -1, MAXPRIORITY));
|
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));
|
||||||
#if APIVERSNUM >= 10715
|
#if APIVERSNUM >= 10715
|
||||||
Add(new cMenuEditIntItem (tr("Broadcast Systems / Cost"), &m_NewSetup.NumProvidedSystems, 1, 15));
|
Add(new cMenuEditIntItem (tr("Broadcast Systems / Cost"), &m_NewSetup.NumProvidedSystems, 1, 15));
|
||||||
#elif APIVERSNUM >= 10700
|
#elif APIVERSNUM >= 10700
|
||||||
@@ -64,27 +79,25 @@ cStreamdevClientMenuSetupPage::~cStreamdevClientMenuSetupPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void cStreamdevClientMenuSetupPage::Store(void) {
|
void cStreamdevClientMenuSetupPage::Store(void) {
|
||||||
if (m_NewSetup.StartClient != StreamdevClientSetup.StartClient) {
|
|
||||||
if (m_NewSetup.StartClient)
|
|
||||||
cStreamdevDevice::Init();
|
|
||||||
}
|
|
||||||
|
|
||||||
SetupStore("StartClient", m_NewSetup.StartClient);
|
SetupStore("StartClient", m_NewSetup.StartClient);
|
||||||
if (strcmp(m_NewSetup.RemoteIp, "") == 0)
|
if (strcmp(m_NewSetup.RemoteIp, "") == 0)
|
||||||
SetupStore("RemoteIp", "-none-");
|
SetupStore("RemoteIp", "-none-");
|
||||||
else
|
else
|
||||||
SetupStore("RemoteIp", m_NewSetup.RemoteIp);
|
SetupStore("RemoteIp", m_NewSetup.RemoteIp);
|
||||||
SetupStore("RemotePort", m_NewSetup.RemotePort);
|
SetupStore("RemotePort", m_NewSetup.RemotePort);
|
||||||
|
SetupStore("Timeout", m_NewSetup.Timeout);
|
||||||
SetupStore("StreamFilters", m_NewSetup.StreamFilters);
|
SetupStore("StreamFilters", m_NewSetup.StreamFilters);
|
||||||
SetupStore("HideMenuEntry", m_NewSetup.HideMenuEntry);
|
SetupStore("HideMenuEntry", m_NewSetup.HideMenuEntry);
|
||||||
|
SetupStore("LivePriority", m_NewSetup.LivePriority);
|
||||||
SetupStore("MinPriority", m_NewSetup.MinPriority);
|
SetupStore("MinPriority", m_NewSetup.MinPriority);
|
||||||
SetupStore("MaxPriority", m_NewSetup.MaxPriority);
|
SetupStore("MaxPriority", m_NewSetup.MaxPriority);
|
||||||
#if APIVERSNUM >= 10700
|
#if APIVERSNUM >= 10700
|
||||||
SetupStore("NumProvidedSystems", m_NewSetup.NumProvidedSystems);
|
SetupStore("NumProvidedSystems", m_NewSetup.NumProvidedSystems);
|
||||||
#endif
|
#endif
|
||||||
|
SetupStore("FilterSockBufSize", m_NewSetup.FilterSockBufSize);
|
||||||
|
|
||||||
StreamdevClientSetup = m_NewSetup;
|
StreamdevClientSetup = m_NewSetup;
|
||||||
|
|
||||||
cStreamdevDevice::ReInit();
|
m_Plugin->Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,11 @@ struct cStreamdevClientSetup {
|
|||||||
int StartClient;
|
int StartClient;
|
||||||
char RemoteIp[20];
|
char RemoteIp[20];
|
||||||
int RemotePort;
|
int RemotePort;
|
||||||
|
int Timeout;
|
||||||
int StreamFilters;
|
int StreamFilters;
|
||||||
|
int FilterSockBufSize;
|
||||||
int HideMenuEntry;
|
int HideMenuEntry;
|
||||||
|
int LivePriority;
|
||||||
int MinPriority;
|
int MinPriority;
|
||||||
int MaxPriority;
|
int MaxPriority;
|
||||||
#if APIVERSNUM >= 10700
|
#if APIVERSNUM >= 10700
|
||||||
@@ -26,15 +29,18 @@ struct cStreamdevClientSetup {
|
|||||||
|
|
||||||
extern cStreamdevClientSetup StreamdevClientSetup;
|
extern cStreamdevClientSetup StreamdevClientSetup;
|
||||||
|
|
||||||
|
class cPluginStreamdevClient;
|
||||||
|
|
||||||
class cStreamdevClientMenuSetupPage: public cMenuSetupPage {
|
class cStreamdevClientMenuSetupPage: public cMenuSetupPage {
|
||||||
private:
|
private:
|
||||||
|
cPluginStreamdevClient *m_Plugin;
|
||||||
cStreamdevClientSetup m_NewSetup;
|
cStreamdevClientSetup m_NewSetup;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void Store(void);
|
virtual void Store(void);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cStreamdevClientMenuSetupPage(void);
|
cStreamdevClientMenuSetupPage(cPluginStreamdevClient *Plugin);
|
||||||
virtual ~cStreamdevClientMenuSetupPage();
|
virtual ~cStreamdevClientMenuSetupPage();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
223
client/socket.c
223
client/socket.c
@@ -11,19 +11,24 @@
|
|||||||
|
|
||||||
#define MINLOGREPEAT 10 //don't log connect failures too often (seconds)
|
#define MINLOGREPEAT 10 //don't log connect failures too often (seconds)
|
||||||
|
|
||||||
#include "client/socket.h"
|
// timeout for writing to command socket
|
||||||
#include "client/setup.h"
|
#define WRITE_TIMEOUT_MS 200
|
||||||
#include "common.h"
|
#define QUIT_TIMEOUT_MS 500
|
||||||
|
|
||||||
cClientSocket ClientSocket;
|
#include "client/socket.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
cClientSocket::cClientSocket(void)
|
cClientSocket::cClientSocket(void)
|
||||||
{
|
{
|
||||||
memset(m_DataSockets, 0, sizeof(cTBSocket*) * si_Count);
|
memset(m_DataSockets, 0, sizeof(cTBSocket*) * si_Count);
|
||||||
|
m_ServerVersion = 0;
|
||||||
|
m_Priority = -100;
|
||||||
m_Prio = false;
|
m_Prio = false;
|
||||||
|
m_Abort = false;
|
||||||
m_LastSignalUpdate = 0;
|
m_LastSignalUpdate = 0;
|
||||||
m_LastSignalStrength = -1;
|
m_LastSignalStrength = -1;
|
||||||
m_LastSignalQuality = -1;
|
m_LastSignalQuality = -1;
|
||||||
|
m_LastDev = -1;
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,53 +40,62 @@ cClientSocket::~cClientSocket()
|
|||||||
|
|
||||||
void cClientSocket::Reset(void)
|
void cClientSocket::Reset(void)
|
||||||
{
|
{
|
||||||
for (int it = 0; it < si_Count; ++it) {
|
for (int it = 0; it < si_Count; ++it)
|
||||||
if (m_DataSockets[it] != NULL)
|
|
||||||
DELETENULL(m_DataSockets[it]);
|
DELETENULL(m_DataSockets[it]);
|
||||||
}
|
m_Priority = -100;
|
||||||
}
|
}
|
||||||
|
|
||||||
cTBSocket *cClientSocket::DataSocket(eSocketId Id) const {
|
cTBSocket *cClientSocket::DataSocket(eSocketId Id) const {
|
||||||
return m_DataSockets[Id];
|
return m_DataSockets[Id];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cClientSocket::Command(const std::string &Command, uint Expected, uint TimeoutMs)
|
bool cClientSocket::Command(const std::string &Command, uint Expected)
|
||||||
{
|
{
|
||||||
errno = 0;
|
uint code = 0;
|
||||||
|
std::string buffer;
|
||||||
|
if (Send(Command) && Receive(Command, &code, &buffer)) {
|
||||||
|
if (code == Expected)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
dsyslog("streamdev-client: Command '%s' rejected by %s:%d: %s",
|
||||||
|
Command.c_str(), RemoteIp().c_str(), RemotePort(), buffer.c_str());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool cClientSocket::Send(const std::string &Command)
|
||||||
|
{
|
||||||
std::string pkt = Command + "\015\012";
|
std::string pkt = Command + "\015\012";
|
||||||
Dprintf("OUT: |%s|\n", Command.c_str());
|
Dprintf("OUT: |%s|\n", Command.c_str());
|
||||||
|
|
||||||
cTimeMs starttime;
|
errno = 0;
|
||||||
if (!TimedWrite(pkt.c_str(), pkt.size(), TimeoutMs)) {
|
if (!TimedWrite(pkt.c_str(), pkt.size(), WRITE_TIMEOUT_MS)) {
|
||||||
esyslog("Streamdev: Lost connection to %s:%d: %s", RemoteIp().c_str(), RemotePort(),
|
esyslog("ERROR: streamdev-client: Failed sending command '%s' to %s:%d: %s",
|
||||||
strerror(errno));
|
Command.c_str(), RemoteIp().c_str(), RemotePort(), strerror(errno));
|
||||||
Close();
|
Close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t elapsed = starttime.Elapsed();
|
|
||||||
if (Expected != 0) { // XXX+ What if elapsed > TimeoutMs?
|
|
||||||
TimeoutMs -= elapsed;
|
|
||||||
return Expect(Expected, NULL, TimeoutMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cClientSocket::Expect(uint Expected, std::string *Result, uint TimeoutMs) {
|
#define TIMEOUT_MS 1000
|
||||||
char *endptr;
|
bool cClientSocket::Receive(const std::string &Command, uint *Code, std::string *Result, uint TimeoutMs) {
|
||||||
int bufcount;
|
int bufcount;
|
||||||
bool res;
|
do
|
||||||
|
{
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
bufcount = ReadUntil(m_Buffer, sizeof(m_Buffer) - 1, "\012", TimeoutMs < TIMEOUT_MS ? TimeoutMs : TIMEOUT_MS);
|
||||||
if ((bufcount = ReadUntil(m_Buffer, sizeof(m_Buffer) - 1, "\012", TimeoutMs)) == -1) {
|
if (bufcount == -1) {
|
||||||
esyslog("Streamdev: Lost connection to %s:%d: %s", RemoteIp().c_str(), RemotePort(),
|
if (m_Abort)
|
||||||
strerror(errno));
|
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();
|
Close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
TimeoutMs -= TIMEOUT_MS;
|
||||||
|
}
|
||||||
|
} while (bufcount == -1);
|
||||||
if (m_Buffer[bufcount - 1] == '\015')
|
if (m_Buffer[bufcount - 1] == '\015')
|
||||||
--bufcount;
|
--bufcount;
|
||||||
m_Buffer[bufcount] = '\0';
|
m_Buffer[bufcount] = '\0';
|
||||||
@@ -89,9 +103,9 @@ bool cClientSocket::Expect(uint Expected, std::string *Result, uint TimeoutMs) {
|
|||||||
|
|
||||||
if (Result != NULL)
|
if (Result != NULL)
|
||||||
*Result = m_Buffer;
|
*Result = m_Buffer;
|
||||||
|
if (Code != NULL)
|
||||||
res = strtoul(m_Buffer, &endptr, 10) == Expected;
|
*Code = strtoul(m_Buffer, NULL, 10);
|
||||||
return res;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cClientSocket::CheckConnection(void) {
|
bool cClientSocket::CheckConnection(void) {
|
||||||
@@ -100,25 +114,22 @@ bool cClientSocket::CheckConnection(void) {
|
|||||||
if (IsOpen()) {
|
if (IsOpen()) {
|
||||||
cTBSelect select;
|
cTBSelect select;
|
||||||
|
|
||||||
Dprintf("connection open\n");
|
|
||||||
|
|
||||||
// XXX+ check if connection is still alive (is there a better way?)
|
// XXX+ check if connection is still alive (is there a better way?)
|
||||||
// There REALLY shouldn't be anything readable according to PROTOCOL here
|
// There REALLY shouldn't be anything readable according to PROTOCOL here
|
||||||
// If there is, assume it's an eof signal (subseq. read would return 0)
|
// If there is, assume it's an eof signal (subseq. read would return 0)
|
||||||
select.Add(*this, false);
|
select.Add(*this, false);
|
||||||
int res;
|
int res;
|
||||||
if ((res = select.Select(0)) == 0) {
|
if ((res = select.Select(0)) == 0) {
|
||||||
Dprintf("select said nothing happened\n");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Dprintf("closing connection (res was %d)", res);
|
Dprintf("closing connection (res was %d)\n", res);
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Connect(StreamdevClientSetup.RemoteIp, StreamdevClientSetup.RemotePort)){
|
if (!Connect(StreamdevClientSetup.RemoteIp, StreamdevClientSetup.RemotePort, StreamdevClientSetup.Timeout * 1000)){
|
||||||
static time_t lastTime = 0;
|
static time_t lastTime = 0;
|
||||||
if (time(NULL) - lastTime > MINLOGREPEAT) {
|
if (time(NULL) - lastTime > MINLOGREPEAT) {
|
||||||
esyslog("ERROR: Streamdev: Couldn't connect to %s:%d: %s",
|
esyslog("ERROR: streamdev-client: Couldn't connect to %s:%d: %s",
|
||||||
(const char*)StreamdevClientSetup.RemoteIp,
|
(const char*)StreamdevClientSetup.RemoteIp,
|
||||||
StreamdevClientSetup.RemotePort, strerror(errno));
|
StreamdevClientSetup.RemotePort, strerror(errno));
|
||||||
lastTime = time(NULL);
|
lastTime = time(NULL);
|
||||||
@@ -126,18 +137,25 @@ bool cClientSocket::CheckConnection(void) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Expect(220)) {
|
uint code = 0;
|
||||||
if (errno == 0)
|
std::string buffer;
|
||||||
esyslog("ERROR: Streamdev: Didn't receive greeting from %s:%d",
|
if (!Receive("<connect>", &code, &buffer)) {
|
||||||
RemoteIp().c_str(), RemotePort());
|
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());
|
||||||
Close();
|
Close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int major, minor;
|
||||||
|
if (sscanf(buffer.c_str(), "%*u VTP/%u.%u", &major, &minor) == 2)
|
||||||
|
m_ServerVersion = major * 100 + minor;
|
||||||
|
|
||||||
|
if (m_ServerVersion == 0) {
|
||||||
if (!Command("CAPS TSPIDS", 220)) {
|
if (!Command("CAPS TSPIDS", 220)) {
|
||||||
if (errno == 0)
|
|
||||||
esyslog("ERROR: Streamdev: Couldn't negotiate capabilities on %s:%d",
|
|
||||||
RemoteIp().c_str(), RemotePort());
|
|
||||||
Close();
|
Close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -151,9 +169,19 @@ bool cClientSocket::CheckConnection(void) {
|
|||||||
Prio = ",PRIO";
|
Prio = ",PRIO";
|
||||||
m_Prio = true;
|
m_Prio = true;
|
||||||
}
|
}
|
||||||
|
isyslog("streamdev-client: Connected to server %s:%d using capabilities TSPIDS%s%s",
|
||||||
isyslog("Streamdev: Connected to server %s:%d using capabilities TSPIDS%s%s",
|
|
||||||
RemoteIp().c_str(), RemotePort(), Filters, Prio);
|
RemoteIp().c_str(), RemotePort(), Filters, Prio);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(!Command("VERS 1.0", 220)) {
|
||||||
|
Close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_Prio = true;
|
||||||
|
isyslog("streamdev-client: Connected to server %s:%d using protocol version %u.%u",
|
||||||
|
RemoteIp().c_str(), RemotePort(), major, minor);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,17 +192,19 @@ bool cClientSocket::ProvidesChannel(const cChannel *Channel, int Priority) {
|
|||||||
|
|
||||||
std::string command = (std::string)"PROV " + (const char*)itoa(Priority) + " "
|
std::string command = (std::string)"PROV " + (const char*)itoa(Priority) + " "
|
||||||
+ (const char*)Channel->GetChannelID().ToString();
|
+ (const char*)Channel->GetChannelID().ToString();
|
||||||
if (!Command(command))
|
if (!Send(command))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
uint code;
|
||||||
std::string buffer;
|
std::string buffer;
|
||||||
if (!Expect(220, &buffer)) {
|
if (!Receive(command, &code, &buffer))
|
||||||
if (buffer.substr(0, 3) != "560" && errno == 0)
|
return false;
|
||||||
esyslog("ERROR: Streamdev: Couldn't check if %s:%d provides channel %s",
|
if (code != 220 && code != 560) {
|
||||||
RemoteIp().c_str(), RemotePort(), Channel->Name());
|
esyslog("streamdev-client: Unexpected reply to '%s' from %s:%d: %s",
|
||||||
|
command.c_str(), RemoteIp().c_str(), RemotePort(), buffer.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return code == 220;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cClientSocket::CreateDataConnection(eSocketId Id) {
|
bool cClientSocket::CreateDataConnection(eSocketId Id) {
|
||||||
@@ -186,7 +216,7 @@ bool cClientSocket::CreateDataConnection(eSocketId Id) {
|
|||||||
DELETENULL(m_DataSockets[Id]);
|
DELETENULL(m_DataSockets[Id]);
|
||||||
|
|
||||||
if (!listen.Listen(LocalIp(), 0, 1)) {
|
if (!listen.Listen(LocalIp(), 0, 1)) {
|
||||||
esyslog("ERROR: Streamdev: Couldn't create data connection: %s",
|
esyslog("ERROR: streamdev-client: Couldn't create data connection: %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -201,13 +231,8 @@ bool cClientSocket::CreateDataConnection(eSocketId Id) {
|
|||||||
|
|
||||||
CMD_LOCK;
|
CMD_LOCK;
|
||||||
|
|
||||||
if (!Command(command, 220)) {
|
if (!Command(command, 220))
|
||||||
Dprintf("error: %m\n");
|
|
||||||
if (errno == 0)
|
|
||||||
esyslog("ERROR: Streamdev: Couldn't establish data connection to %s:%d",
|
|
||||||
RemoteIp().c_str(), RemotePort());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
/* The server SHOULD do the following:
|
/* The server SHOULD do the following:
|
||||||
* - get PORT command
|
* - get PORT command
|
||||||
@@ -217,7 +242,7 @@ bool cClientSocket::CreateDataConnection(eSocketId Id) {
|
|||||||
|
|
||||||
m_DataSockets[Id] = new cTBSocket;
|
m_DataSockets[Id] = new cTBSocket;
|
||||||
if (!m_DataSockets[Id]->Accept(listen)) {
|
if (!m_DataSockets[Id]->Accept(listen)) {
|
||||||
esyslog("ERROR: Streamdev: Couldn't establish data connection to %s:%d%s%s",
|
esyslog("ERROR: streamdev-client: Couldn't establish data connection to %s:%d%s%s",
|
||||||
RemoteIp().c_str(), RemotePort(), errno == 0 ? "" : ": ",
|
RemoteIp().c_str(), RemotePort(), errno == 0 ? "" : ": ",
|
||||||
errno == 0 ? "" : strerror(errno));
|
errno == 0 ? "" : strerror(errno));
|
||||||
DELETENULL(m_DataSockets[Id]);
|
DELETENULL(m_DataSockets[Id]);
|
||||||
@@ -228,18 +253,12 @@ bool cClientSocket::CreateDataConnection(eSocketId Id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool cClientSocket::CloseDataConnection(eSocketId Id) {
|
bool cClientSocket::CloseDataConnection(eSocketId Id) {
|
||||||
//if (!CheckConnection()) return false;
|
|
||||||
|
|
||||||
CMD_LOCK;
|
CMD_LOCK;
|
||||||
|
|
||||||
if(Id == siLive || Id == siLiveFilter)
|
if(Id == siLive || Id == siLiveFilter)
|
||||||
if (m_DataSockets[Id] != NULL) {
|
if (m_DataSockets[Id] != NULL) {
|
||||||
std::string command = (std::string)"ABRT " + (const char*)itoa(Id);
|
std::string command = (std::string)"ABRT " + (const char*)itoa(Id);
|
||||||
if (!Command(command, 220)) {
|
Command(command, 220);
|
||||||
if (errno == 0)
|
|
||||||
esyslog("ERROR: Streamdev: Couldn't cleanly close data connection");
|
|
||||||
//return false;
|
|
||||||
}
|
|
||||||
DELETENULL(m_DataSockets[Id]);
|
DELETENULL(m_DataSockets[Id]);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -252,41 +271,41 @@ bool cClientSocket::SetChannelDevice(const cChannel *Channel) {
|
|||||||
|
|
||||||
std::string command = (std::string)"TUNE "
|
std::string command = (std::string)"TUNE "
|
||||||
+ (const char*)Channel->GetChannelID().ToString();
|
+ (const char*)Channel->GetChannelID().ToString();
|
||||||
if (!Command(command, 220, 10000)) {
|
if (!Command(command, 220))
|
||||||
if (errno == 0)
|
|
||||||
esyslog("ERROR: Streamdev: Couldn't tune %s:%d to channel %s",
|
|
||||||
RemoteIp().c_str(), RemotePort(), Channel->Name());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
m_LastSignalUpdate = 0;
|
m_LastSignalUpdate = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cClientSocket::SetPriority(int Priority) {
|
bool cClientSocket::SetPriority(int Priority) {
|
||||||
|
if (Priority == m_Priority)
|
||||||
|
return true;
|
||||||
|
|
||||||
if (!CheckConnection()) return false;
|
if (!CheckConnection()) return false;
|
||||||
|
|
||||||
CMD_LOCK;
|
CMD_LOCK;
|
||||||
|
|
||||||
std::string command = (std::string)"PRIO " + (const char*)itoa(Priority);
|
std::string command = (std::string)"PRIO " + (const char*)itoa(Priority);
|
||||||
if (!Command(command, 220)) {
|
if (!Command(command, 220))
|
||||||
if (errno == 0)
|
|
||||||
esyslog("Streamdev: Failed to update priority on %s:%d", RemoteIp().c_str(),
|
|
||||||
RemotePort());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
m_Priority = Priority;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cClientSocket::GetSignal(int *SignalStrength, int *SignalQuality) {
|
bool cClientSocket::GetSignal(int *SignalStrength, int *SignalQuality, int *Dev) {
|
||||||
if (!CheckConnection()) return -1;
|
if (!CheckConnection()) return -1;
|
||||||
|
|
||||||
CMD_LOCK;
|
CMD_LOCK;
|
||||||
|
|
||||||
if (m_LastSignalUpdate != time(NULL)) {
|
if (m_LastSignalUpdate != time(NULL)) {
|
||||||
|
uint code = 0;
|
||||||
std::string buffer;
|
std::string buffer;
|
||||||
if (!Command("SGNL") || !Expect(220, &buffer)
|
std::string command("SGNL");
|
||||||
|| sscanf(buffer.c_str(), "%*d %*d %d:%d", &m_LastSignalStrength, &m_LastSignalQuality) != 2) {
|
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;
|
||||||
m_LastSignalStrength = -1;
|
m_LastSignalStrength = -1;
|
||||||
m_LastSignalQuality = -1;
|
m_LastSignalQuality = -1;
|
||||||
}
|
}
|
||||||
@@ -296,6 +315,8 @@ bool cClientSocket::GetSignal(int *SignalStrength, int *SignalQuality) {
|
|||||||
*SignalStrength = m_LastSignalStrength;
|
*SignalStrength = m_LastSignalStrength;
|
||||||
if (SignalQuality)
|
if (SignalQuality)
|
||||||
*SignalQuality = m_LastSignalQuality;
|
*SignalQuality = m_LastSignalQuality;
|
||||||
|
if (Dev)
|
||||||
|
*Dev = m_LastDev;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,13 +326,7 @@ bool cClientSocket::SetPid(int Pid, bool On) {
|
|||||||
CMD_LOCK;
|
CMD_LOCK;
|
||||||
|
|
||||||
std::string command = (std::string)(On ? "ADDP " : "DELP ") + (const char*)itoa(Pid);
|
std::string command = (std::string)(On ? "ADDP " : "DELP ") + (const char*)itoa(Pid);
|
||||||
if (!Command(command, 220)) {
|
return Command(command, 220);
|
||||||
if (errno == 0)
|
|
||||||
esyslog("Streamdev: Pid %d not available from %s:%d", Pid, RemoteIp().c_str(),
|
|
||||||
RemotePort());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cClientSocket::SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On) {
|
bool cClientSocket::SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On) {
|
||||||
@@ -321,13 +336,7 @@ bool cClientSocket::SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On) {
|
|||||||
|
|
||||||
std::string command = (std::string)(On ? "ADDF " : "DELF ") + (const char*)itoa(Pid)
|
std::string command = (std::string)(On ? "ADDF " : "DELF ") + (const char*)itoa(Pid)
|
||||||
+ " " + (const char*)itoa(Tid) + " " + (const char*)itoa(Mask);
|
+ " " + (const char*)itoa(Tid) + " " + (const char*)itoa(Mask);
|
||||||
if (!Command(command, 220)) {
|
return Command(command, 220);
|
||||||
if (errno == 0)
|
|
||||||
esyslog("Streamdev: Filter %hu, %hhu, %hhu not available from %s:%d",
|
|
||||||
Pid, Tid, Mask, RemoteIp().c_str(), RemotePort());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cClientSocket::CloseDvr(void) {
|
bool cClientSocket::CloseDvr(void) {
|
||||||
@@ -337,27 +346,20 @@ bool cClientSocket::CloseDvr(void) {
|
|||||||
|
|
||||||
if (m_DataSockets[siLive] != NULL) {
|
if (m_DataSockets[siLive] != NULL) {
|
||||||
std::string command = (std::string)"ABRT " + (const char*)itoa(siLive);
|
std::string command = (std::string)"ABRT " + (const char*)itoa(siLive);
|
||||||
if (!Command(command, 220)) {
|
if (!Command(command, 220))
|
||||||
if (errno == 0)
|
|
||||||
esyslog("ERROR: Streamdev: Couldn't cleanly close data connection");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
DELETENULL(m_DataSockets[siLive]);
|
DELETENULL(m_DataSockets[siLive]);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cClientSocket::Quit(void) {
|
bool cClientSocket::Quit(void) {
|
||||||
bool res;
|
m_Abort = true;
|
||||||
|
if (!IsOpen()) return false;
|
||||||
|
|
||||||
if (!CheckConnection()) return false;
|
CMD_LOCK;
|
||||||
|
std::string command("QUIT");
|
||||||
if (!(res = Command("QUIT", 221))) {
|
bool res = Send(command) && Receive(command, NULL, NULL, QUIT_TIMEOUT_MS);
|
||||||
if (errno == 0)
|
|
||||||
esyslog("ERROR: Streamdev: Couldn't quit command connection to %s:%d",
|
|
||||||
RemoteIp().c_str(), RemotePort());
|
|
||||||
}
|
|
||||||
Close();
|
Close();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -367,10 +369,5 @@ bool cClientSocket::SuspendServer(void) {
|
|||||||
|
|
||||||
CMD_LOCK;
|
CMD_LOCK;
|
||||||
|
|
||||||
if (!Command("SUSP", 220)) {
|
return Command("SUSP", 220);
|
||||||
if (errno == 0)
|
|
||||||
esyslog("ERROR: Streamdev: Couldn't suspend server");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <tools/socket.h>
|
#include <tools/socket.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "client/setup.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -20,23 +21,27 @@ private:
|
|||||||
cTBSocket *m_DataSockets[si_Count];
|
cTBSocket *m_DataSockets[si_Count];
|
||||||
cMutex m_Mutex;
|
cMutex m_Mutex;
|
||||||
char m_Buffer[BUFSIZ + 1]; // various uses
|
char m_Buffer[BUFSIZ + 1]; // various uses
|
||||||
|
unsigned int m_ServerVersion;
|
||||||
bool m_Prio; // server supports command PRIO
|
bool m_Prio; // server supports command PRIO
|
||||||
|
int m_Priority; // current device priority
|
||||||
|
bool m_Abort; // quit command pending
|
||||||
|
|
||||||
time_t m_LastSignalUpdate;
|
time_t m_LastSignalUpdate;
|
||||||
int m_LastSignalStrength;
|
int m_LastSignalStrength;
|
||||||
int m_LastSignalQuality;
|
int m_LastSignalQuality;
|
||||||
|
int m_LastDev;
|
||||||
protected:
|
protected:
|
||||||
/* Send Command, and return true if the command results in Expected.
|
/* Send Command, and return true if the command results in Expected.
|
||||||
Returns false on failure, setting errno appropriately if it has been
|
Returns false on failure. */
|
||||||
a system failure. If Expected is zero, returns immediately after
|
bool Command(const std::string &Command, uint Expected);
|
||||||
sending the command. */
|
|
||||||
bool Command(const std::string &Command, uint Expected = 0, uint TimeoutMs = 1500);
|
|
||||||
|
|
||||||
/* Fetch results from an ongoing Command called with Expected == 0. Returns
|
/* Send the given command. Returns false on failure. */
|
||||||
true if the response has the code Expected, returning an internal buffer
|
bool Send(const std::string &Command);
|
||||||
in the array pointer pointed to by Result. Returns false on failure,
|
|
||||||
setting errno appropriately if it has been a system failure. */
|
/* Fetch results from an ongoing Command. The status code and the
|
||||||
bool Expect(uint Expected, std::string *Result = NULL, uint TimeoutMs = 1500);
|
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);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cClientSocket(void);
|
cClientSocket(void);
|
||||||
@@ -50,10 +55,12 @@ public:
|
|||||||
bool CloseDataConnection(eSocketId Id);
|
bool CloseDataConnection(eSocketId Id);
|
||||||
bool SetChannelDevice(const cChannel *Channel);
|
bool SetChannelDevice(const cChannel *Channel);
|
||||||
bool SupportsPrio() { return m_Prio; }
|
bool SupportsPrio() { return m_Prio; }
|
||||||
|
unsigned int ServerVersion() { return m_ServerVersion; }
|
||||||
|
int Priority() const { return m_Priority; }
|
||||||
bool SetPriority(int Priority);
|
bool SetPriority(int Priority);
|
||||||
bool SetPid(int Pid, bool On);
|
bool SetPid(int Pid, bool On);
|
||||||
bool SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On);
|
bool SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On);
|
||||||
bool GetSignal(int *SignalStrength, int *SignalQuality);
|
bool GetSignal(int *SignalStrength, int *SignalQuality, int *Dev);
|
||||||
bool CloseDvr(void);
|
bool CloseDvr(void);
|
||||||
bool SuspendServer(void);
|
bool SuspendServer(void);
|
||||||
bool Quit(void);
|
bool Quit(void);
|
||||||
@@ -61,6 +68,4 @@ public:
|
|||||||
cTBSocket *DataSocket(eSocketId Id) const;
|
cTBSocket *DataSocket(eSocketId Id) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern class cClientSocket ClientSocket;
|
|
||||||
|
|
||||||
#endif // VDR_STREAMDEV_CLIENT_CONNECTION_H
|
#endif // VDR_STREAMDEV_CLIENT_CONNECTION_H
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
const char *cPluginStreamdevClient::DESCRIPTION = trNOOP("VTP Streaming Client");
|
const char *cPluginStreamdevClient::DESCRIPTION = trNOOP("VTP Streaming Client");
|
||||||
|
|
||||||
cPluginStreamdevClient::cPluginStreamdevClient(void) {
|
cPluginStreamdevClient::cPluginStreamdevClient(void): m_Devices() {
|
||||||
}
|
}
|
||||||
|
|
||||||
cPluginStreamdevClient::~cPluginStreamdevClient() {
|
cPluginStreamdevClient::~cPluginStreamdevClient() {
|
||||||
@@ -26,9 +26,13 @@ const char *cPluginStreamdevClient::Description(void) {
|
|||||||
return tr(DESCRIPTION);
|
return tr(DESCRIPTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cPluginStreamdevClient::Start(void) {
|
bool cPluginStreamdevClient::Initialize(void) {
|
||||||
I18nRegister(PLUGIN_NAME_I18N);
|
for (int i = 0; i < STREAMDEV_MAXDEVICES; i++) {
|
||||||
cStreamdevDevice::Init();
|
if (m_Devices[i])
|
||||||
|
m_Devices[i]->ReInit(i >= StreamdevClientSetup.StartClient);
|
||||||
|
else if (i < StreamdevClientSetup.StartClient)
|
||||||
|
m_Devices[i] = new cStreamdevDevice();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +41,7 @@ const char *cPluginStreamdevClient::MainMenuEntry(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cOsdObject *cPluginStreamdevClient::MainMenuAction(void) {
|
cOsdObject *cPluginStreamdevClient::MainMenuAction(void) {
|
||||||
if (ClientSocket.SuspendServer())
|
if (StreamdevClientSetup.StartClient && m_Devices[0]->SuspendServer())
|
||||||
Skins.Message(mtInfo, tr("Server is suspended"));
|
Skins.Message(mtInfo, tr("Server is suspended"));
|
||||||
else
|
else
|
||||||
Skins.Message(mtError, tr("Couldn't suspend Server!"));
|
Skins.Message(mtError, tr("Couldn't suspend Server!"));
|
||||||
@@ -45,15 +49,24 @@ cOsdObject *cPluginStreamdevClient::MainMenuAction(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cMenuSetupPage *cPluginStreamdevClient::SetupMenu(void) {
|
cMenuSetupPage *cPluginStreamdevClient::SetupMenu(void) {
|
||||||
return new cStreamdevClientMenuSetupPage;
|
return new cStreamdevClientMenuSetupPage(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cPluginStreamdevClient::SetupParse(const char *Name, const char *Value) {
|
bool cPluginStreamdevClient::SetupParse(const char *Name, const char *Value) {
|
||||||
return StreamdevClientSetup.SetupParse(Name, Value);
|
return StreamdevClientSetup.SetupParse(Name, Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cPluginStreamdevClient::Service(const char *Id, void *Data) {
|
||||||
|
if (!strcmp(Id, LOOP_PREVENTION_SERVICE)) {
|
||||||
|
cStreamdevDevice::DenyChannel((const cChannel*) Data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void cPluginStreamdevClient::MainThreadHook(void) {
|
void cPluginStreamdevClient::MainThreadHook(void) {
|
||||||
cStreamdevDevice::UpdatePriority();
|
for (int i = 0; i < StreamdevClientSetup.StartClient; i++)
|
||||||
|
m_Devices[i]->UpdatePriority();
|
||||||
}
|
}
|
||||||
|
|
||||||
VDRPLUGINCREATOR(cPluginStreamdevClient); // Don't touch this!
|
VDRPLUGINCREATOR(cPluginStreamdevClient); // Don't touch this!
|
||||||
|
|||||||
@@ -9,20 +9,25 @@
|
|||||||
|
|
||||||
#include <vdr/plugin.h>
|
#include <vdr/plugin.h>
|
||||||
|
|
||||||
|
#define STREAMDEV_MAXDEVICES 8
|
||||||
|
class cStreamdevDevice;
|
||||||
|
|
||||||
class cPluginStreamdevClient : public cPlugin {
|
class cPluginStreamdevClient : public cPlugin {
|
||||||
private:
|
private:
|
||||||
static const char *DESCRIPTION;
|
static const char *DESCRIPTION;
|
||||||
|
cStreamdevDevice *m_Devices[STREAMDEV_MAXDEVICES];
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cPluginStreamdevClient(void);
|
cPluginStreamdevClient(void);
|
||||||
virtual ~cPluginStreamdevClient();
|
virtual ~cPluginStreamdevClient();
|
||||||
virtual const char *Version(void) { return VERSION; }
|
virtual const char *Version(void) { return VERSION; }
|
||||||
virtual const char *Description(void);
|
virtual const char *Description(void);
|
||||||
virtual bool Start(void);
|
virtual bool Initialize(void);
|
||||||
virtual const char *MainMenuEntry(void);
|
virtual const char *MainMenuEntry(void);
|
||||||
virtual cOsdObject *MainMenuAction(void);
|
virtual cOsdObject *MainMenuAction(void);
|
||||||
virtual cMenuSetupPage *SetupMenu(void);
|
virtual cMenuSetupPage *SetupMenu(void);
|
||||||
virtual bool SetupParse(const char *Name, const char *Value);
|
virtual bool SetupParse(const char *Name, const char *Value);
|
||||||
|
virtual bool Service(const char *Id, void *Data = NULL);
|
||||||
virtual void MainThreadHook(void);
|
virtual void MainThreadHook(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
2
common.c
2
common.c
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
const char *VERSION = "0.5.1-git";
|
const char *VERSION = "0.6.1-git";
|
||||||
|
|
||||||
const char cMenuEditIpItem::IpCharacters[] = "0123456789.";
|
const char cMenuEditIpItem::IpCharacters[] = "0123456789.";
|
||||||
|
|
||||||
|
|||||||
24
common.h
24
common.h
@@ -18,19 +18,22 @@
|
|||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
# define Dprintf(x...) fprintf(stderr, x)
|
#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);\
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
#define Dprintf(x...)
|
#define Dprintf(x...)
|
||||||
#endif
|
#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)
|
#define MAXPARSEBUFFER KILOBYTE(16)
|
||||||
|
|
||||||
|
/* Service ID for loop prevention */
|
||||||
|
#define LOOP_PREVENTION_SERVICE "StreamdevLoopPrevention"
|
||||||
|
|
||||||
/* Check if a channel is a radio station. */
|
/* Check if a channel is a radio station. */
|
||||||
#define ISRADIO(x) ((x)->Vpid()==0||(x)->Vpid()==1||(x)->Vpid()==0x1fff)
|
#define ISRADIO(x) ((x)->Vpid()==0||(x)->Vpid()==1||(x)->Vpid()==0x1fff)
|
||||||
|
|
||||||
@@ -46,13 +49,6 @@ enum eStreamType {
|
|||||||
st_Count
|
st_Count
|
||||||
};
|
};
|
||||||
|
|
||||||
enum eSuspendMode {
|
|
||||||
smOffer,
|
|
||||||
smAlways,
|
|
||||||
smNever,
|
|
||||||
sm_Count
|
|
||||||
};
|
|
||||||
|
|
||||||
enum eSocketId {
|
enum eSocketId {
|
||||||
siLive,
|
siLive,
|
||||||
siReplay,
|
siReplay,
|
||||||
|
|||||||
@@ -298,7 +298,6 @@ void write_pes(int fd, pes_packet *p){
|
|||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int find_length(int f){
|
static unsigned int find_length(int f){
|
||||||
uint64_t p = 0;
|
|
||||||
uint64_t start = 0;
|
uint64_t start = 0;
|
||||||
uint64_t q = 0;
|
uint64_t q = 0;
|
||||||
int found = 0;
|
int found = 0;
|
||||||
@@ -309,7 +308,7 @@ static unsigned int find_length(int f){
|
|||||||
start -=2;
|
start -=2;
|
||||||
lseek(f,start,SEEK_SET);
|
lseek(f,start,SEEK_SET);
|
||||||
while ( neof > 0 && !found ){
|
while ( neof > 0 && !found ){
|
||||||
p = lseek(f,0,SEEK_CUR);
|
lseek(f,0,SEEK_CUR);
|
||||||
neof = save_read(f,&sync4,4);
|
neof = save_read(f,&sync4,4);
|
||||||
if (sync4[0] == 0x00 && sync4[1] == 0x00 && sync4[2] == 0x01) {
|
if (sync4[0] == 0x00 && sync4[1] == 0x00 && sync4[2] == 0x01) {
|
||||||
switch ( sync4[3] ) {
|
switch ( sync4[3] ) {
|
||||||
@@ -558,7 +557,7 @@ int read_pes(int f, pes_packet *p){
|
|||||||
|
|
||||||
while (neof > 0 && !found) {
|
while (neof > 0 && !found) {
|
||||||
po = lseek(f,0,SEEK_CUR);
|
po = lseek(f,0,SEEK_CUR);
|
||||||
if (po < 0) return -1;
|
if (po == (off_t) -1) return -1;
|
||||||
if ((neof = save_read(f,&sync4,4)) < 4) return -1;
|
if ((neof = save_read(f,&sync4,4)) < 4) return -1;
|
||||||
if (sync4[0] == 0x00 && sync4[1] == 0x00 && sync4[2] == 0x01) {
|
if (sync4[0] == 0x00 && sync4[1] == 0x00 && sync4[2] == 0x01) {
|
||||||
p->stream_id = sync4[3];
|
p->stream_id = sync4[3];
|
||||||
@@ -1334,7 +1333,7 @@ void tfilter(trans *p)
|
|||||||
{
|
{
|
||||||
int l,c;
|
int l,c;
|
||||||
int tpid;
|
int tpid;
|
||||||
uint8_t flag,flags;
|
uint8_t flags;
|
||||||
uint8_t adapt_length = 0;
|
uint8_t adapt_length = 0;
|
||||||
uint8_t cpid[2];
|
uint8_t cpid[2];
|
||||||
|
|
||||||
@@ -1350,7 +1349,6 @@ void tfilter(trans *p)
|
|||||||
tpid);
|
tpid);
|
||||||
}
|
}
|
||||||
|
|
||||||
flag = cpid[0];
|
|
||||||
flags = p->packet[3];
|
flags = p->packet[3];
|
||||||
|
|
||||||
if ( flags & ADAPT_FIELD ) {
|
if ( flags & ADAPT_FIELD ) {
|
||||||
|
|||||||
@@ -756,7 +756,6 @@ int write_video_pes( Remux *rem, uint8_t *buf, int *vlength)
|
|||||||
int pos = 0;
|
int pos = 0;
|
||||||
int p = 0;
|
int p = 0;
|
||||||
uint32_t pts = 0;
|
uint32_t pts = 0;
|
||||||
uint32_t dts = 0;
|
|
||||||
int stuff = 0;
|
int stuff = 0;
|
||||||
int length = *vlength;
|
int length = *vlength;
|
||||||
long diff = 0;
|
long diff = 0;
|
||||||
@@ -787,7 +786,6 @@ int write_video_pes( Remux *rem, uint8_t *buf, int *vlength)
|
|||||||
if (add < 0) return -1;
|
if (add < 0) return -1;
|
||||||
pos += add;
|
pos += add;
|
||||||
rem->vpts_old = rem->vpts;
|
rem->vpts_old = rem->vpts;
|
||||||
dts = rem->vdts;
|
|
||||||
rem->vpts = rem->vpts_list[0].PTS;
|
rem->vpts = rem->vpts_list[0].PTS;
|
||||||
rem->vdts = rem->vpts_list[0].dts;
|
rem->vdts = rem->vpts_list[0].dts;
|
||||||
if ( diff > 0) rem->SCR += diff;
|
if ( diff > 0) rem->SCR += diff;
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
# 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);
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
# 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);
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
# 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)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
--- 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;
|
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "server/connection.h"
|
#include "server/connection.h"
|
||||||
#include "server/streamer.h"
|
#include "server/streamer.h"
|
||||||
#include <vdr/channels.h>
|
#include <vdr/channels.h>
|
||||||
|
#include <vdr/remux.h>
|
||||||
#include <vdr/tools.h>
|
#include <vdr/tools.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
@@ -25,7 +26,7 @@ protected:
|
|||||||
virtual void Action(void);
|
virtual void Action(void);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connection, const cChannel *Channel, const int *Apids, const int *Dpids);
|
cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connection, const cChannel *Channel, const cPatPmtParser *PatPmt, const int *Apids, const int *Dpids);
|
||||||
virtual ~cTSExt();
|
virtual ~cTSExt();
|
||||||
|
|
||||||
void Put(const uchar *Data, int Count);
|
void Put(const uchar *Data, int Count);
|
||||||
@@ -34,7 +35,7 @@ public:
|
|||||||
} // namespace Streamdev
|
} // namespace Streamdev
|
||||||
using namespace Streamdev;
|
using namespace Streamdev;
|
||||||
|
|
||||||
cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connection, const cChannel *Channel, const int *Apids, const int *Dpids):
|
cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connection, const cChannel *Channel, const cPatPmtParser *PatPmt, const int *Apids, const int *Dpids):
|
||||||
m_ResultBuffer(ResultBuffer),
|
m_ResultBuffer(ResultBuffer),
|
||||||
m_Active(false),
|
m_Active(false),
|
||||||
m_Process(-1),
|
m_Process(-1),
|
||||||
@@ -73,17 +74,24 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
|
|||||||
#define ADDENV(x...) if (asprintf(&env[i++], x) < 0) i--
|
#define ADDENV(x...) if (asprintf(&env[i++], x) < 0) i--
|
||||||
|
|
||||||
// add channel ID, name and pids to environment
|
// add channel ID, name and pids to environment
|
||||||
|
if (Channel) {
|
||||||
ADDENV("REMUX_CHANNEL_ID=%s", *Channel->GetChannelID().ToString());
|
ADDENV("REMUX_CHANNEL_ID=%s", *Channel->GetChannelID().ToString());
|
||||||
ADDENV("REMUX_CHANNEL_NAME=%s", Channel->Name());
|
ADDENV("REMUX_CHANNEL_NAME=%s", Channel->Name());
|
||||||
#if APIVERSNUM >= 10701
|
|
||||||
ADDENV("REMUX_VTYPE=%d", Channel->Vtype());
|
ADDENV("REMUX_VTYPE=%d", Channel->Vtype());
|
||||||
#endif
|
|
||||||
if (Channel->Vpid())
|
if (Channel->Vpid())
|
||||||
ADDENV("REMUX_VPID=%d", Channel->Vpid());
|
ADDENV("REMUX_VPID=%d", Channel->Vpid());
|
||||||
if (Channel->Ppid() != Channel->Vpid())
|
if (Channel->Ppid() != Channel->Vpid())
|
||||||
ADDENV("REMUX_PPID=%d", Channel->Ppid());
|
ADDENV("REMUX_PPID=%d", Channel->Ppid());
|
||||||
if (Channel->Tpid())
|
if (Channel->Tpid())
|
||||||
ADDENV("REMUX_TPID=%d", Channel->Tpid());
|
ADDENV("REMUX_TPID=%d", Channel->Tpid());
|
||||||
|
}
|
||||||
|
else if (PatPmt) {
|
||||||
|
ADDENV("REMUX_VTYPE=%d", PatPmt->Vtype());
|
||||||
|
if (PatPmt->Vpid())
|
||||||
|
ADDENV("REMUX_VPID=%d", PatPmt->Vpid());
|
||||||
|
if (PatPmt->Ppid() != PatPmt->Vpid())
|
||||||
|
ADDENV("REMUX_PPID=%d", PatPmt->Ppid());
|
||||||
|
}
|
||||||
|
|
||||||
std::string buffer;
|
std::string buffer;
|
||||||
if (Apids && *Apids) {
|
if (Apids && *Apids) {
|
||||||
@@ -94,10 +102,17 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
|
|||||||
buffer.clear();
|
buffer.clear();
|
||||||
for (const int *pid = Apids; *pid; pid++) {
|
for (const int *pid = Apids; *pid; pid++) {
|
||||||
int j;
|
int j;
|
||||||
|
if (Channel) {
|
||||||
for (j = 0; Channel->Apid(j) && Channel->Apid(j) != *pid; j++)
|
for (j = 0; Channel->Apid(j) && Channel->Apid(j) != *pid; j++)
|
||||||
;
|
;
|
||||||
(buffer += Channel->Alang(j)) += (*(pid + 1) ? " " : "");
|
(buffer += Channel->Alang(j)) += (*(pid + 1) ? " " : "");
|
||||||
}
|
}
|
||||||
|
else if (PatPmt) {
|
||||||
|
for (j = 0; PatPmt->Apid(j) && PatPmt->Apid(j) != *pid; j++)
|
||||||
|
;
|
||||||
|
(buffer += PatPmt->Alang(j)) += (*(pid + 1) ? " " : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
ADDENV("REMUX_ALANG=%s", buffer.c_str());
|
ADDENV("REMUX_ALANG=%s", buffer.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,14 +125,21 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
|
|||||||
buffer.clear();
|
buffer.clear();
|
||||||
for (const int *pid = Dpids; *pid; pid++) {
|
for (const int *pid = Dpids; *pid; pid++) {
|
||||||
int j;
|
int j;
|
||||||
|
if (Channel) {
|
||||||
for (j = 0; Channel->Dpid(j) && Channel->Dpid(j) != *pid; j++)
|
for (j = 0; Channel->Dpid(j) && Channel->Dpid(j) != *pid; j++)
|
||||||
;
|
;
|
||||||
(buffer += Channel->Dlang(j)) += (*(pid + 1) ? " " : "");
|
(buffer += Channel->Dlang(j)) += (*(pid + 1) ? " " : "");
|
||||||
}
|
}
|
||||||
|
else if (PatPmt) {
|
||||||
|
for (j = 0; PatPmt->Dpid(j) && PatPmt->Dpid(j) != *pid; j++)
|
||||||
|
;
|
||||||
|
(buffer += PatPmt->Dlang(j)) += (*(pid + 1) ? " " : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
ADDENV("REMUX_DLANG=%s", buffer.c_str());
|
ADDENV("REMUX_DLANG=%s", buffer.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Channel->Spid(0)) {
|
if (Channel && Channel->Spid(0)) {
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
for (const int *pid = Channel->Spids(); *pid; pid++)
|
for (const int *pid = Channel->Spids(); *pid; pid++)
|
||||||
(buffer += (const char *) itoa(*pid)) += (*(pid + 1) ? " " : "");
|
(buffer += (const char *) itoa(*pid)) += (*(pid + 1) ? " " : "");
|
||||||
@@ -128,6 +150,17 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
|
|||||||
(buffer += Channel->Slang(j)) += (Channel->Spid(j + 1) ? " " : "");
|
(buffer += Channel->Slang(j)) += (Channel->Spid(j + 1) ? " " : "");
|
||||||
ADDENV("REMUX_SLANG=%s", buffer.c_str());
|
ADDENV("REMUX_SLANG=%s", buffer.c_str());
|
||||||
}
|
}
|
||||||
|
else if (PatPmt && PatPmt->Spid(0)) {
|
||||||
|
buffer.clear();
|
||||||
|
for (const int *pid = PatPmt->Spids(); *pid; pid++)
|
||||||
|
(buffer += (const char *) itoa(*pid)) += (*(pid + 1) ? " " : "");
|
||||||
|
ADDENV("REMUX_SPID=%s", buffer.c_str());
|
||||||
|
|
||||||
|
buffer.clear();
|
||||||
|
for (int j = 0; PatPmt->Spid(j); j++)
|
||||||
|
(buffer += PatPmt->Slang(j)) += (PatPmt->Spid(j + 1) ? " " : "");
|
||||||
|
ADDENV("REMUX_SLANG=%s", buffer.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
if (Connection) {
|
if (Connection) {
|
||||||
// add vars for a CGI like interface
|
// add vars for a CGI like interface
|
||||||
@@ -270,7 +303,7 @@ void cTSExt::Action(void)
|
|||||||
dsyslog("streamdev-server: buffer full while reading from externremux");
|
dsyslog("streamdev-server: buffer full while reading from externremux");
|
||||||
|
|
||||||
if (result == -1) {
|
if (result == -1) {
|
||||||
if (errno != EINTR) {
|
if (errno != EINTR && errno != EAGAIN) {
|
||||||
LOG_ERROR_STR("read failed");
|
LOG_ERROR_STR("read failed");
|
||||||
m_Active = false;
|
m_Active = false;
|
||||||
}
|
}
|
||||||
@@ -297,8 +330,14 @@ void cTSExt::Put(const uchar *Data, int Count)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cExternRemux::cExternRemux(const cServerConnection *Connection, const cChannel *Channel, const int *Apids, const int *Dpids):
|
cExternRemux::cExternRemux(const cServerConnection *Connection, const cChannel *Channel, const int *Apids, const int *Dpids):
|
||||||
m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE, TS_SIZE * 2)),
|
m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE)),
|
||||||
m_Remux(new cTSExt(m_ResultBuffer, Connection, Channel, Apids, Dpids))
|
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->SetTimeouts(500, 100);
|
m_ResultBuffer->SetTimeouts(500, 100);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class cChannel;
|
class cChannel;
|
||||||
|
class cPatPmtParser;
|
||||||
class cServerConnection;
|
class cServerConnection;
|
||||||
|
|
||||||
namespace Streamdev {
|
namespace Streamdev {
|
||||||
@@ -19,6 +20,7 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
cExternRemux(const cServerConnection *Connection, const cChannel *Channel, const int *APids, const int *Dpids);
|
cExternRemux(const cServerConnection *Connection, const cChannel *Channel, const int *APids, const int *Dpids);
|
||||||
|
cExternRemux(const cServerConnection *Connection, const cPatPmtParser *PatPmt, const int *APids, const int *Dpids);
|
||||||
virtual ~cExternRemux();
|
virtual ~cExternRemux();
|
||||||
|
|
||||||
int Put(const uchar *Data, int Count);
|
int Put(const uchar *Data, int Count);
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#ifdef STREAMDEV_PS
|
||||||
|
|
||||||
#include "remux/ts2ps.h"
|
#include "remux/ts2ps.h"
|
||||||
#include "server/streamer.h"
|
#include "server/streamer.h"
|
||||||
#include <vdr/channels.h>
|
#include <vdr/channels.h>
|
||||||
@@ -216,3 +218,4 @@ uchar *cTS2PSRemux::Get(int &Count)
|
|||||||
return resultData;
|
return resultData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#ifdef STREAMDEV_PS
|
||||||
|
|
||||||
#ifndef VDR_STREAMDEV_TS2PSREMUX_H
|
#ifndef VDR_STREAMDEV_TS2PSREMUX_H
|
||||||
#define VDR_STREAMDEV_TS2PSREMUX_H
|
#define VDR_STREAMDEV_TS2PSREMUX_H
|
||||||
|
|
||||||
@@ -34,3 +36,5 @@ public:
|
|||||||
} // namespace Streamdev
|
} // namespace Streamdev
|
||||||
|
|
||||||
#endif // VDR_STREAMDEV_TS2PSREMUX_H
|
#endif // VDR_STREAMDEV_TS2PSREMUX_H
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
#
|
#
|
||||||
# Makefile for a Video Disk Recorder plugin
|
# Makefile for a Video Disk Recorder plugin
|
||||||
#
|
#
|
||||||
# $Id: Makefile,v 1.2 2010/07/19 13:49:31 schmirl Exp $
|
# $Id: $
|
||||||
|
|
||||||
# The official name of this plugin.
|
# The official name of this plugin.
|
||||||
# This name will be used in the '-P...' option of VDR to load the plugin.
|
# This name will be used in the '-P...' option of VDR to load the plugin.
|
||||||
# By default the main source file also carries this name.
|
# By default the main source file also carries this name.
|
||||||
#
|
|
||||||
PLUGIN = streamdev-server
|
PLUGIN = streamdev-server
|
||||||
|
|
||||||
|
### The name of the shared object file:
|
||||||
|
|
||||||
|
SOFILE = libvdr-$(PLUGIN).so
|
||||||
|
|
||||||
### Includes and Defines (add further entries here):
|
### Includes and Defines (add further entries here):
|
||||||
|
|
||||||
DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
|
DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
|
||||||
@@ -22,13 +26,12 @@ SERVEROBJS = $(PLUGIN).o \
|
|||||||
componentVTP.o connectionVTP.o \
|
componentVTP.o connectionVTP.o \
|
||||||
componentHTTP.o connectionHTTP.o menuHTTP.o \
|
componentHTTP.o connectionHTTP.o menuHTTP.o \
|
||||||
componentIGMP.o connectionIGMP.o \
|
componentIGMP.o connectionIGMP.o \
|
||||||
streamer.o livestreamer.o livefilter.o recplayer.o \
|
streamer.o livestreamer.o livefilter.o recstreamer.o recplayer.o \
|
||||||
suspend.o setup.o
|
menu.o suspend.o setup.o
|
||||||
|
|
||||||
### The main target:
|
### The main target:
|
||||||
|
|
||||||
.PHONY: all i18n clean
|
all: $(SOFILE) i18n
|
||||||
all: libvdr-$(PLUGIN).so i18n
|
|
||||||
|
|
||||||
### Implicit rules:
|
### Implicit rules:
|
||||||
|
|
||||||
@@ -39,44 +42,48 @@ all: libvdr-$(PLUGIN).so i18n
|
|||||||
|
|
||||||
MAKEDEP = $(CXX) -MM -MG
|
MAKEDEP = $(CXX) -MM -MG
|
||||||
DEPFILE = .dependencies
|
DEPFILE = .dependencies
|
||||||
|
|
||||||
$(DEPFILE): Makefile
|
$(DEPFILE): Makefile
|
||||||
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(SERVEROBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) > $@
|
@$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(SERVEROBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) > $@
|
||||||
|
|
||||||
-include $(DEPFILE)
|
-include $(DEPFILE)
|
||||||
|
|
||||||
### Internationalization (I18N):
|
### Internationalization (I18N):
|
||||||
|
|
||||||
PODIR = po
|
PODIR = po
|
||||||
LOCALEDIR = $(VDRDIR)/locale
|
|
||||||
I18Npo = $(wildcard $(PODIR)/*.po)
|
I18Npo = $(wildcard $(PODIR)/*.po)
|
||||||
I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
|
I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file))))
|
||||||
|
I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
|
||||||
I18Npot = $(PODIR)/$(PLUGIN).pot
|
I18Npot = $(PODIR)/$(PLUGIN).pot
|
||||||
|
|
||||||
%.mo: %.po
|
%.mo: %.po
|
||||||
msgfmt -c -o $@ $<
|
msgfmt -c -o $@ $<
|
||||||
|
|
||||||
$(I18Npot): $(SERVEROBJS:%.o=%.c)
|
$(I18Npot): $(SERVEROBJS:%.o=%.c)
|
||||||
xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --msgid-bugs-address='<http://www.vdr-developer.org/mantisbt/>' -o $@ $^
|
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 $^`
|
||||||
|
|
||||||
%.po: $(I18Npot)
|
%.po: $(I18Npot)
|
||||||
msgmerge -U --no-wrap --no-location --backup=none -q $@ $<
|
msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $<
|
||||||
@touch $@
|
@touch $@
|
||||||
|
|
||||||
$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
|
$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
|
||||||
@mkdir -p $(dir $@)
|
install -D -m644 $< $@
|
||||||
cp $< $@
|
|
||||||
|
|
||||||
i18n: $(I18Nmsgs)
|
.PHONY: i18n
|
||||||
|
i18n: $(I18Nmo) $(I18Npot)
|
||||||
|
|
||||||
|
install-i18n: $(I18Nmsgs)
|
||||||
|
|
||||||
### Targets:
|
### Targets:
|
||||||
|
|
||||||
libvdr-$(PLUGIN).so: $(SERVEROBJS) $(COMMONOBJS) \
|
$(SOFILE): $(SERVEROBJS) $(COMMONOBJS) \
|
||||||
../tools/sockettools.a ../remux/remux.a ../libdvbmpeg/libdvbmpegtools.a
|
../tools/sockettools.a ../remux/remux.a ../libdvbmpeg/libdvbmpegtools.a
|
||||||
|
$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $^ -o $@
|
||||||
|
|
||||||
%.so:
|
install-lib: $(SOFILE)
|
||||||
$(CXX) $(CXXFLAGS) -shared $^ -o $@
|
install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION)
|
||||||
@cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION)
|
|
||||||
|
install: install-lib install-i18n
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@-rm -f $(COMMONOBJS) $(SERVEROBJS) $(DEPFILE) $(PODIR)/*.mo $(PODIR)/*.pot *.so *.tgz core* *~
|
@-rm -f $(PODIR)/*.mo $(PODIR)/*.pot
|
||||||
|
@-rm -f $(COMMONOBJS) $(SERVEROBJS) $(DEPFILE) *.so *.tgz core* *~
|
||||||
|
|||||||
82
server/Makefile-1.7.33
Normal file
82
server/Makefile-1.7.33
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
#
|
||||||
|
# Makefile for a Video Disk Recorder plugin
|
||||||
|
#
|
||||||
|
# $Id: Makefile,v 1.2 2010/07/19 13:49:31 schmirl Exp $
|
||||||
|
|
||||||
|
# The official name of this plugin.
|
||||||
|
# This name will be used in the '-P...' option of VDR to load the plugin.
|
||||||
|
# By default the main source file also carries this name.
|
||||||
|
#
|
||||||
|
PLUGIN = streamdev-server
|
||||||
|
|
||||||
|
### Includes and Defines (add further entries here):
|
||||||
|
|
||||||
|
DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
|
||||||
|
|
||||||
|
### The object files (add further files here):
|
||||||
|
|
||||||
|
COMMONOBJS = ../common.o
|
||||||
|
|
||||||
|
SERVEROBJS = $(PLUGIN).o \
|
||||||
|
server.o component.o connection.o \
|
||||||
|
componentVTP.o connectionVTP.o \
|
||||||
|
componentHTTP.o connectionHTTP.o menuHTTP.o \
|
||||||
|
componentIGMP.o connectionIGMP.o \
|
||||||
|
streamer.o livestreamer.o livefilter.o 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* *~
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "server/componentIGMP.h"
|
#include "server/componentIGMP.h"
|
||||||
#include "server/connectionIGMP.h"
|
#include "server/connectionIGMP.h"
|
||||||
|
#include "server/server.h"
|
||||||
#include "server/setup.h"
|
#include "server/setup.h"
|
||||||
|
|
||||||
#ifndef IGMP_ALL_HOSTS
|
#ifndef IGMP_ALL_HOSTS
|
||||||
@@ -37,7 +38,6 @@
|
|||||||
class cMulticastGroup: public cListObject
|
class cMulticastGroup: public cListObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
cConnectionIGMP *connection;
|
|
||||||
in_addr_t group;
|
in_addr_t group;
|
||||||
in_addr_t reporter;
|
in_addr_t reporter;
|
||||||
struct timeval timeout;
|
struct timeval timeout;
|
||||||
@@ -48,7 +48,6 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
cMulticastGroup::cMulticastGroup(in_addr_t Group) :
|
cMulticastGroup::cMulticastGroup(in_addr_t Group) :
|
||||||
connection(NULL),
|
|
||||||
group(Group),
|
group(Group),
|
||||||
reporter(0)
|
reporter(0)
|
||||||
{
|
{
|
||||||
@@ -106,7 +105,11 @@ cComponentIGMP::~cComponentIGMP(void)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
cMulticastGroup* cComponentIGMP::FindGroup(in_addr_t Group)
|
||||||
|
#else
|
||||||
cMulticastGroup* cComponentIGMP::FindGroup(in_addr_t Group) const
|
cMulticastGroup* cComponentIGMP::FindGroup(in_addr_t Group) const
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
cMulticastGroup *group = m_Groups.First();
|
cMulticastGroup *group = m_Groups.First();
|
||||||
while (group && group->group != Group)
|
while (group && group->group != Group)
|
||||||
@@ -118,7 +121,12 @@ bool cComponentIGMP::Initialize(void)
|
|||||||
{
|
{
|
||||||
if (cServerComponent::Initialize() && IGMPMembership(IGMP_ALL_ROUTER))
|
if (cServerComponent::Initialize() && IGMPMembership(IGMP_ALL_ROUTER))
|
||||||
{
|
{
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
for (const cChannel *channel = Channels->First(); channel; channel = Channels->Next(channel))
|
||||||
|
#else
|
||||||
for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
|
for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
if (channel->GroupSep())
|
if (channel->GroupSep())
|
||||||
continue;
|
continue;
|
||||||
@@ -147,7 +155,12 @@ void cComponentIGMP::Destruct(void)
|
|||||||
Cancel(-1);
|
Cancel(-1);
|
||||||
m_CondWait.Signal();
|
m_CondWait.Signal();
|
||||||
Cancel(2);
|
Cancel(2);
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
for (const cChannel *channel = Channels->First(); channel; channel = Channels->Next(channel))
|
||||||
|
#else
|
||||||
for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
|
for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
if (channel->GroupSep())
|
if (channel->GroupSep())
|
||||||
continue;
|
continue;
|
||||||
@@ -235,10 +248,7 @@ cServerConnection* cComponentIGMP::ProcessMessage(struct igmp *Igmp, in_addr_t G
|
|||||||
group = new cMulticastGroup(Group);
|
group = new cMulticastGroup(Group);
|
||||||
m_Groups.Add(group);
|
m_Groups.Add(group);
|
||||||
}
|
}
|
||||||
if (!group->connection) {
|
conn = IGMPStartMulticast(group);
|
||||||
IGMPStartMulticast(group);
|
|
||||||
conn = group->connection;
|
|
||||||
}
|
|
||||||
IGMPStartTimer(group, Sender);
|
IGMPStartTimer(group, Sender);
|
||||||
if (Igmp->igmp_type == IGMP_V1_MEMBERSHIP_REPORT)
|
if (Igmp->igmp_type == IGMP_V1_MEMBERSHIP_REPORT)
|
||||||
IGMPStartV1HostTimer(group);
|
IGMPStartV1HostTimer(group);
|
||||||
@@ -430,20 +440,49 @@ void cComponentIGMP::IGMPSendGroupQuery(cMulticastGroup* Group)
|
|||||||
IGMPSendQuery(Group->group, IGMP_LAST_MEMBER_QUERY_INTERVAL_TS);
|
IGMPSendQuery(Group->group, IGMP_LAST_MEMBER_QUERY_INTERVAL_TS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cComponentIGMP::IGMPStartMulticast(cMulticastGroup* Group)
|
cServerConnection* cComponentIGMP::IGMPStartMulticast(cMulticastGroup* Group)
|
||||||
{
|
{
|
||||||
|
cServerConnection *conn = NULL;
|
||||||
in_addr_t g = ntohl(Group->group);
|
in_addr_t g = ntohl(Group->group);
|
||||||
if (g > MULTICAST_PRIV_MIN && g <= MULTICAST_PRIV_MAX) {
|
if (g > MULTICAST_PRIV_MIN && g <= MULTICAST_PRIV_MAX) {
|
||||||
|
cThreadLock lock;
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
const cChannel *channel = Channels->GetByNumber(g - MULTICAST_PRIV_MIN);
|
||||||
|
#else
|
||||||
cChannel *channel = Channels.GetByNumber(g - MULTICAST_PRIV_MIN);
|
cChannel *channel = Channels.GetByNumber(g - MULTICAST_PRIV_MIN);
|
||||||
Group->connection = (cConnectionIGMP*) NewClient();
|
#endif
|
||||||
if (!Group->connection->SetChannel(channel, Group->group)) {
|
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
||||||
DELETENULL(Group->connection);
|
#if APIVERSNUM >= 20300
|
||||||
|
const cServerConnection *s = clients.First();
|
||||||
|
#else
|
||||||
|
cServerConnection *s = clients.First();
|
||||||
|
#endif
|
||||||
|
while (s) {
|
||||||
|
if (s->RemoteIpAddr() == Group->group)
|
||||||
|
break;
|
||||||
|
s = clients.Next(s);
|
||||||
|
}
|
||||||
|
if (!s) {
|
||||||
|
conn = NewClient();
|
||||||
|
if (!((cConnectionIGMP *)conn)->SetChannel(channel, Group->group)) {
|
||||||
|
DELETENULL(conn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
void cComponentIGMP::IGMPStopMulticast(cMulticastGroup* Group)
|
void cComponentIGMP::IGMPStopMulticast(cMulticastGroup* Group)
|
||||||
{
|
{
|
||||||
if (Group->connection)
|
cThreadLock lock;
|
||||||
Group->connection->Stop();
|
#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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <vdr/thread.h>
|
#include <vdr/thread.h>
|
||||||
#include "server/component.h"
|
#include "server/component.h"
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
class cConnectionIGMP;
|
|
||||||
class cMulticastGroup;
|
class cMulticastGroup;
|
||||||
|
|
||||||
class cComponentIGMP: public cServerComponent, public cThread {
|
class cComponentIGMP: public cServerComponent, public cThread {
|
||||||
@@ -24,7 +24,11 @@ private:
|
|||||||
bool m_Querier;
|
bool m_Querier;
|
||||||
cCondWait m_CondWait;
|
cCondWait m_CondWait;
|
||||||
|
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
cMulticastGroup* FindGroup(in_addr_t Group);
|
||||||
|
#else
|
||||||
cMulticastGroup* FindGroup(in_addr_t Group) const;
|
cMulticastGroup* FindGroup(in_addr_t Group) const;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Add or remove local host to multicast group */
|
/* Add or remove local host to multicast group */
|
||||||
bool IGMPMembership(in_addr_t Group, bool Add = true);
|
bool IGMPMembership(in_addr_t Group, bool Add = true);
|
||||||
@@ -42,7 +46,7 @@ private:
|
|||||||
void IGMPStartRetransmitTimer(cMulticastGroup* Group);
|
void IGMPStartRetransmitTimer(cMulticastGroup* Group);
|
||||||
void IGMPClearRetransmitTimer(cMulticastGroup* Group);
|
void IGMPClearRetransmitTimer(cMulticastGroup* Group);
|
||||||
void IGMPSendGroupQuery(cMulticastGroup* Group);
|
void IGMPSendGroupQuery(cMulticastGroup* Group);
|
||||||
void IGMPStartMulticast(cMulticastGroup* Group);
|
cServerConnection* IGMPStartMulticast(cMulticastGroup* Group);
|
||||||
void IGMPStopMulticast(cMulticastGroup* Group);
|
void IGMPStopMulticast(cMulticastGroup* Group);
|
||||||
|
|
||||||
virtual void Action();
|
virtual void Action();
|
||||||
|
|||||||
@@ -9,59 +9,10 @@
|
|||||||
|
|
||||||
#include <vdr/tools.h>
|
#include <vdr/tools.h>
|
||||||
#include <vdr/thread.h>
|
#include <vdr/thread.h>
|
||||||
#include <vdr/transfer.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
class cSwitchLive {
|
|
||||||
private:
|
|
||||||
cMutex mutex;
|
|
||||||
cCondWait switched;
|
|
||||||
cDevice *device;
|
|
||||||
const cChannel *channel;
|
|
||||||
public:
|
|
||||||
cDevice* Switch(cDevice *Device, const cChannel *Channel);
|
|
||||||
void Switch(void);
|
|
||||||
cSwitchLive(void);
|
|
||||||
};
|
|
||||||
|
|
||||||
cSwitchLive::cSwitchLive(): device(NULL), channel(NULL)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
cDevice* cSwitchLive::Switch(cDevice *Device, const cChannel *Channel)
|
|
||||||
{
|
|
||||||
mutex.Lock();
|
|
||||||
device = Device;
|
|
||||||
channel = Channel;
|
|
||||||
mutex.Unlock();
|
|
||||||
switched.Wait();
|
|
||||||
return device;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cSwitchLive::Switch(void)
|
|
||||||
{
|
|
||||||
mutex.Lock();
|
|
||||||
if (channel && device) {
|
|
||||||
cDevice::SetAvoidDevice(device);
|
|
||||||
if (!Channels.SwitchTo(cDevice::CurrentChannel())) {
|
|
||||||
if (StreamdevServerSetup.SuspendMode == smAlways) {
|
|
||||||
Channels.SwitchTo(channel->Number());
|
|
||||||
Skins.Message(mtInfo, tr("Streaming active"));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
esyslog("streamdev: Can't receive channel %d (%s) from device %d. Moving live TV to other device failed (PrimaryDevice=%d, ActualDevice=%d)", channel->Number(), channel->Name(), device->CardIndex(), cDevice::PrimaryDevice()->CardIndex(), cDevice::ActualDevice()->CardIndex());
|
|
||||||
device = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// make sure we don't come in here next time
|
|
||||||
channel = NULL;
|
|
||||||
switched.Signal();
|
|
||||||
}
|
|
||||||
mutex.Unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
cServerConnection::cServerConnection(const char *Protocol, int Type):
|
cServerConnection::cServerConnection(const char *Protocol, int Type):
|
||||||
cTBSocket(Type),
|
cTBSocket(Type),
|
||||||
m_Protocol(Protocol),
|
m_Protocol(Protocol),
|
||||||
@@ -69,14 +20,14 @@ cServerConnection::cServerConnection(const char *Protocol, int Type):
|
|||||||
m_Pending(false),
|
m_Pending(false),
|
||||||
m_ReadBytes(0),
|
m_ReadBytes(0),
|
||||||
m_WriteBytes(0),
|
m_WriteBytes(0),
|
||||||
m_WriteIndex(0)
|
m_WriteIndex(0),
|
||||||
|
m_Streamer(NULL)
|
||||||
{
|
{
|
||||||
m_SwitchLive = new cSwitchLive();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cServerConnection::~cServerConnection()
|
cServerConnection::~cServerConnection()
|
||||||
{
|
{
|
||||||
delete m_SwitchLive;
|
delete(m_Streamer);
|
||||||
}
|
}
|
||||||
|
|
||||||
const cChannel* cServerConnection::ChannelFromString(const char *String, int *Apid, int *Dpid) {
|
const cChannel* cServerConnection::ChannelFromString(const char *String, int *Apid, int *Dpid) {
|
||||||
@@ -93,14 +44,31 @@ const cChannel* cServerConnection::ChannelFromString(const char *String, int *Ap
|
|||||||
|
|
||||||
if (isnumber(string)) {
|
if (isnumber(string)) {
|
||||||
int temp = strtol(String, NULL, 10);
|
int temp = strtol(String, NULL, 10);
|
||||||
|
if (temp == 0)
|
||||||
|
temp = cDevice::CurrentChannel();
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
if (temp >= 1 && temp <= Channels->MaxNumber())
|
||||||
|
channel = Channels->GetByNumber(temp);
|
||||||
|
#else
|
||||||
if (temp >= 1 && temp <= Channels.MaxNumber())
|
if (temp >= 1 && temp <= Channels.MaxNumber())
|
||||||
channel = Channels.GetByNumber(temp);
|
channel = Channels.GetByNumber(temp);
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
channel = Channels->GetByChannelID(tChannelID::FromString(string));
|
||||||
|
#else
|
||||||
channel = Channels.GetByChannelID(tChannelID::FromString(string));
|
channel = Channels.GetByChannelID(tChannelID::FromString(string));
|
||||||
|
#endif
|
||||||
|
|
||||||
if (channel == NULL) {
|
if (channel == NULL) {
|
||||||
int i = 1;
|
int i = 1;
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
while ((channel = Channels->GetByNumber(i, 1)) != NULL) {
|
||||||
|
#else
|
||||||
while ((channel = Channels.GetByNumber(i, 1)) != NULL) {
|
while ((channel = Channels.GetByNumber(i, 1)) != NULL) {
|
||||||
|
#endif
|
||||||
if (String == channel->Name())
|
if (String == channel->Name())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -237,173 +205,14 @@ bool cServerConnection::Respond(const char *Message, bool Last, ...)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if APIVERSNUM >= 10700
|
bool cServerConnection::Close()
|
||||||
static int GetClippedNumProvidedSystems(int AvailableBits, cDevice *Device)
|
|
||||||
{
|
{
|
||||||
int MaxNumProvidedSystems = (1 << AvailableBits) - 1;
|
if (IsOpen())
|
||||||
int NumProvidedSystems = Device->NumProvidedSystems();
|
isyslog("streamdev-server: closing %s connection to %s:%d", Protocol(), RemoteIp().c_str(), RemotePort());
|
||||||
if (NumProvidedSystems > MaxNumProvidedSystems) {
|
return cTBSocket::Close();
|
||||||
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
|
|
||||||
|
|
||||||
/*
|
cString cServerConnection::ToText(char Delimiter) const
|
||||||
* 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;
|
return cString::sprintf("%s%c%s:%d", Protocol(), Delimiter, RemoteIp().c_str(), RemotePort());
|
||||||
//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();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,15 +7,15 @@
|
|||||||
|
|
||||||
#include "tools/socket.h"
|
#include "tools/socket.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "server/streamer.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
typedef std::map<std::string,std::string> tStrStrMap;
|
typedef std::map<std::string,std::string> tStrStrMap;
|
||||||
typedef std::pair<std::string,std::string> tStrStr;
|
typedef std::pair<std::string,std::string> tStrStr;
|
||||||
|
|
||||||
class cChannel;
|
class cChannel;
|
||||||
class cDevice;
|
|
||||||
class cSwitchLive;
|
|
||||||
|
|
||||||
/* Basic capabilities of a straight text-based protocol, most functions
|
/* Basic capabilities of a straight text-based protocol, most functions
|
||||||
virtual to support more complicated protocols */
|
virtual to support more complicated protocols */
|
||||||
@@ -34,19 +34,10 @@ private:
|
|||||||
uint m_WriteBytes;
|
uint m_WriteBytes;
|
||||||
uint m_WriteIndex;
|
uint m_WriteIndex;
|
||||||
|
|
||||||
cSwitchLive *m_SwitchLive;
|
cStreamdevStreamer *m_Streamer;
|
||||||
|
|
||||||
tStrStrMap m_Headers;
|
tStrStrMap m_Headers;
|
||||||
|
|
||||||
/* Check if a device would be available for transfering the given
|
|
||||||
channel. This call has no side effects except for temporarily
|
|
||||||
detaching this connection's receivers. */
|
|
||||||
cDevice *CheckDevice(const cChannel *Channel, int Priority, bool LiveView, const cDevice *AvoidDevice = NULL);
|
|
||||||
|
|
||||||
/* Test if device is in use as the transfer mode receiver device
|
|
||||||
or a FF card, displaying live TV from internal tuner */
|
|
||||||
static bool UsedByLiveTV(cDevice *device);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/* Will be called when a command terminated by a newline has been
|
/* Will be called when a command terminated by a newline has been
|
||||||
received */
|
received */
|
||||||
@@ -63,6 +54,12 @@ protected:
|
|||||||
/* Add a request header */
|
/* Add a request header */
|
||||||
void SetHeader(const char *Name, const char *Value, const char *Prefix = "") { m_Headers.insert(tStrStr(std::string(Prefix) + Name, Value)); }
|
void SetHeader(const char *Name, const char *Value, const char *Prefix = "") { m_Headers.insert(tStrStr(std::string(Prefix) + Name, Value)); }
|
||||||
|
|
||||||
|
/* Set the streamer */
|
||||||
|
void SetStreamer(cStreamdevStreamer* Streamer) { delete m_Streamer; m_Streamer = Streamer; }
|
||||||
|
|
||||||
|
/* Return the streamer */
|
||||||
|
cStreamdevStreamer *Streamer() const { return m_Streamer; }
|
||||||
|
|
||||||
static const cChannel *ChannelFromString(const char *String, int *Apid = NULL, int *Dpid = NULL);
|
static const cChannel *ChannelFromString(const char *String, int *Apid = NULL, int *Dpid = NULL);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -103,25 +100,17 @@ public:
|
|||||||
/* Will make the socket close after sending all queued output data */
|
/* Will make the socket close after sending all queued output data */
|
||||||
void DeferClose(void) { m_DeferClose = true; }
|
void DeferClose(void) { m_DeferClose = true; }
|
||||||
|
|
||||||
/* Will retrieve an unused device for transmitting data. Receivers have
|
/* Close the socket */
|
||||||
already been attached from the device if necessary. Use the returned
|
virtual bool Close(void);
|
||||||
cDevice in a following call to StartTransfer */
|
|
||||||
cDevice *GetDevice(const cChannel *Channel, int Priority);
|
|
||||||
|
|
||||||
/* Test if a call to GetDevice would return a usable device. */
|
|
||||||
bool ProvidesChannel(const cChannel *Channel, int Priority);
|
|
||||||
|
|
||||||
/* Do things which must be done in VDR's main loop */
|
|
||||||
void MainThreadHook();
|
|
||||||
|
|
||||||
virtual void Flushed(void) {}
|
virtual void Flushed(void) {}
|
||||||
|
|
||||||
virtual void Detach(void) = 0;
|
|
||||||
virtual void Attach(void) = 0;
|
|
||||||
|
|
||||||
/* This connections protocol name */
|
/* This connections protocol name */
|
||||||
virtual const char* Protocol(void) const { return m_Protocol; }
|
virtual const char* Protocol(void) const { return m_Protocol; }
|
||||||
|
|
||||||
|
/* Text description of stream */
|
||||||
|
virtual cString ToText(char Delimiter = ' ') const;
|
||||||
|
|
||||||
/* std::map with additional information */
|
/* std::map with additional information */
|
||||||
const tStrStrMap& Headers(void) const { return m_Headers; }
|
const tStrStrMap& Headers(void) const { return m_Headers; }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <vdr/thread.h>
|
||||||
|
#include <vdr/recording.h>
|
||||||
|
|
||||||
#include "server/connectionHTTP.h"
|
#include "server/connectionHTTP.h"
|
||||||
#include "server/menuHTTP.h"
|
#include "server/menuHTTP.h"
|
||||||
@@ -12,10 +21,12 @@
|
|||||||
cConnectionHTTP::cConnectionHTTP(void):
|
cConnectionHTTP::cConnectionHTTP(void):
|
||||||
cServerConnection("HTTP"),
|
cServerConnection("HTTP"),
|
||||||
m_Status(hsRequest),
|
m_Status(hsRequest),
|
||||||
m_LiveStreamer(NULL),
|
|
||||||
m_Channel(NULL),
|
|
||||||
m_StreamType((eStreamType)StreamdevServerSetup.HTTPStreamType),
|
m_StreamType((eStreamType)StreamdevServerSetup.HTTPStreamType),
|
||||||
m_ChannelList(NULL)
|
m_Channel(NULL),
|
||||||
|
m_RecPlayer(NULL),
|
||||||
|
m_ReplayPos(0),
|
||||||
|
m_ReplayFakeRange(false),
|
||||||
|
m_MenuList(NULL)
|
||||||
{
|
{
|
||||||
Dprintf("constructor hsRequest\n");
|
Dprintf("constructor hsRequest\n");
|
||||||
m_Apid[0] = m_Apid[1] = 0;
|
m_Apid[0] = m_Apid[1] = 0;
|
||||||
@@ -24,7 +35,9 @@ cConnectionHTTP::cConnectionHTTP(void):
|
|||||||
|
|
||||||
cConnectionHTTP::~cConnectionHTTP()
|
cConnectionHTTP::~cConnectionHTTP()
|
||||||
{
|
{
|
||||||
delete m_LiveStreamer;
|
SetStreamer(NULL);
|
||||||
|
delete m_RecPlayer;
|
||||||
|
delete m_MenuList;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cConnectionHTTP::CanAuthenticate(void)
|
bool cConnectionHTTP::CanAuthenticate(void)
|
||||||
@@ -48,9 +61,24 @@ bool cConnectionHTTP::Command(char *Cmd)
|
|||||||
*v = 0;
|
*v = 0;
|
||||||
SetHeader("REQUEST_METHOD", Cmd);
|
SetHeader("REQUEST_METHOD", Cmd);
|
||||||
q = strchr(p, '?');
|
q = strchr(p, '?');
|
||||||
if (q)
|
if (q) {
|
||||||
*q = 0;
|
*q = 0;
|
||||||
SetHeader("QUERY_STRING", q ? ++q : "");
|
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("PATH_INFO", p);
|
SetHeader("PATH_INFO", p);
|
||||||
m_Status = hsHeaders;
|
m_Status = hsHeaders;
|
||||||
return true;
|
return true;
|
||||||
@@ -133,97 +161,260 @@ bool cConnectionHTTP::ProcessRequest(void)
|
|||||||
}
|
}
|
||||||
if (!authOk) {
|
if (!authOk) {
|
||||||
isyslog("streamdev-server: HTTP authorization required");
|
isyslog("streamdev-server: HTTP authorization required");
|
||||||
DeferClose();
|
return HttpResponse(401, true, NULL, "WWW-authenticate: basic Realm=\"Streamdev-Server\"");
|
||||||
return Respond("HTTP/1.0 401 Authorization Required")
|
|
||||||
&& Respond("WWW-authenticate: basic Realm=\"Streamdev-Server\")")
|
|
||||||
&& Respond("");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tStrStrMap::const_iterator it;
|
||||||
|
it = m_Params.find("apid");
|
||||||
|
if (it != m_Params.end())
|
||||||
|
m_Apid[0] = atoi(it->second.c_str());
|
||||||
|
it = m_Params.find("dpid");
|
||||||
|
if (it != m_Params.end())
|
||||||
|
m_Dpid[0] = atoi(it->second.c_str());
|
||||||
|
|
||||||
tStrStrMap::const_iterator it_method = Headers().find(REQUEST_METHOD);
|
tStrStrMap::const_iterator it_method = Headers().find(REQUEST_METHOD);
|
||||||
tStrStrMap::const_iterator it_pathinfo = Headers().find(PATH_INFO);
|
tStrStrMap::const_iterator it_pathinfo = Headers().find(PATH_INFO);
|
||||||
if (it_method == Headers().end() || it_pathinfo == Headers().end()) {
|
if (it_method == Headers().end() || it_pathinfo == Headers().end()) {
|
||||||
// should never happen
|
// should never happen
|
||||||
esyslog("streamdev-server connectionHTTP: Missing method or pathinfo");
|
esyslog("streamdev-server connectionHTTP: Missing method or pathinfo");
|
||||||
} else if (it_method->second.compare("GET") == 0 && ProcessURI(it_pathinfo->second)) {
|
} else if (it_method->second.compare("GET") == 0 && ProcessURI(it_pathinfo->second)) {
|
||||||
if (m_ChannelList)
|
if (m_MenuList)
|
||||||
return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
|
return Respond("%s", true, m_MenuList->HttpHeader().c_str());
|
||||||
else if (m_Channel != NULL) {
|
else if (m_Channel != NULL) {
|
||||||
cDevice *device = NULL;
|
if (cStreamdevLiveStreamer::ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority)) {
|
||||||
if (ProvidesChannel(m_Channel, 0))
|
cStreamdevLiveStreamer* liveStreamer = new cStreamdevLiveStreamer(this, m_Channel, StreamdevServerSetup.HTTPPriority, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
|
||||||
device = GetDevice(m_Channel, 0);
|
if (liveStreamer->GetDevice()) {
|
||||||
if (device != NULL) {
|
SetStreamer(liveStreamer);
|
||||||
device->SwitchChannel(m_Channel, false);
|
|
||||||
m_LiveStreamer = new cStreamdevLiveStreamer(0, this);
|
|
||||||
if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL)) {
|
|
||||||
m_LiveStreamer->SetDevice(device);
|
|
||||||
if (!SetDSCP())
|
if (!SetDSCP())
|
||||||
LOG_ERROR_STR("unable to set DSCP sockopt");
|
LOG_ERROR_STR("unable to set DSCP sockopt");
|
||||||
if (m_StreamType == stEXT) {
|
if (m_StreamType == stEXT) {
|
||||||
return Respond("HTTP/1.0 200 OK");
|
return Respond("HTTP/1.0 200 OK");
|
||||||
} else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) {
|
} else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) {
|
||||||
return Respond("HTTP/1.0 200 OK")
|
return HttpResponse(200, false, "audio/mpeg", "icy-name: %s", m_Channel->Name());
|
||||||
&& Respond("Content-Type: audio/mpeg")
|
|
||||||
&& Respond("icy-name: %s", true, m_Channel->Name())
|
|
||||||
&& Respond("");
|
|
||||||
} else if (ISRADIO(m_Channel)) {
|
} else if (ISRADIO(m_Channel)) {
|
||||||
return Respond("HTTP/1.0 200 OK")
|
return HttpResponse(200, false, "audio/mpeg");
|
||||||
&& Respond("Content-Type: audio/mpeg")
|
|
||||||
&& Respond("");
|
|
||||||
} else {
|
} else {
|
||||||
return Respond("HTTP/1.0 200 OK")
|
return HttpResponse(200, false, "video/mpeg");
|
||||||
&& Respond("Content-Type: video/mpeg")
|
|
||||||
&& Respond("");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DELETENULL(m_LiveStreamer);
|
SetStreamer(NULL);
|
||||||
|
delete liveStreamer;
|
||||||
}
|
}
|
||||||
DeferClose();
|
return HttpResponse(503, true);
|
||||||
return Respond("HTTP/1.0 409 Channel not available")
|
}
|
||||||
&& Respond("");
|
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");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
DeferClose();
|
return HttpResponse(404, true);
|
||||||
return Respond("HTTP/1.0 404 not found")
|
|
||||||
&& Respond("");
|
|
||||||
}
|
}
|
||||||
} else if (it_method->second.compare("HEAD") == 0 && ProcessURI(it_pathinfo->second)) {
|
} else if (it_method->second.compare("HEAD") == 0 && ProcessURI(it_pathinfo->second)) {
|
||||||
|
if (m_MenuList) {
|
||||||
DeferClose();
|
DeferClose();
|
||||||
if (m_ChannelList)
|
return Respond("%s", true, m_MenuList->HttpHeader().c_str());
|
||||||
return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
|
}
|
||||||
else if (m_Channel != NULL) {
|
else if (m_Channel != NULL) {
|
||||||
if (ProvidesChannel(m_Channel, 0)) {
|
if (cStreamdevLiveStreamer::ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority)) {
|
||||||
if (m_StreamType == stEXT) {
|
if (m_StreamType == stEXT) {
|
||||||
// TODO
|
cStreamdevLiveStreamer *liveStreamer = new cStreamdevLiveStreamer(this, m_Channel, IDLEPRIORITY, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
|
||||||
return Respond("HTTP/1.0 200 OK")
|
SetStreamer(liveStreamer);
|
||||||
&& Respond("");
|
return Respond("HTTP/1.0 200 OK");
|
||||||
} else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) {
|
} else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) {
|
||||||
return Respond("HTTP/1.0 200 OK")
|
return HttpResponse(200, true, "audio/mpeg", "icy-name: %s", m_Channel->Name());
|
||||||
&& Respond("Content-Type: audio/mpeg")
|
|
||||||
&& Respond("icy-name: %s", true, m_Channel->Name())
|
|
||||||
&& Respond("");
|
|
||||||
} else if (ISRADIO(m_Channel)) {
|
} else if (ISRADIO(m_Channel)) {
|
||||||
return Respond("HTTP/1.0 200 OK")
|
return HttpResponse(200, true, "audio/mpeg");
|
||||||
&& Respond("Content-Type: audio/mpeg")
|
|
||||||
&& Respond("");
|
|
||||||
} else {
|
} else {
|
||||||
return Respond("HTTP/1.0 200 OK")
|
return HttpResponse(200, true, "video/mpeg");
|
||||||
&& Respond("Content-Type: video/mpeg")
|
|
||||||
&& Respond("");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Respond("HTTP/1.0 409 Channel not available")
|
return HttpResponse(503, true);
|
||||||
&& Respond("");
|
}
|
||||||
|
else if (m_RecPlayer != NULL) {
|
||||||
|
Dprintf("HEAD recording\n");
|
||||||
|
bool isPes = m_RecPlayer->getCurrentRecording()->IsPesRecording();
|
||||||
|
// no remuxing for old PES recordings
|
||||||
|
if (isPes && m_StreamType != stPES)
|
||||||
|
return HttpResponse(503, true);
|
||||||
|
|
||||||
|
int64_t from, to;
|
||||||
|
bool hasRange = ParseRange(from, to);
|
||||||
|
|
||||||
|
cStreamdevRecStreamer* recStreamer;
|
||||||
|
if (from == 0 && hasRange && m_ReplayFakeRange) {
|
||||||
|
recStreamer = new cStreamdevRecStreamer(this, m_RecPlayer, m_StreamType, m_ReplayPos, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
|
||||||
|
from += m_ReplayPos;
|
||||||
|
if (to >= 0)
|
||||||
|
to += m_ReplayPos;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
recStreamer = new cStreamdevRecStreamer(this, m_RecPlayer, m_StreamType, m_ReplayPos, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
|
||||||
|
SetStreamer(recStreamer);
|
||||||
|
|
||||||
|
if (m_StreamType == stEXT)
|
||||||
|
return Respond("HTTP/1.0 200 OK");
|
||||||
|
else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0]))
|
||||||
|
return HttpResponse(200, true, "audio/mpeg");
|
||||||
|
|
||||||
|
const char* contentType = (isPes || m_RecPlayer->getPatPmtData()->Vpid()) ? "video/mpeg" : "audio/mpeg";
|
||||||
|
// range not supported when remuxing
|
||||||
|
if (m_StreamType != stTS && !isPes)
|
||||||
|
return HttpResponse(200, false, contentType);
|
||||||
|
|
||||||
|
uint64_t total = recStreamer->GetLength();
|
||||||
|
if (hasRange) {
|
||||||
|
int64_t length = recStreamer->SetRange(from, to);
|
||||||
|
if (length < 0L)
|
||||||
|
return HttpResponse(416, true, contentType, "Accept-Ranges: bytes\r\nContent-Range: bytes */%llu", (unsigned long long) total);
|
||||||
|
else
|
||||||
|
return HttpResponse(206, true, contentType, "Accept-Ranges: bytes\r\nContent-Range: bytes %lld-%lld/%llu\r\nContent-Length: %lld", (long long) from, (long long) to, (unsigned long long) total, (long long) length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return HttpResponse(200, true, contentType, "Accept-Ranges: bytes");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return Respond("HTTP/1.0 404 not found")
|
return HttpResponse(404, true);
|
||||||
&& Respond("");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return HttpResponse(400, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *AAA[] = {
|
||||||
|
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
||||||
|
};
|
||||||
|
static const char *MMM[] = {
|
||||||
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||||
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||||
|
};
|
||||||
|
|
||||||
|
bool cConnectionHTTP::HttpResponse(int Code, bool Last, const char* ContentType, const char* Headers, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, Headers);
|
||||||
|
#if APIVERSNUM >= 10728
|
||||||
|
cString headers = cString::vsprintf(Headers, ap);
|
||||||
|
#else
|
||||||
|
cString headers = cString::sprintf(Headers, ap);
|
||||||
|
#endif
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
bool rc;
|
||||||
|
if (Last)
|
||||||
DeferClose();
|
DeferClose();
|
||||||
return Respond("HTTP/1.0 400 Bad Request")
|
switch (Code)
|
||||||
&& Respond("");
|
{
|
||||||
|
case 200: rc = Respond("HTTP/1.1 200 OK"); break;
|
||||||
|
case 206: rc = Respond("HTTP/1.1 206 Partial Content"); break;
|
||||||
|
case 400: rc = Respond("HTTP/1.1 400 Bad Request"); break;
|
||||||
|
case 401: rc = Respond("HTTP/1.1 401 Authorization Required"); break;
|
||||||
|
case 404: rc = Respond("HTTP/1.1 404 Not Found"); break;
|
||||||
|
case 416: rc = Respond("HTTP/1.1 416 Requested range not satisfiable"); break;
|
||||||
|
case 503: rc = Respond("HTTP/1.1 503 Service Unavailable"); break;
|
||||||
|
default: rc = Respond("HTTP/1.1 500 Internal Server Error");
|
||||||
|
}
|
||||||
|
if (rc && ContentType)
|
||||||
|
rc = Respond("Content-Type: %s", true, ContentType);
|
||||||
|
|
||||||
|
if (rc)
|
||||||
|
rc = Respond("Connection: close")
|
||||||
|
&& Respond("Pragma: no-cache")
|
||||||
|
&& Respond("Cache-Control: no-cache")
|
||||||
|
&& Respond("Server: VDR-%s / streamdev-server-%s", true, VDRVERSION, VERSION);
|
||||||
|
|
||||||
|
time_t t = time(NULL);
|
||||||
|
struct tm *gmt = gmtime(&t);
|
||||||
|
if (rc && gmt) {
|
||||||
|
char buf[] = "Date: AAA, DD MMM YYYY HH:MM:SS GMT";
|
||||||
|
if (snprintf(buf, sizeof(buf), "Date: %s, %.2d %s %.4d %.2d:%.2d:%.2d GMT", AAA[gmt->tm_wday], gmt->tm_mday, MMM[gmt->tm_mon], gmt->tm_year + 1900, gmt->tm_hour, gmt->tm_min, gmt->tm_sec) == sizeof(buf) - 1)
|
||||||
|
rc = Respond(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc && strlen(Headers) > 0)
|
||||||
|
rc = Respond(headers);
|
||||||
|
|
||||||
|
tStrStrMap::iterator it = m_Params.begin();
|
||||||
|
while (rc && it != m_Params.end()) {
|
||||||
|
static const char DLNA_POSTFIX[] = ".dlna.org";
|
||||||
|
if (it->first.rfind(DLNA_POSTFIX) + sizeof(DLNA_POSTFIX) - 1 == it->first.length())
|
||||||
|
rc = Respond("%s: %s", true, it->first.c_str(), it->second.c_str());
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
return rc && Respond("");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cConnectionHTTP::ParseRange(int64_t &From, int64_t &To) const
|
||||||
|
{
|
||||||
|
const static std::string RANGE("HTTP_RANGE");
|
||||||
|
From = To = 0L;
|
||||||
|
tStrStrMap::const_iterator it = Headers().find(RANGE);
|
||||||
|
if (it != Headers().end()) {
|
||||||
|
size_t b = it->second.find("bytes=");
|
||||||
|
if (b != std::string::npos) {
|
||||||
|
char* e = NULL;
|
||||||
|
const char* r = it->second.c_str() + b + sizeof("bytes=") - 1;
|
||||||
|
if (strchr(r, ',') != NULL)
|
||||||
|
esyslog("streamdev-server cConnectionHTTP::GetRange: Multi-ranges not supported");
|
||||||
|
From = strtol(r, &e, 10);
|
||||||
|
if (r != e) {
|
||||||
|
if (From < 0L) {
|
||||||
|
To = -1L;
|
||||||
|
return *e == 0 || *e == ',';
|
||||||
|
}
|
||||||
|
else if (*e == '-') {
|
||||||
|
r = e + 1;
|
||||||
|
if (*r == 0 || *e == ',') {
|
||||||
|
To = -1L;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
To = strtol(r, &e, 10);
|
||||||
|
return r != e && To >= From &&
|
||||||
|
(*e == 0 || *e == ',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cConnectionHTTP::Flushed(void)
|
void cConnectionHTTP::Flushed(void)
|
||||||
@@ -231,21 +422,21 @@ void cConnectionHTTP::Flushed(void)
|
|||||||
if (m_Status != hsBody)
|
if (m_Status != hsBody)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (m_ChannelList) {
|
if (m_MenuList) {
|
||||||
if (m_ChannelList->HasNext()) {
|
if (m_MenuList->HasNext()) {
|
||||||
if (!Respond("%s", true, m_ChannelList->Next().c_str()))
|
if (!Respond("%s", true, m_MenuList->Next().c_str()))
|
||||||
DeferClose();
|
DeferClose();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
DELETENULL(m_ChannelList);
|
DELETENULL(m_MenuList);
|
||||||
m_Status = hsFinished;
|
m_Status = hsFinished;
|
||||||
DeferClose();
|
DeferClose();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (m_Channel != NULL) {
|
else if (Streamer()) {
|
||||||
Dprintf("streamer start\n");
|
Dprintf("streamer start\n");
|
||||||
m_LiveStreamer->Start(this);
|
Streamer()->Start(this);
|
||||||
m_Status = hsFinished;
|
m_Status = hsFinished;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -255,49 +446,35 @@ void cConnectionHTTP::Flushed(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cChannelList* cConnectionHTTP::ChannelListFromString(const std::string& Path, const std::string& Filebase, const std::string& Fileext) const
|
cMenuList* cConnectionHTTP::MenuListFromString(const std::string& Path, const std::string& Filebase, const std::string& Fileext) const
|
||||||
{
|
{
|
||||||
// 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;
|
std::string groupTarget;
|
||||||
cChannelIterator *iterator = NULL;
|
cItemIterator *iterator = NULL;
|
||||||
|
|
||||||
|
const static std::string GROUP("group");
|
||||||
if (Filebase.compare("tree") == 0) {
|
if (Filebase.compare("tree") == 0) {
|
||||||
const cChannel* c = NULL;
|
tStrStrMap::const_iterator it = m_Params.find(GROUP);
|
||||||
size_t groupIndex = query.find("group=");
|
iterator = new cListTree(it == m_Params.end() ? NULL : it->second.c_str());
|
||||||
if (groupIndex != std::string::npos)
|
|
||||||
c = cChannelList::GetGroup(atoi(query.c_str() + groupIndex + 6));
|
|
||||||
iterator = new cListTree(c);
|
|
||||||
groupTarget = Filebase + Fileext;
|
groupTarget = Filebase + Fileext;
|
||||||
} else if (Filebase.compare("groups") == 0) {
|
} else if (Filebase.compare("groups") == 0) {
|
||||||
iterator = new cListGroups();
|
iterator = new cListGroups();
|
||||||
groupTarget = (std::string) "group" + Fileext;
|
groupTarget = (std::string) "group" + Fileext;
|
||||||
} else if (Filebase.compare("group") == 0) {
|
} else if (Filebase.compare("group") == 0) {
|
||||||
const cChannel* c = NULL;
|
tStrStrMap::const_iterator it = m_Params.find(GROUP);
|
||||||
size_t groupIndex = query.find("group=");
|
iterator = new cListGroup(it == m_Params.end() ? NULL : it->second.c_str());
|
||||||
if (groupIndex != std::string::npos)
|
|
||||||
c = cChannelList::GetGroup(atoi(query.c_str() + groupIndex + 6));
|
|
||||||
iterator = new cListGroup(c);
|
|
||||||
} else if (Filebase.compare("channels") == 0) {
|
} else if (Filebase.compare("channels") == 0) {
|
||||||
iterator = new cListChannels();
|
iterator = new cListChannels();
|
||||||
} else if (Filebase.compare("all") == 0 ||
|
} else if (Filebase.compare("all") == 0 ||
|
||||||
(Filebase.empty() && Fileext.empty())) {
|
(Filebase.empty() && Fileext.empty())) {
|
||||||
iterator = new cListAll();
|
iterator = new cListAll();
|
||||||
|
} else if (Filebase.compare("recordings") == 0) {
|
||||||
|
iterator = new cRecordingsIterator(m_StreamType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iterator) {
|
if (iterator) {
|
||||||
if (Filebase.empty() || Fileext.compare(".htm") == 0 || Fileext.compare(".html") == 0) {
|
// assemble base url: http://host/path/
|
||||||
std::string self = Filebase + Fileext;
|
|
||||||
if (!query.empty())
|
|
||||||
self += '?' + query;
|
|
||||||
return new cHtmlChannelList(iterator, m_StreamType, self.c_str(), groupTarget.c_str());
|
|
||||||
} else if (Fileext.compare(".m3u") == 0) {
|
|
||||||
std::string base;
|
std::string base;
|
||||||
|
const static std::string HOST("HTTP_HOST");
|
||||||
tStrStrMap::const_iterator it = Headers().find(HOST);
|
tStrStrMap::const_iterator it = Headers().find(HOST);
|
||||||
if (it != Headers().end())
|
if (it != Headers().end())
|
||||||
base = "http://" + it->second + "/";
|
base = "http://" + it->second + "/";
|
||||||
@@ -305,7 +482,25 @@ cChannelList* cConnectionHTTP::ChannelListFromString(const std::string& Path, co
|
|||||||
base = (std::string) "http://" + LocalIp() + ":" +
|
base = (std::string) "http://" + LocalIp() + ":" +
|
||||||
(const char*) itoa(StreamdevServerSetup.HTTPServerPort) + "/";
|
(const char*) itoa(StreamdevServerSetup.HTTPServerPort) + "/";
|
||||||
base += Path;
|
base += Path;
|
||||||
return new cM3uChannelList(iterator, base.c_str());
|
|
||||||
|
if (Filebase.empty() || Fileext.compare(".htm") == 0 || Fileext.compare(".html") == 0) {
|
||||||
|
std::string self = Filebase + Fileext;
|
||||||
|
std::string rss = Filebase + ".rss";
|
||||||
|
tStrStrMap::const_iterator it = Headers().find("QUERY_STRING");
|
||||||
|
if (it != Headers().end() && !it->second.empty()) {
|
||||||
|
self += '?' + it->second;
|
||||||
|
rss += '?' + it->second;
|
||||||
|
}
|
||||||
|
return new cHtmlMenuList(iterator, m_StreamType, self.c_str(), rss.c_str(), groupTarget.c_str());
|
||||||
|
} else if (Fileext.compare(".m3u") == 0) {
|
||||||
|
return new cM3uMenuList(iterator, base.c_str());
|
||||||
|
} else if (Fileext.compare(".rss") == 0) {
|
||||||
|
std::string html = Filebase + ".html";
|
||||||
|
tStrStrMap::const_iterator it = Headers().find("QUERY_STRING");
|
||||||
|
if (it != Headers().end() && !it->second.empty()) {
|
||||||
|
html += '?' + it->second;
|
||||||
|
}
|
||||||
|
return new cRssMenuList(iterator, base.c_str(), html.c_str());
|
||||||
} else {
|
} else {
|
||||||
delete iterator;
|
delete iterator;
|
||||||
}
|
}
|
||||||
@@ -313,6 +508,93 @@ cChannelList* cConnectionHTTP::ChannelListFromString(const std::string& Path, co
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecPlayer* cConnectionHTTP::RecPlayerFromString(const char *FileBase, const char *FileExt)
|
||||||
|
{
|
||||||
|
RecPlayer *recPlayer = NULL;
|
||||||
|
|
||||||
|
if (strcasecmp(FileExt, ".rec") != 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
char *p = NULL;
|
||||||
|
unsigned long l = strtoul(FileBase, &p, 0);
|
||||||
|
if (p != FileBase && l > 0L) {
|
||||||
|
if (*p == ':') {
|
||||||
|
// get recording by dev:inode
|
||||||
|
ino_t inode = (ino_t) strtoull(p + 1, &p, 0);
|
||||||
|
if (*p == 0 && inode > 0) {
|
||||||
|
struct stat st;
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_RECORDINGS_READ;
|
||||||
|
for (const cRecording *rec = Recordings->First(); rec; rec = Recordings->Next(rec)) {
|
||||||
|
#else
|
||||||
|
cThreadLock RecordingsLock(&Recordings);
|
||||||
|
for (cRecording *rec = Recordings.First(); rec; rec = Recordings.Next(rec)) {
|
||||||
|
#endif
|
||||||
|
if (stat(rec->FileName(), &st) == 0 && st.st_dev == (dev_t) l && st.st_ino == inode)
|
||||||
|
recPlayer = new RecPlayer(rec->FileName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (*p == 0) {
|
||||||
|
// get recording by index
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_RECORDINGS_READ;
|
||||||
|
const cRecording *rec = Recordings->Get((int) l - 1);
|
||||||
|
#else
|
||||||
|
cThreadLock RecordingsLock(&Recordings);
|
||||||
|
cRecording *rec = Recordings.Get((int) l - 1);
|
||||||
|
#endif
|
||||||
|
if (rec)
|
||||||
|
recPlayer = new RecPlayer(rec->FileName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recPlayer) {
|
||||||
|
const char *pos = NULL;
|
||||||
|
tStrStrMap::const_iterator it = m_Params.begin();
|
||||||
|
while (it != m_Params.end()) {
|
||||||
|
if (it->first == "pos") {
|
||||||
|
pos = it->second.c_str();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
if (pos) {
|
||||||
|
// With prefix "full_" we try to fool players
|
||||||
|
// by replying with a content range starting
|
||||||
|
// at the requested position instead of 0.
|
||||||
|
// This is a heavy violation of standards.
|
||||||
|
// Use at your own risk!
|
||||||
|
if (strncasecmp(pos, "full_", 5) == 0) {
|
||||||
|
m_ReplayFakeRange = true;
|
||||||
|
pos += 5;
|
||||||
|
}
|
||||||
|
if (strncasecmp(pos, "resume", 6) == 0) {
|
||||||
|
int id = pos[6] == '.' ? atoi(pos + 7) : 0;
|
||||||
|
m_ReplayPos = recPlayer->positionFromResume(id);
|
||||||
|
}
|
||||||
|
else if (strncasecmp(pos, "mark.", 5) == 0) {
|
||||||
|
int index = atoi(pos + 5);
|
||||||
|
m_ReplayPos = recPlayer->positionFromMark(index);
|
||||||
|
}
|
||||||
|
else if (strncasecmp(pos, "time.", 5) == 0) {
|
||||||
|
int seconds = atoi(pos + 5);
|
||||||
|
m_ReplayPos = recPlayer->positionFromTime(seconds);
|
||||||
|
}
|
||||||
|
else if (strncasecmp(pos, "frame.", 6) == 0) {
|
||||||
|
int frame = atoi(pos + 6);
|
||||||
|
m_ReplayPos = recPlayer->positionFromFrameNumber(frame);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_ReplayPos = atol(pos);
|
||||||
|
if (m_ReplayPos > 0L && m_ReplayPos < 100L)
|
||||||
|
m_ReplayPos = recPlayer->positionFromPercent((int) m_ReplayPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return recPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
|
bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
|
||||||
{
|
{
|
||||||
std::string filespec, fileext;
|
std::string filespec, fileext;
|
||||||
@@ -320,11 +602,24 @@ bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
|
|||||||
|
|
||||||
if (file_pos != std::string::npos) {
|
if (file_pos != std::string::npos) {
|
||||||
size_t ext_pos = PathInfo.rfind('.');
|
size_t ext_pos = PathInfo.rfind('.');
|
||||||
// file basename with leading / stripped off
|
if (ext_pos != std::string::npos) {
|
||||||
filespec = PathInfo.substr(file_pos + 1, ext_pos - file_pos - 1);
|
|
||||||
if (ext_pos != std::string::npos)
|
|
||||||
// file extension including leading .
|
// file extension including leading .
|
||||||
fileext = PathInfo.substr(ext_pos);
|
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 (fileext.length() > 5) {
|
if (fileext.length() > 5) {
|
||||||
//probably not an extension
|
//probably not an extension
|
||||||
@@ -335,10 +630,12 @@ bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
|
|||||||
// Streamtype with leading / stripped off
|
// Streamtype with leading / stripped off
|
||||||
std::string type = PathInfo.substr(1, PathInfo.find_first_of("/;", 1) - 1);
|
std::string type = PathInfo.substr(1, PathInfo.find_first_of("/;", 1) - 1);
|
||||||
const char* pType = type.c_str();
|
const char* pType = type.c_str();
|
||||||
if (strcasecmp(pType, "PS") == 0) {
|
if (strcasecmp(pType, "PES") == 0) {
|
||||||
m_StreamType = stPS;
|
|
||||||
} else if (strcasecmp(pType, "PES") == 0) {
|
|
||||||
m_StreamType = stPES;
|
m_StreamType = stPES;
|
||||||
|
#ifdef STREAMDEV_PS
|
||||||
|
} else if (strcasecmp(pType, "PS") == 0) {
|
||||||
|
m_StreamType = stPS;
|
||||||
|
#endif
|
||||||
} else if (strcasecmp(pType, "TS") == 0) {
|
} else if (strcasecmp(pType, "TS") == 0) {
|
||||||
m_StreamType = stTS;
|
m_StreamType = stTS;
|
||||||
} else if (strcasecmp(pType, "ES") == 0) {
|
} else if (strcasecmp(pType, "ES") == 0) {
|
||||||
@@ -349,9 +646,12 @@ bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
|
|||||||
|
|
||||||
Dprintf("before channelfromstring: type(%s) filespec(%s) fileext(%s)\n", type.c_str(), filespec.c_str(), fileext.c_str());
|
Dprintf("before channelfromstring: type(%s) filespec(%s) fileext(%s)\n", type.c_str(), filespec.c_str(), fileext.c_str());
|
||||||
|
|
||||||
if ((m_ChannelList = ChannelListFromString(PathInfo.substr(1, file_pos), filespec.c_str(), fileext.c_str())) != NULL) {
|
if ((m_MenuList = MenuListFromString(PathInfo.substr(1, file_pos), filespec.c_str(), fileext.c_str())) != NULL) {
|
||||||
Dprintf("Channel list requested\n");
|
Dprintf("Channel list requested\n");
|
||||||
return true;
|
return true;
|
||||||
|
} else if ((m_RecPlayer = RecPlayerFromString(filespec.c_str(), fileext.c_str())) != NULL) {
|
||||||
|
Dprintf("Recording %s found\n", m_RecPlayer->getCurrentRecording()->Name());
|
||||||
|
return true;
|
||||||
} else if ((m_Channel = ChannelFromString(filespec.c_str(), &m_Apid[0], &m_Dpid[0])) != NULL) {
|
} else if ((m_Channel = ChannelFromString(filespec.c_str(), &m_Apid[0], &m_Dpid[0])) != NULL) {
|
||||||
Dprintf("Channel found. Apid/Dpid is %d/%d\n", m_Apid[0], m_Dpid[0]);
|
Dprintf("Channel found. Apid/Dpid is %d/%d\n", m_Apid[0], m_Dpid[0]);
|
||||||
return true;
|
return true;
|
||||||
@@ -359,3 +659,8 @@ bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cString cConnectionHTTP::ToText(char Delimiter) const
|
||||||
|
{
|
||||||
|
cString str = cServerConnection::ToText(Delimiter);
|
||||||
|
return Streamer() ? cString::sprintf("%s%c%s", *str, Delimiter, *Streamer()->ToText()) : str;
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,13 +7,13 @@
|
|||||||
|
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
#include "server/livestreamer.h"
|
#include "server/livestreamer.h"
|
||||||
|
#include "server/recstreamer.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <tools/select.h>
|
#include <tools/select.h>
|
||||||
|
|
||||||
class cChannel;
|
class cChannel;
|
||||||
class cStreamdevLiveStreamer;
|
class cMenuList;
|
||||||
class cChannelList;
|
|
||||||
|
|
||||||
class cConnectionHTTP: public cServerConnection {
|
class cConnectionHTTP: public cServerConnection {
|
||||||
private:
|
private:
|
||||||
@@ -26,17 +26,32 @@ private:
|
|||||||
|
|
||||||
std::string m_Authorization;
|
std::string m_Authorization;
|
||||||
eHTTPStatus m_Status;
|
eHTTPStatus m_Status;
|
||||||
|
tStrStrMap m_Params;
|
||||||
|
eStreamType m_StreamType;
|
||||||
// job: transfer
|
// job: transfer
|
||||||
cStreamdevLiveStreamer *m_LiveStreamer;
|
|
||||||
const cChannel *m_Channel;
|
const cChannel *m_Channel;
|
||||||
int m_Apid[2];
|
int m_Apid[2];
|
||||||
int m_Dpid[2];
|
int m_Dpid[2];
|
||||||
eStreamType m_StreamType;
|
// job: replay
|
||||||
|
RecPlayer *m_RecPlayer;
|
||||||
|
int64_t m_ReplayPos;
|
||||||
|
bool m_ReplayFakeRange;
|
||||||
// job: listing
|
// job: listing
|
||||||
cChannelList *m_ChannelList;
|
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* ChannelListFromString(const std::string &PathInfo, const std::string &Filebase, const std::string &Fileext) const;
|
|
||||||
bool ProcessURI(const std::string &PathInfo);
|
bool ProcessURI(const std::string &PathInfo);
|
||||||
|
bool HttpResponse(int Code, bool Last, const char* ContentType = NULL, const char* Headers = "", ...);
|
||||||
|
//__attribute__ ((format (printf, 5, 6)));
|
||||||
|
/**
|
||||||
|
* Extract byte range from HTTP Range header. Returns false if no valid
|
||||||
|
* range is found. The contents of From and To are undefined in this
|
||||||
|
* case. From may be negative in which case To is undefined.
|
||||||
|
* TODO: support for multiple ranges.
|
||||||
|
*/
|
||||||
|
bool ParseRange(int64_t &From, int64_t &To) const;
|
||||||
protected:
|
protected:
|
||||||
bool ProcessRequest(void);
|
bool ProcessRequest(void);
|
||||||
|
|
||||||
@@ -44,8 +59,7 @@ public:
|
|||||||
cConnectionHTTP(void);
|
cConnectionHTTP(void);
|
||||||
virtual ~cConnectionHTTP();
|
virtual ~cConnectionHTTP();
|
||||||
|
|
||||||
virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); }
|
virtual cString ToText(char Delimiter = ' ') const;
|
||||||
virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); }
|
|
||||||
|
|
||||||
virtual bool CanAuthenticate(void);
|
virtual bool CanAuthenticate(void);
|
||||||
|
|
||||||
@@ -57,7 +71,7 @@ public:
|
|||||||
|
|
||||||
inline bool cConnectionHTTP::Abort(void) const
|
inline bool cConnectionHTTP::Abort(void) const
|
||||||
{
|
{
|
||||||
return m_LiveStreamer && m_LiveStreamer->Abort();
|
return !IsOpen() || (Streamer() && Streamer()->Abort());
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
|
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
cConnectionIGMP::cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType) :
|
cConnectionIGMP::cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType) :
|
||||||
cServerConnection(Name, SOCK_DGRAM),
|
cServerConnection(Name, SOCK_DGRAM),
|
||||||
m_LiveStreamer(NULL),
|
|
||||||
m_ClientPort(ClientPort),
|
m_ClientPort(ClientPort),
|
||||||
m_StreamType(StreamType),
|
m_StreamType(StreamType),
|
||||||
m_Channel(NULL)
|
m_Channel(NULL)
|
||||||
@@ -20,10 +19,13 @@ cConnectionIGMP::cConnectionIGMP(const char* Name, int ClientPort, eStreamType S
|
|||||||
|
|
||||||
cConnectionIGMP::~cConnectionIGMP()
|
cConnectionIGMP::~cConnectionIGMP()
|
||||||
{
|
{
|
||||||
delete m_LiveStreamer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
bool cConnectionIGMP::SetChannel(const cChannel *Channel, in_addr_t Dst)
|
||||||
|
#else
|
||||||
bool cConnectionIGMP::SetChannel(cChannel *Channel, in_addr_t Dst)
|
bool cConnectionIGMP::SetChannel(cChannel *Channel, in_addr_t Dst)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
if (Channel) {
|
if (Channel) {
|
||||||
m_Channel = Channel;
|
m_Channel = Channel;
|
||||||
@@ -42,32 +44,34 @@ bool cConnectionIGMP::SetChannel(cChannel *Channel, in_addr_t Dst)
|
|||||||
|
|
||||||
void cConnectionIGMP::Welcome()
|
void cConnectionIGMP::Welcome()
|
||||||
{
|
{
|
||||||
cDevice *device = NULL;
|
if (cStreamdevLiveStreamer::ProvidesChannel(m_Channel, StreamdevServerSetup.IGMPPriority)) {
|
||||||
if (ProvidesChannel(m_Channel, 0))
|
cStreamdevLiveStreamer * liveStreamer = new cStreamdevLiveStreamer(this, m_Channel, StreamdevServerSetup.IGMPPriority, m_StreamType);
|
||||||
device = GetDevice(m_Channel, 0);
|
if (liveStreamer->GetDevice()) {
|
||||||
if (device != NULL) {
|
SetStreamer(liveStreamer);
|
||||||
device->SwitchChannel(m_Channel, false);
|
|
||||||
m_LiveStreamer = new cStreamdevLiveStreamer(0, this);
|
|
||||||
if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType)) {
|
|
||||||
m_LiveStreamer->SetDevice(device);
|
|
||||||
if (!SetDSCP())
|
if (!SetDSCP())
|
||||||
LOG_ERROR_STR("unable to set DSCP sockopt");
|
LOG_ERROR_STR("unable to set DSCP sockopt");
|
||||||
Dprintf("streamer start\n");
|
Dprintf("streamer start\n");
|
||||||
m_LiveStreamer->Start(this);
|
liveStreamer->Start(this);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
SetStreamer(NULL);
|
||||||
|
delete liveStreamer;
|
||||||
esyslog("streamdev-server IGMP: SetChannel failed");
|
esyslog("streamdev-server IGMP: SetChannel failed");
|
||||||
DELETENULL(m_LiveStreamer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
esyslog("streamdev-server IGMP: GetDevice failed");
|
esyslog("streamdev-server IGMP: SwitchDevice failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
void cConnectionIGMP::Stop()
|
bool cConnectionIGMP::Close()
|
||||||
{
|
{
|
||||||
if (m_LiveStreamer) {
|
if (Streamer())
|
||||||
m_LiveStreamer->Stop();
|
Streamer()->Stop();
|
||||||
DELETENULL(m_LiveStreamer);
|
return cServerConnection::Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cString cConnectionIGMP::ToText(char Delimiter) const
|
||||||
|
{
|
||||||
|
cString str = cServerConnection::ToText(Delimiter);
|
||||||
|
return Streamer() ? cString::sprintf("%s%c%s", *str, Delimiter, *Streamer()->ToText()) : str;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,31 +17,37 @@ class cStreamdevLiveStreamer;
|
|||||||
|
|
||||||
class cConnectionIGMP: public cServerConnection {
|
class cConnectionIGMP: public cServerConnection {
|
||||||
private:
|
private:
|
||||||
cStreamdevLiveStreamer *m_LiveStreamer;
|
|
||||||
int m_ClientPort;
|
int m_ClientPort;
|
||||||
eStreamType m_StreamType;
|
eStreamType m_StreamType;
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
const cChannel *m_Channel;
|
||||||
|
#else
|
||||||
cChannel *m_Channel;
|
cChannel *m_Channel;
|
||||||
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType);
|
cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType);
|
||||||
virtual ~cConnectionIGMP();
|
virtual ~cConnectionIGMP();
|
||||||
|
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
bool SetChannel(const cChannel *Channel, in_addr_t Dst);
|
||||||
|
#else
|
||||||
bool SetChannel(cChannel *Channel, in_addr_t Dst);
|
bool SetChannel(cChannel *Channel, in_addr_t Dst);
|
||||||
|
#endif
|
||||||
virtual void Welcome(void);
|
virtual void Welcome(void);
|
||||||
void Stop();
|
virtual cString ToText(char Delimiter = ' ') const;
|
||||||
|
|
||||||
/* Not used here */
|
/* Not used here */
|
||||||
virtual bool Command(char *Cmd) { return false; }
|
virtual bool Command(char *Cmd) { return false; }
|
||||||
|
|
||||||
virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); }
|
virtual bool Close(void);
|
||||||
virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); }
|
|
||||||
|
|
||||||
virtual bool Abort(void) const;
|
virtual bool Abort(void) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool cConnectionIGMP::Abort(void) const
|
inline bool cConnectionIGMP::Abort(void) const
|
||||||
{
|
{
|
||||||
return !m_LiveStreamer || m_LiveStreamer->Abort();
|
return !IsOpen() || !Streamer() || Streamer()->Abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H
|
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,6 @@
|
|||||||
#include "server/recplayer.h"
|
#include "server/recplayer.h"
|
||||||
|
|
||||||
class cTBSocket;
|
class cTBSocket;
|
||||||
class cStreamdevLiveStreamer;
|
|
||||||
class cStreamdevFilterStreamer;
|
class cStreamdevFilterStreamer;
|
||||||
class cLSTEHandler;
|
class cLSTEHandler;
|
||||||
class cLSTCHandler;
|
class cLSTCHandler;
|
||||||
@@ -20,7 +19,6 @@ class cConnectionVTP: public cServerConnection {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
cTBSocket *m_LiveSocket;
|
cTBSocket *m_LiveSocket;
|
||||||
cStreamdevLiveStreamer *m_LiveStreamer;
|
|
||||||
cTBSocket *m_FilterSocket;
|
cTBSocket *m_FilterSocket;
|
||||||
cStreamdevFilterStreamer *m_FilterStreamer;
|
cStreamdevFilterStreamer *m_FilterStreamer;
|
||||||
cTBSocket *m_RecSocket;
|
cTBSocket *m_RecSocket;
|
||||||
@@ -28,7 +26,9 @@ private:
|
|||||||
|
|
||||||
char *m_LastCommand;
|
char *m_LastCommand;
|
||||||
eStreamType m_StreamType;
|
eStreamType m_StreamType;
|
||||||
|
unsigned int m_ClientVersion;
|
||||||
bool m_FiltersSupport;
|
bool m_FiltersSupport;
|
||||||
|
bool m_LoopPrevention;
|
||||||
RecPlayer *m_RecPlayer;
|
RecPlayer *m_RecPlayer;
|
||||||
|
|
||||||
// Priority is only known in PROV command
|
// Priority is only known in PROV command
|
||||||
@@ -53,12 +53,15 @@ public:
|
|||||||
virtual void Welcome(void);
|
virtual void Welcome(void);
|
||||||
virtual void Reject(void);
|
virtual void Reject(void);
|
||||||
|
|
||||||
|
virtual cString ToText(char Delimiter = ' ') const;
|
||||||
|
|
||||||
virtual bool Abort(void) const;
|
virtual bool Abort(void) const;
|
||||||
virtual void Detach(void);
|
virtual void Detach(void);
|
||||||
virtual void Attach(void);
|
virtual void Attach(void);
|
||||||
|
|
||||||
virtual bool Command(char *Cmd);
|
virtual bool Command(char *Cmd);
|
||||||
bool CmdCAPS(char *Opts);
|
bool CmdCAPS(char *Opts);
|
||||||
|
bool CmdVERS(char *Opts);
|
||||||
bool CmdPROV(char *Opts);
|
bool CmdPROV(char *Opts);
|
||||||
bool CmdPORT(char *Opts);
|
bool CmdPORT(char *Opts);
|
||||||
bool CmdREAD(char *Opts);
|
bool CmdREAD(char *Opts);
|
||||||
|
|||||||
@@ -2,18 +2,52 @@
|
|||||||
* $Id: livefilter.c,v 1.7 2009/02/13 13:02:40 schmirl Exp $
|
* $Id: livefilter.c,v 1.7 2009/02/13 13:02:40 schmirl Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <vdr/filter.h>
|
||||||
|
|
||||||
#include "server/livefilter.h"
|
#include "server/livefilter.h"
|
||||||
#include "server/streamer.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#ifndef TS_SYNC_BYTE
|
#ifndef TS_SYNC_BYTE
|
||||||
# define TS_SYNC_BYTE 0x47
|
# define TS_SYNC_BYTE 0x47
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
cStreamdevLiveFilter::cStreamdevLiveFilter(cStreamdevStreamer *Streamer) {
|
#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;
|
||||||
m_Streamer = Streamer;
|
m_Streamer = Streamer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cStreamdevLiveFilter::SetStatus(bool On)
|
||||||
|
{
|
||||||
|
m_On = On;
|
||||||
|
cFilter::SetStatus(On);
|
||||||
|
}
|
||||||
|
|
||||||
void cStreamdevLiveFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
|
void cStreamdevLiveFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
|
||||||
{
|
{
|
||||||
uchar buffer[TS_SIZE];
|
uchar buffer[TS_SIZE];
|
||||||
@@ -32,8 +66,86 @@ void cStreamdevLiveFilter::Process(u_short Pid, u_char Tid, const u_char *Data,
|
|||||||
length -= chunk;
|
length -= chunk;
|
||||||
pos += chunk;
|
pos += chunk;
|
||||||
|
|
||||||
int p = m_Streamer->Put(buffer, TS_SIZE);
|
m_Streamer->Receive(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cStreamdevFilterStreamer -------------------------------------------------
|
||||||
|
|
||||||
|
cStreamdevFilterStreamer::cStreamdevFilterStreamer():
|
||||||
|
cStreamdevStreamer("streamdev-filterstreaming"),
|
||||||
|
m_Device(NULL),
|
||||||
|
m_Filter(NULL)/*,
|
||||||
|
m_Channel(NULL)*/
|
||||||
|
{
|
||||||
|
m_ReceiveBuffer = new cStreamdevBuffer(FILTERBUFSIZE, TS_SIZE);
|
||||||
|
m_ReceiveBuffer->SetTimeouts(0, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
cStreamdevFilterStreamer::~cStreamdevFilterStreamer()
|
||||||
|
{
|
||||||
|
Dprintf("Desctructing Filter streamer\n");
|
||||||
|
Detach();
|
||||||
|
m_Device = NULL;
|
||||||
|
DELETENULL(m_Filter);
|
||||||
|
Stop();
|
||||||
|
delete m_ReceiveBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cStreamdevFilterStreamer::Receive(uchar *Data)
|
||||||
|
{
|
||||||
|
int p = m_ReceiveBuffer->PutTS(Data, TS_SIZE);
|
||||||
if (p != TS_SIZE)
|
if (p != TS_SIZE)
|
||||||
m_Streamer->ReportOverflow(TS_SIZE - p);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,28 +5,33 @@
|
|||||||
#ifndef VDR_STREAMEV_LIVEFILTER_H
|
#ifndef VDR_STREAMEV_LIVEFILTER_H
|
||||||
#define VDR_STREAMEV_LIVEFILTER_H
|
#define VDR_STREAMEV_LIVEFILTER_H
|
||||||
|
|
||||||
#include <vdr/config.h>
|
#include "server/streamer.h"
|
||||||
|
|
||||||
#include <vdr/filter.h>
|
class cDevice;
|
||||||
|
class cStreamdevLiveFilter;
|
||||||
|
|
||||||
class cStreamdevStreamer;
|
class cStreamdevFilterStreamer: public cStreamdevStreamer {
|
||||||
|
|
||||||
class cStreamdevLiveFilter: public cFilter {
|
|
||||||
private:
|
private:
|
||||||
cStreamdevStreamer *m_Streamer;
|
cDevice *m_Device;
|
||||||
|
cStreamdevLiveFilter *m_Filter;
|
||||||
|
cStreamdevBuffer *m_ReceiveBuffer;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
|
virtual uchar* GetFromReceiver(int &Count) { return m_ReceiveBuffer->Get(Count); }
|
||||||
|
virtual void DelFromReceiver(int Count) { m_ReceiveBuffer->Del(Count); }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cStreamdevLiveFilter(cStreamdevStreamer *Streamer);
|
cStreamdevFilterStreamer();
|
||||||
|
virtual ~cStreamdevFilterStreamer();
|
||||||
|
|
||||||
void Set(u_short Pid, u_char Tid, u_char Mask) {
|
void SetDevice(cDevice *Device);
|
||||||
cFilter::Set(Pid, Tid, Mask);
|
bool SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On);
|
||||||
}
|
|
||||||
void Del(u_short Pid, u_char Tid, u_char Mask) {
|
virtual bool IsReceiving(void) const;
|
||||||
cFilter::Del(Pid, Tid, Mask);
|
void Receive(uchar *Data);
|
||||||
}
|
|
||||||
|
virtual void Attach(void);
|
||||||
|
virtual void Detach(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VDR_STREAMEV_LIVEFILTER_H
|
#endif // VDR_STREAMEV_LIVEFILTER_H
|
||||||
|
|||||||
@@ -8,45 +8,47 @@
|
|||||||
#include "remux/ts2es.h"
|
#include "remux/ts2es.h"
|
||||||
#include "remux/extern.h"
|
#include "remux/extern.h"
|
||||||
|
|
||||||
#include <vdr/ringbuffer.h>
|
#include <vdr/transfer.h>
|
||||||
|
|
||||||
#include "server/livestreamer.h"
|
#include "server/livestreamer.h"
|
||||||
#include "server/livefilter.h"
|
#include "server/setup.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
using namespace Streamdev;
|
using namespace Streamdev;
|
||||||
|
|
||||||
|
// device occupied timeout to prevent VDR main loop to immediately switch back
|
||||||
|
// when streamdev switched the live TV channel.
|
||||||
|
// Note that there is still a gap between the GetDevice() and SetOccupied()
|
||||||
|
// calls where the VDR main loop could strike
|
||||||
|
#define STREAMDEVTUNETIMEOUT 5
|
||||||
|
|
||||||
// --- cStreamdevLiveReceiver -------------------------------------------------
|
// --- cStreamdevLiveReceiver -------------------------------------------------
|
||||||
|
|
||||||
class cStreamdevLiveReceiver: public cReceiver {
|
class cStreamdevLiveReceiver: public cReceiver {
|
||||||
friend class cStreamdevStreamer;
|
friend class cStreamdevStreamer;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
cStreamdevStreamer *m_Streamer;
|
cStreamdevLiveStreamer *m_Streamer;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void Activate(bool On);
|
#if APIVERSNUM >= 20300
|
||||||
|
virtual void Receive(const uchar *Data, int Length);
|
||||||
|
#else
|
||||||
virtual void Receive(uchar *Data, int Length);
|
virtual void Receive(uchar *Data, int Length);
|
||||||
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, const cChannel *Channel, int Priority, const int *Pids);
|
cStreamdevLiveReceiver(cStreamdevLiveStreamer *Streamer, const cChannel *Channel, int Priority, const int *Pids);
|
||||||
virtual ~cStreamdevLiveReceiver();
|
virtual ~cStreamdevLiveReceiver();
|
||||||
};
|
};
|
||||||
|
|
||||||
cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, const cChannel *Channel,
|
cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevLiveStreamer *Streamer, const cChannel *Channel, int Priority, const int *Pids):
|
||||||
int Priority, const int *Pids):
|
|
||||||
#if APIVERSNUM >= 10712
|
|
||||||
cReceiver(Channel, Priority),
|
cReceiver(Channel, Priority),
|
||||||
#else
|
|
||||||
cReceiver(Channel->GetChannelID(), Priority, 0, Pids),
|
|
||||||
#endif
|
|
||||||
m_Streamer(Streamer)
|
m_Streamer(Streamer)
|
||||||
{
|
{
|
||||||
#if APIVERSNUM >= 10712
|
|
||||||
// clears all PIDs but channel remains set
|
// clears all PIDs but channel remains set
|
||||||
SetPids(NULL);
|
SetPids(NULL);
|
||||||
AddPids(Pids);
|
AddPids(Pids);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cStreamdevLiveReceiver::~cStreamdevLiveReceiver()
|
cStreamdevLiveReceiver::~cStreamdevLiveReceiver()
|
||||||
@@ -55,16 +57,12 @@ cStreamdevLiveReceiver::~cStreamdevLiveReceiver()
|
|||||||
Detach();
|
Detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
void cStreamdevLiveReceiver::Receive(const uchar *Data, int Length) {
|
||||||
|
#else
|
||||||
void cStreamdevLiveReceiver::Receive(uchar *Data, int Length) {
|
void cStreamdevLiveReceiver::Receive(uchar *Data, int Length) {
|
||||||
int p = m_Streamer->Receive(Data, Length);
|
#endif
|
||||||
if (p != Length)
|
m_Streamer->Receive(Data, Length);
|
||||||
m_Streamer->ReportOverflow(Length - p);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void cStreamdevLiveReceiver::Activate(bool On)
|
|
||||||
{
|
|
||||||
Dprintf("LiveReceiver->Activate(%d)\n", On);
|
|
||||||
m_Streamer->Activate(On);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cStreamdevPatFilter ----------------------------------------------------
|
// --- cStreamdevPatFilter ----------------------------------------------------
|
||||||
@@ -260,7 +258,12 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
|
|||||||
SI::PAT::Association assoc;
|
SI::PAT::Association assoc;
|
||||||
for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); ) {
|
for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); ) {
|
||||||
if (!assoc.isNITPid()) {
|
if (!assoc.isNITPid()) {
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
const cChannel *Channel = Channels->GetByServiceID(Source(), Transponder(), assoc.getServiceId());
|
||||||
|
#else
|
||||||
const cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId());
|
const cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId());
|
||||||
|
#endif
|
||||||
if (Channel && (Channel == m_Channel)) {
|
if (Channel && (Channel == m_Channel)) {
|
||||||
int prevPmtPid = pmtPid;
|
int prevPmtPid = pmtPid;
|
||||||
if (0 != (pmtPid = assoc.getPid())) {
|
if (0 != (pmtPid = assoc.getPid())) {
|
||||||
@@ -344,29 +347,36 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
|
|||||||
|
|
||||||
// --- cStreamdevLiveStreamer -------------------------------------------------
|
// --- cStreamdevLiveStreamer -------------------------------------------------
|
||||||
|
|
||||||
cStreamdevLiveStreamer::cStreamdevLiveStreamer(int Priority, const cServerConnection *Connection):
|
cStreamdevLiveStreamer::cStreamdevLiveStreamer(const cServerConnection *Connection, const cChannel *Channel, int Priority, eStreamType StreamType, const int* Apid, const int *Dpid) :
|
||||||
cStreamdevStreamer("streamdev-livestreaming", Connection),
|
cStreamdevStreamer("streamdev-livestreaming", Connection),
|
||||||
m_Priority(Priority),
|
m_Priority(Priority),
|
||||||
m_NumPids(0),
|
m_NumPids(0),
|
||||||
m_StreamType(stTSPIDS),
|
m_Channel(Channel),
|
||||||
m_Channel(NULL),
|
|
||||||
m_Device(NULL),
|
m_Device(NULL),
|
||||||
m_Receiver(NULL),
|
m_Receiver(NULL),
|
||||||
m_PatFilter(NULL),
|
m_PatFilter(NULL),
|
||||||
m_Remux(NULL)
|
m_SwitchLive(false)
|
||||||
{
|
{
|
||||||
|
m_ReceiveBuffer = new cStreamdevBuffer(LIVEBUFSIZE, TS_SIZE *2, true, "streamdev-livestreamer"),
|
||||||
|
m_ReceiveBuffer->SetTimeouts(0, 100);
|
||||||
|
if (Priority == IDLEPRIORITY) {
|
||||||
|
SetChannel(StreamType, Apid, Dpid);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_Device = SwitchDevice(Channel, Priority);
|
||||||
|
if (m_Device)
|
||||||
|
SetChannel(StreamType, Apid, Dpid);
|
||||||
|
memcpy(m_Caids,Channel->Caids(),sizeof(m_Caids));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cStreamdevLiveStreamer::~cStreamdevLiveStreamer()
|
cStreamdevLiveStreamer::~cStreamdevLiveStreamer()
|
||||||
{
|
{
|
||||||
Dprintf("Desctructing Live streamer\n");
|
Dprintf("Desctructing Live streamer\n");
|
||||||
Stop();
|
Stop();
|
||||||
if(m_PatFilter) {
|
|
||||||
Detach();
|
|
||||||
DELETENULL(m_PatFilter);
|
DELETENULL(m_PatFilter);
|
||||||
}
|
|
||||||
DELETENULL(m_Receiver);
|
DELETENULL(m_Receiver);
|
||||||
delete m_Remux;
|
delete m_ReceiveBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cStreamdevLiveStreamer::HasPid(int Pid)
|
bool cStreamdevLiveStreamer::HasPid(int Pid)
|
||||||
@@ -446,6 +456,12 @@ bool cStreamdevLiveStreamer::SetPids(int Pid, const int *Pids1, const int *Pids2
|
|||||||
void cStreamdevLiveStreamer::SetPriority(int Priority)
|
void cStreamdevLiveStreamer::SetPriority(int Priority)
|
||||||
{
|
{
|
||||||
m_Priority = Priority;
|
m_Priority = Priority;
|
||||||
|
#if VDRVERSNUM >= 20104
|
||||||
|
cThreadLock ThreadLock(m_Device);
|
||||||
|
if (m_Receiver)
|
||||||
|
m_Receiver->SetPriority(Priority);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
StartReceiver();
|
StartReceiver();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -453,20 +469,32 @@ void cStreamdevLiveStreamer::GetSignal(int *DevNum, int *Strength, int *Quality)
|
|||||||
{
|
{
|
||||||
if (m_Device) {
|
if (m_Device) {
|
||||||
*DevNum = m_Device->DeviceNumber() + 1;
|
*DevNum = m_Device->DeviceNumber() + 1;
|
||||||
#if APIVERSNUM >= 10719
|
|
||||||
*Strength = m_Device->SignalStrength();
|
*Strength = m_Device->SignalStrength();
|
||||||
*Quality = m_Device->SignalQuality();
|
*Quality = m_Device->SignalQuality();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cStreamdevLiveStreamer::StartReceiver(void)
|
cString cStreamdevLiveStreamer::ToText() const
|
||||||
{
|
{
|
||||||
if (m_NumPids > 0) {
|
if (m_Device && m_Channel) {
|
||||||
|
return cString::sprintf("DVB%-2d %3d %s", m_Device->DeviceNumber() + 1, m_Channel->Number(), m_Channel->Name());
|
||||||
|
}
|
||||||
|
return cString("");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cStreamdevLiveStreamer::IsReceiving(void) const
|
||||||
|
{
|
||||||
|
cThreadLock ThreadLock(m_Device);
|
||||||
|
return m_Receiver && m_Receiver->IsAttached();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cStreamdevLiveStreamer::StartReceiver(bool Force)
|
||||||
|
{
|
||||||
|
if (m_NumPids > 0 || Force) {
|
||||||
Dprintf("Creating Receiver to respect changed pids\n");
|
Dprintf("Creating Receiver to respect changed pids\n");
|
||||||
cReceiver *current = m_Receiver;
|
cReceiver *current = m_Receiver;
|
||||||
m_Receiver = new cStreamdevLiveReceiver(this, m_Channel, m_Priority, m_Pids);
|
|
||||||
cThreadLock ThreadLock(m_Device);
|
cThreadLock ThreadLock(m_Device);
|
||||||
|
m_Receiver = new cStreamdevLiveReceiver(this, m_Channel, m_Priority, m_Pids);
|
||||||
if (IsRunning())
|
if (IsRunning())
|
||||||
Attach();
|
Attach();
|
||||||
delete current;
|
delete current;
|
||||||
@@ -475,17 +503,15 @@ void cStreamdevLiveStreamer::StartReceiver(void)
|
|||||||
DELETENULL(m_Receiver);
|
DELETENULL(m_Receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType StreamType, const int* Apid, const int *Dpid)
|
bool cStreamdevLiveStreamer::SetChannel(eStreamType StreamType, const int* Apid, const int *Dpid)
|
||||||
{
|
{
|
||||||
Dprintf("Initializing Remuxer for full channel transfer\n");
|
Dprintf("Initializing Remuxer for full channel transfer\n");
|
||||||
//printf("ca pid: %d\n", Channel->Ca());
|
//printf("ca pid: %d\n", Channel->Ca());
|
||||||
m_Channel = Channel;
|
|
||||||
m_StreamType = StreamType;
|
|
||||||
|
|
||||||
const int *Apids = Apid ? Apid : m_Channel->Apids();
|
const int *Apids = Apid ? Apid : m_Channel->Apids();
|
||||||
const int *Dpids = Dpid ? Dpid : m_Channel->Dpids();
|
const int *Dpids = Dpid ? Dpid : m_Channel->Dpids();
|
||||||
|
|
||||||
switch (m_StreamType) {
|
switch (StreamType) {
|
||||||
case stES:
|
case stES:
|
||||||
{
|
{
|
||||||
int pid = ISRADIO(m_Channel) ? m_Channel->Apid(0) : m_Channel->Vpid();
|
int pid = ISRADIO(m_Channel) ? m_Channel->Apid(0) : m_Channel->Vpid();
|
||||||
@@ -493,20 +519,22 @@ bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType Str
|
|||||||
pid = Apid[0];
|
pid = Apid[0];
|
||||||
else if (Dpid && Dpid[0])
|
else if (Dpid && Dpid[0])
|
||||||
pid = Dpid[0];
|
pid = Dpid[0];
|
||||||
m_Remux = new cTS2ESRemux(pid);
|
SetRemux(new cTS2ESRemux(pid));
|
||||||
return SetPids(pid);
|
return SetPids(pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
case stPES:
|
case stPES:
|
||||||
m_Remux = new cTS2PESRemux(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
|
SetRemux(new cTS2PESRemux(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids()));
|
||||||
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
|
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
|
||||||
|
|
||||||
|
#ifdef STREAMDEV_PS
|
||||||
case stPS:
|
case stPS:
|
||||||
m_Remux = new cTS2PSRemux(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
|
SetRemux(new cTS2PSRemux(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids()));
|
||||||
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
|
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
|
||||||
|
#endif
|
||||||
|
|
||||||
case stEXT:
|
case stEXT:
|
||||||
m_Remux = new cExternRemux(Connection(), m_Channel, Apids, Dpids);
|
SetRemux(new cExternRemux(Connection(), m_Channel, Apids, Dpids));
|
||||||
// fall through
|
// fall through
|
||||||
case stTS:
|
case stTS:
|
||||||
// This should never happen, but ...
|
// This should never happen, but ...
|
||||||
@@ -524,12 +552,40 @@ bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType Str
|
|||||||
|
|
||||||
case stTSPIDS:
|
case stTSPIDS:
|
||||||
Dprintf("pid streaming mode\n");
|
Dprintf("pid streaming mode\n");
|
||||||
|
// No PIDs requested yet. Start receiver anyway to occupy device
|
||||||
|
StartReceiver(true);
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
void cStreamdevLiveStreamer::Receive(const uchar *Data, int Length)
|
||||||
|
#else
|
||||||
|
void cStreamdevLiveStreamer::Receive(uchar *Data, int Length)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
int p = m_ReceiveBuffer->PutTS(Data, Length);
|
||||||
|
if (p != Length)
|
||||||
|
m_ReceiveBuffer->ReportOverflow(Length - p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cStreamdevLiveStreamer::Action(void)
|
||||||
|
{
|
||||||
|
if (StreamdevServerSetup.LiveBufferMs) {
|
||||||
|
// wait for first data block
|
||||||
|
int count = 0;
|
||||||
|
while (Running()) {
|
||||||
|
if (m_ReceiveBuffer->Get(count) != NULL) {
|
||||||
|
cCondWait::SleepMs(StreamdevServerSetup.LiveBufferMs);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cStreamdevStreamer::Action();
|
||||||
|
}
|
||||||
|
|
||||||
int cStreamdevLiveStreamer::Put(const uchar *Data, int Count)
|
int cStreamdevLiveStreamer::Put(const uchar *Data, int Count)
|
||||||
{
|
{
|
||||||
// insert si data
|
// insert si data
|
||||||
@@ -537,41 +593,20 @@ int cStreamdevLiveStreamer::Put(const uchar *Data, int Count)
|
|||||||
int siCount;
|
int siCount;
|
||||||
uchar *siData = m_PatFilter->Get(siCount);
|
uchar *siData = m_PatFilter->Get(siCount);
|
||||||
if (siData) {
|
if (siData) {
|
||||||
if (m_Remux)
|
|
||||||
siCount = m_Remux->Put(siData, siCount);
|
|
||||||
else
|
|
||||||
siCount = cStreamdevStreamer::Put(siData, siCount);
|
siCount = cStreamdevStreamer::Put(siData, siCount);
|
||||||
if (siCount)
|
if (siCount)
|
||||||
m_PatFilter->Del(siCount);
|
m_PatFilter->Del(siCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m_Remux)
|
|
||||||
return m_Remux->Put(Data, Count);
|
|
||||||
else
|
|
||||||
return cStreamdevStreamer::Put(Data, Count);
|
return cStreamdevStreamer::Put(Data, Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
uchar *cStreamdevLiveStreamer::Get(int &Count)
|
|
||||||
{
|
|
||||||
if (m_Remux)
|
|
||||||
return m_Remux->Get(Count);
|
|
||||||
else
|
|
||||||
return cStreamdevStreamer::Get(Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cStreamdevLiveStreamer::Del(int Count)
|
|
||||||
{
|
|
||||||
if (m_Remux)
|
|
||||||
m_Remux->Del(Count);
|
|
||||||
else
|
|
||||||
cStreamdevStreamer::Del(Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cStreamdevLiveStreamer::Attach(void)
|
void cStreamdevLiveStreamer::Attach(void)
|
||||||
{
|
{
|
||||||
Dprintf("cStreamdevLiveStreamer::Attach()\n");
|
Dprintf("cStreamdevLiveStreamer::Attach()\n");
|
||||||
if (m_Device) {
|
if (m_Device) {
|
||||||
if (m_Receiver) {
|
if (m_Receiver) {
|
||||||
|
if (m_Receiver->IsAttached())
|
||||||
m_Device->Detach(m_Receiver);
|
m_Device->Detach(m_Receiver);
|
||||||
m_Device->AttachReceiver(m_Receiver);
|
m_Device->AttachReceiver(m_Receiver);
|
||||||
}
|
}
|
||||||
@@ -593,6 +628,101 @@ void cStreamdevLiveStreamer::Detach(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cStreamdevLiveStreamer::UsedByLiveTV(cDevice *device)
|
||||||
|
{
|
||||||
|
return device == cTransferControl::ReceiverDevice() ||
|
||||||
|
(device->IsPrimaryDevice() && device->HasDecoder() && !device->Replaying());
|
||||||
|
}
|
||||||
|
|
||||||
|
cDevice *cStreamdevLiveStreamer::SwitchDevice(const cChannel *Channel, int Priority)
|
||||||
|
{
|
||||||
|
cDevice *device = cDevice::GetDevice(Channel, Priority, false);
|
||||||
|
if (!device) {
|
||||||
|
dsyslog("streamdev: GetDevice failed for channel %d (%s) at priority %d (PrimaryDevice=%d, ActualDevice=%d)", Channel->Number(), Channel->Name(), Priority, cDevice::PrimaryDevice()->CardIndex(), cDevice::ActualDevice()->CardIndex());
|
||||||
|
}
|
||||||
|
else if (!device->IsTunedToTransponder(Channel) && UsedByLiveTV(device)) {
|
||||||
|
// make sure VDR main loop doesn't switch back
|
||||||
|
device->SetOccupied(STREAMDEVTUNETIMEOUT);
|
||||||
|
if (device->SwitchChannel(Channel, false)) {
|
||||||
|
// switched away live TV
|
||||||
|
m_SwitchLive = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dsyslog("streamdev: SwitchChannel (live) failed for channel %d (%s) at priority %d (PrimaryDevice=%d, ActualDevice=%d, device=%d)", Channel->Number(), Channel->Name(), Priority, cDevice::PrimaryDevice()->CardIndex(), cDevice::ActualDevice()->CardIndex(), device->CardIndex());
|
||||||
|
device->SetOccupied(0);
|
||||||
|
device = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!device->SwitchChannel(Channel, false)) {
|
||||||
|
dsyslog("streamdev: SwitchChannel failed for channel %d (%s) at priority %d (PrimaryDevice=%d, ActualDevice=%d, device=%d)", Channel->Number(), Channel->Name(), Priority, cDevice::PrimaryDevice()->CardIndex(), cDevice::ActualDevice()->CardIndex(), device->CardIndex());
|
||||||
|
device = NULL;
|
||||||
|
}
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cStreamdevLiveStreamer::ProvidesChannel(const cChannel *Channel, int Priority)
|
||||||
|
{
|
||||||
|
cDevice *device = cDevice::GetDevice(Channel, Priority, false, true);
|
||||||
|
if (!device)
|
||||||
|
dsyslog("streamdev: No device provides channel %d (%s) at priority %d", Channel->Number(), Channel->Name(), Priority);
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cStreamdevLiveStreamer::ChannelChange(const cChannel *Channel)
|
||||||
|
{
|
||||||
|
if (Running() && m_Device && m_Channel == Channel) {
|
||||||
|
// Check whether the Caids actually changed
|
||||||
|
// If not, no need to re-tune, probably just an Audio PID update
|
||||||
|
if (!memcmp(m_Caids, Channel->Caids(), sizeof(m_Caids))) {
|
||||||
|
dsyslog("streamdev: channel %d (%s) changed, but caids remained the same, not re-tuning", Channel->Number(), Channel->Name());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Detach();
|
||||||
|
if (m_Device->SwitchChannel(m_Channel, false)) {
|
||||||
|
Attach();
|
||||||
|
dsyslog("streamdev: channel %d (%s) changed, re-tuned", Channel->Number(), Channel->Name());
|
||||||
|
memcpy(m_Caids, Channel->Caids(), sizeof(m_Caids));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
isyslog("streamdev: failed to re-tune after channel %d (%s) changed", Channel->Number(), Channel->Name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cStreamdevLiveStreamer::MainThreadHook()
|
||||||
|
{
|
||||||
|
if (!m_SwitchLive && Running() && m_Device && !m_Device->IsTunedToTransponder(m_Channel) && !IsReceiving()) {
|
||||||
|
cDevice *dev = SwitchDevice(m_Channel, m_Priority);
|
||||||
|
if (dev) {
|
||||||
|
dsyslog("streamdev: Lost channel %d (%s) on device %d. Continuing on device %d.", m_Channel->Number(), m_Channel->Name(), m_Device->CardIndex(), dev->CardIndex());
|
||||||
|
m_Device = dev;
|
||||||
|
StartReceiver();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
isyslog("streamdev: Lost channel %d (%s) on device %d.", m_Channel->Number(), m_Channel->Name(), m_Device->CardIndex());
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_SwitchLive) {
|
||||||
|
// switched away live TV. Try previous channel on other device first
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
if (!Channels->SwitchTo(cDevice::CurrentChannel())) {
|
||||||
|
// switch to streamdev channel otherwise
|
||||||
|
Channels->SwitchTo(m_Channel->Number());
|
||||||
|
#else
|
||||||
|
if (!Channels.SwitchTo(cDevice::CurrentChannel())) {
|
||||||
|
// switch to streamdev channel otherwise
|
||||||
|
Channels.SwitchTo(m_Channel->Number());
|
||||||
|
#endif
|
||||||
|
Skins.Message(mtInfo, tr("Streaming active"));
|
||||||
|
}
|
||||||
|
if (m_Device)
|
||||||
|
m_Device->SetOccupied(0);
|
||||||
|
m_SwitchLive = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string cStreamdevLiveStreamer::Report(void)
|
std::string cStreamdevLiveStreamer::Report(void)
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
@@ -609,105 +739,3 @@ std::string cStreamdevLiveStreamer::Report(void)
|
|||||||
result += "\n";
|
result += "\n";
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cStreamdevFilterStreamer -------------------------------------------------
|
|
||||||
|
|
||||||
cStreamdevFilterStreamer::cStreamdevFilterStreamer():
|
|
||||||
cStreamdevStreamer("streamdev-filterstreaming"),
|
|
||||||
m_Device(NULL),
|
|
||||||
m_Filter(NULL)/*,
|
|
||||||
m_Channel(NULL)*/
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
cStreamdevFilterStreamer::~cStreamdevFilterStreamer()
|
|
||||||
{
|
|
||||||
Dprintf("Desctructing Filter streamer\n");
|
|
||||||
Detach();
|
|
||||||
m_Device = NULL;
|
|
||||||
DELETENULL(m_Filter);
|
|
||||||
Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void cStreamdevFilterStreamer::Attach(void)
|
|
||||||
{
|
|
||||||
Dprintf("cStreamdevFilterStreamer::Attach()\n");
|
|
||||||
LOCK_THREAD;
|
|
||||||
if(m_Device && m_Filter)
|
|
||||||
m_Device->AttachFilter(m_Filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cStreamdevFilterStreamer::Detach(void)
|
|
||||||
{
|
|
||||||
Dprintf("cStreamdevFilterStreamer::Detach()\n");
|
|
||||||
LOCK_THREAD;
|
|
||||||
if(m_Device && m_Filter)
|
|
||||||
m_Device->Detach(m_Filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
void cStreamdevFilterStreamer::SetChannel(const cChannel *Channel)
|
|
||||||
{
|
|
||||||
LOCK_THREAD;
|
|
||||||
Dprintf("cStreamdevFilterStreamer::SetChannel(%s : %s)", Channel?Channel->Name():"<null>",
|
|
||||||
Channel ? *Channel->GetChannelID().ToString() : "");
|
|
||||||
m_Channel = Channel;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void cStreamdevFilterStreamer::SetDevice(cDevice *Device)
|
|
||||||
{
|
|
||||||
Dprintf("cStreamdevFilterStreamer::SetDevice()\n");
|
|
||||||
LOCK_THREAD;
|
|
||||||
Detach();
|
|
||||||
m_Device = Device;
|
|
||||||
//m_Channel = NULL;
|
|
||||||
Attach();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cStreamdevFilterStreamer::SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On)
|
|
||||||
{
|
|
||||||
Dprintf("cStreamdevFilterStreamer::SetFilter(%u,0x%x,0x%x,%s)\n", Pid, Tid, Mask, On?"On":"Off");
|
|
||||||
|
|
||||||
if(!m_Device)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (On) {
|
|
||||||
if (m_Filter == NULL) {
|
|
||||||
m_Filter = new cStreamdevLiveFilter(this);
|
|
||||||
Dprintf("attaching filter to device\n");
|
|
||||||
Attach();
|
|
||||||
}
|
|
||||||
m_Filter->Set(Pid, Tid, Mask);
|
|
||||||
} else if (m_Filter != NULL)
|
|
||||||
m_Filter->Del(Pid, Tid, Mask);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
void cStreamdevFilterStreamer::ChannelSwitch(const cDevice *Device, int ChannelNumber) {
|
|
||||||
LOCK_THREAD;
|
|
||||||
if(Device == m_Device) {
|
|
||||||
if(ChannelNumber > 0) {
|
|
||||||
cChannel *ch = Channels.GetByNumber(ChannelNumber);
|
|
||||||
if(ch != NULL) {
|
|
||||||
if(m_Filter != NULL &&
|
|
||||||
m_Channel != NULL &&
|
|
||||||
(! TRANSPONDER(ch, m_Channel))) {
|
|
||||||
|
|
||||||
isyslog("***** LiveFilterStreamer: transponder changed ! %s",
|
|
||||||
*ch->GetChannelID().ToString());
|
|
||||||
|
|
||||||
uchar buffer[TS_SIZE] = {TS_SYNC_BYTE, 0xff, 0xff, 0xff, 0x7f, 0};
|
|
||||||
strcpy((char*)(buffer + 5), ch->GetChannelID().ToString());
|
|
||||||
int p = Put(buffer, TS_SIZE);
|
|
||||||
if (p != TS_SIZE)
|
|
||||||
ReportOverflow(TS_SIZE - p);
|
|
||||||
}
|
|
||||||
m_Channel = ch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -2,82 +2,88 @@
|
|||||||
#define VDR_STREAMDEV_LIVESTREAMER_H
|
#define VDR_STREAMDEV_LIVESTREAMER_H
|
||||||
|
|
||||||
#include <vdr/config.h>
|
#include <vdr/config.h>
|
||||||
|
#include <vdr/status.h>
|
||||||
#include <vdr/receiver.h>
|
#include <vdr/receiver.h>
|
||||||
|
|
||||||
#include "server/streamer.h"
|
#include "server/streamer.h"
|
||||||
|
#include "server/streamdev-server.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
namespace Streamdev {
|
#define LIVEBUFSIZE (20000 * TS_SIZE)
|
||||||
class cTSRemux;
|
|
||||||
}
|
|
||||||
class cStreamdevPatFilter;
|
class cStreamdevPatFilter;
|
||||||
class cStreamdevLiveReceiver;
|
class cStreamdevLiveReceiver;
|
||||||
|
|
||||||
// --- cStreamdevLiveStreamer -------------------------------------------------
|
// --- cStreamdevLiveStreamer -------------------------------------------------
|
||||||
|
|
||||||
class cStreamdevLiveStreamer: public cStreamdevStreamer {
|
class cStreamdevLiveStreamer: public cStreamdevStreamer, public cMainThreadHookSubscriber
|
||||||
|
#if VDRVERSNUM >= 20104
|
||||||
|
, public cStatus
|
||||||
|
#endif
|
||||||
|
{
|
||||||
private:
|
private:
|
||||||
int m_Priority;
|
int m_Priority;
|
||||||
int m_Pids[MAXRECEIVEPIDS + 1];
|
int m_Pids[MAXRECEIVEPIDS + 1];
|
||||||
int m_NumPids;
|
int m_NumPids;
|
||||||
eStreamType m_StreamType;
|
int m_Caids[MAXCAIDS + 1];
|
||||||
const cChannel *m_Channel;
|
const cChannel *m_Channel;
|
||||||
cDevice *m_Device;
|
cDevice *m_Device;
|
||||||
cStreamdevLiveReceiver *m_Receiver;
|
cStreamdevLiveReceiver *m_Receiver;
|
||||||
|
cStreamdevBuffer *m_ReceiveBuffer;
|
||||||
cStreamdevPatFilter *m_PatFilter;
|
cStreamdevPatFilter *m_PatFilter;
|
||||||
Streamdev::cTSRemux *m_Remux;
|
bool m_SwitchLive;
|
||||||
|
|
||||||
void StartReceiver(void);
|
void StartReceiver(bool Force = false);
|
||||||
bool HasPid(int Pid);
|
bool HasPid(int Pid);
|
||||||
|
|
||||||
public:
|
/* Test if device is in use as the transfer mode receiver device
|
||||||
cStreamdevLiveStreamer(int Priority, const cServerConnection *Connection);
|
or a FF card, displaying live TV from internal tuner */
|
||||||
virtual ~cStreamdevLiveStreamer();
|
static bool UsedByLiveTV(cDevice *device);
|
||||||
|
|
||||||
void SetDevice(cDevice *Device) { m_Device = Device; }
|
/* Find a suitable device and tune it to the requested channel. */
|
||||||
bool SetPid(int Pid, bool On);
|
cDevice *SwitchDevice(const cChannel *Channel, int Priority);
|
||||||
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);
|
bool SetChannel(eStreamType StreamType, const int* Apid = NULL, const int* Dpid = NULL);
|
||||||
void SetPriority(int Priority);
|
|
||||||
void GetSignal(int *DevNum, int *Strength, int *Quality) const;
|
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 int Put(const uchar *Data, int Count);
|
||||||
virtual uchar *Get(int &Count);
|
virtual void Action(void);
|
||||||
virtual void Del(int Count);
|
|
||||||
|
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);
|
||||||
|
virtual ~cStreamdevLiveStreamer();
|
||||||
|
|
||||||
|
bool SetPid(int Pid, bool On);
|
||||||
|
bool SetPids(int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL);
|
||||||
|
void SetPriority(int Priority);
|
||||||
|
void GetSignal(int *DevNum, int *Strength, int *Quality) const;
|
||||||
|
virtual 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 void Attach(void);
|
virtual void Attach(void);
|
||||||
virtual void Detach(void);
|
virtual void Detach(void);
|
||||||
|
|
||||||
|
cDevice *GetDevice() const { return m_Device; }
|
||||||
|
|
||||||
|
/* Test if a call to GetDevice would return a usable device. */
|
||||||
|
static bool ProvidesChannel(const cChannel *Channel, int Priority);
|
||||||
|
|
||||||
|
/* Do things which must be done in VDR's main loop */
|
||||||
|
void MainThreadHook();
|
||||||
|
|
||||||
// Statistical purposes:
|
// Statistical purposes:
|
||||||
virtual std::string Report(void);
|
virtual std::string Report(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// --- cStreamdevFilterStreamer -------------------------------------------------
|
|
||||||
|
|
||||||
//#include <vdr/status.h>
|
|
||||||
|
|
||||||
class cStreamdevLiveFilter;
|
|
||||||
|
|
||||||
class cStreamdevFilterStreamer: public cStreamdevStreamer /*, public cStatus*/ {
|
|
||||||
private:
|
|
||||||
cDevice *m_Device;
|
|
||||||
cStreamdevLiveFilter *m_Filter;
|
|
||||||
//const cChannel *m_Channel;
|
|
||||||
|
|
||||||
public:
|
|
||||||
cStreamdevFilterStreamer();
|
|
||||||
virtual ~cStreamdevFilterStreamer();
|
|
||||||
|
|
||||||
void SetDevice(cDevice *Device);
|
|
||||||
//void SetChannel(const cChannel *Channel);
|
|
||||||
bool SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On);
|
|
||||||
|
|
||||||
virtual void Attach(void);
|
|
||||||
virtual void Detach(void);
|
|
||||||
|
|
||||||
// cStatus message handlers
|
|
||||||
//virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // VDR_STREAMDEV_LIVESTREAMER_H
|
#endif // VDR_STREAMDEV_LIVESTREAMER_H
|
||||||
|
|||||||
76
server/menu.c
Normal file
76
server/menu.c
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* $Id: menu.c,v 1.10 2010/07/19 13:49:31 schmirl Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <vdr/menuitems.h>
|
||||||
|
#include <vdr/thread.h>
|
||||||
|
#include <vdr/player.h>
|
||||||
|
|
||||||
|
#include "server/menu.h"
|
||||||
|
#include "server/setup.h"
|
||||||
|
#include "server/server.h"
|
||||||
|
#include "server/suspend.h"
|
||||||
|
|
||||||
|
cStreamdevServerMenu::cStreamdevServerMenu(): cOsdMenu(tr("Streamdev Connections"), 4, 20) {
|
||||||
|
cThreadLock lock;
|
||||||
|
#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')));
|
||||||
|
SetHelpKeys();
|
||||||
|
Display();
|
||||||
|
}
|
||||||
|
|
||||||
|
cStreamdevServerMenu::~cStreamdevServerMenu() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void cStreamdevServerMenu::SetHelpKeys() {
|
||||||
|
SetHelp(Count() ? tr("Disconnect") : NULL, NULL, NULL, tr("Suspend"));
|
||||||
|
}
|
||||||
|
|
||||||
|
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'))) {
|
||||||
|
s->Close();
|
||||||
|
Del(Current());
|
||||||
|
SetHelpKeys();
|
||||||
|
Display();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return osContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
eOSState cStreamdevServerMenu::Suspend() {
|
||||||
|
if (!cSuspendCtl::IsActive()) {
|
||||||
|
cControl::Launch(new cSuspendCtl);
|
||||||
|
return osBack;
|
||||||
|
}
|
||||||
|
return osContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
eOSState cStreamdevServerMenu::ProcessKey(eKeys Key) {
|
||||||
|
eOSState state = cOsdMenu::ProcessKey(Key);
|
||||||
|
if (state == osUnknown) {
|
||||||
|
switch (Key) {
|
||||||
|
case kRed: return Disconnect();
|
||||||
|
case kBlue: return Suspend();
|
||||||
|
case kOk: return osBack;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
24
server/menu.h
Normal file
24
server/menu.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* $Id: menu.h,v 1.4 2010/07/19 13:49:31 schmirl Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef VDR_STREAMDEV_MENU_H
|
||||||
|
#define VDR_STREAMDEV_MENU_H
|
||||||
|
|
||||||
|
#include <vdr/osdbase.h>
|
||||||
|
#include "connection.h"
|
||||||
|
|
||||||
|
class cStreamdevServerMenu: public cOsdMenu {
|
||||||
|
private:
|
||||||
|
void SetHelpKeys();
|
||||||
|
eOSState Disconnect();
|
||||||
|
eOSState Suspend();
|
||||||
|
protected:
|
||||||
|
virtual eOSState ProcessKey(eKeys Key);
|
||||||
|
|
||||||
|
public:
|
||||||
|
cStreamdevServerMenu();
|
||||||
|
virtual ~cStreamdevServerMenu();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VDR_STREAMDEV_MENU_H
|
||||||
@@ -1,58 +1,244 @@
|
|||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <vdr/channels.h>
|
#include <vdr/channels.h>
|
||||||
#include "server/menuHTTP.h"
|
#include "server/menuHTTP.h"
|
||||||
|
|
||||||
//**************************** cChannelIterator **************
|
//**************************** cRecordingIterator **************
|
||||||
cChannelIterator::cChannelIterator(const cChannel *First): channel(First)
|
#if APIVERSNUM >= 20300
|
||||||
{}
|
cRecordingsIterator::cRecordingsIterator(eStreamType StreamType)
|
||||||
|
#else
|
||||||
const cChannel* cChannelIterator::Next()
|
cRecordingsIterator::cRecordingsIterator(eStreamType StreamType): RecordingsLock(&Recordings)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
const cChannel *current = channel;
|
streamType = StreamType;
|
||||||
channel = NextChannel(channel);
|
#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;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cChannelIterator::Next()
|
||||||
|
{
|
||||||
|
if (first)
|
||||||
|
{
|
||||||
|
current = first;
|
||||||
|
first = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
current = NextChannel(current);
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cString cChannelIterator::ItemId() const
|
||||||
|
{
|
||||||
|
if (current)
|
||||||
|
{
|
||||||
|
if (current->GroupSep())
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
for (int curr = Channels->GetNextGroup(-1); curr >= 0; curr = Channels->GetNextGroup(curr))
|
||||||
|
{
|
||||||
|
if (Channels->Get(curr) == current)
|
||||||
|
#else
|
||||||
|
for (int curr = Channels.GetNextGroup(-1); curr >= 0; curr = Channels.GetNextGroup(curr))
|
||||||
|
{
|
||||||
|
if (Channels.Get(curr) == current)
|
||||||
|
#endif
|
||||||
|
return itoa(index);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return itoa(current->Number());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cString("-1");
|
||||||
|
}
|
||||||
|
|
||||||
|
const cChannel* cChannelIterator::GetGroup(const char* GroupId)
|
||||||
|
{
|
||||||
|
int group = -1;
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
#endif
|
||||||
|
if (GroupId)
|
||||||
|
{
|
||||||
|
int Index = atoi(GroupId);
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
group = Channels->GetNextGroup(-1);
|
||||||
|
while (Index-- && group >= 0)
|
||||||
|
group = Channels->GetNextGroup(group);
|
||||||
|
}
|
||||||
|
return group >= 0 ? Channels->Get(group) : NULL;
|
||||||
|
#else
|
||||||
|
group = Channels.GetNextGroup(-1);
|
||||||
|
while (Index-- && group >= 0)
|
||||||
|
group = Channels.GetNextGroup(group);
|
||||||
|
}
|
||||||
|
return group >= 0 ? Channels.Get(group) : NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
const cChannel* cChannelIterator::FirstChannel()
|
||||||
|
{
|
||||||
|
const cChannel *Channel;
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
Channel = Channels->First();
|
||||||
|
#else
|
||||||
|
Channel = Channels.First();
|
||||||
|
#endif
|
||||||
|
return Channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cChannel* cChannelIterator::NextNormal()
|
||||||
|
{
|
||||||
|
const cChannel *Channel;
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
Channel = Channels->Get(Channels->GetNextNormal(-1));
|
||||||
|
#else
|
||||||
|
Channel = Channels.Get(Channels.GetNextNormal(-1));
|
||||||
|
#endif
|
||||||
|
return Channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cChannel* cChannelIterator::NextGroup()
|
||||||
|
{
|
||||||
|
const cChannel *Channel;
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
Channel = Channels->Get(Channels->GetNextGroup(-1));
|
||||||
|
#else
|
||||||
|
Channel = Channels.Get(Channels.GetNextGroup(-1));
|
||||||
|
#endif
|
||||||
|
return Channel;
|
||||||
|
}
|
||||||
|
|
||||||
//**************************** cListAll **************
|
//**************************** cListAll **************
|
||||||
cListAll::cListAll(): cChannelIterator(Channels.First())
|
cListAll::cListAll(): cChannelIterator(FirstChannel())
|
||||||
{}
|
{}
|
||||||
|
|
||||||
const cChannel* cListAll::NextChannel(const cChannel *Channel)
|
const cChannel* cListAll::NextChannel(const cChannel *Channel)
|
||||||
{
|
{
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
if (Channel)
|
||||||
|
Channel = SkipFakeGroups(Channels->Next(Channel));
|
||||||
|
#else
|
||||||
if (Channel)
|
if (Channel)
|
||||||
Channel = SkipFakeGroups(Channels.Next(Channel));
|
Channel = SkipFakeGroups(Channels.Next(Channel));
|
||||||
|
#endif
|
||||||
return Channel;
|
return Channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
//**************************** cListChannels **************
|
//**************************** cListChannels **************
|
||||||
cListChannels::cListChannels(): cChannelIterator(Channels.Get(Channels.GetNextNormal(-1)))
|
cListChannels::cListChannels(): cChannelIterator(NextNormal())
|
||||||
{}
|
{}
|
||||||
|
|
||||||
const cChannel* cListChannels::NextChannel(const cChannel *Channel)
|
const cChannel* cListChannels::NextChannel(const cChannel *Channel)
|
||||||
{
|
{
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
if (Channel)
|
||||||
|
Channel = Channels->Get(Channels->GetNextNormal(Channel->Index()));
|
||||||
|
#else
|
||||||
if (Channel)
|
if (Channel)
|
||||||
Channel = Channels.Get(Channels.GetNextNormal(Channel->Index()));
|
Channel = Channels.Get(Channels.GetNextNormal(Channel->Index()));
|
||||||
|
#endif
|
||||||
return Channel;
|
return Channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ********************* cListGroups ****************
|
// ********************* cListGroups ****************
|
||||||
cListGroups::cListGroups(): cChannelIterator(Channels.Get(Channels.GetNextGroup(-1)))
|
cListGroups::cListGroups(): cChannelIterator(NextGroup())
|
||||||
{}
|
{}
|
||||||
|
|
||||||
const cChannel* cListGroups::NextChannel(const cChannel *Channel)
|
const cChannel* cListGroups::NextChannel(const cChannel *Channel)
|
||||||
{
|
{
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
if (Channel)
|
||||||
|
Channel = Channels->Get(Channels->GetNextGroup(Channel->Index()));
|
||||||
|
#else
|
||||||
if (Channel)
|
if (Channel)
|
||||||
Channel = Channels.Get(Channels.GetNextGroup(Channel->Index()));
|
Channel = Channels.Get(Channels.GetNextGroup(Channel->Index()));
|
||||||
|
#endif
|
||||||
return Channel;
|
return Channel;
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
// ********************* cListGroup ****************
|
// ********************* cListGroup ****************
|
||||||
cListGroup::cListGroup(const cChannel *Group): cChannelIterator(GetNextChannelInGroup(Group))
|
cListGroup::cListGroup(const char *GroupId): cChannelIterator(GetNextChannelInGroup(GetGroup(GroupId)))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
const cChannel* cListGroup::GetNextChannelInGroup(const cChannel *Channel)
|
const cChannel* cListGroup::GetNextChannelInGroup(const cChannel *Channel)
|
||||||
{
|
{
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
if (Channel)
|
||||||
|
Channel = SkipFakeGroups(Channels->Next(Channel));
|
||||||
|
#else
|
||||||
if (Channel)
|
if (Channel)
|
||||||
Channel = SkipFakeGroups(Channels.Next(Channel));
|
Channel = SkipFakeGroups(Channels.Next(Channel));
|
||||||
|
#endif
|
||||||
return Channel && !Channel->GroupSep() ? Channel : NULL;
|
return Channel && !Channel->GroupSep() ? Channel : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,67 +248,65 @@ const cChannel* cListGroup::NextChannel(const cChannel *Channel)
|
|||||||
}
|
}
|
||||||
//
|
//
|
||||||
// ********************* cListTree ****************
|
// ********************* cListTree ****************
|
||||||
cListTree::cListTree(const cChannel *SelectedGroup): cChannelIterator(Channels.Get(Channels.GetNextGroup(-1)))
|
cListTree::cListTree(const char *SelectedGroupId): cChannelIterator(NextGroup())
|
||||||
{
|
{
|
||||||
selectedGroup = SelectedGroup;
|
selectedGroup = GetGroup(SelectedGroupId);
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
currentGroup = Channels->Get(Channels->GetNextGroup(-1));
|
||||||
|
#else
|
||||||
currentGroup = Channels.Get(Channels.GetNextGroup(-1));
|
currentGroup = Channels.Get(Channels.GetNextGroup(-1));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
const cChannel* cListTree::NextChannel(const cChannel *Channel)
|
const cChannel* cListTree::NextChannel(const cChannel *Channel)
|
||||||
{
|
{
|
||||||
if (currentGroup == selectedGroup)
|
if (currentGroup == selectedGroup)
|
||||||
{
|
{
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
if (Channel)
|
||||||
|
Channel = SkipFakeGroups(Channels->Next(Channel));
|
||||||
|
#else
|
||||||
if (Channel)
|
if (Channel)
|
||||||
Channel = SkipFakeGroups(Channels.Next(Channel));
|
Channel = SkipFakeGroups(Channels.Next(Channel));
|
||||||
|
#endif
|
||||||
if (Channel && Channel->GroupSep())
|
if (Channel && Channel->GroupSep())
|
||||||
currentGroup = Channel;
|
currentGroup = Channel;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
if (Channel)
|
||||||
|
Channel = Channels->Get(Channels->GetNextGroup(Channel->Index()));
|
||||||
|
#else
|
||||||
if (Channel)
|
if (Channel)
|
||||||
Channel = Channels.Get(Channels.GetNextGroup(Channel->Index()));
|
Channel = Channels.Get(Channels.GetNextGroup(Channel->Index()));
|
||||||
|
#endif
|
||||||
currentGroup = Channel;
|
currentGroup = Channel;
|
||||||
}
|
}
|
||||||
return Channel;
|
return Channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ******************** cChannelList ******************
|
// ******************** cMenuList ******************
|
||||||
cChannelList::cChannelList(cChannelIterator *Iterator) : iterator(Iterator)
|
cMenuList::cMenuList(cItemIterator *Iterator) : iterator(Iterator)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
cChannelList::~cChannelList()
|
cMenuList::~cMenuList()
|
||||||
{
|
{
|
||||||
delete iterator;
|
delete iterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cChannelList::GetGroupIndex(const cChannel *Group)
|
// ******************** cHtmlMenuList ******************
|
||||||
{
|
const char* cHtmlMenuList::menu =
|
||||||
int index = 0;
|
|
||||||
for (int curr = Channels.GetNextGroup(-1); curr >= 0; curr = Channels.GetNextGroup(curr))
|
|
||||||
{
|
|
||||||
if (Channels.Get(curr) == Group)
|
|
||||||
return index;
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cChannel* cChannelList::GetGroup(int Index)
|
|
||||||
{
|
|
||||||
int group = Channels.GetNextGroup(-1);
|
|
||||||
while (Index-- && group >= 0)
|
|
||||||
group = Channels.GetNextGroup(group);
|
|
||||||
return group >= 0 ? Channels.Get(group) : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ******************** cHtmlChannelList ******************
|
|
||||||
const char* cHtmlChannelList::menu =
|
|
||||||
"[<a href=\"/\">Home</a> (<a href=\"all.html\" tvid=\"RED\">no script</a>)] "
|
"[<a href=\"/\">Home</a> (<a href=\"all.html\" tvid=\"RED\">no script</a>)] "
|
||||||
"[<a href=\"tree.html\" tvid=\"GREEN\">Tree View</a>] "
|
"[<a href=\"tree.html\" tvid=\"GREEN\">Tree View</a>] "
|
||||||
"[<a href=\"groups.html\" tvid=\"YELLOW\">Groups</a> (<a href=\"groups.m3u\">Playlist</a>)] "
|
"[<a href=\"groups.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.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>)] ";
|
||||||
|
|
||||||
const char* cHtmlChannelList::css =
|
const char* cHtmlMenuList::css =
|
||||||
"<style type=\"text/css\">\n"
|
"<style type=\"text/css\">\n"
|
||||||
"<!--\n"
|
"<!--\n"
|
||||||
"a:link, a:visited, a:hover, a:active, a:focus { color:#333399; }\n"
|
"a:link, a:visited, a:hover, a:active, a:focus { color:#333399; }\n"
|
||||||
@@ -138,7 +322,7 @@ const char* cHtmlChannelList::css =
|
|||||||
"-->\n"
|
"-->\n"
|
||||||
"</style>";
|
"</style>";
|
||||||
|
|
||||||
const char* cHtmlChannelList::js =
|
const char* cHtmlMenuList::js =
|
||||||
"<script language=\"JavaScript\">\n"
|
"<script language=\"JavaScript\">\n"
|
||||||
"<!--\n"
|
"<!--\n"
|
||||||
|
|
||||||
@@ -199,13 +383,15 @@ const char* cHtmlChannelList::js =
|
|||||||
"</script>";
|
"</script>";
|
||||||
|
|
||||||
|
|
||||||
std::string cHtmlChannelList::StreamTypeMenu()
|
std::string cHtmlMenuList::StreamTypeMenu()
|
||||||
{
|
{
|
||||||
std::string typeMenu;
|
std::string typeMenu;
|
||||||
typeMenu += (streamType == stTS ? (std::string) "[TS] " :
|
typeMenu += (streamType == stTS ? (std::string) "[TS] " :
|
||||||
(std::string) "[<a href=\"/TS/" + self + "\">TS</a>] ");
|
(std::string) "[<a href=\"/TS/" + self + "\">TS</a>] ");
|
||||||
|
#ifdef STREAMDEV_PS
|
||||||
typeMenu += (streamType == stPS ? (std::string) "[PS] " :
|
typeMenu += (streamType == stPS ? (std::string) "[PS] " :
|
||||||
(std::string) "[<a href=\"/PS/" + self + "\">PS</a>] ");
|
(std::string) "[<a href=\"/PS/" + self + "\">PS</a>] ");
|
||||||
|
#endif
|
||||||
typeMenu += (streamType == stPES ? (std::string) "[PES] " :
|
typeMenu += (streamType == stPES ? (std::string) "[PES] " :
|
||||||
(std::string) "[<a href=\"/PES/" + self + "\">PES</a>] ");
|
(std::string) "[<a href=\"/PES/" + self + "\">PES</a>] ");
|
||||||
typeMenu += (streamType == stES ? (std::string) "[ES] " :
|
typeMenu += (streamType == stES ? (std::string) "[ES] " :
|
||||||
@@ -215,27 +401,29 @@ std::string cHtmlChannelList::StreamTypeMenu()
|
|||||||
return typeMenu;
|
return typeMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
cHtmlChannelList::cHtmlChannelList(cChannelIterator *Iterator, eStreamType StreamType, const char *Self, const char *GroupTarget): cChannelList(Iterator)
|
cHtmlMenuList::cHtmlMenuList(cItemIterator *Iterator, eStreamType StreamType, const char *Self, const char *Rss, const char *GroupTarget): cMenuList(Iterator)
|
||||||
{
|
{
|
||||||
streamType = StreamType;
|
streamType = StreamType;
|
||||||
self = strdup(Self);
|
self = strdup(Self);
|
||||||
|
rss = strdup(Rss);
|
||||||
groupTarget = (GroupTarget && *GroupTarget) ? strdup(GroupTarget) : NULL;
|
groupTarget = (GroupTarget && *GroupTarget) ? strdup(GroupTarget) : NULL;
|
||||||
htmlState = hsRoot;
|
htmlState = hsRoot;
|
||||||
current = NULL;
|
onItem = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
cHtmlChannelList::~cHtmlChannelList()
|
cHtmlMenuList::~cHtmlMenuList()
|
||||||
{
|
{
|
||||||
free((void *) self);
|
free((void *) self);
|
||||||
|
free((void *) rss);
|
||||||
free((void *) groupTarget);
|
free((void *) groupTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cHtmlChannelList::HasNext()
|
bool cHtmlMenuList::HasNext()
|
||||||
{
|
{
|
||||||
return htmlState != hsPageBottom;
|
return htmlState != hsPageBottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cHtmlChannelList::Next()
|
std::string cHtmlMenuList::Next()
|
||||||
{
|
{
|
||||||
switch (htmlState)
|
switch (htmlState)
|
||||||
{
|
{
|
||||||
@@ -252,39 +440,39 @@ std::string cHtmlChannelList::Next()
|
|||||||
htmlState = hsPageTop;
|
htmlState = hsPageTop;
|
||||||
break;
|
break;
|
||||||
case hsPageTop:
|
case hsPageTop:
|
||||||
current = NextChannel();
|
onItem = NextItem();
|
||||||
htmlState = current ? (current->GroupSep() ? hsGroupTop : hsPlainTop) : hsPageBottom;
|
htmlState = onItem ? (IsGroup() ? hsGroupTop : hsPlainTop) : hsPageBottom;
|
||||||
break;
|
break;
|
||||||
case hsPlainTop:
|
case hsPlainTop:
|
||||||
htmlState = hsPlainItem;
|
htmlState = hsPlainItem;
|
||||||
break;
|
break;
|
||||||
case hsPlainItem:
|
case hsPlainItem:
|
||||||
current = NextChannel();
|
onItem = NextItem();
|
||||||
htmlState = current && !current->GroupSep() ? hsPlainItem : hsPlainBottom;
|
htmlState = onItem && !IsGroup() ? hsPlainItem : hsPlainBottom;
|
||||||
break;
|
break;
|
||||||
case hsPlainBottom:
|
case hsPlainBottom:
|
||||||
htmlState = current ? hsGroupTop : hsPageBottom;
|
htmlState = onItem ? hsGroupTop : hsPageBottom;
|
||||||
break;
|
break;
|
||||||
case hsGroupTop:
|
case hsGroupTop:
|
||||||
current = NextChannel();
|
onItem = NextItem();
|
||||||
htmlState = current && !current->GroupSep() ? hsItemsTop : hsGroupBottom;
|
htmlState = onItem && !IsGroup() ? hsItemsTop : hsGroupBottom;
|
||||||
break;
|
break;
|
||||||
case hsItemsTop:
|
case hsItemsTop:
|
||||||
htmlState = hsItem;
|
htmlState = hsItem;
|
||||||
break;
|
break;
|
||||||
case hsItem:
|
case hsItem:
|
||||||
current = NextChannel();
|
onItem = NextItem();
|
||||||
htmlState = current && !current->GroupSep() ? hsItem : hsItemsBottom;
|
htmlState = onItem && !IsGroup() ? hsItem : hsItemsBottom;
|
||||||
break;
|
break;
|
||||||
case hsItemsBottom:
|
case hsItemsBottom:
|
||||||
htmlState = hsGroupBottom;
|
htmlState = hsGroupBottom;
|
||||||
break;
|
break;
|
||||||
case hsGroupBottom:
|
case hsGroupBottom:
|
||||||
htmlState = current ? hsGroupTop : hsPageBottom;
|
htmlState = onItem ? hsGroupTop : hsPageBottom;
|
||||||
break;
|
break;
|
||||||
case hsPageBottom:
|
case hsPageBottom:
|
||||||
default:
|
default:
|
||||||
esyslog("streamdev-server cHtmlChannelList: invalid call to Next()");
|
esyslog("streamdev-server cHtmlMenuList: invalid call to Next()");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
switch (htmlState)
|
switch (htmlState)
|
||||||
@@ -309,99 +497,100 @@ std::string cHtmlChannelList::Next()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cHtmlChannelList::HtmlHead()
|
std::string cHtmlMenuList::HtmlHead()
|
||||||
{
|
{
|
||||||
return (std::string) "";
|
return (std::string) "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"RSS\" href=\"" + rss + "\"/>";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cHtmlChannelList::PageTop()
|
std::string cHtmlMenuList::PageTop()
|
||||||
{
|
{
|
||||||
return (std::string) "<div class=\"menu\"><div>" + menu + "</div><div>" + StreamTypeMenu() + "</div></div>";
|
return (std::string) "<div class=\"menu\"><div>" + menu + "</div><div>" + StreamTypeMenu() + "</div></div>";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cHtmlChannelList::PageBottom()
|
std::string cHtmlMenuList::PageBottom()
|
||||||
{
|
{
|
||||||
return (std::string) "";
|
return (std::string) "";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cHtmlChannelList::GroupTitle()
|
std::string cHtmlMenuList::GroupTitle()
|
||||||
{
|
{
|
||||||
if (groupTarget)
|
if (groupTarget)
|
||||||
{
|
{
|
||||||
return (std::string) "<a href=\"" + groupTarget + "?group=" +
|
return (std::string) "<a href=\"" + groupTarget + "?group=" + (const char*) ItemId() + "\">" +
|
||||||
(const char*) itoa(cChannelList::GetGroupIndex(current)) +
|
ItemTitle() + "</a>";
|
||||||
"\">" + current->Name() + "</a>";
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return (std::string) current->Name();
|
return (std::string) ItemTitle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cHtmlChannelList::ItemText()
|
std::string cHtmlMenuList::ItemText()
|
||||||
{
|
{
|
||||||
std::string line;
|
std::string line;
|
||||||
std::string suffix;
|
std::string suffix;
|
||||||
|
|
||||||
switch (streamType) {
|
switch (streamType) {
|
||||||
case stTS: suffix = (std::string) ".ts"; break;
|
case stTS: suffix = (std::string) ".ts"; break;
|
||||||
|
#ifdef STREAMDEV_PS
|
||||||
case stPS: suffix = (std::string) ".vob"; break;
|
case stPS: suffix = (std::string) ".vob"; break;
|
||||||
|
#endif
|
||||||
// for Network Media Tank
|
// for Network Media Tank
|
||||||
case stPES: suffix = (std::string) ".vdr"; break;
|
case stPES: suffix = (std::string) ".vdr"; break;
|
||||||
default: suffix = "";
|
default: suffix = "";
|
||||||
}
|
}
|
||||||
line += (std::string) "<li value=\"" + (const char*) itoa(current->Number()) + "\">";
|
line += (std::string) "<li value=\"" + (const char*) ItemId() + "\">";
|
||||||
line += (std::string) "<a href=\"" + (std::string) current->GetChannelID().ToString() + suffix + "\"";
|
line += (std::string) "<a href=\"" + (const char*) ItemRessource() + suffix + "\"";
|
||||||
|
|
||||||
// for Network Media Tank
|
// for Network Media Tank
|
||||||
line += (std::string) " vod ";
|
line += (std::string) " vod ";
|
||||||
if (current->Number() < 1000)
|
if (strlen(ItemId()) < 4)
|
||||||
line += (std::string) " tvid=\"" + (const char*) itoa(current->Number()) + "\"";
|
line += (std::string) " tvid=\"" + (const char*) ItemId() + "\"";
|
||||||
|
|
||||||
line += (std::string) ">" + current->Name() + "</a>";
|
line += (std::string) ">" + ItemTitle() + "</a>";
|
||||||
|
|
||||||
int count = 0;
|
// TS always streams all PIDs
|
||||||
for (int i = 0; current->Apid(i) != 0; ++i, ++count)
|
if (streamType != stTS)
|
||||||
;
|
|
||||||
for (int i = 0; current->Dpid(i) != 0; ++i, ++count)
|
|
||||||
;
|
|
||||||
|
|
||||||
if (count > 1)
|
|
||||||
{
|
{
|
||||||
int index = 1;
|
int index = 1;
|
||||||
for (int i = 0; current->Apid(i) != 0; ++i, ++index) {
|
const char* lang;
|
||||||
line += (std::string) " <a href=\"" + (std::string) current->GetChannelID().ToString() +
|
std::string pids;
|
||||||
"+" + (const char*)itoa(index) + suffix + "\" class=\"apid\" vod>" + current->Alang(i) + "</a>";
|
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; current->Dpid(i) != 0; ++i, ++index) {
|
for (int i = 0; (lang = Dlang(i)) != NULL; ++i, ++index) {
|
||||||
line += (std::string) " <a href=\"" + (std::string) current->GetChannelID().ToString() +
|
pids += (std::string) " <a href=\"" + (const char*) ItemRessource() +
|
||||||
"+" + (const char*)itoa(index) + suffix + "\" class=\"dpid\" vod>" + current->Dlang(i) + "</a>";
|
"+" + (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;
|
||||||
}
|
}
|
||||||
line += "</li>";
|
line += "</li>";
|
||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ******************** cM3uChannelList ******************
|
// ******************** cM3uMenuList ******************
|
||||||
cM3uChannelList::cM3uChannelList(cChannelIterator *Iterator, const char* Base)
|
cM3uMenuList::cM3uMenuList(cItemIterator *Iterator, const char* Base)
|
||||||
: cChannelList(Iterator),
|
: cMenuList(Iterator),
|
||||||
m_IConv(cCharSetConv::SystemCharacterTable(), "UTF-8")
|
m_IConv(cCharSetConv::SystemCharacterTable(), "UTF-8")
|
||||||
{
|
{
|
||||||
base = strdup(Base);
|
base = strdup(Base);
|
||||||
m3uState = msFirst;
|
m3uState = msFirst;
|
||||||
}
|
}
|
||||||
|
|
||||||
cM3uChannelList::~cM3uChannelList()
|
cM3uMenuList::~cM3uMenuList()
|
||||||
{
|
{
|
||||||
free(base);
|
free(base);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cM3uChannelList::HasNext()
|
bool cM3uMenuList::HasNext()
|
||||||
{
|
{
|
||||||
return m3uState != msLast;
|
return m3uState != msLast;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cM3uChannelList::Next()
|
std::string cM3uMenuList::Next()
|
||||||
{
|
{
|
||||||
if (m3uState == msFirst)
|
if (m3uState == msFirst)
|
||||||
{
|
{
|
||||||
@@ -409,26 +598,83 @@ std::string cM3uChannelList::Next()
|
|||||||
return "#EXTM3U";
|
return "#EXTM3U";
|
||||||
}
|
}
|
||||||
|
|
||||||
const cChannel *channel = NextChannel();
|
if (!NextItem())
|
||||||
if (!channel)
|
|
||||||
{
|
{
|
||||||
m3uState = msLast;
|
m3uState = msLast;
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string name = (std::string) m_IConv.Convert(channel->Name());
|
std::string name = (std::string) m_IConv.Convert(ItemTitle());
|
||||||
|
|
||||||
if (channel->GroupSep())
|
if (IsGroup())
|
||||||
{
|
{
|
||||||
return (std::string) "#EXTINF:-1," + name + "\r\n" +
|
return (std::string) "#EXTINF:-1," + name + "\r\n" +
|
||||||
base + "group.m3u?group=" +
|
base + "group.m3u?group=" + (const char*) ItemId();
|
||||||
(const char*) itoa(cChannelList::GetGroupIndex(channel));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return (std::string) "#EXTINF:-1," +
|
return (std::string) "#EXTINF:-1," +
|
||||||
(const char*) itoa(channel->Number()) + " " + name + "\r\n" +
|
(const char*) ItemId() + " " + name + "\r\n" +
|
||||||
base + (std::string) channel->GetChannelID().ToString();
|
base + (const char*) ItemRessource();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ******************** 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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,19 +3,66 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "../common.h"
|
#include "../common.h"
|
||||||
|
#include <vdr/recording.h>
|
||||||
|
|
||||||
class cChannel;
|
class cChannel;
|
||||||
|
|
||||||
// ******************** cChannelIterator ******************
|
// ******************** cItemIterator ******************
|
||||||
class cChannelIterator
|
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
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
const cChannel *channel;
|
eStreamType streamType;
|
||||||
|
const cRecording *first;
|
||||||
|
const cRecording *current;
|
||||||
|
cThreadLock RecordingsLock;
|
||||||
protected:
|
protected:
|
||||||
|
virtual const cRecording* NextSuitable(const cRecording *Recording);
|
||||||
|
public:
|
||||||
|
virtual bool Next();
|
||||||
|
virtual bool IsGroup() const { return false; }
|
||||||
|
virtual const cString ItemId() const { return current ? itoa(current->Index() + 1) : "0"; }
|
||||||
|
virtual const char* ItemTitle() const { return current ? current->Title() : ""; }
|
||||||
|
virtual const cString ItemRessource() const;
|
||||||
|
virtual const char* Alang(int i) const { return NULL; }
|
||||||
|
virtual const char* Dlang(int i) const { return NULL; }
|
||||||
|
cRecordingsIterator(eStreamType StreamType);
|
||||||
|
virtual ~cRecordingsIterator() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
class cChannelIterator: public cItemIterator
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
const cChannel *first;
|
||||||
|
const cChannel *current;
|
||||||
|
protected:
|
||||||
|
virtual const cChannel* FirstChannel();
|
||||||
|
virtual const cChannel* NextNormal();
|
||||||
|
virtual const cChannel* NextGroup();
|
||||||
virtual const cChannel* NextChannel(const cChannel *Channel) = 0;
|
virtual const cChannel* NextChannel(const cChannel *Channel) = 0;
|
||||||
static inline const cChannel* SkipFakeGroups(const cChannel *Channel);
|
static inline const cChannel* SkipFakeGroups(const cChannel *Channel);
|
||||||
|
// Helper which returns the group by its index
|
||||||
|
static const cChannel* GetGroup(const char* GroupId);
|
||||||
public:
|
public:
|
||||||
const cChannel* Next();
|
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; }
|
||||||
cChannelIterator(const cChannel *First);
|
cChannelIterator(const cChannel *First);
|
||||||
virtual ~cChannelIterator() {};
|
virtual ~cChannelIterator() {};
|
||||||
};
|
};
|
||||||
@@ -54,7 +101,7 @@ class cListGroup: public cChannelIterator
|
|||||||
protected:
|
protected:
|
||||||
virtual const cChannel* NextChannel(const cChannel *Channel);
|
virtual const cChannel* NextChannel(const cChannel *Channel);
|
||||||
public:
|
public:
|
||||||
cListGroup(const cChannel *Group);
|
cListGroup(const char *GroupId);
|
||||||
virtual ~cListGroup() {};
|
virtual ~cListGroup() {};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -66,31 +113,32 @@ class cListTree: public cChannelIterator
|
|||||||
protected:
|
protected:
|
||||||
virtual const cChannel* NextChannel(const cChannel *Channel);
|
virtual const cChannel* NextChannel(const cChannel *Channel);
|
||||||
public:
|
public:
|
||||||
cListTree(const cChannel *SelectedGroup);
|
cListTree(const char *SelectedGroupId);
|
||||||
virtual ~cListTree() {};
|
virtual ~cListTree() {};
|
||||||
};
|
};
|
||||||
|
|
||||||
// ******************** cChannelList ******************
|
// ******************** cMenuList ******************
|
||||||
class cChannelList
|
class cMenuList
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
cChannelIterator *iterator;
|
cItemIterator *iterator;
|
||||||
protected:
|
protected:
|
||||||
const cChannel* NextChannel() { return iterator->Next(); }
|
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); }
|
||||||
public:
|
public:
|
||||||
// Helper which returns the group index
|
|
||||||
static int GetGroupIndex(const cChannel* Group);
|
|
||||||
// Helper which returns the group by its index
|
|
||||||
static const cChannel* GetGroup(int Index);
|
|
||||||
|
|
||||||
virtual std::string HttpHeader() { return "HTTP/1.0 200 OK\r\n"; };
|
virtual std::string HttpHeader() { return "HTTP/1.0 200 OK\r\n"; };
|
||||||
virtual bool HasNext() = 0;
|
virtual bool HasNext() = 0;
|
||||||
virtual std::string Next() = 0;
|
virtual std::string Next() = 0;
|
||||||
cChannelList(cChannelIterator *Iterator);
|
cMenuList(cItemIterator *Iterator);
|
||||||
virtual ~cChannelList();
|
virtual ~cMenuList();
|
||||||
};
|
};
|
||||||
|
|
||||||
class cHtmlChannelList: public cChannelList
|
class cHtmlMenuList: public cMenuList
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
static const char* menu;
|
static const char* menu;
|
||||||
@@ -104,9 +152,10 @@ class cHtmlChannelList: public cChannelList
|
|||||||
hsItemsTop, hsItem, hsItemsBottom
|
hsItemsTop, hsItem, hsItemsBottom
|
||||||
};
|
};
|
||||||
eHtmlState htmlState;
|
eHtmlState htmlState;
|
||||||
const cChannel *current;
|
bool onItem;
|
||||||
eStreamType streamType;
|
eStreamType streamType;
|
||||||
const char* self;
|
const char* self;
|
||||||
|
const char* rss;
|
||||||
const char* groupTarget;
|
const char* groupTarget;
|
||||||
|
|
||||||
std::string StreamTypeMenu();
|
std::string StreamTypeMenu();
|
||||||
@@ -117,18 +166,18 @@ class cHtmlChannelList: public cChannelList
|
|||||||
std::string PageBottom();
|
std::string PageBottom();
|
||||||
public:
|
public:
|
||||||
virtual std::string HttpHeader() {
|
virtual std::string HttpHeader() {
|
||||||
return cChannelList::HttpHeader()
|
return cMenuList::HttpHeader()
|
||||||
+ "Content-type: text/html; charset="
|
+ "Content-type: text/html; charset="
|
||||||
+ (cCharSetConv::SystemCharacterTable() ? cCharSetConv::SystemCharacterTable() : "UTF-8")
|
+ (cCharSetConv::SystemCharacterTable() ? cCharSetConv::SystemCharacterTable() : "UTF-8")
|
||||||
+ "\r\n";
|
+ "\r\n";
|
||||||
}
|
}
|
||||||
virtual bool HasNext();
|
virtual bool HasNext();
|
||||||
virtual std::string Next();
|
virtual std::string Next();
|
||||||
cHtmlChannelList(cChannelIterator *Iterator, eStreamType StreamType, const char *Self, const char *GroupTarget);
|
cHtmlMenuList(cItemIterator *Iterator, eStreamType StreamType, const char *Self, const char *Rss, const char *GroupTarget);
|
||||||
virtual ~cHtmlChannelList();
|
virtual ~cHtmlMenuList();
|
||||||
};
|
};
|
||||||
|
|
||||||
class cM3uChannelList: public cChannelList
|
class cM3uMenuList: public cMenuList
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
char *base;
|
char *base;
|
||||||
@@ -136,17 +185,41 @@ class cM3uChannelList: public cChannelList
|
|||||||
eM3uState m3uState;
|
eM3uState m3uState;
|
||||||
cCharSetConv m_IConv;
|
cCharSetConv m_IConv;
|
||||||
public:
|
public:
|
||||||
virtual std::string HttpHeader() { return cChannelList::HttpHeader() + "Content-type: audio/x-mpegurl; charset=UTF-8\r\n"; };
|
virtual std::string HttpHeader() { return cMenuList::HttpHeader() + "Content-type: audio/x-mpegurl; charset=UTF-8\r\n"; };
|
||||||
virtual bool HasNext();
|
virtual bool HasNext();
|
||||||
virtual std::string Next();
|
virtual std::string Next();
|
||||||
cM3uChannelList(cChannelIterator *Iterator, const char* Base);
|
cM3uMenuList(cItemIterator *Iterator, const char* Base);
|
||||||
virtual ~cM3uChannelList();
|
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();
|
||||||
};
|
};
|
||||||
|
|
||||||
inline const cChannel* cChannelIterator::SkipFakeGroups(const cChannel* Group)
|
inline const cChannel* cChannelIterator::SkipFakeGroups(const cChannel* Group)
|
||||||
{
|
{
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
LOCK_CHANNELS_READ;
|
||||||
|
#endif
|
||||||
while (Group && Group->GroupSep() && !*Group->Name())
|
while (Group && Group->GroupSep() && !*Group->Name())
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
Group = Channels->Next(Group);
|
||||||
|
#else
|
||||||
Group = Channels.Next(Group);
|
Group = Channels.Next(Group);
|
||||||
|
#endif
|
||||||
return Group;
|
return Group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,45 +5,43 @@
|
|||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: streamdev 0.5.0\n"
|
"Project-Id-Version: streamdev\n"
|
||||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||||
"POT-Creation-Date: 2010-06-14 13:06+0200\n"
|
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
|
||||||
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
||||||
"Last-Translator: Frank Schmirler <vdrdev@schmirler.de>\n"
|
"Last-Translator: Frank Schmirler <vdrdev@schmirler.de>\n"
|
||||||
"Language-Team: <vdr@linuxtv.org>\n"
|
"Language-Team: German <vdr@linuxtv.org>\n"
|
||||||
|
"Language: de\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
msgid "VDR Streaming Server"
|
|
||||||
msgstr "VDR Streaming Server"
|
|
||||||
|
|
||||||
msgid "Streaming active"
|
msgid "Streaming active"
|
||||||
msgstr "Streamen im Gange"
|
msgstr "Streamen im Gange"
|
||||||
|
|
||||||
msgid "Suspend Live TV"
|
msgid "Streamdev Connections"
|
||||||
msgstr "Live-TV pausieren"
|
msgstr "Streamdev Verbindungen"
|
||||||
|
|
||||||
msgid "Offer suspend mode"
|
msgid "Disconnect"
|
||||||
msgstr "Pausieren anbieten"
|
msgstr "Trennen"
|
||||||
|
|
||||||
msgid "Always suspended"
|
msgid "Suspend"
|
||||||
msgstr "Immer pausiert"
|
msgstr "Pausieren"
|
||||||
|
|
||||||
msgid "Never suspended"
|
|
||||||
msgstr "Nie pausiert"
|
|
||||||
|
|
||||||
msgid "Common Settings"
|
msgid "Common Settings"
|
||||||
msgstr "Allgemeines"
|
msgstr "Allgemeines"
|
||||||
|
|
||||||
|
msgid "Hide Mainmenu Entry"
|
||||||
|
msgstr "Hauptmenüeintrag verstecken"
|
||||||
|
|
||||||
|
msgid "Start with Live TV suspended"
|
||||||
|
msgstr "Live-TV beim Start pausieren"
|
||||||
|
|
||||||
msgid "Maximum Number of Clients"
|
msgid "Maximum Number of Clients"
|
||||||
msgstr "Maximalanzahl an Clients"
|
msgstr "Maximalanzahl an Clients"
|
||||||
|
|
||||||
msgid "Suspend behaviour"
|
msgid "Live TV buffer delay (ms)"
|
||||||
msgstr "Pausierverhalten"
|
msgstr "Live-TV Pufferdauer (ms)"
|
||||||
|
|
||||||
msgid "Client may suspend"
|
|
||||||
msgstr "Client darf pausieren"
|
|
||||||
|
|
||||||
msgid "VDR-to-VDR Server"
|
msgid "VDR-to-VDR Server"
|
||||||
msgstr "VDR-zu-VDR Server"
|
msgstr "VDR-zu-VDR Server"
|
||||||
@@ -57,6 +55,15 @@ msgstr "Port des VDR-zu-VDR Servers"
|
|||||||
msgid "Bind to IP"
|
msgid "Bind to IP"
|
||||||
msgstr "Binde an IP"
|
msgstr "Binde an IP"
|
||||||
|
|
||||||
|
msgid "Legacy Client Priority"
|
||||||
|
msgstr "Priorität für alte Clients"
|
||||||
|
|
||||||
|
msgid "Client may suspend"
|
||||||
|
msgstr "Client darf pausieren"
|
||||||
|
|
||||||
|
msgid "Loop Prevention"
|
||||||
|
msgstr "Schleifen verhindern"
|
||||||
|
|
||||||
msgid "HTTP Server"
|
msgid "HTTP Server"
|
||||||
msgstr "HTTP Server"
|
msgstr "HTTP Server"
|
||||||
|
|
||||||
@@ -66,6 +73,9 @@ msgstr "HTTP Server starten"
|
|||||||
msgid "HTTP Server Port"
|
msgid "HTTP Server Port"
|
||||||
msgstr "Port des HTTP Servers"
|
msgstr "Port des HTTP Servers"
|
||||||
|
|
||||||
|
msgid "Priority"
|
||||||
|
msgstr "Priorität"
|
||||||
|
|
||||||
msgid "HTTP Streamtype"
|
msgid "HTTP Streamtype"
|
||||||
msgstr "HTTP Streamtyp"
|
msgstr "HTTP Streamtyp"
|
||||||
|
|
||||||
@@ -81,3 +91,5 @@ msgstr "Port des Multicast Clients"
|
|||||||
msgid "Multicast Streamtype"
|
msgid "Multicast Streamtype"
|
||||||
msgstr "Multicast Streamtyp"
|
msgstr "Multicast Streamtyp"
|
||||||
|
|
||||||
|
msgid "VDR Streaming Server"
|
||||||
|
msgstr "VDR Streaming Server"
|
||||||
|
|||||||
@@ -6,44 +6,42 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: streamdev 0.5.0\n"
|
"Project-Id-Version: streamdev 0.5.0\n"
|
||||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||||
"POT-Creation-Date: 2010-06-14 13:06+0200\n"
|
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
|
||||||
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
|
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
|
||||||
"Last-Translator: Javier Bradineras <jbradi@hotmail.com>\n"
|
"Last-Translator: Javier Bradineras <jbradi@hotmail.com>\n"
|
||||||
"Language-Team: <vdr@linuxtv.org>\n"
|
"Language-Team: Spanish <vdr@linuxtv.org>\n"
|
||||||
|
"Language: es\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
msgid "VDR Streaming Server"
|
|
||||||
msgstr "Servidor de transmisiones del VDR"
|
|
||||||
|
|
||||||
msgid "Streaming active"
|
msgid "Streaming active"
|
||||||
msgstr "Trasmisión activa"
|
msgstr "Trasmisión activa"
|
||||||
|
|
||||||
msgid "Suspend Live TV"
|
msgid "Streamdev Connections"
|
||||||
msgstr "Suspender TV en vivo"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Offer suspend mode"
|
msgid "Disconnect"
|
||||||
msgstr "Ofrecer modo de suspensión"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Always suspended"
|
msgid "Suspend"
|
||||||
msgstr "Siempre suspendido"
|
msgstr "Suspender"
|
||||||
|
|
||||||
msgid "Never suspended"
|
|
||||||
msgstr "Nunca suspendido"
|
|
||||||
|
|
||||||
msgid "Common Settings"
|
msgid "Common Settings"
|
||||||
msgstr "Configuración común"
|
msgstr "Configuración común"
|
||||||
|
|
||||||
|
msgid "Hide Mainmenu Entry"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Start with Live TV suspended"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Maximum Number of Clients"
|
msgid "Maximum Number of Clients"
|
||||||
msgstr "Numero máximo de clientes"
|
msgstr "Numero máximo de clientes"
|
||||||
|
|
||||||
msgid "Suspend behaviour"
|
msgid "Live TV buffer delay (ms)"
|
||||||
msgstr "Comportamiento de la suspensión"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Client may suspend"
|
|
||||||
msgstr "Permitir suspender al cliente"
|
|
||||||
|
|
||||||
msgid "VDR-to-VDR Server"
|
msgid "VDR-to-VDR Server"
|
||||||
msgstr "Servidor VDR-a-VDR"
|
msgstr "Servidor VDR-a-VDR"
|
||||||
@@ -57,6 +55,15 @@ msgstr "Puerto del Servidor VDR-a-VDR"
|
|||||||
msgid "Bind to IP"
|
msgid "Bind to IP"
|
||||||
msgstr "IP asociada"
|
msgstr "IP asociada"
|
||||||
|
|
||||||
|
msgid "Legacy Client Priority"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Client may suspend"
|
||||||
|
msgstr "Permitir suspender al cliente"
|
||||||
|
|
||||||
|
msgid "Loop Prevention"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "HTTP Server"
|
msgid "HTTP Server"
|
||||||
msgstr "Servidor HTTP"
|
msgstr "Servidor HTTP"
|
||||||
|
|
||||||
@@ -66,6 +73,9 @@ msgstr "Iniciar Servidor HTTP"
|
|||||||
msgid "HTTP Server Port"
|
msgid "HTTP Server Port"
|
||||||
msgstr "Puerto del Servidor HTTP"
|
msgstr "Puerto del Servidor HTTP"
|
||||||
|
|
||||||
|
msgid "Priority"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "HTTP Streamtype"
|
msgid "HTTP Streamtype"
|
||||||
msgstr "Tipo de flujo HTTP"
|
msgstr "Tipo de flujo HTTP"
|
||||||
|
|
||||||
@@ -81,4 +91,5 @@ msgstr "Puerto del Cliente Multicast"
|
|||||||
msgid "Multicast Streamtype"
|
msgid "Multicast Streamtype"
|
||||||
msgstr "Tipo de flujo Multicast"
|
msgstr "Tipo de flujo Multicast"
|
||||||
|
|
||||||
|
msgid "VDR Streaming Server"
|
||||||
|
msgstr "Servidor de transmisiones del VDR"
|
||||||
|
|||||||
@@ -1,55 +1,53 @@
|
|||||||
# VDR streamdev plugin language source file.
|
# VDR streamdev plugin language source file.
|
||||||
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
||||||
# This file is distributed under the same license as the VDR streamdev package.
|
# This file is distributed under the same license as the VDR streamdev package.
|
||||||
# Rolf Ahrenberg <rahrenbe@cc.hut.fi>, 2008
|
# Rolf Ahrenberg, 2008-
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: streamdev 0.5.0\n"
|
"Project-Id-Version: streamdev 0.5.0\n"
|
||||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||||
"POT-Creation-Date: 2010-06-14 13:06+0200\n"
|
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
|
||||||
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
||||||
"Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"
|
"Last-Translator: Rolf Ahrenberg\n"
|
||||||
"Language-Team: <vdr@linuxtv.org>\n"
|
"Language-Team: Finnish <vdr@linuxtv.org>\n"
|
||||||
|
"Language: fi\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
msgid "VDR Streaming Server"
|
|
||||||
msgstr "VDR-suoratoistopalvelin"
|
|
||||||
|
|
||||||
msgid "Streaming active"
|
msgid "Streaming active"
|
||||||
msgstr "Suoratoistopalvelin aktiivinen"
|
msgstr "Suoratoistopalvelin aktiivinen"
|
||||||
|
|
||||||
msgid "Suspend Live TV"
|
msgid "Streamdev Connections"
|
||||||
msgstr "Pysäytä suora TV-lähetys"
|
msgstr "Suoratoistoyhteydet"
|
||||||
|
|
||||||
msgid "Offer suspend mode"
|
msgid "Disconnect"
|
||||||
msgstr "tyrkytä"
|
msgstr "Katkaise"
|
||||||
|
|
||||||
msgid "Always suspended"
|
msgid "Suspend"
|
||||||
msgstr "aina"
|
msgstr "Pysäytä"
|
||||||
|
|
||||||
msgid "Never suspended"
|
|
||||||
msgstr "ei koskaan"
|
|
||||||
|
|
||||||
msgid "Common Settings"
|
msgid "Common Settings"
|
||||||
msgstr "Yleiset asetukset"
|
msgstr "Yleiset asetukset"
|
||||||
|
|
||||||
|
msgid "Hide Mainmenu Entry"
|
||||||
|
msgstr "Piilota valinta päävalikosta"
|
||||||
|
|
||||||
|
msgid "Start with Live TV suspended"
|
||||||
|
msgstr "Käynnistä Live-katselu pysäytettynä"
|
||||||
|
|
||||||
msgid "Maximum Number of Clients"
|
msgid "Maximum Number of Clients"
|
||||||
msgstr "Suurin sallittu asiakkaiden määrä"
|
msgstr "Suurin sallittu asiakkaiden määrä"
|
||||||
|
|
||||||
msgid "Suspend behaviour"
|
msgid "Live TV buffer delay (ms)"
|
||||||
msgstr "Pysäytystoiminto"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Client may suspend"
|
|
||||||
msgstr "Asiakas saa pysäyttää palvelimen"
|
|
||||||
|
|
||||||
msgid "VDR-to-VDR Server"
|
msgid "VDR-to-VDR Server"
|
||||||
msgstr "VDR-palvelin"
|
msgstr "VDR-palvelin"
|
||||||
|
|
||||||
msgid "Start VDR-to-VDR Server"
|
msgid "Start VDR-to-VDR Server"
|
||||||
msgstr "Käynnistä VDR-palvelin"
|
msgstr "Käynnistä VDR-palvelin"
|
||||||
|
|
||||||
msgid "VDR-to-VDR Server Port"
|
msgid "VDR-to-VDR Server Port"
|
||||||
msgstr "VDR-palvelimen portti"
|
msgstr "VDR-palvelimen portti"
|
||||||
@@ -57,27 +55,41 @@ msgstr "VDR-palvelimen portti"
|
|||||||
msgid "Bind to IP"
|
msgid "Bind to IP"
|
||||||
msgstr "Sido osoitteeseen"
|
msgstr "Sido osoitteeseen"
|
||||||
|
|
||||||
|
msgid "Legacy Client Priority"
|
||||||
|
msgstr "Legacy-asiakkaan prioriteetti"
|
||||||
|
|
||||||
|
msgid "Client may suspend"
|
||||||
|
msgstr "Asiakas saa pysäyttää palvelimen"
|
||||||
|
|
||||||
|
msgid "Loop Prevention"
|
||||||
|
msgstr "Estä asiakaslaitesilmukat"
|
||||||
|
|
||||||
msgid "HTTP Server"
|
msgid "HTTP Server"
|
||||||
msgstr "HTTP-palvelin"
|
msgstr "HTTP-palvelin"
|
||||||
|
|
||||||
msgid "Start HTTP Server"
|
msgid "Start HTTP Server"
|
||||||
msgstr "Käynnistä HTTP-palvelin"
|
msgstr "Käynnistä HTTP-palvelin"
|
||||||
|
|
||||||
msgid "HTTP Server Port"
|
msgid "HTTP Server Port"
|
||||||
msgstr "HTTP-palvelimen portti"
|
msgstr "HTTP-palvelimen portti"
|
||||||
|
|
||||||
|
msgid "Priority"
|
||||||
|
msgstr "Prioriteetti"
|
||||||
|
|
||||||
msgid "HTTP Streamtype"
|
msgid "HTTP Streamtype"
|
||||||
msgstr "HTTP-lähetysmuoto"
|
msgstr "HTTP-lähetysmuoto"
|
||||||
|
|
||||||
msgid "Multicast Streaming Server"
|
msgid "Multicast Streaming Server"
|
||||||
msgstr "Multicast-suoratoistopalvelin"
|
msgstr "Multicast-suoratoistopalvelin"
|
||||||
|
|
||||||
msgid "Start IGMP Server"
|
msgid "Start IGMP Server"
|
||||||
msgstr "Käynnistä IGMP-palvelin"
|
msgstr "Käynnistä IGMP-palvelin"
|
||||||
|
|
||||||
msgid "Multicast Client Port"
|
msgid "Multicast Client Port"
|
||||||
msgstr "Multicast-portti"
|
msgstr "Multicast-portti"
|
||||||
|
|
||||||
msgid "Multicast Streamtype"
|
msgid "Multicast Streamtype"
|
||||||
msgstr "Multicast-lähetysmuoto"
|
msgstr "Multicast-lähetysmuoto"
|
||||||
|
|
||||||
|
msgid "VDR Streaming Server"
|
||||||
|
msgstr "VDR-suoratoistopalvelin"
|
||||||
|
|||||||
@@ -6,44 +6,42 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: streamdev 0.5.0\n"
|
"Project-Id-Version: streamdev 0.5.0\n"
|
||||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||||
"POT-Creation-Date: 2010-06-14 13:06+0200\n"
|
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
|
||||||
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
||||||
"Last-Translator: micky979 <micky979@free.fr>\n"
|
"Last-Translator: micky979 <micky979@free.fr>\n"
|
||||||
"Language-Team: <vdr@linuxtv.org>\n"
|
"Language-Team: French <vdr@linuxtv.org>\n"
|
||||||
|
"Language: fr\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
msgid "VDR Streaming Server"
|
|
||||||
msgstr "Serveur de streaming VDR"
|
|
||||||
|
|
||||||
msgid "Streaming active"
|
msgid "Streaming active"
|
||||||
msgstr "Streaming actif"
|
msgstr "Streaming actif"
|
||||||
|
|
||||||
msgid "Suspend Live TV"
|
msgid "Streamdev Connections"
|
||||||
msgstr "Suspendre Live TV"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Offer suspend mode"
|
msgid "Disconnect"
|
||||||
msgstr "Offrir le mode suspendre"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Always suspended"
|
msgid "Suspend"
|
||||||
msgstr "Toujours suspendre"
|
msgstr "Suspendre"
|
||||||
|
|
||||||
msgid "Never suspended"
|
|
||||||
msgstr "Jamais suspendre"
|
|
||||||
|
|
||||||
msgid "Common Settings"
|
msgid "Common Settings"
|
||||||
msgstr "Paramètres communs"
|
msgstr "Paramètres communs"
|
||||||
|
|
||||||
|
msgid "Hide Mainmenu Entry"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Start with Live TV suspended"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Maximum Number of Clients"
|
msgid "Maximum Number of Clients"
|
||||||
msgstr "Nombre maximun de clients"
|
msgstr "Nombre maximun de clients"
|
||||||
|
|
||||||
msgid "Suspend behaviour"
|
msgid "Live TV buffer delay (ms)"
|
||||||
msgstr "Suspendre"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Client may suspend"
|
|
||||||
msgstr "Le client peut suspendre"
|
|
||||||
|
|
||||||
msgid "VDR-to-VDR Server"
|
msgid "VDR-to-VDR Server"
|
||||||
msgstr "VDR-to-VDR Serveur"
|
msgstr "VDR-to-VDR Serveur"
|
||||||
@@ -57,6 +55,15 @@ msgstr "Port du serveur VDR-to-VDR"
|
|||||||
msgid "Bind to IP"
|
msgid "Bind to IP"
|
||||||
msgstr "Attacher aux IP"
|
msgstr "Attacher aux IP"
|
||||||
|
|
||||||
|
msgid "Legacy Client Priority"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Client may suspend"
|
||||||
|
msgstr "Le client peut suspendre"
|
||||||
|
|
||||||
|
msgid "Loop Prevention"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "HTTP Server"
|
msgid "HTTP Server"
|
||||||
msgstr "Serveur HTTP"
|
msgstr "Serveur HTTP"
|
||||||
|
|
||||||
@@ -66,6 +73,9 @@ msgstr "D
|
|||||||
msgid "HTTP Server Port"
|
msgid "HTTP Server Port"
|
||||||
msgstr "Port du serveur HTTP"
|
msgstr "Port du serveur HTTP"
|
||||||
|
|
||||||
|
msgid "Priority"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "HTTP Streamtype"
|
msgid "HTTP Streamtype"
|
||||||
msgstr "Type de Streaming HTTP"
|
msgstr "Type de Streaming HTTP"
|
||||||
|
|
||||||
@@ -81,3 +91,5 @@ msgstr ""
|
|||||||
msgid "Multicast Streamtype"
|
msgid "Multicast Streamtype"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "VDR Streaming Server"
|
||||||
|
msgstr "Serveur de streaming VDR"
|
||||||
|
|||||||
@@ -8,44 +8,42 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: streamdev 0.5.0\n"
|
"Project-Id-Version: streamdev 0.5.0\n"
|
||||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||||
"POT-Creation-Date: 2010-06-14 13:06+0200\n"
|
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
|
||||||
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
|
"PO-Revision-Date: 2012-06-12 19:57+0100\n"
|
||||||
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
|
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
|
||||||
"Language-Team: <vdr@linuxtv.org>\n"
|
"Language-Team: Italian <vdr@linuxtv.org>\n"
|
||||||
|
"Language: it\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
msgid "VDR Streaming Server"
|
|
||||||
msgstr "Server trasmissione VDR"
|
|
||||||
|
|
||||||
msgid "Streaming active"
|
msgid "Streaming active"
|
||||||
msgstr "Trasmissione attiva"
|
msgstr "Trasmissione attiva"
|
||||||
|
|
||||||
msgid "Suspend Live TV"
|
msgid "Streamdev Connections"
|
||||||
msgstr "Sospendi TV dal vivo"
|
msgstr "Connessioni Streamdev"
|
||||||
|
|
||||||
msgid "Offer suspend mode"
|
msgid "Disconnect"
|
||||||
msgstr "Offri mod. sospensione"
|
msgstr "Disconnetti"
|
||||||
|
|
||||||
msgid "Always suspended"
|
msgid "Suspend"
|
||||||
msgstr "Sempre sospeso"
|
msgstr "Sospendi"
|
||||||
|
|
||||||
msgid "Never suspended"
|
|
||||||
msgstr "Mai sospeso"
|
|
||||||
|
|
||||||
msgid "Common Settings"
|
msgid "Common Settings"
|
||||||
msgstr "Impostazioni comuni"
|
msgstr "Impostazioni comuni"
|
||||||
|
|
||||||
|
msgid "Hide Mainmenu Entry"
|
||||||
|
msgstr "Nascondi voce menu principale"
|
||||||
|
|
||||||
|
msgid "Start with Live TV suspended"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Maximum Number of Clients"
|
msgid "Maximum Number of Clients"
|
||||||
msgstr "Numero massimo di Client"
|
msgstr "Numero massimo di Client"
|
||||||
|
|
||||||
msgid "Suspend behaviour"
|
msgid "Live TV buffer delay (ms)"
|
||||||
msgstr "Tipo sospensione"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Client may suspend"
|
|
||||||
msgstr "Permetti sospensione al Client"
|
|
||||||
|
|
||||||
msgid "VDR-to-VDR Server"
|
msgid "VDR-to-VDR Server"
|
||||||
msgstr "Server VDR-a-VDR"
|
msgstr "Server VDR-a-VDR"
|
||||||
@@ -59,6 +57,15 @@ msgstr "Porta Server VDR-a-VDR"
|
|||||||
msgid "Bind to IP"
|
msgid "Bind to IP"
|
||||||
msgstr "IP associati"
|
msgstr "IP associati"
|
||||||
|
|
||||||
|
msgid "Legacy Client Priority"
|
||||||
|
msgstr "Priorità nativa client"
|
||||||
|
|
||||||
|
msgid "Client may suspend"
|
||||||
|
msgstr "Permetti sospensione al Client"
|
||||||
|
|
||||||
|
msgid "Loop Prevention"
|
||||||
|
msgstr "Evita ciclo"
|
||||||
|
|
||||||
msgid "HTTP Server"
|
msgid "HTTP Server"
|
||||||
msgstr "Server HTTP"
|
msgstr "Server HTTP"
|
||||||
|
|
||||||
@@ -68,6 +75,9 @@ msgstr "Avvia Server HTTP"
|
|||||||
msgid "HTTP Server Port"
|
msgid "HTTP Server Port"
|
||||||
msgstr "Porta Server HTTP"
|
msgstr "Porta Server HTTP"
|
||||||
|
|
||||||
|
msgid "Priority"
|
||||||
|
msgstr "Priorità"
|
||||||
|
|
||||||
msgid "HTTP Streamtype"
|
msgid "HTTP Streamtype"
|
||||||
msgstr "Tipo flusso HTTP"
|
msgstr "Tipo flusso HTTP"
|
||||||
|
|
||||||
@@ -83,3 +93,5 @@ msgstr "Porta Client Multicast"
|
|||||||
msgid "Multicast Streamtype"
|
msgid "Multicast Streamtype"
|
||||||
msgstr "Tipo flusso Multicast"
|
msgstr "Tipo flusso Multicast"
|
||||||
|
|
||||||
|
msgid "VDR Streaming Server"
|
||||||
|
msgstr "Server trasmissione VDR"
|
||||||
|
|||||||
@@ -6,44 +6,42 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: streamdev 0.5.0\n"
|
"Project-Id-Version: streamdev 0.5.0\n"
|
||||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||||
"POT-Creation-Date: 2010-06-14 13:06+0200\n"
|
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
|
||||||
"PO-Revision-Date: 2009-11-26 21:57+0200\n"
|
"PO-Revision-Date: 2009-11-26 21:57+0200\n"
|
||||||
"Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
|
"Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
|
||||||
"Language-Team: Lietuvių\n"
|
"Language-Team: Lithuanian <vdr@linuxtv.org>\n"
|
||||||
|
"Language: lt\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
msgid "VDR Streaming Server"
|
|
||||||
msgstr "VDR transliavimo serveris"
|
|
||||||
|
|
||||||
msgid "Streaming active"
|
msgid "Streaming active"
|
||||||
msgstr "Transliavimas vyksta"
|
msgstr "Transliavimas vyksta"
|
||||||
|
|
||||||
msgid "Suspend Live TV"
|
msgid "Streamdev Connections"
|
||||||
msgstr "Pristabdyti Live TV"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Offer suspend mode"
|
msgid "Disconnect"
|
||||||
msgstr "Klausti dėl sustabdymo"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Always suspended"
|
msgid "Suspend"
|
||||||
msgstr "Visada stabdyti"
|
msgstr "Pristabdyti"
|
||||||
|
|
||||||
msgid "Never suspended"
|
|
||||||
msgstr "Niekada nestabdyti"
|
|
||||||
|
|
||||||
msgid "Common Settings"
|
msgid "Common Settings"
|
||||||
msgstr "Bendri nustatymai"
|
msgstr "Bendri nustatymai"
|
||||||
|
|
||||||
|
msgid "Hide Mainmenu Entry"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Start with Live TV suspended"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Maximum Number of Clients"
|
msgid "Maximum Number of Clients"
|
||||||
msgstr "Maksimalus klientų skaičius"
|
msgstr "Maksimalus klientų skaičius"
|
||||||
|
|
||||||
msgid "Suspend behaviour"
|
msgid "Live TV buffer delay (ms)"
|
||||||
msgstr "Pristabdyti veikimą"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Client may suspend"
|
|
||||||
msgstr "Klientas gali pristabdyti"
|
|
||||||
|
|
||||||
msgid "VDR-to-VDR Server"
|
msgid "VDR-to-VDR Server"
|
||||||
msgstr "VDR-su-VDR Serveris"
|
msgstr "VDR-su-VDR Serveris"
|
||||||
@@ -57,6 +55,15 @@ msgstr "VDR-su-VDR Serverio portas"
|
|||||||
msgid "Bind to IP"
|
msgid "Bind to IP"
|
||||||
msgstr "Pririšti IP"
|
msgstr "Pririšti IP"
|
||||||
|
|
||||||
|
msgid "Legacy Client Priority"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Client may suspend"
|
||||||
|
msgstr "Klientas gali pristabdyti"
|
||||||
|
|
||||||
|
msgid "Loop Prevention"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "HTTP Server"
|
msgid "HTTP Server"
|
||||||
msgstr "HTTP Serveris"
|
msgstr "HTTP Serveris"
|
||||||
|
|
||||||
@@ -66,6 +73,9 @@ msgstr "Paleisti HTTP serverį"
|
|||||||
msgid "HTTP Server Port"
|
msgid "HTTP Server Port"
|
||||||
msgstr "HTTP serverio portas"
|
msgstr "HTTP serverio portas"
|
||||||
|
|
||||||
|
msgid "Priority"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "HTTP Streamtype"
|
msgid "HTTP Streamtype"
|
||||||
msgstr "HTTP transliavimo tipas"
|
msgstr "HTTP transliavimo tipas"
|
||||||
|
|
||||||
@@ -81,3 +91,5 @@ msgstr "Multicast kliento portas"
|
|||||||
msgid "Multicast Streamtype"
|
msgid "Multicast Streamtype"
|
||||||
msgstr "Multicast transliavimo tipas"
|
msgstr "Multicast transliavimo tipas"
|
||||||
|
|
||||||
|
msgid "VDR Streaming Server"
|
||||||
|
msgstr "VDR transliavimo serveris"
|
||||||
|
|||||||
96
server/po/pl_PL.po
Normal file
96
server/po/pl_PL.po
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
# 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"
|
||||||
@@ -6,44 +6,42 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: streamdev 0.5.0\n"
|
"Project-Id-Version: streamdev 0.5.0\n"
|
||||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||||
"POT-Creation-Date: 2010-06-14 13:06+0200\n"
|
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
|
||||||
"PO-Revision-Date: 2008-06-26 15:36+0100\n"
|
"PO-Revision-Date: 2008-06-26 15:36+0100\n"
|
||||||
"Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n"
|
"Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n"
|
||||||
"Language-Team: <vdr@linuxtv.org>\n"
|
"Language-Team: Russian <vdr@linuxtv.org>\n"
|
||||||
|
"Language: ru\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=ISO-8859-5\n"
|
"Content-Type: text/plain; charset=ISO-8859-5\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
msgid "VDR Streaming Server"
|
|
||||||
msgstr "VDR Streaming áÕàÒÕà"
|
|
||||||
|
|
||||||
msgid "Streaming active"
|
msgid "Streaming active"
|
||||||
msgstr "ÁâàØÜØÝÓ ÐÚâØÒÕÝ"
|
msgstr "ÁâàØÜØÝÓ ÐÚâØÒÕÝ"
|
||||||
|
|
||||||
msgid "Suspend Live TV"
|
msgid "Streamdev Connections"
|
||||||
msgstr "¾áâÐÝÞÒÚÐ Live TV"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Offer suspend mode"
|
msgid "Disconnect"
|
||||||
msgstr "¿àÕÔÛÐÓÐâì ÞáâÐÝÞÒÚã"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Always suspended"
|
msgid "Suspend"
|
||||||
msgstr "²áÕÓÔÐ ÞáâÐÝÞÒÛÕÝ"
|
msgstr "¾áâÐÝÞÒÚÐ"
|
||||||
|
|
||||||
msgid "Never suspended"
|
|
||||||
msgstr "½ØÚÞÓÔÐ ÝÕ ÞáâÐÝÞÒÛÕÝ"
|
|
||||||
|
|
||||||
msgid "Common Settings"
|
msgid "Common Settings"
|
||||||
msgstr "½ÐáâàÞÙÚØ"
|
msgstr "½ÐáâàÞÙÚØ"
|
||||||
|
|
||||||
|
msgid "Hide Mainmenu Entry"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Start with Live TV suspended"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Maximum Number of Clients"
|
msgid "Maximum Number of Clients"
|
||||||
msgstr "¼ÐÚá. ÚÞÛØçÕáâÒÞ ÚÛØÕÝâÞÒ"
|
msgstr "¼ÐÚá. ÚÞÛØçÕáâÒÞ ÚÛØÕÝâÞÒ"
|
||||||
|
|
||||||
msgid "Suspend behaviour"
|
msgid "Live TV buffer delay (ms)"
|
||||||
msgstr "¿ÞÒÕÔÕÝØÕ ÞáâÐÝÞÒÚØ"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Client may suspend"
|
|
||||||
msgstr "ºÛØÕÝâ ÜÞÖÕâ ÞáâÐÝÐÒÛØÒÐâì"
|
|
||||||
|
|
||||||
msgid "VDR-to-VDR Server"
|
msgid "VDR-to-VDR Server"
|
||||||
msgstr "VDR-to-VDR áÕàÒÕà"
|
msgstr "VDR-to-VDR áÕàÒÕà"
|
||||||
@@ -57,6 +55,15 @@ msgstr "VDR-to-VDR
|
|||||||
msgid "Bind to IP"
|
msgid "Bind to IP"
|
||||||
msgstr "¿àØáÞÕÔØÝØâìáï Ú IP"
|
msgstr "¿àØáÞÕÔØÝØâìáï Ú IP"
|
||||||
|
|
||||||
|
msgid "Legacy Client Priority"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Client may suspend"
|
||||||
|
msgstr "ºÛØÕÝâ ÜÞÖÕâ ÞáâÐÝÐÒÛØÒÐâì"
|
||||||
|
|
||||||
|
msgid "Loop Prevention"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "HTTP Server"
|
msgid "HTTP Server"
|
||||||
msgstr "HTTP áÕàÒÕà"
|
msgstr "HTTP áÕàÒÕà"
|
||||||
|
|
||||||
@@ -66,6 +73,9 @@ msgstr "
|
|||||||
msgid "HTTP Server Port"
|
msgid "HTTP Server Port"
|
||||||
msgstr "HTTP áÕàÒÕà ¿Þàâ"
|
msgstr "HTTP áÕàÒÕà ¿Þàâ"
|
||||||
|
|
||||||
|
msgid "Priority"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "HTTP Streamtype"
|
msgid "HTTP Streamtype"
|
||||||
msgstr "ÂØß HTTP ßÞâÞÚÐ"
|
msgstr "ÂØß HTTP ßÞâÞÚÐ"
|
||||||
|
|
||||||
@@ -81,3 +91,5 @@ msgstr ""
|
|||||||
msgid "Multicast Streamtype"
|
msgid "Multicast Streamtype"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "VDR Streaming Server"
|
||||||
|
msgstr "VDR Streaming áÕàÒÕà"
|
||||||
|
|||||||
74
server/po/sk_SK.po
Normal file → Executable file
74
server/po/sk_SK.po
Normal file → Executable file
@@ -6,80 +6,92 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: streamdev_SK\n"
|
"Project-Id-Version: streamdev_SK\n"
|
||||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||||
"POT-Creation-Date: 2010-06-14 13:06+0200\n"
|
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
|
||||||
"PO-Revision-Date: \n"
|
"PO-Revision-Date: 2013-11-22 23:39+0100\n"
|
||||||
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
|
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
|
||||||
"Language-Team: Slovak <hrala.milan@gmail.com>\n"
|
"Language-Team: Slovak <hrala.milan@gmail.com>\n"
|
||||||
|
"Language: sk\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=iso-8859-2\n"
|
"Content-Type: text/plain; charset=iso-8859-2\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Poedit-Language: Slovak\n"
|
"X-Poedit-Language: Slovak\n"
|
||||||
"X-Poedit-Country: SLOVAKIA\n"
|
"X-Poedit-Country: SLOVAKIA\n"
|
||||||
|
|
||||||
msgid "VDR Streaming Server"
|
|
||||||
msgstr "VDR prúdový server"
|
|
||||||
|
|
||||||
msgid "Streaming active"
|
msgid "Streaming active"
|
||||||
msgstr "streamovanie aktivne"
|
msgstr "Streamovanie aktívne"
|
||||||
|
|
||||||
msgid "Suspend Live TV"
|
msgid "Streamdev Connections"
|
||||||
msgstr "Pozastavenie ¾ivého vysielania"
|
msgstr "Streamdev spojenia"
|
||||||
|
|
||||||
msgid "Offer suspend mode"
|
msgid "Disconnect"
|
||||||
msgstr "Výber re¾ímu pozastavenia"
|
msgstr "Odpoji»"
|
||||||
|
|
||||||
msgid "Always suspended"
|
msgid "Suspend"
|
||||||
msgstr "V¾dy pozastavi»"
|
msgstr "Pozastavi»"
|
||||||
|
|
||||||
msgid "Never suspended"
|
|
||||||
msgstr "Nikdy nepozastavi»"
|
|
||||||
|
|
||||||
msgid "Common Settings"
|
msgid "Common Settings"
|
||||||
msgstr "V¹eobecné nastavenia"
|
msgstr "V¹eobecné nastavenia"
|
||||||
|
|
||||||
|
msgid "Hide Mainmenu Entry"
|
||||||
|
msgstr "Schova» v hlavnom menu"
|
||||||
|
|
||||||
|
msgid "Start with Live TV suspended"
|
||||||
|
msgstr "Pozastavi» Live TV pri ¹tarte"
|
||||||
|
|
||||||
msgid "Maximum Number of Clients"
|
msgid "Maximum Number of Clients"
|
||||||
msgstr "Maximály poèet klientov"
|
msgstr "Maximály poèet klientov"
|
||||||
|
|
||||||
msgid "Suspend behaviour"
|
msgid "Live TV buffer delay (ms)"
|
||||||
msgstr "Správanie preru¹enia"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Client may suspend"
|
|
||||||
msgstr "Klient mô¾e pozastavi»"
|
|
||||||
|
|
||||||
msgid "VDR-to-VDR Server"
|
msgid "VDR-to-VDR Server"
|
||||||
msgstr "VDR-do-VDR server"
|
msgstr "Prenos z VDR do VDR"
|
||||||
|
|
||||||
msgid "Start VDR-to-VDR Server"
|
msgid "Start VDR-to-VDR Server"
|
||||||
msgstr "Spusti» VDR-do-VDR Server"
|
msgstr "Spusti» prenos z VDR do VDR"
|
||||||
|
|
||||||
msgid "VDR-to-VDR Server Port"
|
msgid "VDR-to-VDR Server Port"
|
||||||
msgstr "Port serveru pre VDR-do-VDR"
|
msgstr "Port servera prenosu z VDR do VDR"
|
||||||
|
|
||||||
msgid "Bind to IP"
|
msgid "Bind to IP"
|
||||||
msgstr "viaza» na IP"
|
msgstr "Viaza» na IP"
|
||||||
|
|
||||||
|
msgid "Legacy Client Priority"
|
||||||
|
msgstr "Dodatoèná priorita klienta"
|
||||||
|
|
||||||
|
msgid "Client may suspend"
|
||||||
|
msgstr "Klient mô¾e server pozastavi»"
|
||||||
|
|
||||||
|
msgid "Loop Prevention"
|
||||||
|
msgstr "Prevencia sluèky"
|
||||||
|
|
||||||
msgid "HTTP Server"
|
msgid "HTTP Server"
|
||||||
msgstr "server HTTP"
|
msgstr "HTTP server "
|
||||||
|
|
||||||
msgid "Start HTTP Server"
|
msgid "Start HTTP Server"
|
||||||
msgstr "Spusti» HTTP Server"
|
msgstr "Spusti» HTTP Server"
|
||||||
|
|
||||||
msgid "HTTP Server Port"
|
msgid "HTTP Server Port"
|
||||||
msgstr "Port serveru HTTP"
|
msgstr "Port HTTP servera"
|
||||||
|
|
||||||
|
msgid "Priority"
|
||||||
|
msgstr "Priorita"
|
||||||
|
|
||||||
msgid "HTTP Streamtype"
|
msgid "HTTP Streamtype"
|
||||||
msgstr "typ prúdu HTTP"
|
msgstr "Typ HTTP streamu"
|
||||||
|
|
||||||
msgid "Multicast Streaming Server"
|
msgid "Multicast Streaming Server"
|
||||||
msgstr "Multicast prúdový server"
|
msgstr "Streamovanie Multicastového servera"
|
||||||
|
|
||||||
msgid "Start IGMP Server"
|
msgid "Start IGMP Server"
|
||||||
msgstr "Spusti» IGMP Server"
|
msgstr "Spusti» IGMP Server"
|
||||||
|
|
||||||
msgid "Multicast Client Port"
|
msgid "Multicast Client Port"
|
||||||
msgstr "Port klienta Multicast"
|
msgstr "Port Multicast klienta"
|
||||||
|
|
||||||
msgid "Multicast Streamtype"
|
msgid "Multicast Streamtype"
|
||||||
msgstr "Multicast typ streamu"
|
msgstr "Typ Multicast streamu"
|
||||||
|
|
||||||
|
msgid "VDR Streaming Server"
|
||||||
|
msgstr "VDR server streamovania"
|
||||||
|
|||||||
@@ -24,27 +24,30 @@
|
|||||||
// for TSPLAY patch detection
|
// for TSPLAY patch detection
|
||||||
#include "vdr/device.h"
|
#include "vdr/device.h"
|
||||||
|
|
||||||
|
#undef _XOPEN_SOURCE
|
||||||
#define _XOPEN_SOURCE 600
|
#define _XOPEN_SOURCE 600
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
RecPlayer::RecPlayer(cRecording* rec)
|
RecPlayer::RecPlayer(const char* FileName)
|
||||||
{
|
{
|
||||||
file = NULL;
|
file = NULL;
|
||||||
fileOpen = 0;
|
fileOpen = 0;
|
||||||
lastPosition = 0;
|
lastPosition = 0;
|
||||||
recording = rec;
|
recording = new cRecording(FileName);
|
||||||
for(int i = 1; i < 1000; i++) segments[i] = NULL;
|
for(int i = 1; i < 1000; i++) segments[i] = NULL;
|
||||||
|
|
||||||
// FIXME find out max file path / name lengths
|
// FIXME find out max file path / name lengths
|
||||||
|
|
||||||
#if VDRVERSNUM >= 10703 || defined(TSPLAY_PATCH_VERSION)
|
indexFile = new cIndexFile(recording->FileName(), false, recording->IsPesRecording());
|
||||||
indexFile = new cIndexFile(recording->FileName(), false, rec->IsPesRecording());
|
|
||||||
#else
|
|
||||||
indexFile = new cIndexFile(recording->FileName(), false);
|
|
||||||
#endif
|
|
||||||
if (!indexFile) esyslog("ERROR: Streamdev: Failed to create indexfile!");
|
if (!indexFile) esyslog("ERROR: Streamdev: Failed to create indexfile!");
|
||||||
|
|
||||||
scan();
|
scan();
|
||||||
|
|
||||||
|
parser = new cPatPmtParser();
|
||||||
|
unsigned char buffer[2 * TS_SIZE];
|
||||||
|
unsigned long l = getBlock(buffer, 0UL, sizeof(buffer));
|
||||||
|
if (!l || !parser->ParsePatPmt(buffer, (int) l))
|
||||||
|
esyslog("ERROR: Streamdev: Failed to parse PAT/PMT");
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecPlayer::scan()
|
void RecPlayer::scan()
|
||||||
@@ -61,24 +64,18 @@ void RecPlayer::scan()
|
|||||||
for(i = 1; i < 1000; i++)
|
for(i = 1; i < 1000; i++)
|
||||||
{
|
{
|
||||||
|
|
||||||
#if APIVERSNUM < 10703 || !defined(TSPLAY_PATCH_VERSION)
|
|
||||||
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
|
|
||||||
//log->log("RecPlayer", Log::DEBUG, "FILENAME: %s", fileName);
|
|
||||||
file = fopen(fileName, "r");
|
|
||||||
#else
|
|
||||||
snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), i);
|
snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), i);
|
||||||
file = fopen(fileName, "r");
|
file = fopen(fileName, "r");
|
||||||
if (!file) {
|
if (!file) {
|
||||||
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
|
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
|
||||||
file = fopen(fileName, "r");
|
file = fopen(fileName, "r");
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
if (!file) break;
|
if (!file) break;
|
||||||
|
|
||||||
segments[i] = new Segment();
|
segments[i] = new Segment();
|
||||||
segments[i]->start = totalLength;
|
segments[i]->start = totalLength;
|
||||||
fseek(file, 0, SEEK_END);
|
fseek(file, 0, SEEK_END);
|
||||||
totalLength += ftell(file);
|
totalLength += ftello(file);
|
||||||
totalFrames = indexFile->Last();
|
totalFrames = indexFile->Last();
|
||||||
//log->log("RecPlayer", Log::DEBUG, "File %i found, totalLength now %llu, numFrames = %lu", i, totalLength, totalFrames);
|
//log->log("RecPlayer", Log::DEBUG, "File %i found, totalLength now %llu, numFrames = %lu", i, totalLength, totalFrames);
|
||||||
segments[i]->end = totalLength;
|
segments[i]->end = totalLength;
|
||||||
@@ -94,6 +91,9 @@ RecPlayer::~RecPlayer()
|
|||||||
int i = 1;
|
int i = 1;
|
||||||
while(segments[i++]) delete segments[i];
|
while(segments[i++]) delete segments[i];
|
||||||
if (file) fclose(file);
|
if (file) fclose(file);
|
||||||
|
delete indexFile;
|
||||||
|
delete recording;
|
||||||
|
delete parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RecPlayer::openFile(int index)
|
int RecPlayer::openFile(int index)
|
||||||
@@ -102,7 +102,6 @@ int RecPlayer::openFile(int index)
|
|||||||
|
|
||||||
char fileName[2048];
|
char fileName[2048];
|
||||||
|
|
||||||
#if APIVERSNUM >= 10703 || defined(TSPLAY_PATCH_VERSION)
|
|
||||||
snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), index);
|
snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), index);
|
||||||
isyslog("openFile called for index %i string:%s", index, fileName);
|
isyslog("openFile called for index %i string:%s", index, fileName);
|
||||||
|
|
||||||
@@ -112,7 +111,6 @@ int RecPlayer::openFile(int index)
|
|||||||
fileOpen = index;
|
fileOpen = index;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), index);
|
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), index);
|
||||||
isyslog("openFile called for index %i string:%s", index, fileName);
|
isyslog("openFile called for index %i string:%s", index, fileName);
|
||||||
@@ -178,7 +176,7 @@ unsigned long RecPlayer::getBlock(unsigned char* buffer, uint64_t position, unsi
|
|||||||
uint32_t yetToGet = amount;
|
uint32_t yetToGet = amount;
|
||||||
uint32_t got = 0;
|
uint32_t got = 0;
|
||||||
uint32_t getFromThisSegment = 0;
|
uint32_t getFromThisSegment = 0;
|
||||||
uint32_t filePosition;
|
uint64_t filePosition;
|
||||||
|
|
||||||
while(got < amount)
|
while(got < amount)
|
||||||
{
|
{
|
||||||
@@ -200,7 +198,7 @@ unsigned long RecPlayer::getBlock(unsigned char* buffer, uint64_t position, unsi
|
|||||||
if (fread(&buffer[got], getFromThisSegment, 1, file) != 1) return 0; // umm, big problem.
|
if (fread(&buffer[got], getFromThisSegment, 1, file) != 1) return 0; // umm, big problem.
|
||||||
|
|
||||||
// Tell linux not to bother keeping the data in the FS cache
|
// Tell linux not to bother keeping the data in the FS cache
|
||||||
posix_fadvise(file->_fileno, filePosition, getFromThisSegment, POSIX_FADV_DONTNEED);
|
posix_fadvise(fileno(file), filePosition, getFromThisSegment, POSIX_FADV_DONTNEED);
|
||||||
|
|
||||||
got += getFromThisSegment;
|
got += getFromThisSegment;
|
||||||
currentPosition += getFromThisSegment;
|
currentPosition += getFromThisSegment;
|
||||||
@@ -221,17 +219,47 @@ cRecording* RecPlayer::getCurrentRecording()
|
|||||||
return recording;
|
return recording;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if VDRVERSNUM < 10732
|
||||||
|
#define ALIGNED_POS(x) (positionFromFrameNumber(indexFile->GetNextIFrame(x, 1)))
|
||||||
|
#else
|
||||||
|
#define ALIGNED_POS(x) (positionFromFrameNumber(indexFile->GetClosestIFrame(x)))
|
||||||
|
#endif
|
||||||
|
uint64_t RecPlayer::positionFromResume(int ResumeID)
|
||||||
|
{
|
||||||
|
int resumeBackup = Setup.ResumeID;
|
||||||
|
Setup.ResumeID = ResumeID;
|
||||||
|
cResumeFile resume(recording->FileName(), recording->IsPesRecording());
|
||||||
|
Setup.ResumeID = resumeBackup;
|
||||||
|
return ALIGNED_POS(resume.Read());
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t RecPlayer::positionFromMark(int MarkIndex)
|
||||||
|
{
|
||||||
|
cMarks marks;
|
||||||
|
if (marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording()) && marks.Count()) {
|
||||||
|
cMark *mark = marks.cConfig<cMark>::Get(MarkIndex);
|
||||||
|
if (mark)
|
||||||
|
return ALIGNED_POS(mark->Position());
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t RecPlayer::positionFromTime(int Seconds)
|
||||||
|
{
|
||||||
|
return ALIGNED_POS(SecondsToFrames(Seconds, recording->FramesPerSecond()));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t RecPlayer::positionFromPercent(int Percent)
|
||||||
|
{
|
||||||
|
return ALIGNED_POS(getLengthFrames() * Percent / 100L);
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t RecPlayer::positionFromFrameNumber(uint32_t frameNumber)
|
uint64_t RecPlayer::positionFromFrameNumber(uint32_t frameNumber)
|
||||||
{
|
{
|
||||||
if (!indexFile) return 0;
|
if (!indexFile) return 0;
|
||||||
|
|
||||||
#if VDRVERSNUM >= 10703 || defined(TSPLAY_PATCH_VERSION)
|
|
||||||
uint16_t retFileNumber;
|
uint16_t retFileNumber;
|
||||||
off_t retFileOffset;
|
off_t retFileOffset;
|
||||||
#else
|
|
||||||
uchar retFileNumber;
|
|
||||||
int retFileOffset;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!indexFile->Get((int)frameNumber, &retFileNumber, &retFileOffset))
|
if (!indexFile->Get((int)frameNumber, &retFileNumber, &retFileOffset))
|
||||||
{
|
{
|
||||||
@@ -262,7 +290,7 @@ uint32_t RecPlayer::frameNumberFromPosition(uint64_t position)
|
|||||||
if ((position >= segments[segmentNumber]->start) && (position < segments[segmentNumber]->end)) break;
|
if ((position >= segments[segmentNumber]->start) && (position < segments[segmentNumber]->end)) break;
|
||||||
// position is in this block
|
// position is in this block
|
||||||
}
|
}
|
||||||
uint32_t askposition = position - segments[segmentNumber]->start;
|
uint64_t askposition = position - segments[segmentNumber]->start;
|
||||||
return indexFile->Get((int)segmentNumber, askposition);
|
return indexFile->Get((int)segmentNumber, askposition);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <vdr/recording.h>
|
#include <vdr/recording.h>
|
||||||
|
#include <vdr/remux.h>
|
||||||
|
|
||||||
#include "server/streamer.h"
|
#include "server/streamer.h"
|
||||||
|
|
||||||
@@ -36,7 +37,7 @@ class Segment
|
|||||||
class RecPlayer
|
class RecPlayer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RecPlayer(cRecording* rec);
|
RecPlayer(const char* FileName);
|
||||||
~RecPlayer();
|
~RecPlayer();
|
||||||
uint64_t getLengthBytes();
|
uint64_t getLengthBytes();
|
||||||
uint32_t getLengthFrames();
|
uint32_t getLengthFrames();
|
||||||
@@ -44,7 +45,12 @@ class RecPlayer
|
|||||||
int openFile(int index);
|
int openFile(int index);
|
||||||
uint64_t getLastPosition();
|
uint64_t getLastPosition();
|
||||||
cRecording* getCurrentRecording();
|
cRecording* getCurrentRecording();
|
||||||
|
const cPatPmtParser* getPatPmtData() { return parser; }
|
||||||
void scan();
|
void scan();
|
||||||
|
uint64_t positionFromResume(int ResumeID);
|
||||||
|
uint64_t positionFromMark(int MarkIndex);
|
||||||
|
uint64_t positionFromTime(int Seconds);
|
||||||
|
uint64_t positionFromPercent(int Percent);
|
||||||
uint64_t positionFromFrameNumber(uint32_t frameNumber);
|
uint64_t positionFromFrameNumber(uint32_t frameNumber);
|
||||||
uint32_t frameNumberFromPosition(uint64_t position);
|
uint32_t frameNumberFromPosition(uint64_t position);
|
||||||
bool getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength);
|
bool getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength);
|
||||||
@@ -52,6 +58,7 @@ class RecPlayer
|
|||||||
private:
|
private:
|
||||||
cRecording* recording;
|
cRecording* recording;
|
||||||
cIndexFile* indexFile;
|
cIndexFile* indexFile;
|
||||||
|
cPatPmtParser* parser;
|
||||||
FILE* file;
|
FILE* file;
|
||||||
int fileOpen;
|
int fileOpen;
|
||||||
Segment* segments[1000];
|
Segment* segments[1000];
|
||||||
|
|||||||
98
server/recstreamer.c
Normal file
98
server/recstreamer.c
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#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";
|
||||||
|
}
|
||||||
34
server/recstreamer.h
Normal file
34
server/recstreamer.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#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
|
||||||
@@ -152,8 +152,7 @@ void cStreamdevServer::Action(void)
|
|||||||
|
|
||||||
cServerConnection *next = m_Clients.Next(s);
|
cServerConnection *next = m_Clients.Next(s);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
isyslog("streamdev: closing streamdev connection to %s:%d",
|
if (s->IsOpen())
|
||||||
s->RemoteIp().c_str(), s->RemotePort());
|
|
||||||
s->Close();
|
s->Close();
|
||||||
Lock();
|
Lock();
|
||||||
m_Clients.Del(s);
|
m_Clients.Del(s);
|
||||||
@@ -178,9 +177,12 @@ void cStreamdevServer::Action(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cStreamdevServer::MainThreadHook(void)
|
#if APIVERSNUM >= 20300
|
||||||
|
cList<cServerConnection>& cStreamdevServer::Clients(cThreadLock& Lock)
|
||||||
|
#else
|
||||||
|
const cList<cServerConnection>& cStreamdevServer::Clients(cThreadLock& Lock)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
cThreadLock lock(m_Instance);
|
Lock.Lock(m_Instance);
|
||||||
for (cServerConnection *s = m_Clients.First(); s; s = m_Clients.Next(s))
|
return m_Clients;
|
||||||
s->MainThreadHook();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,12 @@ public:
|
|||||||
static void Initialize(void);
|
static void Initialize(void);
|
||||||
static void Destruct(void);
|
static void Destruct(void);
|
||||||
static bool Active(void);
|
static bool Active(void);
|
||||||
static void MainThreadHook(void);
|
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
static cList<cServerConnection>& Clients(cThreadLock& Lock);
|
||||||
|
#else
|
||||||
|
static const cList<cServerConnection>& Clients(cThreadLock& Lock);
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool cStreamdevServer::Active(void)
|
inline bool cStreamdevServer::Active(void)
|
||||||
|
|||||||
@@ -10,16 +10,22 @@
|
|||||||
cStreamdevServerSetup StreamdevServerSetup;
|
cStreamdevServerSetup StreamdevServerSetup;
|
||||||
|
|
||||||
cStreamdevServerSetup::cStreamdevServerSetup(void) {
|
cStreamdevServerSetup::cStreamdevServerSetup(void) {
|
||||||
|
HideMenuEntry = false;
|
||||||
MaxClients = 5;
|
MaxClients = 5;
|
||||||
|
StartSuspended = ssAuto;
|
||||||
|
LiveBufferMs = 0;
|
||||||
StartVTPServer = true;
|
StartVTPServer = true;
|
||||||
VTPServerPort = 2004;
|
VTPServerPort = 2004;
|
||||||
|
VTPPriority = 0;
|
||||||
|
LoopPrevention = false;
|
||||||
StartHTTPServer = true;
|
StartHTTPServer = true;
|
||||||
HTTPServerPort = 3000;
|
HTTPServerPort = 3000;
|
||||||
|
HTTPPriority = 0;
|
||||||
HTTPStreamType = stTS;
|
HTTPStreamType = stTS;
|
||||||
StartIGMPServer = false;
|
StartIGMPServer = false;
|
||||||
IGMPClientPort = 1234;
|
IGMPClientPort = 1234;
|
||||||
|
IGMPPriority = 0;
|
||||||
IGMPStreamType = stTS;
|
IGMPStreamType = stTS;
|
||||||
SuspendMode = smAlways;
|
|
||||||
AllowSuspend = false;
|
AllowSuspend = false;
|
||||||
strcpy(VTPBindIP, "0.0.0.0");
|
strcpy(VTPBindIP, "0.0.0.0");
|
||||||
strcpy(HTTPBindIP, "0.0.0.0");
|
strcpy(HTTPBindIP, "0.0.0.0");
|
||||||
@@ -27,19 +33,25 @@ cStreamdevServerSetup::cStreamdevServerSetup(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool cStreamdevServerSetup::SetupParse(const char *Name, const char *Value) {
|
bool cStreamdevServerSetup::SetupParse(const char *Name, const char *Value) {
|
||||||
if (strcmp(Name, "MaxClients") == 0) MaxClients = atoi(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);
|
||||||
else if (strcmp(Name, "StartServer") == 0) StartVTPServer = atoi(Value);
|
else if (strcmp(Name, "StartServer") == 0) StartVTPServer = atoi(Value);
|
||||||
else if (strcmp(Name, "ServerPort") == 0) VTPServerPort = atoi(Value);
|
else if (strcmp(Name, "ServerPort") == 0) VTPServerPort = atoi(Value);
|
||||||
|
else if (strcmp(Name, "VTPPriority") == 0) VTPPriority = atoi(Value);
|
||||||
else if (strcmp(Name, "VTPBindIP") == 0) strcpy(VTPBindIP, Value);
|
else if (strcmp(Name, "VTPBindIP") == 0) strcpy(VTPBindIP, Value);
|
||||||
|
else if (strcmp(Name, "LoopPrevention") == 0) LoopPrevention = atoi(Value);
|
||||||
else if (strcmp(Name, "StartHTTPServer") == 0) StartHTTPServer = atoi(Value);
|
else if (strcmp(Name, "StartHTTPServer") == 0) StartHTTPServer = atoi(Value);
|
||||||
else if (strcmp(Name, "HTTPServerPort") == 0) HTTPServerPort = atoi(Value);
|
else if (strcmp(Name, "HTTPServerPort") == 0) HTTPServerPort = atoi(Value);
|
||||||
|
else if (strcmp(Name, "HTTPPriority") == 0) HTTPPriority = atoi(Value);
|
||||||
else if (strcmp(Name, "HTTPStreamType") == 0) HTTPStreamType = atoi(Value);
|
else if (strcmp(Name, "HTTPStreamType") == 0) HTTPStreamType = atoi(Value);
|
||||||
else if (strcmp(Name, "HTTPBindIP") == 0) strcpy(HTTPBindIP, Value);
|
else if (strcmp(Name, "HTTPBindIP") == 0) strcpy(HTTPBindIP, Value);
|
||||||
else if (strcmp(Name, "StartIGMPServer") == 0) StartIGMPServer = atoi(Value);
|
else if (strcmp(Name, "StartIGMPServer") == 0) StartIGMPServer = atoi(Value);
|
||||||
else if (strcmp(Name, "IGMPClientPort") == 0) IGMPClientPort = atoi(Value);
|
else if (strcmp(Name, "IGMPClientPort") == 0) IGMPClientPort = atoi(Value);
|
||||||
|
else if (strcmp(Name, "IGMPPriority") == 0) IGMPPriority = atoi(Value);
|
||||||
else if (strcmp(Name, "IGMPStreamType") == 0) IGMPStreamType = atoi(Value);
|
else if (strcmp(Name, "IGMPStreamType") == 0) IGMPStreamType = atoi(Value);
|
||||||
else if (strcmp(Name, "IGMPBindIP") == 0) strcpy(IGMPBindIP, Value);
|
else if (strcmp(Name, "IGMPBindIP") == 0) strcpy(IGMPBindIP, Value);
|
||||||
else if (strcmp(Name, "SuspendMode") == 0) SuspendMode = atoi(Value);
|
|
||||||
else if (strcmp(Name, "AllowSuspend") == 0) AllowSuspend = atoi(Value);
|
else if (strcmp(Name, "AllowSuspend") == 0) AllowSuspend = atoi(Value);
|
||||||
else return false;
|
else return false;
|
||||||
return true;
|
return true;
|
||||||
@@ -53,12 +65,6 @@ const char* cStreamdevServerMenuSetupPage::StreamTypes[st_Count - 1] = {
|
|||||||
"EXT"
|
"EXT"
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* cStreamdevServerMenuSetupPage::SuspendModes[sm_Count] = {
|
|
||||||
trNOOP("Offer suspend mode"),
|
|
||||||
trNOOP("Always suspended"),
|
|
||||||
trNOOP("Never suspended")
|
|
||||||
};
|
|
||||||
|
|
||||||
cStreamdevServerMenuSetupPage::cStreamdevServerMenuSetupPage(void) {
|
cStreamdevServerMenuSetupPage::cStreamdevServerMenuSetupPage(void) {
|
||||||
m_NewSetup = StreamdevServerSetup;
|
m_NewSetup = StreamdevServerSetup;
|
||||||
|
|
||||||
@@ -69,34 +75,44 @@ cStreamdevServerMenuSetupPage::~cStreamdevServerMenuSetupPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void cStreamdevServerMenuSetupPage::Set(void) {
|
void cStreamdevServerMenuSetupPage::Set(void) {
|
||||||
static const char* modes[sm_Count];
|
static const char *StartSuspendedItems[ss_Count] =
|
||||||
for (int i = 0; i < sm_Count; i++)
|
{
|
||||||
modes[i] = tr(SuspendModes[i]);
|
trVDR("no"),
|
||||||
|
trVDR("yes"),
|
||||||
|
trVDR("auto")
|
||||||
|
};
|
||||||
|
|
||||||
int current = Current();
|
int current = Current();
|
||||||
Clear();
|
Clear();
|
||||||
AddCategory (tr("Common Settings"));
|
AddCategory (tr("Common Settings"));
|
||||||
|
Add(new cMenuEditBoolItem(tr("Hide Mainmenu Entry"), &m_NewSetup.HideMenuEntry));
|
||||||
|
Add(new cMenuEditStraItem(tr("Start with Live TV suspended"), &m_NewSetup.StartSuspended, ss_Count, StartSuspendedItems));
|
||||||
Add(new cMenuEditIntItem (tr("Maximum Number of Clients"), &m_NewSetup.MaxClients, 0, 100));
|
Add(new cMenuEditIntItem (tr("Maximum Number of Clients"), &m_NewSetup.MaxClients, 0, 100));
|
||||||
|
Add(new cMenuEditIntItem (tr("Live TV buffer delay (ms)"), &m_NewSetup.LiveBufferMs, 0, 1500));
|
||||||
|
|
||||||
Add(new cMenuEditStraItem(tr("Suspend behaviour"), &m_NewSetup.SuspendMode, sm_Count, modes));
|
|
||||||
if (m_NewSetup.SuspendMode == smOffer)
|
|
||||||
Add(new cMenuEditBoolItem(tr("Client may suspend"), &m_NewSetup.AllowSuspend));
|
|
||||||
|
|
||||||
AddCategory (tr("VDR-to-VDR Server"));
|
AddCategory (tr("VDR-to-VDR Server"));
|
||||||
Add(new cMenuEditBoolItem(tr("Start VDR-to-VDR Server"), &m_NewSetup.StartVTPServer));
|
Add(new cMenuEditBoolItem(tr("Start VDR-to-VDR Server"), &m_NewSetup.StartVTPServer));
|
||||||
Add(new cMenuEditIntItem (tr("VDR-to-VDR Server Port"), &m_NewSetup.VTPServerPort, 0, 65535));
|
Add(new cMenuEditIntItem (tr("VDR-to-VDR Server Port"), &m_NewSetup.VTPServerPort, 0, 65535));
|
||||||
Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.VTPBindIP));
|
Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.VTPBindIP));
|
||||||
|
Add(new cMenuEditIntItem (tr("Legacy Client Priority"), &m_NewSetup.VTPPriority, MINPRIORITY, MAXPRIORITY));
|
||||||
|
Add(new cMenuEditBoolItem(tr("Client may suspend"), &m_NewSetup.AllowSuspend));
|
||||||
|
if (cPluginManager::CallFirstService(LOOP_PREVENTION_SERVICE))
|
||||||
|
Add(new cMenuEditBoolItem(tr("Loop Prevention"), &m_NewSetup.LoopPrevention));
|
||||||
|
|
||||||
AddCategory (tr("HTTP Server"));
|
AddCategory (tr("HTTP Server"));
|
||||||
Add(new cMenuEditBoolItem(tr("Start HTTP Server"), &m_NewSetup.StartHTTPServer));
|
Add(new cMenuEditBoolItem(tr("Start HTTP Server"), &m_NewSetup.StartHTTPServer));
|
||||||
Add(new cMenuEditIntItem (tr("HTTP Server Port"), &m_NewSetup.HTTPServerPort, 0, 65535));
|
Add(new cMenuEditIntItem (tr("HTTP Server Port"), &m_NewSetup.HTTPServerPort, 0, 65535));
|
||||||
Add(new cMenuEditStraItem(tr("HTTP Streamtype"), &m_NewSetup.HTTPStreamType, st_Count - 1, StreamTypes));
|
|
||||||
Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.HTTPBindIP));
|
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));
|
||||||
|
|
||||||
AddCategory (tr("Multicast Streaming Server"));
|
AddCategory (tr("Multicast Streaming Server"));
|
||||||
Add(new cMenuEditBoolItem(tr("Start IGMP Server"), &m_NewSetup.StartIGMPServer));
|
Add(new cMenuEditBoolItem(tr("Start IGMP Server"), &m_NewSetup.StartIGMPServer));
|
||||||
Add(new cMenuEditIntItem (tr("Multicast Client Port"), &m_NewSetup.IGMPClientPort, 0, 65535));
|
Add(new cMenuEditIntItem (tr("Multicast Client Port"), &m_NewSetup.IGMPClientPort, 0, 65535));
|
||||||
Add(new cMenuEditStraItem(tr("Multicast Streamtype"), &m_NewSetup.IGMPStreamType, st_Count - 1, StreamTypes));
|
|
||||||
Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.IGMPBindIP));
|
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));
|
||||||
SetCurrent(Get(current));
|
SetCurrent(Get(current));
|
||||||
Display();
|
Display();
|
||||||
}
|
}
|
||||||
@@ -126,19 +142,25 @@ void cStreamdevServerMenuSetupPage::Store(void) {
|
|||||||
cStreamdevServer::Destruct();
|
cStreamdevServer::Destruct();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetupStore("HideMenuEntry", m_NewSetup.HideMenuEntry);
|
||||||
SetupStore("MaxClients", m_NewSetup.MaxClients);
|
SetupStore("MaxClients", m_NewSetup.MaxClients);
|
||||||
|
SetupStore("StartSuspended", m_NewSetup.StartSuspended);
|
||||||
|
SetupStore("LiveBufferMs", m_NewSetup.LiveBufferMs);
|
||||||
SetupStore("StartServer", m_NewSetup.StartVTPServer);
|
SetupStore("StartServer", m_NewSetup.StartVTPServer);
|
||||||
SetupStore("ServerPort", m_NewSetup.VTPServerPort);
|
SetupStore("ServerPort", m_NewSetup.VTPServerPort);
|
||||||
SetupStore("VTPBindIP", m_NewSetup.VTPBindIP);
|
SetupStore("VTPBindIP", m_NewSetup.VTPBindIP);
|
||||||
|
SetupStore("VTPPriority", m_NewSetup.VTPPriority);
|
||||||
|
SetupStore("LoopPrevention", m_NewSetup.LoopPrevention);
|
||||||
SetupStore("StartHTTPServer", m_NewSetup.StartHTTPServer);
|
SetupStore("StartHTTPServer", m_NewSetup.StartHTTPServer);
|
||||||
SetupStore("HTTPServerPort", m_NewSetup.HTTPServerPort);
|
SetupStore("HTTPServerPort", m_NewSetup.HTTPServerPort);
|
||||||
SetupStore("HTTPStreamType", m_NewSetup.HTTPStreamType);
|
|
||||||
SetupStore("HTTPBindIP", m_NewSetup.HTTPBindIP);
|
SetupStore("HTTPBindIP", m_NewSetup.HTTPBindIP);
|
||||||
|
SetupStore("HTTPPriority", m_NewSetup.HTTPPriority);
|
||||||
|
SetupStore("HTTPStreamType", m_NewSetup.HTTPStreamType);
|
||||||
SetupStore("StartIGMPServer", m_NewSetup.StartIGMPServer);
|
SetupStore("StartIGMPServer", m_NewSetup.StartIGMPServer);
|
||||||
SetupStore("IGMPClientPort", m_NewSetup.IGMPClientPort);
|
SetupStore("IGMPClientPort", m_NewSetup.IGMPClientPort);
|
||||||
SetupStore("IGMPStreamType", m_NewSetup.IGMPStreamType);
|
|
||||||
SetupStore("IGMPBindIP", m_NewSetup.IGMPBindIP);
|
SetupStore("IGMPBindIP", m_NewSetup.IGMPBindIP);
|
||||||
SetupStore("SuspendMode", m_NewSetup.SuspendMode);
|
SetupStore("IGMPPriority", m_NewSetup.IGMPPriority);
|
||||||
|
SetupStore("IGMPStreamType", m_NewSetup.IGMPStreamType);
|
||||||
SetupStore("AllowSuspend", m_NewSetup.AllowSuspend);
|
SetupStore("AllowSuspend", m_NewSetup.AllowSuspend);
|
||||||
|
|
||||||
StreamdevServerSetup = m_NewSetup;
|
StreamdevServerSetup = m_NewSetup;
|
||||||
@@ -146,11 +168,3 @@ void cStreamdevServerMenuSetupPage::Store(void) {
|
|||||||
if (restart)
|
if (restart)
|
||||||
cStreamdevServer::Initialize();
|
cStreamdevServer::Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
eOSState cStreamdevServerMenuSetupPage::ProcessKey(eKeys Key) {
|
|
||||||
int oldMode = m_NewSetup.SuspendMode;
|
|
||||||
eOSState state = cMenuSetupPage::ProcessKey(Key);
|
|
||||||
if (oldMode != m_NewSetup.SuspendMode)
|
|
||||||
Set();
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,25 +7,38 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
enum eStartSuspended {
|
||||||
|
ssNo,
|
||||||
|
ssYes,
|
||||||
|
ssAuto,
|
||||||
|
ss_Count
|
||||||
|
};
|
||||||
|
|
||||||
struct cStreamdevServerSetup {
|
struct cStreamdevServerSetup {
|
||||||
cStreamdevServerSetup(void);
|
cStreamdevServerSetup(void);
|
||||||
|
|
||||||
bool SetupParse(const char *Name, const char *Value);
|
bool SetupParse(const char *Name, const char *Value);
|
||||||
|
|
||||||
|
int HideMenuEntry;
|
||||||
int MaxClients;
|
int MaxClients;
|
||||||
|
int StartSuspended;
|
||||||
|
int LiveBufferMs;
|
||||||
int StartVTPServer;
|
int StartVTPServer;
|
||||||
int VTPServerPort;
|
int VTPServerPort;
|
||||||
char VTPBindIP[20];
|
char VTPBindIP[20];
|
||||||
|
int VTPPriority;
|
||||||
|
int AllowSuspend;
|
||||||
|
int LoopPrevention;
|
||||||
int StartHTTPServer;
|
int StartHTTPServer;
|
||||||
int HTTPServerPort;
|
int HTTPServerPort;
|
||||||
|
int HTTPPriority;
|
||||||
int HTTPStreamType;
|
int HTTPStreamType;
|
||||||
char HTTPBindIP[20];
|
char HTTPBindIP[20];
|
||||||
int StartIGMPServer;
|
int StartIGMPServer;
|
||||||
int IGMPClientPort;
|
int IGMPClientPort;
|
||||||
|
int IGMPPriority;
|
||||||
int IGMPStreamType;
|
int IGMPStreamType;
|
||||||
char IGMPBindIP[20];
|
char IGMPBindIP[20];
|
||||||
int SuspendMode;
|
|
||||||
int AllowSuspend;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern cStreamdevServerSetup StreamdevServerSetup;
|
extern cStreamdevServerSetup StreamdevServerSetup;
|
||||||
@@ -33,14 +46,12 @@ extern cStreamdevServerSetup StreamdevServerSetup;
|
|||||||
class cStreamdevServerMenuSetupPage: public cMenuSetupPage {
|
class cStreamdevServerMenuSetupPage: public cMenuSetupPage {
|
||||||
private:
|
private:
|
||||||
static const char* StreamTypes[];
|
static const char* StreamTypes[];
|
||||||
static const char* SuspendModes[];
|
|
||||||
cStreamdevServerSetup m_NewSetup;
|
cStreamdevServerSetup m_NewSetup;
|
||||||
|
|
||||||
void AddCategory(const char *Title);
|
void AddCategory(const char *Title);
|
||||||
void Set();
|
void Set();
|
||||||
protected:
|
protected:
|
||||||
virtual void Store(void);
|
virtual void Store(void);
|
||||||
virtual eOSState ProcessKey(eKeys Key);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cStreamdevServerMenuSetupPage(void);
|
cStreamdevServerMenuSetupPage(void);
|
||||||
|
|||||||
@@ -9,18 +9,47 @@
|
|||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <vdr/tools.h>
|
#include <vdr/tools.h>
|
||||||
#include "streamdev-server.h"
|
#include "streamdev-server.h"
|
||||||
|
#include "server/menu.h"
|
||||||
#include "server/setup.h"
|
#include "server/setup.h"
|
||||||
#include "server/server.h"
|
#include "server/server.h"
|
||||||
#include "server/suspend.h"
|
#include "server/suspend.h"
|
||||||
|
|
||||||
#if !defined(APIVERSNUM) || APIVERSNUM < 10516
|
#if !defined(APIVERSNUM) || APIVERSNUM < 10725
|
||||||
#error "VDR-1.5.16 API version or greater is required!"
|
#error "VDR-1.7.25 or greater required to compile server! Use 'make client' to compile client only."
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
cList<cMainThreadHookSubscriber> cMainThreadHookSubscriber::m_Subscribers;
|
||||||
|
cMutex cMainThreadHookSubscriber::m_Mutex;
|
||||||
|
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
cList<cMainThreadHookSubscriber>& cMainThreadHookSubscriber::Subscribers(cMutexLock& Lock)
|
||||||
|
#else
|
||||||
|
const cList<cMainThreadHookSubscriber>& cMainThreadHookSubscriber::Subscribers(cMutexLock& Lock)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
Lock.Lock(&m_Mutex);
|
||||||
|
return m_Subscribers;
|
||||||
|
}
|
||||||
|
|
||||||
|
cMainThreadHookSubscriber::cMainThreadHookSubscriber()
|
||||||
|
{
|
||||||
|
m_Mutex.Lock();
|
||||||
|
m_Subscribers.Add(this);
|
||||||
|
m_Mutex.Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
cMainThreadHookSubscriber::~cMainThreadHookSubscriber()
|
||||||
|
{
|
||||||
|
m_Mutex.Lock();
|
||||||
|
m_Subscribers.Del(this, false);
|
||||||
|
m_Mutex.Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
const char *cPluginStreamdevServer::DESCRIPTION = trNOOP("VDR Streaming Server");
|
const char *cPluginStreamdevServer::DESCRIPTION = trNOOP("VDR Streaming Server");
|
||||||
|
|
||||||
cPluginStreamdevServer::cPluginStreamdevServer(void)
|
cPluginStreamdevServer::cPluginStreamdevServer(void)
|
||||||
{
|
{
|
||||||
|
m_Suspend = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
cPluginStreamdevServer::~cPluginStreamdevServer()
|
cPluginStreamdevServer::~cPluginStreamdevServer()
|
||||||
@@ -98,6 +127,9 @@ bool cPluginStreamdevServer::Start(void)
|
|||||||
|
|
||||||
cStreamdevServer::Initialize();
|
cStreamdevServer::Initialize();
|
||||||
|
|
||||||
|
m_Suspend = StreamdevServerSetup.StartSuspended == ssAuto ?
|
||||||
|
!cDevice::PrimaryDevice()->HasDecoder() :
|
||||||
|
StreamdevServerSetup.StartSuspended;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,20 +151,29 @@ cString cPluginStreamdevServer::Active(void)
|
|||||||
|
|
||||||
const char *cPluginStreamdevServer::MainMenuEntry(void)
|
const char *cPluginStreamdevServer::MainMenuEntry(void)
|
||||||
{
|
{
|
||||||
if (StreamdevServerSetup.SuspendMode == smOffer && !cSuspendCtl::IsActive())
|
return !StreamdevServerSetup.HideMenuEntry ? tr("Streamdev Connections") : NULL;
|
||||||
return tr("Suspend Live TV");
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cOsdObject *cPluginStreamdevServer::MainMenuAction(void)
|
cOsdObject *cPluginStreamdevServer::MainMenuAction(void)
|
||||||
{
|
{
|
||||||
cControl::Launch(new cSuspendCtl);
|
return new cStreamdevServerMenu();
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cPluginStreamdevServer::MainThreadHook(void)
|
void cPluginStreamdevServer::MainThreadHook(void)
|
||||||
{
|
{
|
||||||
cStreamdevServer::MainThreadHook();
|
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))
|
||||||
|
s->MainThreadHook();
|
||||||
}
|
}
|
||||||
|
|
||||||
cMenuSetupPage *cPluginStreamdevServer::SetupMenu(void)
|
cMenuSetupPage *cPluginStreamdevServer::SetupMenu(void)
|
||||||
@@ -145,4 +186,90 @@ bool cPluginStreamdevServer::SetupParse(const char *Name, const char *Value)
|
|||||||
return StreamdevServerSetup.SetupParse(Name, Value);
|
return StreamdevServerSetup.SetupParse(Name, Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char **cPluginStreamdevServer::SVDRPHelpPages(void)
|
||||||
|
{
|
||||||
|
static const char *HelpPages[]=
|
||||||
|
{
|
||||||
|
"LSTC\n"
|
||||||
|
" List connected clients\n",
|
||||||
|
"DISC client_id\n"
|
||||||
|
" Disconnect a client\n",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
return HelpPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cPluginStreamdevServer::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
|
||||||
|
{
|
||||||
|
|
||||||
|
cString reply = NULL;
|
||||||
|
if (!strcasecmp(Command, "LSTC"))
|
||||||
|
{
|
||||||
|
reply = "";
|
||||||
|
cThreadLock lock;
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
||||||
|
#else
|
||||||
|
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
||||||
|
#endif
|
||||||
|
cServerConnection *s = clients.First();
|
||||||
|
if (!s)
|
||||||
|
{
|
||||||
|
reply = "no client connected";
|
||||||
|
ReplyCode = 550;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
for (; s; s = clients.Next(s))
|
||||||
|
{
|
||||||
|
reply = cString::sprintf("%s%p: %s\n", (const char*) reply, s, (const char *) s->ToText());
|
||||||
|
}
|
||||||
|
ReplyCode = 250;
|
||||||
|
}
|
||||||
|
} else if (!strcasecmp(Command, "DISC"))
|
||||||
|
{
|
||||||
|
void *client = NULL;
|
||||||
|
if (sscanf(Option, "%p", &client) != 1 || !client)
|
||||||
|
{
|
||||||
|
reply = "invalid client handle";
|
||||||
|
ReplyCode = 501;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
cThreadLock lock;
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
||||||
|
#else
|
||||||
|
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
||||||
|
#endif
|
||||||
|
cServerConnection *s = clients.First();
|
||||||
|
for (; s && s != client; s = clients.Next(s));
|
||||||
|
|
||||||
|
if (!s)
|
||||||
|
{
|
||||||
|
reply = "client not found";
|
||||||
|
ReplyCode = 501;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
s->Close();
|
||||||
|
reply = "client disconnected";
|
||||||
|
ReplyCode = 250;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPluginStreamdevServer::Service(const char *Id, void *Data)
|
||||||
|
{
|
||||||
|
if (strcmp(Id, "StreamdevServer::ClientCount-v1.0") == 0) {
|
||||||
|
if (Data) {
|
||||||
|
int *count = (int *) Data;
|
||||||
|
cThreadLock lock;
|
||||||
|
*count = cStreamdevServer::Clients(lock).Count();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
VDRPLUGINCREATOR(cPluginStreamdevServer); // Don't touch this!
|
VDRPLUGINCREATOR(cPluginStreamdevServer); // Don't touch this!
|
||||||
|
|||||||
@@ -7,11 +7,30 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <vdr/tools.h>
|
||||||
#include <vdr/plugin.h>
|
#include <vdr/plugin.h>
|
||||||
|
|
||||||
|
class cMainThreadHookSubscriber: public cListObject {
|
||||||
|
private:
|
||||||
|
static cList<cMainThreadHookSubscriber> m_Subscribers;
|
||||||
|
static cMutex m_Mutex;
|
||||||
|
public:
|
||||||
|
#if APIVERSNUM >= 20300
|
||||||
|
static cList<cMainThreadHookSubscriber>& Subscribers(cMutexLock& Lock);
|
||||||
|
#else
|
||||||
|
static const cList<cMainThreadHookSubscriber>& Subscribers(cMutexLock& Lock);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
virtual void MainThreadHook() = 0;
|
||||||
|
|
||||||
|
cMainThreadHookSubscriber();
|
||||||
|
virtual ~cMainThreadHookSubscriber();
|
||||||
|
};
|
||||||
|
|
||||||
class cPluginStreamdevServer : public cPlugin {
|
class cPluginStreamdevServer : public cPlugin {
|
||||||
private:
|
private:
|
||||||
static const char *DESCRIPTION;
|
static const char *DESCRIPTION;
|
||||||
|
bool m_Suspend;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cPluginStreamdevServer(void);
|
cPluginStreamdevServer(void);
|
||||||
@@ -29,6 +48,9 @@ public:
|
|||||||
virtual void MainThreadHook(void);
|
virtual void MainThreadHook(void);
|
||||||
virtual cMenuSetupPage *SetupMenu(void);
|
virtual cMenuSetupPage *SetupMenu(void);
|
||||||
virtual bool SetupParse(const char *Name, const char *Value);
|
virtual bool SetupParse(const char *Name, const char *Value);
|
||||||
|
virtual const char **SVDRPHelpPages(void);
|
||||||
|
virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
|
||||||
|
virtual bool Service(const char *Id, void *Data = NULL);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VDR_STREAMDEVSERVER_H
|
#endif // VDR_STREAMDEVSERVER_H
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "server/streamer.h"
|
#include "server/streamer.h"
|
||||||
#include "server/suspend.h"
|
|
||||||
#include "server/setup.h"
|
|
||||||
#include "tools/socket.h"
|
#include "tools/socket.h"
|
||||||
#include "tools/select.h"
|
#include "tools/select.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
@@ -47,15 +45,18 @@ void cStreamdevWriter::Action(void)
|
|||||||
int count, offset = 0;
|
int count, offset = 0;
|
||||||
int timeout = 0;
|
int timeout = 0;
|
||||||
|
|
||||||
#if APIVERSNUM >= 10705
|
|
||||||
SetPriority(-3);
|
SetPriority(-3);
|
||||||
#endif
|
|
||||||
sel.Clear();
|
sel.Clear();
|
||||||
sel.Add(*m_Socket, true);
|
sel.Add(*m_Socket, true);
|
||||||
while (Running()) {
|
while (Running()) {
|
||||||
if (block == NULL) {
|
if (block == NULL) {
|
||||||
block = m_Streamer->Get(count);
|
block = m_Streamer->Get(count);
|
||||||
offset = 0;
|
offset = 0;
|
||||||
|
// still no data - are we done?
|
||||||
|
if (block == NULL && !m_Streamer->IsReceiving() && timeout++ > 20) {
|
||||||
|
esyslog("streamdev-server: streamer done - writer exiting");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block != NULL) {
|
if (block != NULL) {
|
||||||
@@ -102,72 +103,82 @@ void cStreamdevWriter::Action(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
m_Socket->Close();
|
||||||
Dprintf("Max. Transmit Blocksize was: %d\n", max);
|
Dprintf("Max. Transmit Blocksize was: %d\n", max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- cRemuxDummy ------------------------------------------------------------
|
||||||
|
|
||||||
|
class cRemuxDummy: public Streamdev::cTSRemux {
|
||||||
|
private:
|
||||||
|
cStreamdevBuffer m_Buffer;
|
||||||
|
public:
|
||||||
|
cRemuxDummy();
|
||||||
|
virtual int Put(const uchar *Data, int Count) { return m_Buffer.Put(Data, Count); }
|
||||||
|
virtual uchar *Get(int& Count) { return m_Buffer.Get(Count); }
|
||||||
|
virtual void Del(int Count) { return m_Buffer.Del(Count); }
|
||||||
|
};
|
||||||
|
|
||||||
|
cRemuxDummy::cRemuxDummy(): m_Buffer(WRITERBUFSIZE, TS_SIZE * 2)
|
||||||
|
{
|
||||||
|
m_Buffer.SetTimeouts(100, 100);
|
||||||
|
}
|
||||||
|
|
||||||
// --- cStreamdevStreamer -----------------------------------------------------
|
// --- cStreamdevStreamer -----------------------------------------------------
|
||||||
|
|
||||||
cStreamdevStreamer::cStreamdevStreamer(const char *Name, const cServerConnection *Connection):
|
cStreamdevStreamer::cStreamdevStreamer(const char *Name, const cServerConnection *Connection):
|
||||||
cThread(Name),
|
cThread(Name),
|
||||||
m_Connection(Connection),
|
m_Connection(Connection),
|
||||||
m_Writer(NULL),
|
m_Remux(new cRemuxDummy()),
|
||||||
m_RingBuffer(new cStreamdevBuffer(STREAMERBUFSIZE, TS_SIZE * 2,
|
m_Writer(NULL)
|
||||||
true, "streamdev-streamer")),
|
|
||||||
m_SendBuffer(new cStreamdevBuffer(WRITERBUFSIZE, TS_SIZE * 2))
|
|
||||||
{
|
{
|
||||||
m_RingBuffer->SetTimeouts(0, 100);
|
|
||||||
m_SendBuffer->SetTimeouts(100, 100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cStreamdevStreamer::~cStreamdevStreamer()
|
cStreamdevStreamer::~cStreamdevStreamer()
|
||||||
{
|
{
|
||||||
Dprintf("Desctructing streamer\n");
|
Dprintf("Desctructing streamer\n");
|
||||||
delete m_RingBuffer;
|
delete m_Remux;
|
||||||
delete m_SendBuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cStreamdevStreamer::Start(cTBSocket *Socket)
|
void cStreamdevStreamer::Start(cTBSocket *Socket)
|
||||||
{
|
{
|
||||||
Dprintf("start streamer\n");
|
Dprintf("start writer\n");
|
||||||
m_Writer = new cStreamdevWriter(Socket, this);
|
m_Writer = new cStreamdevWriter(Socket, this);
|
||||||
Attach();
|
|
||||||
}
|
|
||||||
|
|
||||||
void cStreamdevStreamer::Activate(bool On)
|
|
||||||
{
|
|
||||||
if (On && !Active()) {
|
|
||||||
Dprintf("activate streamer\n");
|
|
||||||
m_Writer->Start();
|
m_Writer->Start();
|
||||||
|
if (!Active()) {
|
||||||
|
Dprintf("start streamer\n");
|
||||||
cThread::Start();
|
cThread::Start();
|
||||||
}
|
}
|
||||||
|
Attach();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cStreamdevStreamer::Stop(void)
|
void cStreamdevStreamer::Stop(void)
|
||||||
{
|
{
|
||||||
|
Detach();
|
||||||
if (Running()) {
|
if (Running()) {
|
||||||
Dprintf("stopping streamer\n");
|
Dprintf("stop streamer\n");
|
||||||
Cancel(3);
|
Cancel(3);
|
||||||
}
|
}
|
||||||
if (m_Writer) {
|
Dprintf("stop writer\n");
|
||||||
Detach();
|
|
||||||
DELETENULL(m_Writer);
|
DELETENULL(m_Writer);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void cStreamdevStreamer::Action(void)
|
void cStreamdevStreamer::Action(void)
|
||||||
{
|
{
|
||||||
#if APIVERSNUM >= 10705
|
|
||||||
SetPriority(-3);
|
SetPriority(-3);
|
||||||
#endif
|
|
||||||
while (Running()) {
|
while (Running()) {
|
||||||
int got;
|
int got;
|
||||||
uchar *block = m_RingBuffer->Get(got);
|
uchar *block = GetFromReceiver(got);
|
||||||
|
|
||||||
if (block) {
|
if (block) {
|
||||||
int count = Put(block, got);
|
int count = Put(block, got);
|
||||||
if (count)
|
if (count)
|
||||||
m_RingBuffer->Del(count);
|
DelFromReceiver(count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int cStreamdevStreamer::Put(const uchar *Data, int Count) {
|
||||||
|
return m_Remux->Put(Data, Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
#include <vdr/ringbuffer.h>
|
#include <vdr/ringbuffer.h>
|
||||||
#include <vdr/tools.h>
|
#include <vdr/tools.h>
|
||||||
|
|
||||||
|
#include "remux/tsremux.h"
|
||||||
|
|
||||||
class cTBSocket;
|
class cTBSocket;
|
||||||
class cStreamdevStreamer;
|
class cStreamdevStreamer;
|
||||||
class cServerConnection;
|
class cServerConnection;
|
||||||
@@ -17,7 +19,6 @@ class cServerConnection;
|
|||||||
#define TS_SIZE 188
|
#define TS_SIZE 188
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define STREAMERBUFSIZE (20000 * TS_SIZE)
|
|
||||||
#define WRITERBUFSIZE (20000 * TS_SIZE)
|
#define WRITERBUFSIZE (20000 * TS_SIZE)
|
||||||
|
|
||||||
// --- cStreamdevBuffer -------------------------------------------------------
|
// --- cStreamdevBuffer -------------------------------------------------------
|
||||||
@@ -66,14 +67,18 @@ public:
|
|||||||
class cStreamdevStreamer: public cThread {
|
class cStreamdevStreamer: public cThread {
|
||||||
private:
|
private:
|
||||||
const cServerConnection *m_Connection;
|
const cServerConnection *m_Connection;
|
||||||
|
Streamdev::cTSRemux *m_Remux;
|
||||||
cStreamdevWriter *m_Writer;
|
cStreamdevWriter *m_Writer;
|
||||||
cStreamdevBuffer *m_RingBuffer;
|
|
||||||
cStreamdevBuffer *m_SendBuffer;
|
cStreamdevBuffer *m_SendBuffer;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
virtual uchar* GetFromReceiver(int &Count) = 0;
|
||||||
|
virtual void DelFromReceiver(int Count) = 0;
|
||||||
|
virtual int Put(const uchar *Data, int Count);
|
||||||
virtual void Action(void);
|
virtual void Action(void);
|
||||||
|
|
||||||
bool IsRunning(void) const { return m_Writer; }
|
bool IsRunning(void) const { return m_Writer; }
|
||||||
|
void SetRemux(Streamdev::cTSRemux *Remux) { delete m_Remux; m_Remux = Remux; }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cStreamdevStreamer(const char *Name, const cServerConnection *Connection = NULL);
|
cStreamdevStreamer(const char *Name, const cServerConnection *Connection = NULL);
|
||||||
@@ -83,18 +88,16 @@ public:
|
|||||||
|
|
||||||
virtual void Start(cTBSocket *Socket);
|
virtual void Start(cTBSocket *Socket);
|
||||||
virtual void Stop(void);
|
virtual void Stop(void);
|
||||||
|
virtual bool IsReceiving(void) const = 0;
|
||||||
bool Abort(void);
|
bool Abort(void);
|
||||||
|
|
||||||
void Activate(bool On);
|
uchar *Get(int &Count) { return m_Remux->Get(Count); }
|
||||||
int Receive(uchar *Data, int Length) { return m_RingBuffer->PutTS(Data, Length); }
|
void Del(int Count) { m_Remux->Del(Count); }
|
||||||
void ReportOverflow(int Bytes) { m_RingBuffer->ReportOverflow(Bytes); }
|
|
||||||
|
|
||||||
virtual int Put(const uchar *Data, int Count) { return m_SendBuffer->PutTS(Data, Count); }
|
|
||||||
virtual uchar *Get(int &Count) { return m_SendBuffer->Get(Count); }
|
|
||||||
virtual void Del(int Count) { m_SendBuffer->Del(Count); }
|
|
||||||
|
|
||||||
virtual void Detach(void) {}
|
virtual void Detach(void) {}
|
||||||
virtual void Attach(void) {}
|
virtual void Attach(void) {}
|
||||||
|
|
||||||
|
virtual cString ToText() const { return ""; };
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool cStreamdevStreamer::Abort(void)
|
inline bool cStreamdevStreamer::Abort(void)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ void cSuspendLive::Action(void) {
|
|||||||
bool cSuspendCtl::m_Active = false;
|
bool cSuspendCtl::m_Active = false;
|
||||||
|
|
||||||
cSuspendCtl::cSuspendCtl(void):
|
cSuspendCtl::cSuspendCtl(void):
|
||||||
cControl(m_Suspend = new cSuspendLive) {
|
cControl(m_Suspend = new cSuspendLive, true) {
|
||||||
m_Active = true;
|
m_Active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2256
server/suspend.dat
2256
server/suspend.dat
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
|||||||
#include "tools/socket.h"
|
#include "tools/socket.h"
|
||||||
|
#include "tools/select.h"
|
||||||
|
|
||||||
#include <vdr/tools.h>
|
#include <vdr/tools.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -27,7 +28,7 @@ cTBSocket::~cTBSocket() {
|
|||||||
if (IsOpen()) Close();
|
if (IsOpen()) Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cTBSocket::Connect(const std::string &Host, unsigned int Port) {
|
bool cTBSocket::Connect(const std::string &Host, unsigned int Port, unsigned int TimeoutMs) {
|
||||||
socklen_t len;
|
socklen_t len;
|
||||||
int socket;
|
int socket;
|
||||||
|
|
||||||
@@ -45,14 +46,37 @@ bool cTBSocket::Connect(const std::string &Host, unsigned int Port) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TimeoutMs > 0 && ::fcntl(socket, F_SETFL, O_NONBLOCK) == -1) {
|
||||||
|
::close(socket);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
m_RemoteAddr.sin_family = AF_INET;
|
m_RemoteAddr.sin_family = AF_INET;
|
||||||
m_RemoteAddr.sin_port = htons(Port);
|
m_RemoteAddr.sin_port = htons(Port);
|
||||||
m_RemoteAddr.sin_addr.s_addr = inet_addr(Host.c_str());
|
m_RemoteAddr.sin_addr.s_addr = inet_addr(Host.c_str());
|
||||||
if (::connect(socket, (struct sockaddr*)&m_RemoteAddr,
|
if (::connect(socket, (struct sockaddr*)&m_RemoteAddr, sizeof(m_RemoteAddr)) == -1) {
|
||||||
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);
|
::close(socket);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (so_error) {
|
||||||
|
errno = so_error;
|
||||||
|
::close(socket);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
::close(socket);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (m_Type == SOCK_STREAM) {
|
if (m_Type == SOCK_STREAM) {
|
||||||
len = sizeof(struct sockaddr_in);
|
len = sizeof(struct sockaddr_in);
|
||||||
|
|||||||
@@ -32,11 +32,13 @@ public:
|
|||||||
Reimplemented for TCP/IPv4 sockets. */
|
Reimplemented for TCP/IPv4 sockets. */
|
||||||
virtual ssize_t SysWrite(const void *Buffer, size_t Length) const;
|
virtual ssize_t SysWrite(const void *Buffer, size_t Length) const;
|
||||||
|
|
||||||
/* Connect() tries to connect an available local socket to the port given
|
/* Connect() tries to connect an available local socket within TimeoutMs
|
||||||
by Port of the target host given by Host in numbers-and-dots notation
|
milliseconds to the port given by Port of the target host given by
|
||||||
(i.e. "212.43.45.21"). Returns true if the connection attempt was
|
Host in numbers-and-dots notation (i.e. "212.43.45.21"). A TimeoutMs
|
||||||
successful and false otherwise, setting errno appropriately. */
|
of 0 will disable non-blocking IO for the connect call. Returns true
|
||||||
virtual bool Connect(const std::string &Host, uint Port);
|
if the connection attempt was successful and false otherwise, setting
|
||||||
|
errno appropriately. */
|
||||||
|
virtual bool Connect(const std::string &Host, uint Port, uint TimeoutMs = 0);
|
||||||
|
|
||||||
/* Shutdown() shuts down one or both ends of a socket. If called with How
|
/* Shutdown() shuts down one or both ends of a socket. If called with How
|
||||||
set to SHUT_RD, further reads on this socket will be denied. If called
|
set to SHUT_RD, further reads on this socket will be denied. If called
|
||||||
@@ -60,7 +62,7 @@ public:
|
|||||||
/* Accept() returns a newly created cTBSocket, which is connected to the
|
/* Accept() returns a newly created cTBSocket, which is connected to the
|
||||||
first connection request on the queue of pending connections of a
|
first connection request on the queue of pending connections of a
|
||||||
listening socket. If no connection request was pending, or if any other
|
listening socket. If no connection request was pending, or if any other
|
||||||
error occured, the resulting cTBSocket is closed. */
|
error occurred, the resulting cTBSocket is closed. */
|
||||||
virtual cTBSocket Accept(void) const;
|
virtual cTBSocket Accept(void) const;
|
||||||
|
|
||||||
/* Accept() extracts the first connection request on the queue of pending
|
/* Accept() extracts the first connection request on the queue of pending
|
||||||
|
|||||||
@@ -57,15 +57,20 @@ ssize_t cTBSource::Write(const void *Buffer, size_t Length) {
|
|||||||
bool cTBSource::TimedWrite(const void *Buffer, size_t Length, uint TimeoutMs) {
|
bool cTBSource::TimedWrite(const void *Buffer, size_t Length, uint TimeoutMs) {
|
||||||
cTBSelect sel;
|
cTBSelect sel;
|
||||||
int ms, offs;
|
int ms, offs;
|
||||||
|
|
||||||
cTimeMs starttime;
|
cTimeMs starttime;
|
||||||
ms = TimeoutMs;
|
|
||||||
offs = 0;
|
offs = 0;
|
||||||
sel.Clear();
|
sel.Clear();
|
||||||
sel.Add(m_Filed, true);
|
sel.Add(m_Filed, true);
|
||||||
while (Length > 0) {
|
while (Length > 0) {
|
||||||
int b;
|
int b;
|
||||||
|
|
||||||
|
ms = TimeoutMs - starttime.Elapsed();
|
||||||
|
if (ms <= 0) {
|
||||||
|
errno = ETIMEDOUT;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (sel.Select(ms) == -1)
|
if (sel.Select(ms) == -1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -76,11 +81,6 @@ bool cTBSource::TimedWrite(const void *Buffer, size_t Length, uint TimeoutMs) {
|
|||||||
Length -= b;
|
Length -= b;
|
||||||
}
|
}
|
||||||
|
|
||||||
ms = TimeoutMs - starttime.Elapsed();
|
|
||||||
if (ms <= 0) {
|
|
||||||
errno = ETIMEDOUT;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ public:
|
|||||||
/* Close() resets the source to the uninitialized state (IsOpen() == false)
|
/* Close() resets the source to the uninitialized state (IsOpen() == false)
|
||||||
and must be called by any derivations after really closing the source.
|
and must be called by any derivations after really closing the source.
|
||||||
Returns true on success and false on error, setting errno appropriately.
|
Returns true on success and false on error, setting errno appropriately.
|
||||||
The object is in closed state afterwards, even if an error occured. */
|
The object is in closed state afterwards, even if an error occurred. */
|
||||||
virtual bool Close(void);
|
virtual bool Close(void);
|
||||||
|
|
||||||
/* Read() reads at most Length bytes into the storage pointed to by Buffer,
|
/* Read() reads at most Length bytes into the storage pointed to by Buffer,
|
||||||
|
|||||||
Reference in New Issue
Block a user