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 suggesting to change the URL path from EXTERN to EXT
|
||||
for suggesting increased thread priorities for cStreamdevWriter/Streamer
|
||||
for adding "Hide mainmenu entry" option
|
||||
for polishing po file headers
|
||||
for adding the special meaning "show current channel" to channel 0
|
||||
|
||||
Rantanen Teemu
|
||||
for providing vdr-incompletesections.diff
|
||||
@@ -116,9 +119,10 @@ Jori Hamalainen
|
||||
for extensive testing while making stream compatible to Network Media Tank
|
||||
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 suggesting use of SO_KEEPALIVE socket option to detect dead sockets
|
||||
for making cStatus::ChannelChange re-tune only if CA IDs changed
|
||||
|
||||
Joachim König-Baltes
|
||||
for fixing Min/MaxPriority parsing
|
||||
@@ -198,3 +202,60 @@ Ville Skytt
|
||||
for restricting VTP command RENR to liemikuutio patch < 1.32
|
||||
for fixing memory and filedescriptor leaks in libdvbmpeg
|
||||
for code cleanup and optimization
|
||||
for correcting typos
|
||||
|
||||
Methodus
|
||||
for suggesting to use HTTP code 503 for unavailable channels
|
||||
|
||||
Uwe
|
||||
for reporting a compiler error in client/device.c with VDR < 1.7.22
|
||||
|
||||
Chris Tallon
|
||||
for his kind permission to use VOMP's recplayer for replaying recordings
|
||||
|
||||
macmenot
|
||||
for adapting Makefiles to VDR 1.7.36+
|
||||
|
||||
thomasjfox
|
||||
for fixing cSuspendCtl preventing idle shutdown
|
||||
|
||||
hivdr
|
||||
for adding the pos= parameter for replaying recordings from a certain position
|
||||
for suggesting to add the HTTP "Server" header
|
||||
|
||||
hummel99
|
||||
for reporting and helping to debug channel switch issues with priority > 0
|
||||
for reporting a race condition when switching the server's LiveTV device
|
||||
|
||||
Henrik Niehaus
|
||||
for fixing replay of large TS files on 32-bit systems
|
||||
|
||||
Guy Martin
|
||||
for adding SVDRP commands to list and disconnect clients
|
||||
|
||||
Martin1234
|
||||
for suggesting a service call, returning the number of clients
|
||||
for implementing GetCurrentlyTunedTransponder() on client
|
||||
|
||||
Toerless Eckert
|
||||
for converting suspend.dat into proper PES format
|
||||
for investigating and fixing problems caused by filter streaming
|
||||
for fixing TimedWrite() so it doesn't fail after a slow but successful write
|
||||
for suggesting to double the size of client's filter buffer
|
||||
|
||||
Tomasz Maciej Nowak
|
||||
for providing Polish language texts
|
||||
|
||||
Christopher Reimer
|
||||
for providing an initial compatibility patch for VDR 2.3.1
|
||||
|
||||
Matthias Senzel
|
||||
for refining the compatibility patch for VDR 2.3.1
|
||||
|
||||
David Binderman
|
||||
for fixing an lseek error check in libdvbmpeg
|
||||
|
||||
Jasmin J
|
||||
for fixing some warnings in libdvbmpeg
|
||||
for adding .gitignore
|
||||
for fixing compilation for VDR 2.3.7
|
||||
|
||||
128
HISTORY
128
HISTORY
@@ -1,6 +1,134 @@
|
||||
VDR Plugin 'streamdev' Revision History
|
||||
---------------------------------------
|
||||
|
||||
- fixed compilation for VDR 2.3.7 (thanks to Jasmin J)
|
||||
- added .gitignore (thanks to Jasmin J)
|
||||
- fixed some warnings in libdvbmpeg (thanks to Jasmin J)
|
||||
- fixed lseek error check in libdvbmpeg (thanks to David Binderman)
|
||||
- server compatibility with VDR 2.3.1 (thanks to Christopher Reimer and
|
||||
Matthias Senzel)
|
||||
- client compatibility with VDR 2.3.1
|
||||
- use cReceiver::SetPriority(...) in VDR 2.1.4+
|
||||
- doubled size of client's filter buffer (suggested by Toerless Eckert)
|
||||
- make sure TimedWrite(...) doesn't return failure after a slow but successful
|
||||
write operation (thanks to Toerless Eckert)
|
||||
- fixed problems related to VTP filter streaming like ringbuffer overflows,
|
||||
stuttering or aborting video stream (thanks to Toerless Eckert)
|
||||
- added Polish translation (thanks to Tomasz Maciej Nowak)
|
||||
- converted suspend.dat into proper PES format (thanks to Toerless Eckert)
|
||||
- implemented GetCurrentlyTunedTransponder() on client (thanks to Martin1234)
|
||||
- added service call returning the number of clients (suggested by Martin1234)
|
||||
- added SVDRP commands to list and disconnect clients (thanks to Guy Martin)
|
||||
- fixed recplayer issues with large TS files (>4GB)
|
||||
- Don't abort externremux when internal read buffer is empty
|
||||
- Implemented remuxing of recordings
|
||||
- Make ChannelChange retune only if CA IDs changed (thanks to Oliver Wagner)
|
||||
- Implemented VDR 2.1.4 cStatus::ChannelChange(...)
|
||||
- Call detach only if receiver is attached
|
||||
- Try changing to other device when receiver got detached
|
||||
- In TSPIDS mode, create and attach receiver with empty pid list to occupy dev
|
||||
- Restructured server classes
|
||||
- New option for server side live TV buffer to prevent buffer underruns
|
||||
|
||||
2013-11-28: Version 0.6.1
|
||||
|
||||
- Updated Slovak translation (thanks to Milan Hrala)
|
||||
- Updated Finnish translation (thanks to Rolf Ahrenberg)
|
||||
- Disabled PS remuxer which is said to produce anything but PS
|
||||
- The patches intcamdevices and ignore_missing_cam are no longer required
|
||||
on VDR >= 1.7.30. The localchannelprovide patch became obsolete with VDR
|
||||
1.7.21.
|
||||
- Added option to suspend live TV when the server starts
|
||||
- Set device occupied when streamdev switches away LiveTV on the server, to
|
||||
reduce the risk that the VDR main loop immediately switches back, resulting
|
||||
in a black screen on the client (reported by hummel99)
|
||||
- Fixed channel switch issues with priority > 0 (reported by hummel99)
|
||||
- Removed noisy debug messages
|
||||
- Fixed HTTP menu destruction
|
||||
- API change of VDR 2.1.2
|
||||
- Fixed priority handling, messed up when adding multi-device support
|
||||
- Added HTTP "Server" header (suggested by hivdr)
|
||||
- Ignore dummy file extensions (.ts, .vob, .vdr) when parsing HTTP URIs
|
||||
- Select start position for replaying a recording by parameter pos=. Supported
|
||||
values are resume, mark.#, time.#, frame.# or a plain # representing a
|
||||
percentage if < 100 or a byte position otherwise (thanks to hivdr)
|
||||
- Start cSuspendCtl hidden or it will prevent idle shutdown (thanks to
|
||||
thomasjfox)
|
||||
- Fixed recordings menu inode numbers: ino_t is a long long on some systems
|
||||
- Updated Slovak translation (thanks to Milan Hrala)
|
||||
- Adapted Makefiles to VDR 1.7.36+ (thanks to macmenot). Old makefiles have
|
||||
been renamed to Makefile-1.7.33.
|
||||
- API changes of VDR 1.7.38 (thanks to mal@vdr-developer)
|
||||
- Added simple recordings menu in HTTP server
|
||||
- Restructured menuHTTP classes
|
||||
- Added RSS format for HTTP menus
|
||||
- Recordings can now also be selected by struct stat "st_dev:st_ino.rec"
|
||||
- Implemented multi-device support for streamdev client (suggested by johns)
|
||||
- Basic support for HTTP streaming of recordings
|
||||
- Close writer when streamer is finished
|
||||
- Don't abort VTP connection if filter stream is broken
|
||||
- Restructured cStreamdevStreamer: Moved inbound buffer into actual subclass.
|
||||
- In cStreamdevStreamer dropped Activate(bool) and moved its code into Start().
|
||||
- Moved cStreamdevFilterStreamer to livefilter.[hc]
|
||||
- Return HTTP/1.1 compliant response headers plus some always useful headers
|
||||
- Return HTTP URL parameters ending with ".dlna.org" as response headers
|
||||
- Store HTTP URL parameters in a map
|
||||
- Support HTTP HEAD requests with external remuxer
|
||||
- Fixed always using priority 0 for HTTP HEAD requests
|
||||
- Start writer right after creating it
|
||||
- Corrected typos (thanks to Ville Skyttä)
|
||||
- Fixed compiler error in client/device.c with VDR < 1.7.22 (reported by
|
||||
Uwe@vdrportal)
|
||||
- Updated Italian translation (thanks to Diego Pierotto)
|
||||
- Added DeviceName() and DeviceType() to client device. The server IP and the
|
||||
number of the device used on the server are returned respectively.
|
||||
|
||||
2012-05-29: Version 0.6.0
|
||||
|
||||
- Reimplemented some client device methods
|
||||
- Proper fix for "client sends ABRT after TUNE". Obsoletes many hacks in client
|
||||
- Added CLOCK_MONOTONIC timestamp and thread id to Dprintf
|
||||
- Silenced warning (thanks to Rolf Ahrenberg)
|
||||
- Updated Finnish translation (thanks to Rolf Ahrenberg)
|
||||
- Replaced server-side suspend modes with priority based precedence handling
|
||||
- Client-side priority handling for VDR >= 1.7.25 and servers running VTP > 1.0
|
||||
- Introduced VTP protocol version numbering for easier compatibility handling
|
||||
between different client and server versions. The server includes the protocol
|
||||
version in its greeting string, the client reports its version with the new
|
||||
command "VERS".
|
||||
- Dropped compatibility of streamdev-server with VDR < 1.7.25
|
||||
|
||||
2012-05-12: Version 0.5.2
|
||||
|
||||
- Use fileno() to retrieve the fd from a FILE structure (submitted by an
|
||||
anonymous user)
|
||||
- New special meaning "show current channel" when channel 0 is requested.
|
||||
Applies to HTTP streaming only (thanks to Rolf Ahrenberg)
|
||||
- Fixed ProvidesChannel() on client always returning true since the new timeout
|
||||
option has been added.
|
||||
- Updated Finnish translation (thanks to Rolf Ahrenberg)
|
||||
- With VDR 1.7.25 priorities down to -99 will be used. Please update
|
||||
"Minimum Priority" in streamdev-client setup.
|
||||
- Use the new streamdev-client setup option "Live TV Priority" to control
|
||||
precedence among multiple clients. The VDR option "Primary Limit" which
|
||||
has previouly been used for this purpose has been dropped in VDR 1.7.25.
|
||||
- Timout for network operations now configurable in streamdev-client setup
|
||||
- Added timeout to Connect()
|
||||
- Report the server-side HTTP status "503 Service unavailable" instead of
|
||||
the client-side error "409 Conflict" when a channel is unavailable
|
||||
(suggested by Methodus)
|
||||
- Update of po headers and Finnish translation (thanks to Rolf Ahrenberg)
|
||||
- support for non-cycle-free setups (e.g. where two VDRs mutually share
|
||||
their DVB cards through streamdev-client/-server). Must be enabled in
|
||||
streamdev-server setup. Obsoletes recursion patches.
|
||||
- API change of VDR 1.7.22
|
||||
- VDR 1.7.22 obsoletes cap_net_raw patch. Added cap_net_raw patch for VDR
|
||||
1.7.5 - 1.7.21.
|
||||
- Update and UTF-8 conversion of Finnish po files (thanks to Rolf Ahrenberg)
|
||||
- Added "Hide mainmenu entry" option on server (thanks to Rolf Ahrenberg)
|
||||
- Added server menu with list of clients. Connections can be terminated
|
||||
with the "red" key. The former main menu action of suspending live TV
|
||||
moved to the "blue" key.
|
||||
- code cleanup and optimization (thanks to Ville Skyttä)
|
||||
- properly shutdown IGMP timeout handler thread when the plugin is stopped.
|
||||
Fixes occasional segfaults on VDR exit.
|
||||
|
||||
70
Makefile
70
Makefile
@@ -1,57 +1,44 @@
|
||||
#
|
||||
# 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
|
||||
|
||||
### 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
|
||||
# Use package data if installed...otherwise assume we're under the VDR source directory:
|
||||
PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
|
||||
LIBDIR = $(call PKGCFG,libdir)
|
||||
LOCDIR = $(call PKGCFG,locdir)
|
||||
PLGCFG = $(call PKGCFG,plgcfg)
|
||||
#
|
||||
TMPDIR ?= /tmp
|
||||
|
||||
### 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')
|
||||
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 }')
|
||||
export CFLAGS = $(call PKGCFG,cflags)
|
||||
export CXXFLAGS = $(call PKGCFG,cxxflags)
|
||||
|
||||
### The version number of VDR's plugin API:
|
||||
|
||||
APIVERSION = $(call PKGCFG,apiversion)
|
||||
|
||||
### 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
|
||||
-include $(PLGCFG)
|
||||
|
||||
### export all vars for sub-makes, using absolute paths
|
||||
|
||||
VDRDIR := $(shell cd $(VDRDIR) >/dev/null 2>&1 && pwd)
|
||||
LIBDIR := $(shell cd $(LIBDIR) >/dev/null 2>&1 && pwd)
|
||||
LOCDIR := $(shell cd $(LOCDIR) >/dev/null 2>&1 && pwd)
|
||||
export
|
||||
unexport PLUGIN
|
||||
|
||||
@@ -63,6 +50,7 @@ PACKAGE = vdr-$(ARCHIVE)
|
||||
### Includes and Defines (add further entries here):
|
||||
|
||||
INCLUDES += -I$(VDRDIR)/include -I..
|
||||
export INCLUDES
|
||||
|
||||
DEFINES += -D_GNU_SOURCE
|
||||
|
||||
@@ -75,7 +63,7 @@ endif
|
||||
|
||||
### The main target:
|
||||
|
||||
.PHONY: all client server dist clean
|
||||
.PHONY: all client server install install-client install-server dist clean
|
||||
all: client server
|
||||
|
||||
### Targets:
|
||||
@@ -83,20 +71,28 @@ all: client server
|
||||
client:
|
||||
$(MAKE) -C ./tools
|
||||
$(MAKE) -C ./client
|
||||
# installs to $(LIBDIR)/libvdr-streamdev-client.so.$(APIVERSION)
|
||||
|
||||
server:
|
||||
$(MAKE) -C ./tools
|
||||
$(MAKE) -C ./libdvbmpeg
|
||||
$(MAKE) -C ./remux
|
||||
$(MAKE) -C ./server
|
||||
|
||||
install-client: client
|
||||
$(MAKE) -C ./client install
|
||||
# installs to $(LIBDIR)/libvdr-streamdev-client.so.$(APIVERSION)
|
||||
|
||||
install-server: server
|
||||
$(MAKE) -C ./server install
|
||||
# installs to $(LIBDIR)/libvdr-streamdev-server.so.$(APIVERSION)
|
||||
|
||||
install: install-client install-server
|
||||
|
||||
dist: clean
|
||||
@-rm -rf $(TMPDIR)/$(ARCHIVE)
|
||||
@mkdir $(TMPDIR)/$(ARCHIVE)
|
||||
@cp -a * $(TMPDIR)/$(ARCHIVE)
|
||||
@tar czf $(PACKAGE).tgz --exclude CVS -C $(TMPDIR) $(ARCHIVE)
|
||||
@tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
|
||||
@-rm -rf $(TMPDIR)/$(ARCHIVE)
|
||||
@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
|
||||
254
README
254
README
@@ -101,13 +101,17 @@ as otherwise -r will be passed to VDR and not to streamdev.
|
||||
2.1 Compatibility:
|
||||
------------------
|
||||
|
||||
This version is not compatible to VDR releases older than 1.5.9. Take one of
|
||||
the streamdev-0.4.x releases if you are running at least VDR 1.4.x. For older
|
||||
VDRs you will probably need one of the streamdev-0.3.x releases.
|
||||
This version is not compatible to VDR releases older than 1.7.25. Use one of
|
||||
the streamdev-0.5.x releases for older versions.
|
||||
|
||||
2.2 Compiling:
|
||||
--------------
|
||||
|
||||
The Makefiles are for VDR 1.7.36 and above. For VDR 1.7.33 and below, please
|
||||
replace the Makefiles in the main directory and in the client/ and server/
|
||||
subdirectories with the corresponding Makefile-1.7.33 files. With VDR 1.7.34 and
|
||||
1.7.35 YMMV ;)
|
||||
|
||||
cd vdr-1.X.X/PLUGINS/src
|
||||
tar xvfz vdr-streamdev-0.5.0.tgz
|
||||
ln -s streamdev-0.5.0 streamdev
|
||||
@@ -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,
|
||||
then read below for the necessary changes.
|
||||
|
||||
* Priorities:
|
||||
-------------
|
||||
(Affected: 0.5.x and older)
|
||||
|
||||
The server-side setting "Suspend behaviour" has been dropped in 0.6.0 in favour
|
||||
of priority based precedence. A priority of 0 and above means that clients
|
||||
have precedence. A negative priority gives precedence to local live TV on the
|
||||
server. So if "Suspend behaviour" was previously set to "Client may suspend" or
|
||||
"Never suspended", you will have to configure a negative priority. If the
|
||||
"Suspend behaviour" was set to "Always suspended", the default values should do.
|
||||
|
||||
Configure the desired priorities for HTTP and IGMP Multicast streaming in the
|
||||
settings of streamdev-server. If you haven't updated all your streamdev-clients
|
||||
to at least 0.5.2, configure "Legacy Client Priority", too.
|
||||
|
||||
In streamdev-client, you should set "Minimum Priority" to -99. Adjust "Live TV
|
||||
Priority" if necessary.
|
||||
|
||||
* Location of files:
|
||||
--------------------
|
||||
(Affected: 0.3.x, 0.4.x, 0.4.0pre, 0.5.0pre)
|
||||
@@ -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
|
||||
can run in one VDR instance, if necessary.
|
||||
|
||||
The parameter "Suspend behaviour" allows you to specify how the server should
|
||||
react in case the client requests a channel that would require switching the
|
||||
primary device (i.e. disrupt live-tv). If set to "Offer suspend mode", you will
|
||||
have a new entry in the main menu. Activating that will put the server into
|
||||
"Suspend Mode" (a picture is displayed on TV). Then, a client may switch the
|
||||
primary card to wherever it likes to. While watching TV (Suspend deactivated),
|
||||
the client may not switch the transponder on the primary device. If you set
|
||||
the behaviour to "Always suspended" (the default), there will be normal live-tv
|
||||
on the server, but whenever a client decides to switch the transponder, the
|
||||
server will lose it's live-tv. Set to "Never suspended", the server always
|
||||
prevents the client from switching transponders. If you set "Client may
|
||||
suspend" to yes, the client can suspend the server remotely (this only applies
|
||||
if "Offer suspend mode" is selected).
|
||||
Precedence between multiple clients and between client and server is controlled
|
||||
with priorities. For HTTP and IGMP Multicast, the priority is configured in
|
||||
streamdev-server's setup menu. A negative priority gives precedence to local
|
||||
live TV on the server. Zero and positive values give precedence to the client.
|
||||
|
||||
NOTE: This mainly applies to One-Card-Systems, since with multiple cards there
|
||||
is no need to switch transponders on the primary interface, if the secondary
|
||||
can stream a given channel (i.e. if it is not blocked by a recording). If both
|
||||
cards are in use (i.e. when something is recorded, or by multiple clients),
|
||||
this applies to Multiple-Card-Systems as well.
|
||||
The priority for VDR clients watching live TV is configured in the plugin setup
|
||||
of streamdev-client. For other client tasks (e.g. recording a client side timer)
|
||||
the same priority as on the client is used. With the parameter "Legacy client
|
||||
Priority" in streamdev-server's setup menu you can configure the priority for
|
||||
clients which cannot be configured to use negative priorities. It is used
|
||||
when an old client is detected an it requests priority "0".
|
||||
|
||||
On the server, the main menu entry "Streamdev Connections" gives you a list
|
||||
of currently connected clients. Use the "red" key to terminate a connection.
|
||||
Note that depending on connection type and client, the client might re-connect
|
||||
sooner or later.
|
||||
|
||||
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:
|
||||
----------------------
|
||||
@@ -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)
|
||||
PES Packetized Elemetary Stream (VDR's native recording format)
|
||||
PS Program Stream (SVCD, DVD like stream)
|
||||
ES Elementary Stream (only Video, if available, otherwise only Audio)
|
||||
EXT Pass stream through external script (e.g. for converting with mencoder)
|
||||
|
||||
@@ -225,16 +262,18 @@ streams directly like this:
|
||||
http://hostname:3000/S19.2E-0-12480-898
|
||||
|
||||
The first one will deliver a channel by number on the server, the second one
|
||||
will request the channel by unique channel id. In addition, you can specify
|
||||
the desired stream type as a path to the channel.
|
||||
will request the channel by unique channel id. Use the special channel number 0
|
||||
to see the server's current live TV channel.
|
||||
|
||||
In addition, you can specify the desired stream type as a path to the channel.
|
||||
|
||||
http://hostname:3000/TS/3
|
||||
http://hostname:3000/PES/S19.2E-0-12480-898
|
||||
|
||||
The first one would deliver the stream in TS, the second one in PES format.
|
||||
Possible values are 'PES', 'TS', 'PS', 'ES' and 'EXT'. You need to specify
|
||||
the ES format explicitly if you want to listen to radio channels. Play them
|
||||
back i.e. with mpg123.
|
||||
Possible values are 'PES', 'TS', 'ES' and 'EXT'. You need to specify the ES
|
||||
format explicitly if you want to listen to radio channels. Play them back i.e.
|
||||
with mpg123.
|
||||
|
||||
mpg123 http://hostname:3000/ES/200
|
||||
|
||||
@@ -270,7 +309,7 @@ For multicast streaming over the public Internet you would even need to register
|
||||
for your own IP range. So don't even think of multicasting via Internet with
|
||||
streamdev! Streamdev will send the stream only to one local ethernet segment and
|
||||
all clients must be connected to this same segment. There must not be a router
|
||||
inbetween. Also note that the client must not run on the streamdev-server
|
||||
in between. Also note that the client must not run on the streamdev-server
|
||||
machine.
|
||||
|
||||
Each channel is offered on a different multicast IP. Channel 1 is available from
|
||||
@@ -280,16 +319,11 @@ reserved according to RFC-2365).
|
||||
|
||||
Before you can use streamdev's multicast server, you might need to patch VDR.
|
||||
Binding an IGMP socket is a privileged operation, so you must start VDR as root.
|
||||
If you pass the -u option to VDR, it will drop almost all priviledges before
|
||||
streamdev is even loaded. Apply vdr-cap_net_raw.diff to keep VDR from dropping
|
||||
the CAP_NET_RAW capability required to bind the IGMP socket. The patch is part
|
||||
of streamdev's source distribution. Check the patches subdirectory. There's no
|
||||
need to patch VDR if it is kept running as root (not recommended).
|
||||
|
||||
The multicast server is disabled by default. Enter the streamdev-server setup
|
||||
menu to enable it and - IMPORTANT - bind the multicast server to the IP of your
|
||||
VDR server's LAN ethernet card. The multicast server will refuse to start with
|
||||
the default bind adresse "0.0.0.0".
|
||||
the default bind address "0.0.0.0".
|
||||
|
||||
Now edit your streamdevhosts.conf. To allow streaming of all channels, it must
|
||||
contain "239.255.0.0/16". Note that you cannot limit connections by client IP
|
||||
@@ -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
|
||||
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:
|
||||
----------------------------
|
||||
|
||||
@@ -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
|
||||
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 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
|
||||
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
|
||||
@@ -348,66 +389,83 @@ type "127001<OK>" on your remote. If you want to enter "192.168.1.12", type
|
||||
"1921681<Right>12<OK>".
|
||||
|
||||
The parameters "Remote IP" and "Remote Port" in the client's setup specify the
|
||||
address of the remote VDR-to-VDR server to connect to. Activate the client by
|
||||
setting "Start Client" to yes. It is disabled by default, because it wouldn't
|
||||
make much sense to start the client without specifying a server anyway. The
|
||||
client is activated after you push the OK button, so there's no need to restart
|
||||
VDR. Deactivation on-the-fly is not possible, so in order to deactivate the
|
||||
client, you will have to restart VDR. However requests to switch channels will
|
||||
be refused by streamdev-client once it has been deactivated. All other settings
|
||||
can be changed without restarting VDR.
|
||||
|
||||
The client will try to connect to the server (in case it isn't yet) whenever
|
||||
a remote channel is requested. Just activate the client and switch to a
|
||||
channel that's not available by local devices. If anything goes wrong with the
|
||||
connection between the two, you will see it in the logfile instantly. If you
|
||||
now switch the client to a channel which isn't covered by it's own local
|
||||
devices, it will ask the server for it. If the server can (currently) receive
|
||||
that channel, the client will show it until you switch again, or until the
|
||||
server needs that card (if no other is free) for a recording on a different
|
||||
transponder.
|
||||
address of the remote VDR-to-VDR server to connect to. The client is disabled
|
||||
by default, because it wouldn't make much sense to start the client without
|
||||
specifying a server anyway. Activate the client by setting "Simultaneously used
|
||||
Devices" to at least 1. Streamdev-client will allocate as many VDR devices as
|
||||
you configure here. Each of these devices opens one connection to the server
|
||||
and becomes associated with one of the server's devices (typically a DVB card)
|
||||
on demand.
|
||||
|
||||
Only the needed PIDs are transferred, and additional PIDs can be turned on
|
||||
during an active transfer. This makes it possible to switch languages, receive
|
||||
additional channels (for recording on the client) and use plugins that use
|
||||
receivers themselves (like osdteletext).
|
||||
additional channels on the same transponder and use plugins that use receivers
|
||||
themselves (like osdteletext).
|
||||
|
||||
So for viewing live TV a single device is sufficient. But if the client needs
|
||||
to receive channels from different transponders simultaneously (e.g. for PiP or
|
||||
client side recordings) a second device becomes necessary.
|
||||
|
||||
To allocate additional devices, just increase the number and push the OK button.
|
||||
There's no need to restart VDR. Deleting VDR devices on-the-fly is not possible.
|
||||
However requests to switch channels will be refused by redundant devices.
|
||||
|
||||
The default timeout of 2 seconds for network operations should be sufficient in
|
||||
most cases. Increase "Timeout" if you get frequent timeout errors in the log.
|
||||
|
||||
With "Filter Streaming" enabled, the client will receive meta information like
|
||||
EPG data and service information, just as if the client had its own DVB card.
|
||||
Link channels and even a client-side EPG scan have been reported to work.
|
||||
|
||||
With maximum and minimum priority you can keep VDR from considering streamdev
|
||||
in certain cases. If for instance you have a streamdev client with its own DVB
|
||||
card, VDR might use streamdev for recording. If this is not what you want, you
|
||||
could set the maximum priority to 0. As recordings usually have a much higher
|
||||
priority (default 50), streamdev is now no longer used for recordings. The two
|
||||
parameters define the inclusive range of priorities for which streamdev will
|
||||
accept to tune. Setting the minimum priority to a higher value than the maximum,
|
||||
you will get two ranges: "up to maximum" and "minimum and above".
|
||||
If you have TV programs with dynamically changing PIDs (such as some german
|
||||
regional programs like NDR), then you need to enable "Filter Streaming" to
|
||||
correctly receive them. You also need to set in VDRs DVB setup the option
|
||||
"Update channels" to at least "PIDs only" (or "names and PIDs") for this
|
||||
to work.
|
||||
|
||||
If you are running at least VDR 1.7.0, you can also configure the "Broadcast
|
||||
Systems / Cost" of the streamdev-client device. On a pure streamdev-client only
|
||||
system it doesn't matter what you configure here. But if your client is equipped
|
||||
with a DVB card, you should read on. VDR always prefers the cheapest device
|
||||
in terms of supported broadcast systems and modulations. A DVB-S2 card supports
|
||||
two broadcast systems (DVB-S and DVB-S2). From VDR 1.7.15 on, the supported
|
||||
modulations are counted as well (QPSK, QAM32/64/128/256, VSB8/16, TURBO_FEC).
|
||||
So for a DVB-S2 card which does QPSK you'll get a total cost of three. A DVB-C
|
||||
card (one broadcast system) which can do QAM32,QAM64,QAM128,QAM256 would give
|
||||
you a total of five. Check your log for "frontend ... provides ... with ..."
|
||||
messages to find out the cost of your DVB cards. Then pick a suitable value for
|
||||
streamdev-client. With equal costs, VDR will usually prefer the DVB card and
|
||||
take streamdev for recordings. If streamdev's costs are higher, live TV will
|
||||
use your DVB card until a recordings kicks in. Then the recording will take the
|
||||
DVB card and live TV will be shifted to streamdev (you'll notice a short
|
||||
interruption of live TV).
|
||||
"Filter streaming" uses internally a socketpair(2) to copy meta data to
|
||||
VDR. This socketpair may require larger than default buffering. If
|
||||
you see a mesage like the following in syslog,
|
||||
|
||||
Note that streamdev-client acts similar to a DVB card. It is possible to receive
|
||||
multiple channels simultaneously, but only from the same transponder. Just add
|
||||
additional instances of streamdev-client and you will be able to receive as many
|
||||
transponders at a time. The same trick allows a client to receive channels from
|
||||
different servers. To create an additional instance, copy the streamdev-client
|
||||
binary to a different name (e.g. streamdev-client2):
|
||||
cStreamdevFilter::PutSection(Pid:18 Tid: 64): Dropped 2995 bytes, max queue: 328640
|
||||
|
||||
then you should increase the streamdev client "FilterSockBufSize" value. A
|
||||
good value is 3072000. You will need to first configure your linux to
|
||||
permit such a large buffer size:
|
||||
|
||||
sysctl net.core.wmem_max=3072000
|
||||
|
||||
The precedence among multiple client VDRs receiving live TV from the same
|
||||
server is controlled with "Live TV Priority".
|
||||
|
||||
With "Maximum Priority" and "Minimum Priority" you can keep VDR from considering
|
||||
streamdev in certain cases. If for instance you have a streamdev client with its
|
||||
own DVB card, VDR might use streamdev for recording. If this is not what you
|
||||
want, you could set the maximum priority to 0. As recordings usually have a much
|
||||
higher priority (default 50), streamdev is now no longer used for recordings.
|
||||
The two parameters define the inclusive range of priorities for which streamdev
|
||||
will accept to tune. Setting the minimum priority to a higher value than the
|
||||
maximum, you will get two ranges: "up to maximum" and "minimum and above".
|
||||
|
||||
You can also configure the "Broadcast Systems / Cost" of the streamdev-client
|
||||
device. On a pure streamdev-client only system it doesn't matter what you
|
||||
configure here. But if your client is equipped with a DVB card, you should read
|
||||
on. VDR always prefers the cheapest device in terms of supported broadcast
|
||||
systems and modulations. A DVB-S2 card supports two broadcast systems (DVB-S and
|
||||
DVB-S2). The supported modulations are counted as well (QPSK, QAM32/64/128/256,
|
||||
VSB8/16, TURBO_FEC). So for a DVB-S2 card which does QPSK you'll get a total
|
||||
cost of three. A DVB-C card (one broadcast system) which can do QAM32, QAM64,
|
||||
QAM128, QAM256 would give you a total of five. Check your log for "frontend ...
|
||||
provides ... with ..." messages to find out the cost of your DVB cards. Then
|
||||
pick a suitable value for streamdev-client. With equal costs, VDR will usually
|
||||
prefer the DVB card and take streamdev for recordings. If streamdev's costs are
|
||||
higher, live TV will use your DVB card until a recordings kicks in. Then the
|
||||
recording will take the DVB card and live TV will be shifted to streamdev
|
||||
(you'll notice a short interruption of live TV).
|
||||
|
||||
To receive channels from multiple servers, create additional instances of the
|
||||
streamdev-client plugin. Simply copy (don't link!) the binary to a different
|
||||
name (e.g. streamdev-client2):
|
||||
|
||||
cd VDRPLUGINLIBDIR
|
||||
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:
|
||||
------------------
|
||||
|
||||
* There have been reports that channel switching with VDR 1.5.x/1.6.x clients
|
||||
sometimes fails. Current version includes a workaround which seems to work, but
|
||||
YMMV ;)
|
||||
|
||||
* Viewing encrypted channels became an issue with VDR's new CAM handling code.
|
||||
Streamdev doesn't provide a (dummy) CAM, so out of the box, VDR won't ever try
|
||||
to receive encrypted channels from streamdev. Pick one of the following
|
||||
solutions to work around the problem:
|
||||
* In VDR before 1.7.30 viewing encrypted channels is an issue as Streamdev
|
||||
doesn't provide a (dummy) CAM. So out of the box, VDR won't ever try to receive
|
||||
encrypted channels from streamdev. Pick one of the following solutions to work
|
||||
around the problem:
|
||||
|
||||
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
|
||||
@@ -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
|
||||
(number 10 becomes an "a", number 11 a "b", ...).
|
||||
|
||||
2. Apply either patch "patches/vdr-1.6.0-intcamdevices.patch" or patch
|
||||
"patches/vdr-1.6.0-ignore_missing_cam.diff" to your client VDR. Intcamdevices
|
||||
is the clean solution, but it modifies the VDR API. So you will need to
|
||||
recompile all of your plugins. The ignore_missing_cam patch is trivial, no need
|
||||
to recompile other plugins. However it is not suitable for clients with a DVB
|
||||
card of their own.
|
||||
2. Apply either patch "patches/vdr-1.6.0-1.7.29-intcamdevices.patch" or patch
|
||||
"patches/vdr-1.6.0-1.7.29-ignore_missing_cam.diff" to your client VDR.
|
||||
Intcamdevices is the clean solution, but it modifies the VDR API. So you will
|
||||
need to recompile all of your plugins. The ignore_missing_cam patch is trivial,
|
||||
no need to recompile other plugins. However it is not suitable for clients with
|
||||
a DVB card of their own.
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
#
|
||||
# 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.
|
||||
# This name will be used in the '-P...' option of VDR to load the plugin.
|
||||
# By default the main source file also carries this name.
|
||||
#
|
||||
|
||||
PLUGIN = streamdev-client
|
||||
|
||||
### The name of the shared object file:
|
||||
|
||||
SOFILE = libvdr-$(PLUGIN).so
|
||||
|
||||
### Includes and Defines (add further entries here):
|
||||
|
||||
DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
|
||||
@@ -22,8 +26,7 @@ CLIENTOBJS = $(PLUGIN).o \
|
||||
|
||||
### The main target:
|
||||
|
||||
.PHONY: all i18n dist clean
|
||||
all: libvdr-$(PLUGIN).so i18n
|
||||
all: $(SOFILE) i18n
|
||||
|
||||
### Implicit rules:
|
||||
|
||||
@@ -34,51 +37,47 @@ all: libvdr-$(PLUGIN).so i18n
|
||||
|
||||
MAKEDEP = $(CXX) -MM -MG
|
||||
DEPFILE = .dependencies
|
||||
|
||||
$(DEPFILE): Makefile
|
||||
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(CLIENTOBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) > $@
|
||||
@$(MAKEDEP) $(CXXFLAGS) $(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))))))
|
||||
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
|
||||
|
||||
%.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 $@ $^
|
||||
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)
|
||||
msgmerge -U --no-wrap --no-location --backup=none -q $@ $<
|
||||
msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $<
|
||||
@touch $@
|
||||
|
||||
$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
|
||||
@mkdir -p $(dir $@)
|
||||
cp $< $@
|
||||
$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
|
||||
install -D -m644 $< $@
|
||||
|
||||
i18n: $(I18Nmsgs)
|
||||
.PHONY: i18n
|
||||
i18n: $(I18Nmo) $(I18Npot)
|
||||
|
||||
install-i18n: $(I18Nmsgs)
|
||||
|
||||
### Targets:
|
||||
|
||||
libvdr-$(PLUGIN).so: $(CLIENTOBJS) $(COMMONOBJS) ../tools/sockettools.a
|
||||
$(SOFILE): $(CLIENTOBJS) $(COMMONOBJS) ../tools/sockettools.a
|
||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $^ -o $@
|
||||
|
||||
%.so:
|
||||
$(CXX) $(CXXFLAGS) -shared $^ -o $@
|
||||
@cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION)
|
||||
install-lib: $(SOFILE)
|
||||
install -D $^ $(DESTDIR)$(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
|
||||
install: install-lib install-i18n
|
||||
|
||||
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* *~
|
||||
302
client/device.c
302
client/device.c
@@ -19,32 +19,38 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
#ifndef LIVEPRIORITY
|
||||
#define LIVEPRIORITY 0
|
||||
#endif
|
||||
|
||||
#ifndef TRANSFERPRIORITY
|
||||
#define TRANSFERPRIORITY -1
|
||||
#endif
|
||||
|
||||
#define VIDEOBUFSIZE MEGABYTE(3)
|
||||
|
||||
cStreamdevDevice *cStreamdevDevice::m_Device = NULL;
|
||||
const cChannel *cStreamdevDevice::m_DenyChannel = NULL;
|
||||
|
||||
cStreamdevDevice::cStreamdevDevice(void) {
|
||||
m_Channel = NULL;
|
||||
m_TSBuffer = NULL;
|
||||
m_Disabled = false;
|
||||
m_ClientSocket = new cClientSocket();
|
||||
m_Channel = NULL;
|
||||
m_TSBuffer = NULL;
|
||||
|
||||
m_Filters = new cStreamdevFilters;
|
||||
m_Filters = new cStreamdevFilters(m_ClientSocket);
|
||||
StartSectionHandler();
|
||||
isyslog("streamdev-client: got device number %d", CardIndex() + 1);
|
||||
|
||||
m_Device = this;
|
||||
m_Pids = 0;
|
||||
m_Priority = -1;
|
||||
m_DvrClosed = true;
|
||||
}
|
||||
|
||||
cStreamdevDevice::~cStreamdevDevice() {
|
||||
Dprintf("Device gets destructed\n");
|
||||
|
||||
Lock();
|
||||
m_Device = NULL;
|
||||
m_Filters->SetConnection(-1);
|
||||
ClientSocket.Quit();
|
||||
ClientSocket.Reset();
|
||||
m_ClientSocket->Quit();
|
||||
m_ClientSocket->Reset();
|
||||
Unlock();
|
||||
|
||||
Cancel(3);
|
||||
@@ -52,6 +58,7 @@ cStreamdevDevice::~cStreamdevDevice() {
|
||||
StopSectionHandler();
|
||||
DELETENULL(m_Filters);
|
||||
DELETENULL(m_TSBuffer);
|
||||
delete m_ClientSocket;
|
||||
}
|
||||
|
||||
#if APIVERSNUM >= 10700
|
||||
@@ -70,27 +77,29 @@ bool cStreamdevDevice::ProvidesTransponder(const cChannel *Channel) const
|
||||
return true;
|
||||
}
|
||||
|
||||
#if APIVERSNUM >= 10722
|
||||
bool cStreamdevDevice::IsTunedToTransponder(const cChannel *Channel) const
|
||||
#else
|
||||
bool cStreamdevDevice::IsTunedToTransponder(const cChannel *Channel)
|
||||
#endif
|
||||
{
|
||||
bool res = false;
|
||||
if (ClientSocket.DataSocket(siLive) != NULL
|
||||
&& TRANSPONDER(Channel, m_Channel)
|
||||
&& Channel->Ca() == CA_FTA
|
||||
&& m_Channel->Ca() == CA_FTA)
|
||||
res = true;
|
||||
return res;
|
||||
return m_ClientSocket->DataSocket(siLive) != NULL &&
|
||||
m_Channel != NULL &&
|
||||
Channel->Transponder() == m_Channel->Transponder();
|
||||
}
|
||||
|
||||
const cChannel *cStreamdevDevice::GetCurrentlyTunedTransponder(void) const {
|
||||
if (m_ClientSocket->DataSocket(siLive) != NULL)
|
||||
return m_Channel;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
|
||||
bool *NeedsDetachReceivers) const {
|
||||
bool res = false;
|
||||
bool prio = Priority < 0 || Priority > this->Priority();
|
||||
bool ndr = false;
|
||||
|
||||
if (!StreamdevClientSetup.StartClient)
|
||||
if (m_Disabled || Channel == m_DenyChannel)
|
||||
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)
|
||||
{
|
||||
@@ -105,12 +114,41 @@ bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ClientSocket.DataSocket(siLive) != NULL
|
||||
&& TRANSPONDER(Channel, m_Channel))
|
||||
res = true;
|
||||
else {
|
||||
res = prio && ClientSocket.ProvidesChannel(Channel, Priority);
|
||||
ndr = true;
|
||||
int newPrio = Priority;
|
||||
if (Priority == LIVEPRIORITY) {
|
||||
if (m_ClientSocket->ServerVersion() >= 100 || StreamdevClientSetup.LivePriority >= 0)
|
||||
newPrio = StreamdevClientSetup.LivePriority;
|
||||
}
|
||||
|
||||
#if APIVERSNUM >= 10725
|
||||
bool prio = Priority == IDLEPRIORITY || newPrio >= m_ClientSocket->Priority();
|
||||
#else
|
||||
bool prio = Priority < 0 || newPrio > m_ClientSocket->Priority();
|
||||
#endif
|
||||
bool res = prio;
|
||||
bool ndr = false;
|
||||
|
||||
#if APIVERSNUM >= 10722
|
||||
if (IsTunedToTransponder(Channel)) {
|
||||
#else
|
||||
if (const_cast<cStreamdevDevice*>(this)->IsTunedToTransponder(Channel)) {
|
||||
#endif
|
||||
if (Channel->Ca() < CA_ENCRYPTED_MIN ||
|
||||
(Channel->Vpid() && HasPid(Channel->Vpid())) ||
|
||||
(Channel->Apid(0) && HasPid(Channel->Apid(0))))
|
||||
res = true;
|
||||
else
|
||||
ndr = true;
|
||||
}
|
||||
else if (prio) {
|
||||
if (Priority == LIVEPRIORITY && m_ClientSocket->ServerVersion() >= 100)
|
||||
UpdatePriority(true);
|
||||
|
||||
res = m_ClientSocket->ProvidesChannel(Channel, newPrio);
|
||||
ndr = Receiving();
|
||||
|
||||
if (m_ClientSocket->ServerVersion() >= 100)
|
||||
UpdatePriority(false);
|
||||
}
|
||||
|
||||
if (NeedsDetachReceivers)
|
||||
@@ -121,139 +159,93 @@ bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
|
||||
|
||||
bool cStreamdevDevice::SetChannelDevice(const cChannel *Channel,
|
||||
bool LiveView) {
|
||||
bool res;
|
||||
Dprintf("SetChannelDevice Channel: %s, LiveView: %s\n", Channel->Name(),
|
||||
LiveView ? "true" : "false");
|
||||
LOCK_THREAD;
|
||||
|
||||
m_UpdatePriority = ClientSocket.SupportsPrio();
|
||||
|
||||
if (LiveView)
|
||||
return false;
|
||||
|
||||
if (ClientSocket.DataSocket(siLive) != NULL
|
||||
&& TRANSPONDER(Channel, m_Channel)
|
||||
&& Channel->Ca() == CA_FTA
|
||||
&& m_Channel->Ca() == CA_FTA)
|
||||
return true;
|
||||
|
||||
DetachAllReceivers();
|
||||
m_Channel = Channel;
|
||||
bool r = ClientSocket.SetChannelDevice(m_Channel);
|
||||
Dprintf("setchanneldevice r=%d\n", r);
|
||||
return r;
|
||||
if (Receiving() && IsTunedToTransponder(Channel) && (
|
||||
Channel->Ca() < CA_ENCRYPTED_MIN ||
|
||||
(Channel->Vpid() && HasPid(Channel->Vpid())) ||
|
||||
(Channel->Apid(0) && HasPid(Channel->Apid(0))))) {
|
||||
res = true;
|
||||
}
|
||||
else {
|
||||
DetachAllReceivers();
|
||||
m_Channel = Channel;
|
||||
// Old servers delete cStreamdevLiveStreamer in ABRT.
|
||||
// Delete it now or it will happen after we tuned to new channel
|
||||
if (m_ClientSocket->ServerVersion() < 100)
|
||||
CloseDvr();
|
||||
res = m_ClientSocket->SetChannelDevice(m_Channel);
|
||||
}
|
||||
Dprintf("setchanneldevice res=%d\n", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool cStreamdevDevice::SetPid(cPidHandle *Handle, int Type, bool On) {
|
||||
Dprintf("SetPid, Pid=%d, Type=%d, On=%d, used=%d\n", Handle->pid, Type, On,
|
||||
Handle->used);
|
||||
Dprintf("SetPid, Pid=%d, Type=%d, On=%d, used=%d\n", Handle->pid, Type, On, Handle->used);
|
||||
LOCK_THREAD;
|
||||
|
||||
m_UpdatePriority = ClientSocket.SupportsPrio();
|
||||
|
||||
if (On && !m_TSBuffer) {
|
||||
Dprintf("SetPid: no data connection -> OpenDvr()");
|
||||
OpenDvrInt();
|
||||
}
|
||||
|
||||
bool res = true;
|
||||
if (Handle->pid && (On || !Handle->used)) {
|
||||
res = ClientSocket.SetPid(Handle->pid, On);
|
||||
res = m_ClientSocket->SetPid(Handle->pid, On);
|
||||
|
||||
m_Pids += (!res) ? 0 : On ? 1 : -1;
|
||||
if (m_Pids < 0)
|
||||
m_Pids = 0;
|
||||
|
||||
if(m_Pids < 1 && m_DvrClosed) {
|
||||
Dprintf("SetPid: 0 pids left -> CloseDvr()");
|
||||
CloseDvrInt();
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool cStreamdevDevice::OpenDvrInt(void) {
|
||||
Dprintf("OpenDvrInt\n");
|
||||
LOCK_THREAD;
|
||||
|
||||
CloseDvrInt();
|
||||
if (m_TSBuffer) {
|
||||
Dprintf("cStreamdevDevice::OpenDvrInt(): DVR connection already open\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
Dprintf("cStreamdevDevice::OpenDvrInt(): Connecting ...\n");
|
||||
if (ClientSocket.CreateDataConnection(siLive)) {
|
||||
m_TSBuffer = new cTSBuffer(*ClientSocket.DataSocket(siLive), MEGABYTE(2), CardIndex() + 1);
|
||||
return true;
|
||||
}
|
||||
esyslog("cStreamdevDevice::OpenDvrInt(): DVR connection FAILED");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cStreamdevDevice::OpenDvr(void) {
|
||||
Dprintf("OpenDvr\n");
|
||||
LOCK_THREAD;
|
||||
|
||||
m_DvrClosed = false;
|
||||
return OpenDvrInt();
|
||||
}
|
||||
|
||||
void cStreamdevDevice::CloseDvrInt(void) {
|
||||
Dprintf("CloseDvrInt\n");
|
||||
LOCK_THREAD;
|
||||
|
||||
if (ClientSocket.CheckConnection()) {
|
||||
if (!m_DvrClosed) {
|
||||
Dprintf("cStreamdevDevice::CloseDvrInt(): m_DvrClosed=false -> not closing yet\n");
|
||||
return;
|
||||
}
|
||||
if (m_Pids > 0) {
|
||||
Dprintf("cStreamdevDevice::CloseDvrInt(): %d active pids -> not closing yet\n", m_Pids);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Dprintf("cStreamdevDevice::CloseDvrInt(): Control connection gone !\n");
|
||||
CloseDvr();
|
||||
if (m_ClientSocket->CreateDataConnection(siLive)) {
|
||||
m_TSBuffer = new cTSBuffer(*m_ClientSocket->DataSocket(siLive), MEGABYTE(2), CardIndex() + 1);
|
||||
}
|
||||
|
||||
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);
|
||||
else {
|
||||
esyslog("cStreamdevDevice::OpenDvr(): DVR connection FAILED");
|
||||
}
|
||||
return m_TSBuffer != NULL;
|
||||
}
|
||||
|
||||
|
||||
void cStreamdevDevice::CloseDvr(void) {
|
||||
Dprintf("CloseDvr\n");
|
||||
LOCK_THREAD;
|
||||
|
||||
m_DvrClosed = true;
|
||||
CloseDvrInt();
|
||||
m_ClientSocket->CloseDvr();
|
||||
DELETENULL(m_TSBuffer);
|
||||
}
|
||||
|
||||
bool cStreamdevDevice::GetTSPacket(uchar *&Data) {
|
||||
if (m_TSBuffer && m_Device) {
|
||||
if (m_TSBuffer) {
|
||||
Data = m_TSBuffer->Get();
|
||||
#if 1 // TODO: this should be fixed in vdr cTSBuffer
|
||||
// simple disconnect detection
|
||||
static int m_TSFails = 0;
|
||||
if (!Data) {
|
||||
LOCK_THREAD;
|
||||
if(!ClientSocket.DataSocket(siLive)) {
|
||||
if(!m_ClientSocket->DataSocket(siLive)) {
|
||||
return false; // triggers CloseDvr() + OpenDvr() in cDevice
|
||||
}
|
||||
cPoller Poller(*ClientSocket.DataSocket(siLive));
|
||||
cPoller Poller(*m_ClientSocket->DataSocket(siLive));
|
||||
errno = 0;
|
||||
if (Poller.Poll() && !errno) {
|
||||
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);
|
||||
m_TSFails++;
|
||||
if (m_TSFails > 10) {
|
||||
isyslog("cStreamdevDevice::GetTSPacket(): disconnected");
|
||||
m_Pids = 0;
|
||||
CloseDvrInt();
|
||||
CloseDvr();
|
||||
m_TSFails = 0;
|
||||
return false;
|
||||
}
|
||||
@@ -275,67 +267,89 @@ int cStreamdevDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask) {
|
||||
return -1;
|
||||
|
||||
|
||||
if (!ClientSocket.DataSocket(siLiveFilter)) {
|
||||
if (ClientSocket.CreateDataConnection(siLiveFilter)) {
|
||||
m_Filters->SetConnection(*ClientSocket.DataSocket(siLiveFilter));
|
||||
if (!m_ClientSocket->DataSocket(siLiveFilter)) {
|
||||
if (m_ClientSocket->CreateDataConnection(siLiveFilter)) {
|
||||
m_Filters->SetConnection(*m_ClientSocket->DataSocket(siLiveFilter));
|
||||
} else {
|
||||
isyslog("cStreamdevDevice::OpenFilter: connect failed: %m");
|
||||
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 -1;
|
||||
}
|
||||
|
||||
bool cStreamdevDevice::Init(void) {
|
||||
if (m_Device == NULL && StreamdevClientSetup.StartClient)
|
||||
new cStreamdevDevice;
|
||||
void cStreamdevDevice::CloseFilter(int Handle) {
|
||||
|
||||
if(m_Filters)
|
||||
m_Filters->CloseFilter(Handle);
|
||||
else
|
||||
esyslog("cStreamdevDevice::CloseFilter called while m_Filters is null");
|
||||
}
|
||||
|
||||
bool cStreamdevDevice::ReInit(bool Disable) {
|
||||
LOCK_THREAD;
|
||||
m_Disabled = Disable;
|
||||
m_Filters->SetConnection(-1);
|
||||
m_Pids = 0;
|
||||
m_ClientSocket->Quit();
|
||||
m_ClientSocket->Reset();
|
||||
//DELETENULL(m_TSBuffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cStreamdevDevice::ReInit(void) {
|
||||
if(m_Device) {
|
||||
m_Device->Lock();
|
||||
m_Device->m_Filters->SetConnection(-1);
|
||||
m_Device->m_Pids = 0;
|
||||
void cStreamdevDevice::UpdatePriority(bool SwitchingChannels) const {
|
||||
if (!m_Disabled) {
|
||||
//LOCK_THREAD;
|
||||
const_cast<cStreamdevDevice*>(this)->Lock();
|
||||
if (m_ClientSocket->SupportsPrio() && m_ClientSocket->DataSocket(siLive)) {
|
||||
int Priority = this->Priority();
|
||||
// override TRANSFERPRIORITY (-1) with live TV priority from setup
|
||||
if (Priority == TRANSFERPRIORITY && this == cDevice::ActualDevice()) {
|
||||
Priority = StreamdevClientSetup.LivePriority;
|
||||
// temporarily lower priority
|
||||
if (SwitchingChannels)
|
||||
Priority--;
|
||||
if (Priority < 0 && m_ClientSocket->ServerVersion() < 100)
|
||||
Priority = 0;
|
||||
}
|
||||
m_ClientSocket->SetPriority(Priority);
|
||||
}
|
||||
const_cast<cStreamdevDevice*>(this)->Unlock();
|
||||
}
|
||||
ClientSocket.Quit();
|
||||
ClientSocket.Reset();
|
||||
if (m_Device != NULL) {
|
||||
//DELETENULL(m_Device->m_TSBuffer);
|
||||
m_Device->Unlock();
|
||||
}
|
||||
return StreamdevClientSetup.StartClient ? Init() : true;
|
||||
}
|
||||
|
||||
void cStreamdevDevice::UpdatePriority(void) {
|
||||
if (m_Device) {
|
||||
m_Device->Lock();
|
||||
if (m_Device->m_UpdatePriority && ClientSocket.DataSocket(siLive)) {
|
||||
int Priority = m_Device->Priority();
|
||||
if (m_Device == cDevice::ActualDevice() && Priority < Setup.PrimaryLimit)
|
||||
Priority = Setup.PrimaryLimit;
|
||||
if (m_Device->m_Priority != Priority && ClientSocket.SetPriority(Priority))
|
||||
m_Device->m_Priority = Priority;
|
||||
}
|
||||
m_Device->Unlock();
|
||||
cString cStreamdevDevice::DeviceName(void) const {
|
||||
return StreamdevClientSetup.RemoteIp;
|
||||
}
|
||||
|
||||
cString cStreamdevDevice::DeviceType(void) const {
|
||||
static int dev = -1;
|
||||
static cString devType("STRDev");
|
||||
int d = -1;
|
||||
if (m_ClientSocket->DataSocket(siLive) != NULL)
|
||||
m_ClientSocket->GetSignal(NULL, NULL, &d);
|
||||
if (d != dev) {
|
||||
dev = d;
|
||||
devType = d < 0 ? "STRDev" : *cString::sprintf("STRD%2d", d);
|
||||
}
|
||||
return devType;
|
||||
}
|
||||
|
||||
int cStreamdevDevice::SignalStrength(void) const {
|
||||
int strength = -1;
|
||||
if (ClientSocket.DataSocket(siLive) != NULL)
|
||||
ClientSocket.GetSignal(&strength, NULL);
|
||||
if (m_ClientSocket->DataSocket(siLive) != NULL)
|
||||
m_ClientSocket->GetSignal(&strength, NULL, NULL);
|
||||
return strength;
|
||||
}
|
||||
|
||||
int cStreamdevDevice::SignalQuality(void) const {
|
||||
int quality = -1;
|
||||
if (ClientSocket.DataSocket(siLive) != NULL)
|
||||
ClientSocket.GetSignal(NULL, &quality);
|
||||
if (m_ClientSocket->DataSocket(siLive) != NULL)
|
||||
m_ClientSocket->GetSignal(NULL, &quality, NULL);
|
||||
return quality;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,22 +17,22 @@ class cTBString;
|
||||
class cStreamdevDevice: public cDevice {
|
||||
|
||||
private:
|
||||
bool m_Disabled;
|
||||
cClientSocket *m_ClientSocket;
|
||||
const cChannel *m_Channel;
|
||||
cTSBuffer *m_TSBuffer;
|
||||
cStreamdevFilters *m_Filters;
|
||||
int m_Pids;
|
||||
int m_Priority;
|
||||
bool m_UpdatePriority;
|
||||
bool m_DvrClosed;
|
||||
|
||||
static cStreamdevDevice *m_Device;
|
||||
|
||||
bool OpenDvrInt(void);
|
||||
void CloseDvrInt(void);
|
||||
static const cChannel *m_DenyChannel;
|
||||
|
||||
protected:
|
||||
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
|
||||
#if APIVERSNUM >= 10738
|
||||
virtual bool HasLock(int TimeoutMs) const
|
||||
#else
|
||||
virtual bool HasLock(int TimeoutMs)
|
||||
#endif
|
||||
{
|
||||
//printf("HasLock is %d\n", (ClientSocket.DataSocket(siLive) != NULL));
|
||||
//return ClientSocket.DataSocket(siLive) != NULL;
|
||||
@@ -45,6 +45,7 @@ protected:
|
||||
virtual bool GetTSPacket(uchar *&Data);
|
||||
|
||||
virtual int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
|
||||
virtual void CloseFilter(int Handle);
|
||||
|
||||
public:
|
||||
cStreamdevDevice(void);
|
||||
@@ -61,15 +62,21 @@ public:
|
||||
#if APIVERSNUM >= 10719
|
||||
virtual bool AvoidRecording(void) const { return true; }
|
||||
#endif
|
||||
#if APIVERSNUM >= 10722
|
||||
virtual bool IsTunedToTransponder(const cChannel *Channel) const;
|
||||
#else
|
||||
virtual bool IsTunedToTransponder(const cChannel *Channel);
|
||||
#endif
|
||||
virtual const cChannel *GetCurrentlyTunedTransponder(void) const;
|
||||
virtual cString DeviceName(void) const;
|
||||
virtual cString DeviceType(void) const;
|
||||
virtual int SignalStrength(void) const;
|
||||
virtual int SignalQuality(void) const;
|
||||
|
||||
static void UpdatePriority(void);
|
||||
static bool Init(void);
|
||||
static bool ReInit(void);
|
||||
|
||||
static cStreamdevDevice *GetDevice(void) { return m_Device; }
|
||||
bool ReInit(bool Disable);
|
||||
void UpdatePriority(bool SwitchingChannels = false) const;
|
||||
bool SuspendServer() { return m_ClientSocket->SuspendServer(); }
|
||||
static void DenyChannel(const cChannel *Channel) { m_DenyChannel = Channel; }
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_DEVICE_H
|
||||
|
||||
169
client/filter.c
169
client/filter.c
@@ -6,20 +6,28 @@
|
||||
#include "client/socket.h"
|
||||
#include "tools/select.h"
|
||||
#include "common.h"
|
||||
#include <sys/ioctl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vdr/device.h>
|
||||
|
||||
#define PID_MASK_HI 0x1F
|
||||
// --- cStreamdevFilter ------------------------------------------------------
|
||||
|
||||
static int FilterSockBufSize_warn = 0;
|
||||
|
||||
class cStreamdevFilter: public cListObject {
|
||||
private:
|
||||
uchar m_Buffer[4096];
|
||||
uchar m_Buffer[8192];
|
||||
int m_Used;
|
||||
int m_Pipe[2];
|
||||
u_short m_Pid;
|
||||
u_char m_Tid;
|
||||
u_char m_Mask;
|
||||
#ifdef TIOCOUTQ
|
||||
unsigned long m_maxq;
|
||||
unsigned long m_flushed;
|
||||
#endif
|
||||
|
||||
public:
|
||||
cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask);
|
||||
@@ -29,7 +37,6 @@ public:
|
||||
bool PutSection(const uchar *Data, int Length, bool Pusi);
|
||||
int ReadPipe(void) const { return m_Pipe[0]; }
|
||||
|
||||
bool IsClosed(void);
|
||||
void Reset(void);
|
||||
|
||||
u_short Pid(void) const { return m_Pid; }
|
||||
@@ -47,6 +54,10 @@ cStreamdevFilter::cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask) {
|
||||
m_Tid = Tid;
|
||||
m_Mask = Mask;
|
||||
m_Pipe[0] = m_Pipe[1] = -1;
|
||||
#ifdef TIOCOUTQ
|
||||
m_flushed = 0;
|
||||
m_maxq = 0;
|
||||
#endif
|
||||
|
||||
#ifdef SOCK_SEQPACKET
|
||||
// SOCK_SEQPACKET (since kernel 2.6.4)
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
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) {
|
||||
esyslog("streamdev-client: couldn't set section filter socket to non-blocking mode: %m");
|
||||
}
|
||||
@@ -67,11 +117,12 @@ cStreamdevFilter::cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask) {
|
||||
cStreamdevFilter::~cStreamdevFilter() {
|
||||
Dprintf("~cStreamdevFilter %p\n", this);
|
||||
|
||||
// ownership of handle m_Pipe[0] has been transferred to VDR section handler
|
||||
//if (m_Pipe[0] >= 0)
|
||||
// close(m_Pipe[0]);
|
||||
if (m_Pipe[1] >= 0)
|
||||
if (m_Pipe[0] >= 0) {
|
||||
close(m_Pipe[0]);
|
||||
}
|
||||
if (m_Pipe[1] >= 0) {
|
||||
close(m_Pipe[1]);
|
||||
}
|
||||
}
|
||||
|
||||
bool cStreamdevFilter::PutSection(const uchar *Data, int Length, bool Pusi) {
|
||||
@@ -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;
|
||||
if (m_Used == length) {
|
||||
m_Used = 0;
|
||||
if (write(m_Pipe[1], m_Buffer, length) < 0) {
|
||||
if(errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
dsyslog("cStreamdevFilter::PutSection socket overflow, "
|
||||
"Pid %4d Tid %3d", m_Pid, m_Tid);
|
||||
#ifdef TIOCOUTQ
|
||||
// If we can determine the queue size of the socket,
|
||||
// we flush rather then let the socket drop random packets.
|
||||
// This ensures that we have more contiguous set of packets
|
||||
// on the receiver side.
|
||||
if(m_flushed) {
|
||||
unsigned long queue = 0;
|
||||
ioctl(m_Pipe[1], TIOCOUTQ, &queue);
|
||||
if(queue > m_maxq)
|
||||
m_maxq = queue;
|
||||
if(queue * 2 < m_maxq) {
|
||||
dsyslog("cStreamdevFilter::PutSection(Pid:%d Tid: %d): "
|
||||
"Flushed %ld bytes, max queue: %ld",
|
||||
m_Pid, m_Tid, m_flushed, m_maxq);
|
||||
m_flushed = m_maxq = 0;
|
||||
|
||||
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;
|
||||
} 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;
|
||||
}
|
||||
|
||||
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(void):
|
||||
cStreamdevFilters::cStreamdevFilters(cClientSocket *ClientSocket):
|
||||
cThread("streamdev-client: sections assembler") {
|
||||
m_ClientSocket = ClientSocket;
|
||||
m_TSBuffer = NULL;
|
||||
}
|
||||
|
||||
@@ -154,43 +216,27 @@ cStreamdevFilters::~cStreamdevFilters() {
|
||||
}
|
||||
|
||||
int cStreamdevFilters::OpenFilter(u_short Pid, u_char Tid, u_char Mask) {
|
||||
CarbageCollect();
|
||||
|
||||
cStreamdevFilter *f = new cStreamdevFilter(Pid, Tid, Mask);
|
||||
int fh = f->ReadPipe();
|
||||
|
||||
Lock();
|
||||
LOCK_THREAD;
|
||||
Add(f);
|
||||
Unlock();
|
||||
|
||||
return fh;
|
||||
}
|
||||
|
||||
void cStreamdevFilters::CarbageCollect(void) {
|
||||
void cStreamdevFilters::CloseFilter(int Handle) {
|
||||
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);
|
||||
Del(fi);
|
||||
fi = next ? Next(next) : First();
|
||||
} else {
|
||||
esyslog("cStreamdevFilters::CarbageCollector() error: "
|
||||
"Pid %4d, Tid %3d, Mask %2x (%d filters left) failed",
|
||||
(int)fi->Pid(), (int)fi->Tid(), fi->Mask(), Count()-1);
|
||||
LOG_ERROR;
|
||||
fi = Next(fi);
|
||||
}
|
||||
} else {
|
||||
fi = Next(fi);
|
||||
for (cStreamdevFilter *fi = First(); fi; fi = Next(fi)) {
|
||||
if(fi->ReadPipe() == Handle) {
|
||||
// isyslog("cStreamdevFilters::CloseFilter(%d): Pid %4d, Tid %3d, Mask %2x (%d filters left)\n",
|
||||
// Handle, (int)fi->Pid(), (int)fi->Tid(), fi->Mask(), Count()-1);
|
||||
Del(fi);
|
||||
return;
|
||||
}
|
||||
}
|
||||
esyslog("cStreamdevFilters::CloseFilter(%d): failed (%d filters left)\n", Handle, Count()-1);
|
||||
}
|
||||
|
||||
bool cStreamdevFilters::ReActivateFilters(void)
|
||||
@@ -198,9 +244,8 @@ bool cStreamdevFilters::ReActivateFilters(void)
|
||||
LOCK_THREAD;
|
||||
|
||||
bool res = true;
|
||||
CarbageCollect();
|
||||
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");
|
||||
}
|
||||
return res;
|
||||
@@ -251,7 +296,7 @@ void cStreamdevFilters::Action(void) {
|
||||
Dprintf("FATAL ERROR: %m\n");
|
||||
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);
|
||||
// Filter was closed.
|
||||
// - need to check remaining filters for another match
|
||||
@@ -261,7 +306,7 @@ void cStreamdevFilters::Action(void) {
|
||||
} else {
|
||||
#if 1 // TODO: this should be fixed in vdr cTSBuffer
|
||||
// Check disconnection
|
||||
int fd = *ClientSocket.DataSocket(siLiveFilter);
|
||||
int fd = *m_ClientSocket->DataSocket(siLiveFilter);
|
||||
if(fd < 0)
|
||||
break;
|
||||
cPoller Poller(fd);
|
||||
@@ -273,7 +318,7 @@ void cStreamdevFilters::Action(void) {
|
||||
++fails;
|
||||
if (fails >= 10) {
|
||||
esyslog("cStreamdevFilters::Action(): stream disconnected ?");
|
||||
ClientSocket.CloseDataConnection(siLiveFilter);
|
||||
m_ClientSocket->CloseDataConnection(siLiveFilter);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -11,23 +11,24 @@
|
||||
|
||||
class cTSBuffer;
|
||||
class cStreamdevFilter;
|
||||
class cClientSocket;
|
||||
|
||||
class cStreamdevFilters: public cList<cStreamdevFilter>, public cThread {
|
||||
private:
|
||||
cClientSocket *m_ClientSocket;
|
||||
cTSBuffer *m_TSBuffer;
|
||||
|
||||
protected:
|
||||
virtual void Action(void);
|
||||
void CarbageCollect(void);
|
||||
|
||||
bool ReActivateFilters(void);
|
||||
|
||||
public:
|
||||
cStreamdevFilters(void);
|
||||
cStreamdevFilters(cClientSocket *ClientSocket);
|
||||
virtual ~cStreamdevFilters();
|
||||
|
||||
void SetConnection(int Handle);
|
||||
int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
|
||||
void CloseFilter(int Handle);
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_FILTER_H
|
||||
|
||||
@@ -6,15 +6,49 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2011-02-16 08:49+0100\n"
|
||||
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||
"POT-Creation-Date: 2014-10-20 22:57+0200\n"
|
||||
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
||||
"Last-Translator: Frank Schmirler <vdrdev@schmirler.de>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"Language-Team: German <vdr@linuxtv.org>\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Hauptmenüeintrag verstecken"
|
||||
|
||||
msgid "Simultaneously used Devices"
|
||||
msgstr "Gleichzeitig genutzte DVB-Karten"
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "IP der Gegenseite"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "Port der Gegenseite"
|
||||
|
||||
msgid "Timeout (s)"
|
||||
msgstr "Timeout (s)"
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "Filter-Daten streamen"
|
||||
|
||||
msgid "Filter SockBufSize"
|
||||
msgstr "Filter Socket Puffergröße"
|
||||
|
||||
msgid "Live TV Priority"
|
||||
msgstr "Live TV Priorität"
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr "Minimale Priorität"
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr "Maximale Priorität"
|
||||
|
||||
msgid "Broadcast Systems / Cost"
|
||||
msgstr "Empfangssysteme / Kosten"
|
||||
|
||||
msgid "VTP Streaming Client"
|
||||
msgstr "VTP Streaming Client"
|
||||
|
||||
@@ -26,27 +60,3 @@ msgstr "Server ist pausiert"
|
||||
|
||||
msgid "Couldn't suspend Server!"
|
||||
msgstr "Konnte Server nicht pausieren!"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Hauptmenüeintrag verstecken"
|
||||
|
||||
msgid "Start Client"
|
||||
msgstr "Client starten"
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "IP der Gegenseite"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "Port der Gegenseite"
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "Filter-Daten streamen"
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr "Minimale Priorität"
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr "Maximale Priorität"
|
||||
|
||||
msgid "Broadcast Systems / Cost"
|
||||
msgstr "Empfangssysteme / Kosten"
|
||||
|
||||
@@ -6,15 +6,49 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
||||
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||
"POT-Creation-Date: 2015-01-16 22:32+0100\n"
|
||||
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
|
||||
"Last-Translator: Javier Bradineras <jbradi@hotmail.com>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"Language-Team: Spanish <vdr@linuxtv.org>\n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Ocultar entrada en menú principal"
|
||||
|
||||
msgid "Simultaneously used Devices"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "Indicar IP del Servidor"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "Indicar puerto remoto del Servidor"
|
||||
|
||||
msgid "Timeout (s)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "Filtrar transmisión"
|
||||
|
||||
msgid "Filter SockBufSize"
|
||||
msgstr ""
|
||||
|
||||
msgid "Live TV Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr "Prioridad mínima"
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr "Prioridad máxima"
|
||||
|
||||
msgid "Broadcast Systems / Cost"
|
||||
msgstr ""
|
||||
|
||||
msgid "VTP Streaming Client"
|
||||
msgstr "Cliente trasmisión VTP"
|
||||
|
||||
@@ -26,26 +60,3 @@ msgstr "Servidor en suspensi
|
||||
|
||||
msgid "Couldn't suspend Server!"
|
||||
msgstr "Imposible suspender el servidor!"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Ocultar entrada en menú principal"
|
||||
|
||||
msgid "Start Client"
|
||||
msgstr "Iniciar Cliente"
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "Indicar IP del Servidor"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "Indicar puerto remoto del Servidor"
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "Filtrar transmisión"
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr "Prioridad mínima"
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr "Prioridad máxima"
|
||||
|
||||
|
||||
|
||||
@@ -1,50 +1,62 @@
|
||||
# VDR streamdev plugin language source file.
|
||||
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
||||
# This file is distributed under the same license as the VDR streamdev package.
|
||||
# Rolf Ahrenberg <rahrenbe@cc.hut.fi>, 2008
|
||||
# Rolf Ahrenberg, 2008-
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
||||
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||
"POT-Creation-Date: 2015-01-16 22:32+0100\n"
|
||||
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
||||
"Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"Last-Translator: Rolf Ahrenberg\n"
|
||||
"Language-Team: Finnish <vdr@linuxtv.org>\n"
|
||||
"Language: fi\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"
|
||||
|
||||
msgid "VTP Streaming Client"
|
||||
msgstr "VTP-suoratoistoasiakas"
|
||||
|
||||
msgid "Suspend Server"
|
||||
msgstr "Pysäytä palvelin"
|
||||
|
||||
msgid "Server is suspended"
|
||||
msgstr "Palvelin on pysäytetty"
|
||||
|
||||
msgid "Couldn't suspend Server!"
|
||||
msgstr "Palvelinta ei onnistuttu pysäyttämään!"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Piilota valinta päävalikosta"
|
||||
msgstr "Piilota valinta päävalikosta"
|
||||
|
||||
msgid "Start Client"
|
||||
msgstr "Käynnistä VDR-asiakas"
|
||||
msgid "Simultaneously used Devices"
|
||||
msgstr "Yhtäaikaiset laitteet"
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "Etäkoneen IP-osoite"
|
||||
msgstr "Etäkoneen IP-osoite"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "Etäkoneen portti"
|
||||
msgstr "Etäkoneen portti"
|
||||
|
||||
msgid "Timeout (s)"
|
||||
msgstr "Yhteyden aikakatkaisu (s)"
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "Suodatetun tiedon suoratoisto"
|
||||
|
||||
msgid "Filter SockBufSize"
|
||||
msgstr ""
|
||||
|
||||
msgid "Live TV Priority"
|
||||
msgstr "Live-katselun prioriteetti"
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr "Pienin prioriteetti"
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr "Suurin prioriteetti"
|
||||
|
||||
msgid "Broadcast Systems / Cost"
|
||||
msgstr "Lähetysjärjestelmien suhdeluku"
|
||||
|
||||
msgid "VTP Streaming Client"
|
||||
msgstr "VTP-suoratoistoasiakas"
|
||||
|
||||
msgid "Suspend Server"
|
||||
msgstr "Pysäytä palvelin"
|
||||
|
||||
msgid "Server is suspended"
|
||||
msgstr "Palvelin on pysäytetty"
|
||||
|
||||
msgid "Couldn't suspend Server!"
|
||||
msgstr "Palvelinta ei onnistuttu pysäyttämään!"
|
||||
|
||||
@@ -6,15 +6,49 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
||||
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||
"POT-Creation-Date: 2015-01-16 22:32+0100\n"
|
||||
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
||||
"Last-Translator: micky979 <micky979@free.fr>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"Language-Team: French <vdr@linuxtv.org>\n"
|
||||
"Language: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Masquer dans le menu principal"
|
||||
|
||||
msgid "Simultaneously used Devices"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "Adresse IP du serveur"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "Port du serveur"
|
||||
|
||||
msgid "Timeout (s)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "Filtre streaming"
|
||||
|
||||
msgid "Filter SockBufSize"
|
||||
msgstr ""
|
||||
|
||||
msgid "Live TV Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "Broadcast Systems / Cost"
|
||||
msgstr ""
|
||||
|
||||
msgid "VTP Streaming Client"
|
||||
msgstr "Client de streaming VTP"
|
||||
|
||||
@@ -26,25 +60,3 @@ msgstr "Le serveur est suspendu"
|
||||
|
||||
msgid "Couldn't suspend Server!"
|
||||
msgstr "Impossible de suspendre le serveur!"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Masquer dans le menu principal"
|
||||
|
||||
msgid "Start Client"
|
||||
msgstr "Démarrage du client"
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "Adresse IP du serveur"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "Port du serveur"
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "Filtre streaming"
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -8,15 +8,49 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
||||
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
|
||||
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||
"POT-Creation-Date: 2015-01-16 22:32+0100\n"
|
||||
"PO-Revision-Date: 2012-06-10 20:34+0100\n"
|
||||
"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"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Nascondi voce menu principale"
|
||||
|
||||
msgid "Simultaneously used Devices"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "Indirizzo IP del Server"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "Porta Server Remoto"
|
||||
|
||||
msgid "Timeout (s)"
|
||||
msgstr "Scadenza (s)"
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "Filtra trasmissione"
|
||||
|
||||
msgid "Filter SockBufSize"
|
||||
msgstr ""
|
||||
|
||||
msgid "Live TV Priority"
|
||||
msgstr "Priorità TV dal vivo"
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr "Priorità minima"
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr "Priorità massima"
|
||||
|
||||
msgid "Broadcast Systems / Cost"
|
||||
msgstr "Costo / sistemi trasmissione"
|
||||
|
||||
msgid "VTP Streaming Client"
|
||||
msgstr "Client trasmissione VTP"
|
||||
|
||||
@@ -28,25 +62,3 @@ msgstr "Server sospeso"
|
||||
|
||||
msgid "Couldn't suspend Server!"
|
||||
msgstr "Impossibile sospendere il server!"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Nascondi voce menu principale"
|
||||
|
||||
msgid "Start Client"
|
||||
msgstr "Avvia Client"
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "Indirizzo IP del Server"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "Porta Server Remoto"
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "Filtra trasmissione"
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr "Priorità minima"
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr "Priorità massima"
|
||||
|
||||
|
||||
@@ -6,15 +6,49 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
||||
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||
"POT-Creation-Date: 2015-01-16 22:32+0100\n"
|
||||
"PO-Revision-Date: 2009-11-26 21:57+0200\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"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Paslėpti pagrindinio meniu įrašą"
|
||||
|
||||
msgid "Simultaneously used Devices"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "Nuotolinis IP adresas"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "Nuotolinis portas"
|
||||
|
||||
msgid "Timeout (s)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "Filtruoti transliavimą"
|
||||
|
||||
msgid "Filter SockBufSize"
|
||||
msgstr ""
|
||||
|
||||
msgid "Live TV Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr "Minimalus prioritetas"
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr "Maksimalus prioritetas"
|
||||
|
||||
msgid "Broadcast Systems / Cost"
|
||||
msgstr ""
|
||||
|
||||
msgid "VTP Streaming Client"
|
||||
msgstr "VTP transliavimo standartas"
|
||||
|
||||
@@ -26,25 +60,3 @@ msgstr "Serveris sustabdytas"
|
||||
|
||||
msgid "Couldn't suspend Server!"
|
||||
msgstr "Negali sustabdyti serverio!"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Paslėpti pagrindinio meniu įrašą"
|
||||
|
||||
msgid "Start Client"
|
||||
msgstr "Paleisti klientą"
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "Nuotolinis IP adresas"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "Nuotolinis portas"
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "Filtruoti transliavimą"
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr "Minimalus prioritetas"
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr "Maksimalus prioritetas"
|
||||
|
||||
|
||||
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 ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
||||
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||
"POT-Creation-Date: 2015-01-16 22:32+0100\n"
|
||||
"PO-Revision-Date: 2008-06-26 15:36+0100\n"
|
||||
"Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"Language-Team: Russian <vdr@linuxtv.org>\n"
|
||||
"Language: ru\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-5\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "ÁßàïâÐâì Ò ÓÛÐÒÝÞÜ ÜÕÝî"
|
||||
|
||||
msgid "Simultaneously used Devices"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "ÃÔÐÛÕÝÝëÙ IP"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "ÃÔÐÛÕÝÝëÙ ßÞàâ"
|
||||
|
||||
msgid "Timeout (s)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "ÄØÛìâà ßÞâÞÚÐ"
|
||||
|
||||
msgid "Filter SockBufSize"
|
||||
msgstr ""
|
||||
|
||||
msgid "Live TV Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "Broadcast Systems / Cost"
|
||||
msgstr ""
|
||||
|
||||
msgid "VTP Streaming Client"
|
||||
msgstr "VTP Streaming ÚÛØÕÝâ"
|
||||
|
||||
@@ -26,25 +60,3 @@ msgstr "
|
||||
|
||||
msgid "Couldn't suspend Server!"
|
||||
msgstr "ÝÕ ÜÞÓã ÞáâÐÝÞÒØâì áÕàÒÕà"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "ÁßàïâÐâì Ò ÓÛÐÒÝÞÜ ÜÕÝî"
|
||||
|
||||
msgid "Start Client"
|
||||
msgstr "ÁâÐàâ ÚÛØÕÝâÐ"
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "ÃÔÐÛÕÝÝëÙ IP"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "ÃÔÐÛÕÝÝëÙ ßÞàâ"
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "ÄØÛìâà ßÞâÞÚÐ"
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr ""
|
||||
|
||||
|
||||
50
client/po/sk_SK.po
Normal file → Executable file
50
client/po/sk_SK.po
Normal file → Executable file
@@ -6,34 +6,23 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev_SK\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
||||
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||
"POT-Creation-Date: 2015-01-16 22:32+0100\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
|
||||
"Language-Team: Slovak <hrala.milan@gmail.com>\n"
|
||||
"Language: sk\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=iso-8859-2\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Poedit-Language: Slovak\n"
|
||||
"X-Poedit-Country: SLOVAKIA\n"
|
||||
|
||||
msgid "VTP Streaming Client"
|
||||
msgstr "VTP prúdový klient"
|
||||
|
||||
msgid "Suspend Server"
|
||||
msgstr "Server pozastavený"
|
||||
|
||||
msgid "Server is suspended"
|
||||
msgstr "Server je doèasne preru¹ený"
|
||||
|
||||
msgid "Couldn't suspend Server!"
|
||||
msgstr "Nepodarilo sa pozastavi» Server!"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Schova» polo¾ku v hlavnom menu"
|
||||
|
||||
msgid "Start Client"
|
||||
msgstr "Spusti» Klienta"
|
||||
msgid "Simultaneously used Devices"
|
||||
msgstr "Súbe¾ne pou¾íva» zariadenia"
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "Vzdialená IP"
|
||||
@@ -41,12 +30,35 @@ msgstr "Vzdialen
|
||||
msgid "Remote Port"
|
||||
msgstr "Vzdialený port"
|
||||
|
||||
msgid "Timeout (s)"
|
||||
msgstr "Èasový limit (s)"
|
||||
|
||||
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"
|
||||
msgstr "minimálna priorita"
|
||||
msgstr "Minimálna priorita"
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr "maximálna priorita"
|
||||
msgstr "Maximálna priorita"
|
||||
|
||||
msgid "Broadcast Systems / Cost"
|
||||
msgstr "Systémy vysielania / Hodnota"
|
||||
|
||||
msgid "VTP Streaming Client"
|
||||
msgstr "VDR klient streamovania"
|
||||
|
||||
msgid "Suspend Server"
|
||||
msgstr "Pozastavi» server"
|
||||
|
||||
msgid "Server is suspended"
|
||||
msgstr "Server je doèasne pozastavený"
|
||||
|
||||
msgid "Couldn't suspend Server!"
|
||||
msgstr "Server sa nepodarilo pozastavi»!"
|
||||
|
||||
@@ -5,21 +5,28 @@
|
||||
#include <vdr/menuitems.h>
|
||||
|
||||
#include "client/setup.h"
|
||||
#include "client/device.h"
|
||||
#include "client/streamdev-client.h"
|
||||
|
||||
#ifndef MINPRIORITY
|
||||
#define MINPRIORITY -MAXPRIORITY
|
||||
#endif
|
||||
|
||||
cStreamdevClientSetup StreamdevClientSetup;
|
||||
|
||||
cStreamdevClientSetup::cStreamdevClientSetup(void) {
|
||||
StartClient = false;
|
||||
RemotePort = 2004;
|
||||
Timeout = 2;
|
||||
StreamFilters = false;
|
||||
HideMenuEntry = false;
|
||||
MinPriority = -1;
|
||||
LivePriority = 0;
|
||||
MinPriority = MINPRIORITY;
|
||||
MaxPriority = MAXPRIORITY;
|
||||
#if APIVERSNUM >= 10700
|
||||
NumProvidedSystems = 1;
|
||||
#endif
|
||||
strcpy(RemoteIp, "");
|
||||
FilterSockBufSize = 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
else if (strcmp(Name, "RemotePort") == 0) RemotePort = atoi(Value);
|
||||
else if (strcmp(Name, "Timeout") == 0) Timeout = atoi(Value);
|
||||
else if (strcmp(Name, "StreamFilters") == 0) StreamFilters = atoi(Value);
|
||||
else if (strcmp(Name, "HideMenuEntry") == 0) HideMenuEntry = atoi(Value);
|
||||
else if (strcmp(Name, "LivePriority") == 0) LivePriority = atoi(Value);
|
||||
else if (strcmp(Name, "MinPriority") == 0) MinPriority = atoi(Value);
|
||||
else if (strcmp(Name, "MaxPriority") == 0) MaxPriority = atoi(Value);
|
||||
else if (strcmp(Name, "FilterSockBufSize") == 0) FilterSockBufSize = atoi(Value);
|
||||
#if APIVERSNUM >= 10700
|
||||
else if (strcmp(Name, "NumProvidedSystems") == 0) NumProvidedSystems = atoi(Value);
|
||||
#endif
|
||||
@@ -42,16 +52,21 @@ bool cStreamdevClientSetup::SetupParse(const char *Name, const char *Value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
cStreamdevClientMenuSetupPage::cStreamdevClientMenuSetupPage(void) {
|
||||
cStreamdevClientMenuSetupPage::cStreamdevClientMenuSetupPage(cPluginStreamdevClient *Plugin) {
|
||||
m_Plugin = Plugin;
|
||||
m_NewSetup = StreamdevClientSetup;
|
||||
|
||||
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 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 cMenuEditIntItem (tr("Minimum Priority"), &m_NewSetup.MinPriority, -1, MAXPRIORITY));
|
||||
Add(new cMenuEditIntItem (tr("Maximum Priority"), &m_NewSetup.MaxPriority, -1, MAXPRIORITY));
|
||||
if(m_NewSetup.StreamFilters)
|
||||
Add(new cMenuEditIntItem (tr("Filter SockBufSize"), &m_NewSetup.FilterSockBufSize, 0, 8192000));
|
||||
Add(new cMenuEditIntItem (tr("Live TV Priority"), &m_NewSetup.LivePriority, MINPRIORITY, MAXPRIORITY));
|
||||
Add(new cMenuEditIntItem (tr("Minimum Priority"), &m_NewSetup.MinPriority, MINPRIORITY, MAXPRIORITY));
|
||||
Add(new cMenuEditIntItem (tr("Maximum Priority"), &m_NewSetup.MaxPriority, MINPRIORITY, MAXPRIORITY));
|
||||
#if APIVERSNUM >= 10715
|
||||
Add(new cMenuEditIntItem (tr("Broadcast Systems / Cost"), &m_NewSetup.NumProvidedSystems, 1, 15));
|
||||
#elif APIVERSNUM >= 10700
|
||||
@@ -64,27 +79,25 @@ cStreamdevClientMenuSetupPage::~cStreamdevClientMenuSetupPage() {
|
||||
}
|
||||
|
||||
void cStreamdevClientMenuSetupPage::Store(void) {
|
||||
if (m_NewSetup.StartClient != StreamdevClientSetup.StartClient) {
|
||||
if (m_NewSetup.StartClient)
|
||||
cStreamdevDevice::Init();
|
||||
}
|
||||
|
||||
SetupStore("StartClient", m_NewSetup.StartClient);
|
||||
if (strcmp(m_NewSetup.RemoteIp, "") == 0)
|
||||
SetupStore("RemoteIp", "-none-");
|
||||
else
|
||||
SetupStore("RemoteIp", m_NewSetup.RemoteIp);
|
||||
SetupStore("RemotePort", m_NewSetup.RemotePort);
|
||||
SetupStore("Timeout", m_NewSetup.Timeout);
|
||||
SetupStore("StreamFilters", m_NewSetup.StreamFilters);
|
||||
SetupStore("HideMenuEntry", m_NewSetup.HideMenuEntry);
|
||||
SetupStore("LivePriority", m_NewSetup.LivePriority);
|
||||
SetupStore("MinPriority", m_NewSetup.MinPriority);
|
||||
SetupStore("MaxPriority", m_NewSetup.MaxPriority);
|
||||
#if APIVERSNUM >= 10700
|
||||
SetupStore("NumProvidedSystems", m_NewSetup.NumProvidedSystems);
|
||||
#endif
|
||||
SetupStore("FilterSockBufSize", m_NewSetup.FilterSockBufSize);
|
||||
|
||||
StreamdevClientSetup = m_NewSetup;
|
||||
|
||||
cStreamdevDevice::ReInit();
|
||||
m_Plugin->Initialize();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,11 @@ struct cStreamdevClientSetup {
|
||||
int StartClient;
|
||||
char RemoteIp[20];
|
||||
int RemotePort;
|
||||
int Timeout;
|
||||
int StreamFilters;
|
||||
int FilterSockBufSize;
|
||||
int HideMenuEntry;
|
||||
int LivePriority;
|
||||
int MinPriority;
|
||||
int MaxPriority;
|
||||
#if APIVERSNUM >= 10700
|
||||
@@ -26,15 +29,18 @@ struct cStreamdevClientSetup {
|
||||
|
||||
extern cStreamdevClientSetup StreamdevClientSetup;
|
||||
|
||||
class cPluginStreamdevClient;
|
||||
|
||||
class cStreamdevClientMenuSetupPage: public cMenuSetupPage {
|
||||
private:
|
||||
cStreamdevClientSetup m_NewSetup;
|
||||
cPluginStreamdevClient *m_Plugin;
|
||||
cStreamdevClientSetup m_NewSetup;
|
||||
|
||||
protected:
|
||||
virtual void Store(void);
|
||||
|
||||
public:
|
||||
cStreamdevClientMenuSetupPage(void);
|
||||
cStreamdevClientMenuSetupPage(cPluginStreamdevClient *Plugin);
|
||||
virtual ~cStreamdevClientMenuSetupPage();
|
||||
};
|
||||
|
||||
|
||||
255
client/socket.c
255
client/socket.c
@@ -11,19 +11,24 @@
|
||||
|
||||
#define MINLOGREPEAT 10 //don't log connect failures too often (seconds)
|
||||
|
||||
#include "client/socket.h"
|
||||
#include "client/setup.h"
|
||||
#include "common.h"
|
||||
// timeout for writing to command socket
|
||||
#define WRITE_TIMEOUT_MS 200
|
||||
#define QUIT_TIMEOUT_MS 500
|
||||
|
||||
cClientSocket ClientSocket;
|
||||
#include "client/socket.h"
|
||||
#include "common.h"
|
||||
|
||||
cClientSocket::cClientSocket(void)
|
||||
{
|
||||
memset(m_DataSockets, 0, sizeof(cTBSocket*) * si_Count);
|
||||
m_ServerVersion = 0;
|
||||
m_Priority = -100;
|
||||
m_Prio = false;
|
||||
m_Abort = false;
|
||||
m_LastSignalUpdate = 0;
|
||||
m_LastSignalStrength = -1;
|
||||
m_LastSignalQuality = -1;
|
||||
m_LastDev = -1;
|
||||
Reset();
|
||||
}
|
||||
|
||||
@@ -35,53 +40,62 @@ cClientSocket::~cClientSocket()
|
||||
|
||||
void cClientSocket::Reset(void)
|
||||
{
|
||||
for (int it = 0; it < si_Count; ++it) {
|
||||
if (m_DataSockets[it] != NULL)
|
||||
DELETENULL(m_DataSockets[it]);
|
||||
}
|
||||
for (int it = 0; it < si_Count; ++it)
|
||||
DELETENULL(m_DataSockets[it]);
|
||||
m_Priority = -100;
|
||||
}
|
||||
|
||||
cTBSocket *cClientSocket::DataSocket(eSocketId Id) const {
|
||||
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";
|
||||
Dprintf("OUT: |%s|\n", Command.c_str());
|
||||
|
||||
cTimeMs starttime;
|
||||
if (!TimedWrite(pkt.c_str(), pkt.size(), TimeoutMs)) {
|
||||
esyslog("Streamdev: Lost connection to %s:%d: %s", RemoteIp().c_str(), RemotePort(),
|
||||
strerror(errno));
|
||||
errno = 0;
|
||||
if (!TimedWrite(pkt.c_str(), pkt.size(), WRITE_TIMEOUT_MS)) {
|
||||
esyslog("ERROR: streamdev-client: Failed sending command '%s' to %s:%d: %s",
|
||||
Command.c_str(), RemoteIp().c_str(), RemotePort(), strerror(errno));
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t elapsed = starttime.Elapsed();
|
||||
if (Expected != 0) { // XXX+ What if elapsed > TimeoutMs?
|
||||
TimeoutMs -= elapsed;
|
||||
return Expect(Expected, NULL, TimeoutMs);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::Expect(uint Expected, std::string *Result, uint TimeoutMs) {
|
||||
char *endptr;
|
||||
#define TIMEOUT_MS 1000
|
||||
bool cClientSocket::Receive(const std::string &Command, uint *Code, std::string *Result, uint TimeoutMs) {
|
||||
int bufcount;
|
||||
bool res;
|
||||
|
||||
errno = 0;
|
||||
|
||||
if ((bufcount = ReadUntil(m_Buffer, sizeof(m_Buffer) - 1, "\012", TimeoutMs)) == -1) {
|
||||
esyslog("Streamdev: Lost connection to %s:%d: %s", RemoteIp().c_str(), RemotePort(),
|
||||
strerror(errno));
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
do
|
||||
{
|
||||
errno = 0;
|
||||
bufcount = ReadUntil(m_Buffer, sizeof(m_Buffer) - 1, "\012", TimeoutMs < TIMEOUT_MS ? TimeoutMs : TIMEOUT_MS);
|
||||
if (bufcount == -1) {
|
||||
if (m_Abort)
|
||||
return false;
|
||||
if (errno != ETIMEDOUT || TimeoutMs <= TIMEOUT_MS) {
|
||||
esyslog("ERROR: streamdev-client: Failed reading reply to '%s' from %s:%d: %s",
|
||||
Command.c_str(), RemoteIp().c_str(), RemotePort(), strerror(errno));
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
TimeoutMs -= TIMEOUT_MS;
|
||||
}
|
||||
} while (bufcount == -1);
|
||||
if (m_Buffer[bufcount - 1] == '\015')
|
||||
--bufcount;
|
||||
m_Buffer[bufcount] = '\0';
|
||||
@@ -89,9 +103,9 @@ bool cClientSocket::Expect(uint Expected, std::string *Result, uint TimeoutMs) {
|
||||
|
||||
if (Result != NULL)
|
||||
*Result = m_Buffer;
|
||||
|
||||
res = strtoul(m_Buffer, &endptr, 10) == Expected;
|
||||
return res;
|
||||
if (Code != NULL)
|
||||
*Code = strtoul(m_Buffer, NULL, 10);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::CheckConnection(void) {
|
||||
@@ -100,25 +114,22 @@ bool cClientSocket::CheckConnection(void) {
|
||||
if (IsOpen()) {
|
||||
cTBSelect select;
|
||||
|
||||
Dprintf("connection open\n");
|
||||
|
||||
// XXX+ check if connection is still alive (is there a better way?)
|
||||
// There REALLY shouldn't be anything readable according to PROTOCOL here
|
||||
// If there is, assume it's an eof signal (subseq. read would return 0)
|
||||
select.Add(*this, false);
|
||||
int res;
|
||||
if ((res = select.Select(0)) == 0) {
|
||||
Dprintf("select said nothing happened\n");
|
||||
return true;
|
||||
}
|
||||
Dprintf("closing connection (res was %d)", res);
|
||||
Dprintf("closing connection (res was %d)\n", res);
|
||||
Close();
|
||||
}
|
||||
|
||||
if (!Connect(StreamdevClientSetup.RemoteIp, StreamdevClientSetup.RemotePort)){
|
||||
if (!Connect(StreamdevClientSetup.RemoteIp, StreamdevClientSetup.RemotePort, StreamdevClientSetup.Timeout * 1000)){
|
||||
static time_t lastTime = 0;
|
||||
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,
|
||||
StreamdevClientSetup.RemotePort, strerror(errno));
|
||||
lastTime = time(NULL);
|
||||
@@ -126,34 +137,51 @@ bool cClientSocket::CheckConnection(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Expect(220)) {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Didn't receive greeting from %s:%d",
|
||||
RemoteIp().c_str(), RemotePort());
|
||||
uint code = 0;
|
||||
std::string buffer;
|
||||
if (!Receive("<connect>", &code, &buffer)) {
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
if (code != 220) {
|
||||
esyslog("ERROR: streamdev-client: Didn't receive greeting from %s:%d: %s",
|
||||
RemoteIp().c_str(), RemotePort(), buffer.c_str());
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Command("CAPS TSPIDS", 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't negotiate capabilities on %s:%d",
|
||||
RemoteIp().c_str(), RemotePort());
|
||||
Close();
|
||||
return false;
|
||||
unsigned int major, minor;
|
||||
if (sscanf(buffer.c_str(), "%*u VTP/%u.%u", &major, &minor) == 2)
|
||||
m_ServerVersion = major * 100 + minor;
|
||||
|
||||
if (m_ServerVersion == 0) {
|
||||
if (!Command("CAPS TSPIDS", 220)) {
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *Filters = "";
|
||||
if(Command("CAPS FILTERS", 220))
|
||||
Filters = ",FILTERS";
|
||||
|
||||
const char *Prio = "";
|
||||
if(Command("CAPS PRIO", 220)) {
|
||||
Prio = ",PRIO";
|
||||
m_Prio = true;
|
||||
}
|
||||
isyslog("streamdev-client: Connected to server %s:%d using capabilities TSPIDS%s%s",
|
||||
RemoteIp().c_str(), RemotePort(), Filters, Prio);
|
||||
}
|
||||
|
||||
const char *Filters = "";
|
||||
if(Command("CAPS FILTERS", 220))
|
||||
Filters = ",FILTERS";
|
||||
|
||||
const char *Prio = "";
|
||||
if(Command("CAPS PRIO", 220)) {
|
||||
Prio = ",PRIO";
|
||||
else {
|
||||
if(!Command("VERS 1.0", 220)) {
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
m_Prio = true;
|
||||
isyslog("streamdev-client: Connected to server %s:%d using protocol version %u.%u",
|
||||
RemoteIp().c_str(), RemotePort(), major, minor);
|
||||
}
|
||||
|
||||
isyslog("Streamdev: Connected to server %s:%d using capabilities TSPIDS%s%s",
|
||||
RemoteIp().c_str(), RemotePort(), Filters, Prio);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -164,17 +192,19 @@ bool cClientSocket::ProvidesChannel(const cChannel *Channel, int Priority) {
|
||||
|
||||
std::string command = (std::string)"PROV " + (const char*)itoa(Priority) + " "
|
||||
+ (const char*)Channel->GetChannelID().ToString();
|
||||
if (!Command(command))
|
||||
if (!Send(command))
|
||||
return false;
|
||||
|
||||
uint code;
|
||||
std::string buffer;
|
||||
if (!Expect(220, &buffer)) {
|
||||
if (buffer.substr(0, 3) != "560" && errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't check if %s:%d provides channel %s",
|
||||
RemoteIp().c_str(), RemotePort(), Channel->Name());
|
||||
if (!Receive(command, &code, &buffer))
|
||||
return false;
|
||||
if (code != 220 && code != 560) {
|
||||
esyslog("streamdev-client: Unexpected reply to '%s' from %s:%d: %s",
|
||||
command.c_str(), RemoteIp().c_str(), RemotePort(), buffer.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return code == 220;
|
||||
}
|
||||
|
||||
bool cClientSocket::CreateDataConnection(eSocketId Id) {
|
||||
@@ -186,7 +216,7 @@ bool cClientSocket::CreateDataConnection(eSocketId Id) {
|
||||
DELETENULL(m_DataSockets[Id]);
|
||||
|
||||
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));
|
||||
return false;
|
||||
}
|
||||
@@ -201,13 +231,8 @@ bool cClientSocket::CreateDataConnection(eSocketId Id) {
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
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());
|
||||
if (!Command(command, 220))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The server SHOULD do the following:
|
||||
* - get PORT command
|
||||
@@ -217,7 +242,7 @@ bool cClientSocket::CreateDataConnection(eSocketId Id) {
|
||||
|
||||
m_DataSockets[Id] = new cTBSocket;
|
||||
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 ? "" : ": ",
|
||||
errno == 0 ? "" : strerror(errno));
|
||||
DELETENULL(m_DataSockets[Id]);
|
||||
@@ -228,18 +253,12 @@ bool cClientSocket::CreateDataConnection(eSocketId Id) {
|
||||
}
|
||||
|
||||
bool cClientSocket::CloseDataConnection(eSocketId Id) {
|
||||
//if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if(Id == siLive || Id == siLiveFilter)
|
||||
if (m_DataSockets[Id] != NULL) {
|
||||
std::string command = (std::string)"ABRT " + (const char*)itoa(Id);
|
||||
if (!Command(command, 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't cleanly close data connection");
|
||||
//return false;
|
||||
}
|
||||
Command(command, 220);
|
||||
DELETENULL(m_DataSockets[Id]);
|
||||
}
|
||||
return true;
|
||||
@@ -252,41 +271,41 @@ bool cClientSocket::SetChannelDevice(const cChannel *Channel) {
|
||||
|
||||
std::string command = (std::string)"TUNE "
|
||||
+ (const char*)Channel->GetChannelID().ToString();
|
||||
if (!Command(command, 220, 10000)) {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't tune %s:%d to channel %s",
|
||||
RemoteIp().c_str(), RemotePort(), Channel->Name());
|
||||
if (!Command(command, 220))
|
||||
return false;
|
||||
}
|
||||
|
||||
m_LastSignalUpdate = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::SetPriority(int Priority) {
|
||||
if (Priority == m_Priority)
|
||||
return true;
|
||||
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
std::string command = (std::string)"PRIO " + (const char*)itoa(Priority);
|
||||
if (!Command(command, 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("Streamdev: Failed to update priority on %s:%d", RemoteIp().c_str(),
|
||||
RemotePort());
|
||||
if (!Command(command, 220))
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Priority = Priority;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::GetSignal(int *SignalStrength, int *SignalQuality) {
|
||||
bool cClientSocket::GetSignal(int *SignalStrength, int *SignalQuality, int *Dev) {
|
||||
if (!CheckConnection()) return -1;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (m_LastSignalUpdate != time(NULL)) {
|
||||
uint code = 0;
|
||||
std::string buffer;
|
||||
if (!Command("SGNL") || !Expect(220, &buffer)
|
||||
|| sscanf(buffer.c_str(), "%*d %*d %d:%d", &m_LastSignalStrength, &m_LastSignalQuality) != 2) {
|
||||
std::string command("SGNL");
|
||||
if (!Send(command) || !Receive(command, &code, &buffer) || code != 220
|
||||
|| sscanf(buffer.c_str(), "%*d %d %d:%d", &m_LastDev, &m_LastSignalStrength, &m_LastSignalQuality) != 3) {
|
||||
m_LastDev = -1;
|
||||
m_LastSignalStrength = -1;
|
||||
m_LastSignalQuality = -1;
|
||||
}
|
||||
@@ -296,6 +315,8 @@ bool cClientSocket::GetSignal(int *SignalStrength, int *SignalQuality) {
|
||||
*SignalStrength = m_LastSignalStrength;
|
||||
if (SignalQuality)
|
||||
*SignalQuality = m_LastSignalQuality;
|
||||
if (Dev)
|
||||
*Dev = m_LastDev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -305,13 +326,7 @@ bool cClientSocket::SetPid(int Pid, bool On) {
|
||||
CMD_LOCK;
|
||||
|
||||
std::string command = (std::string)(On ? "ADDP " : "DELP ") + (const char*)itoa(Pid);
|
||||
if (!Command(command, 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("Streamdev: Pid %d not available from %s:%d", Pid, RemoteIp().c_str(),
|
||||
RemotePort());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return Command(command, 220);
|
||||
}
|
||||
|
||||
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)
|
||||
+ " " + (const char*)itoa(Tid) + " " + (const char*)itoa(Mask);
|
||||
if (!Command(command, 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("Streamdev: Filter %hu, %hhu, %hhu not available from %s:%d",
|
||||
Pid, Tid, Mask, RemoteIp().c_str(), RemotePort());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return Command(command, 220);
|
||||
}
|
||||
|
||||
bool cClientSocket::CloseDvr(void) {
|
||||
@@ -337,27 +346,20 @@ bool cClientSocket::CloseDvr(void) {
|
||||
|
||||
if (m_DataSockets[siLive] != NULL) {
|
||||
std::string command = (std::string)"ABRT " + (const char*)itoa(siLive);
|
||||
if (!Command(command, 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't cleanly close data connection");
|
||||
if (!Command(command, 220))
|
||||
return false;
|
||||
}
|
||||
|
||||
DELETENULL(m_DataSockets[siLive]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::Quit(void) {
|
||||
bool res;
|
||||
m_Abort = true;
|
||||
if (!IsOpen()) return false;
|
||||
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
if (!(res = Command("QUIT", 221))) {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't quit command connection to %s:%d",
|
||||
RemoteIp().c_str(), RemotePort());
|
||||
}
|
||||
CMD_LOCK;
|
||||
std::string command("QUIT");
|
||||
bool res = Send(command) && Receive(command, NULL, NULL, QUIT_TIMEOUT_MS);
|
||||
Close();
|
||||
return res;
|
||||
}
|
||||
@@ -367,10 +369,5 @@ bool cClientSocket::SuspendServer(void) {
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (!Command("SUSP", 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't suspend server");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return Command("SUSP", 220);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <tools/socket.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "client/setup.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -20,23 +21,27 @@ private:
|
||||
cTBSocket *m_DataSockets[si_Count];
|
||||
cMutex m_Mutex;
|
||||
char m_Buffer[BUFSIZ + 1]; // various uses
|
||||
unsigned int m_ServerVersion;
|
||||
bool m_Prio; // server supports command PRIO
|
||||
int m_Priority; // current device priority
|
||||
bool m_Abort; // quit command pending
|
||||
|
||||
time_t m_LastSignalUpdate;
|
||||
int m_LastSignalStrength;
|
||||
int m_LastSignalQuality;
|
||||
int m_LastDev;
|
||||
protected:
|
||||
/* Send Command, and return true if the command results in Expected.
|
||||
Returns false on failure, setting errno appropriately if it has been
|
||||
a system failure. If Expected is zero, returns immediately after
|
||||
sending the command. */
|
||||
bool Command(const std::string &Command, uint Expected = 0, uint TimeoutMs = 1500);
|
||||
Returns false on failure. */
|
||||
bool Command(const std::string &Command, uint Expected);
|
||||
|
||||
/* Fetch results from an ongoing Command called with Expected == 0. Returns
|
||||
true if the response has the code Expected, returning an internal buffer
|
||||
in the array pointer pointed to by Result. Returns false on failure,
|
||||
setting errno appropriately if it has been a system failure. */
|
||||
bool Expect(uint Expected, std::string *Result = NULL, uint TimeoutMs = 1500);
|
||||
/* Send the given command. Returns false on failure. */
|
||||
bool Send(const std::string &Command);
|
||||
|
||||
/* Fetch results from an ongoing Command. The status code and the
|
||||
buffer holding the server's response are stored in Code and Result
|
||||
if non-NULL. Returns false on failure. */
|
||||
bool Receive(const std::string &Command, uint *Code = NULL, std::string *Result = NULL, uint TimeoutMs = StreamdevClientSetup.Timeout * 1000);
|
||||
|
||||
public:
|
||||
cClientSocket(void);
|
||||
@@ -50,10 +55,12 @@ public:
|
||||
bool CloseDataConnection(eSocketId Id);
|
||||
bool SetChannelDevice(const cChannel *Channel);
|
||||
bool SupportsPrio() { return m_Prio; }
|
||||
unsigned int ServerVersion() { return m_ServerVersion; }
|
||||
int Priority() const { return m_Priority; }
|
||||
bool SetPriority(int Priority);
|
||||
bool SetPid(int Pid, bool On);
|
||||
bool SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On);
|
||||
bool GetSignal(int *SignalStrength, int *SignalQuality);
|
||||
bool GetSignal(int *SignalStrength, int *SignalQuality, int *Dev);
|
||||
bool CloseDvr(void);
|
||||
bool SuspendServer(void);
|
||||
bool Quit(void);
|
||||
@@ -61,6 +68,4 @@ public:
|
||||
cTBSocket *DataSocket(eSocketId Id) const;
|
||||
};
|
||||
|
||||
extern class cClientSocket ClientSocket;
|
||||
|
||||
#endif // VDR_STREAMDEV_CLIENT_CONNECTION_H
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
const char *cPluginStreamdevClient::DESCRIPTION = trNOOP("VTP Streaming Client");
|
||||
|
||||
cPluginStreamdevClient::cPluginStreamdevClient(void) {
|
||||
cPluginStreamdevClient::cPluginStreamdevClient(void): m_Devices() {
|
||||
}
|
||||
|
||||
cPluginStreamdevClient::~cPluginStreamdevClient() {
|
||||
@@ -26,9 +26,13 @@ const char *cPluginStreamdevClient::Description(void) {
|
||||
return tr(DESCRIPTION);
|
||||
}
|
||||
|
||||
bool cPluginStreamdevClient::Start(void) {
|
||||
I18nRegister(PLUGIN_NAME_I18N);
|
||||
cStreamdevDevice::Init();
|
||||
bool cPluginStreamdevClient::Initialize(void) {
|
||||
for (int i = 0; i < STREAMDEV_MAXDEVICES; i++) {
|
||||
if (m_Devices[i])
|
||||
m_Devices[i]->ReInit(i >= StreamdevClientSetup.StartClient);
|
||||
else if (i < StreamdevClientSetup.StartClient)
|
||||
m_Devices[i] = new cStreamdevDevice();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -37,7 +41,7 @@ const char *cPluginStreamdevClient::MainMenuEntry(void) {
|
||||
}
|
||||
|
||||
cOsdObject *cPluginStreamdevClient::MainMenuAction(void) {
|
||||
if (ClientSocket.SuspendServer())
|
||||
if (StreamdevClientSetup.StartClient && m_Devices[0]->SuspendServer())
|
||||
Skins.Message(mtInfo, tr("Server is suspended"));
|
||||
else
|
||||
Skins.Message(mtError, tr("Couldn't suspend Server!"));
|
||||
@@ -45,15 +49,24 @@ cOsdObject *cPluginStreamdevClient::MainMenuAction(void) {
|
||||
}
|
||||
|
||||
cMenuSetupPage *cPluginStreamdevClient::SetupMenu(void) {
|
||||
return new cStreamdevClientMenuSetupPage;
|
||||
return new cStreamdevClientMenuSetupPage(this);
|
||||
}
|
||||
|
||||
bool cPluginStreamdevClient::SetupParse(const char *Name, const char *Value) {
|
||||
return StreamdevClientSetup.SetupParse(Name, Value);
|
||||
}
|
||||
|
||||
bool cPluginStreamdevClient::Service(const char *Id, void *Data) {
|
||||
if (!strcmp(Id, LOOP_PREVENTION_SERVICE)) {
|
||||
cStreamdevDevice::DenyChannel((const cChannel*) Data);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cPluginStreamdevClient::MainThreadHook(void) {
|
||||
cStreamdevDevice::UpdatePriority();
|
||||
for (int i = 0; i < StreamdevClientSetup.StartClient; i++)
|
||||
m_Devices[i]->UpdatePriority();
|
||||
}
|
||||
|
||||
VDRPLUGINCREATOR(cPluginStreamdevClient); // Don't touch this!
|
||||
|
||||
@@ -9,20 +9,25 @@
|
||||
|
||||
#include <vdr/plugin.h>
|
||||
|
||||
#define STREAMDEV_MAXDEVICES 8
|
||||
class cStreamdevDevice;
|
||||
|
||||
class cPluginStreamdevClient : public cPlugin {
|
||||
private:
|
||||
static const char *DESCRIPTION;
|
||||
static const char *DESCRIPTION;
|
||||
cStreamdevDevice *m_Devices[STREAMDEV_MAXDEVICES];
|
||||
|
||||
public:
|
||||
cPluginStreamdevClient(void);
|
||||
virtual ~cPluginStreamdevClient();
|
||||
virtual const char *Version(void) { return VERSION; }
|
||||
virtual const char *Description(void);
|
||||
virtual bool Start(void);
|
||||
virtual bool Initialize(void);
|
||||
virtual const char *MainMenuEntry(void);
|
||||
virtual cOsdObject *MainMenuAction(void);
|
||||
virtual cMenuSetupPage *SetupMenu(void);
|
||||
virtual bool SetupParse(const char *Name, const char *Value);
|
||||
virtual bool Service(const char *Id, void *Data = NULL);
|
||||
virtual void MainThreadHook(void);
|
||||
};
|
||||
|
||||
|
||||
2
common.c
2
common.c
@@ -10,7 +10,7 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
const char *VERSION = "0.5.1-git";
|
||||
const char *VERSION = "0.6.1-git";
|
||||
|
||||
const char cMenuEditIpItem::IpCharacters[] = "0123456789.";
|
||||
|
||||
|
||||
28
common.h
28
common.h
@@ -17,20 +17,23 @@
|
||||
#include "tools/socket.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
# include <stdio.h>
|
||||
# define Dprintf(x...) fprintf(stderr, x)
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#define Dprintf(fmt, x...) {\
|
||||
struct timespec ts;\
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);\
|
||||
fprintf(stderr, "%ld.%.3ld [%d] "fmt,\
|
||||
ts.tv_sec, ts.tv_nsec / 1000000, cThread::ThreadId(), ##x);\
|
||||
}
|
||||
#else
|
||||
# define Dprintf(x...)
|
||||
#endif
|
||||
|
||||
#if APIVERSNUM >= 10714
|
||||
#define TRANSPONDER(c1, c2) (c1->Transponder() == c2->Transponder() && !c1->IsSourceType('V'))
|
||||
#else
|
||||
#define TRANSPONDER(c1, c2) (c1->Transponder() == c2->Transponder())
|
||||
#define Dprintf(x...)
|
||||
#endif
|
||||
|
||||
#define MAXPARSEBUFFER KILOBYTE(16)
|
||||
|
||||
/* Service ID for loop prevention */
|
||||
#define LOOP_PREVENTION_SERVICE "StreamdevLoopPrevention"
|
||||
|
||||
/* Check if a channel is a radio station. */
|
||||
#define ISRADIO(x) ((x)->Vpid()==0||(x)->Vpid()==1||(x)->Vpid()==0x1fff)
|
||||
|
||||
@@ -46,13 +49,6 @@ enum eStreamType {
|
||||
st_Count
|
||||
};
|
||||
|
||||
enum eSuspendMode {
|
||||
smOffer,
|
||||
smAlways,
|
||||
smNever,
|
||||
sm_Count
|
||||
};
|
||||
|
||||
enum eSocketId {
|
||||
siLive,
|
||||
siReplay,
|
||||
|
||||
@@ -298,7 +298,6 @@ void write_pes(int fd, pes_packet *p){
|
||||
}
|
||||
|
||||
static unsigned int find_length(int f){
|
||||
uint64_t p = 0;
|
||||
uint64_t start = 0;
|
||||
uint64_t q = 0;
|
||||
int found = 0;
|
||||
@@ -309,7 +308,7 @@ static unsigned int find_length(int f){
|
||||
start -=2;
|
||||
lseek(f,start,SEEK_SET);
|
||||
while ( neof > 0 && !found ){
|
||||
p = lseek(f,0,SEEK_CUR);
|
||||
lseek(f,0,SEEK_CUR);
|
||||
neof = save_read(f,&sync4,4);
|
||||
if (sync4[0] == 0x00 && sync4[1] == 0x00 && sync4[2] == 0x01) {
|
||||
switch ( sync4[3] ) {
|
||||
@@ -558,7 +557,7 @@ int read_pes(int f, pes_packet *p){
|
||||
|
||||
while (neof > 0 && !found) {
|
||||
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 (sync4[0] == 0x00 && sync4[1] == 0x00 && sync4[2] == 0x01) {
|
||||
p->stream_id = sync4[3];
|
||||
@@ -1334,7 +1333,7 @@ void tfilter(trans *p)
|
||||
{
|
||||
int l,c;
|
||||
int tpid;
|
||||
uint8_t flag,flags;
|
||||
uint8_t flags;
|
||||
uint8_t adapt_length = 0;
|
||||
uint8_t cpid[2];
|
||||
|
||||
@@ -1350,7 +1349,6 @@ void tfilter(trans *p)
|
||||
tpid);
|
||||
}
|
||||
|
||||
flag = cpid[0];
|
||||
flags = p->packet[3];
|
||||
|
||||
if ( flags & ADAPT_FIELD ) {
|
||||
|
||||
@@ -756,7 +756,6 @@ int write_video_pes( Remux *rem, uint8_t *buf, int *vlength)
|
||||
int pos = 0;
|
||||
int p = 0;
|
||||
uint32_t pts = 0;
|
||||
uint32_t dts = 0;
|
||||
int stuff = 0;
|
||||
int length = *vlength;
|
||||
long diff = 0;
|
||||
@@ -787,7 +786,6 @@ int write_video_pes( Remux *rem, uint8_t *buf, int *vlength)
|
||||
if (add < 0) return -1;
|
||||
pos += add;
|
||||
rem->vpts_old = rem->vpts;
|
||||
dts = rem->vdts;
|
||||
rem->vpts = rem->vpts_list[0].PTS;
|
||||
rem->vdts = rem->vpts_list[0].dts;
|
||||
if ( diff > 0) rem->SCR += diff;
|
||||
|
||||
@@ -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/streamer.h"
|
||||
#include <vdr/channels.h>
|
||||
#include <vdr/remux.h>
|
||||
#include <vdr/tools.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
@@ -25,7 +26,7 @@ protected:
|
||||
virtual void Action(void);
|
||||
|
||||
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();
|
||||
|
||||
void Put(const uchar *Data, int Count);
|
||||
@@ -34,7 +35,7 @@ public:
|
||||
} // 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_Active(false),
|
||||
m_Process(-1),
|
||||
@@ -73,17 +74,24 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
|
||||
#define ADDENV(x...) if (asprintf(&env[i++], x) < 0) i--
|
||||
|
||||
// add channel ID, name and pids to environment
|
||||
ADDENV("REMUX_CHANNEL_ID=%s", *Channel->GetChannelID().ToString());
|
||||
ADDENV("REMUX_CHANNEL_NAME=%s", Channel->Name());
|
||||
#if APIVERSNUM >= 10701
|
||||
ADDENV("REMUX_VTYPE=%d", Channel->Vtype());
|
||||
#endif
|
||||
if (Channel->Vpid())
|
||||
ADDENV("REMUX_VPID=%d", Channel->Vpid());
|
||||
if (Channel->Ppid() != Channel->Vpid())
|
||||
ADDENV("REMUX_PPID=%d", Channel->Ppid());
|
||||
if (Channel->Tpid())
|
||||
ADDENV("REMUX_TPID=%d", Channel->Tpid());
|
||||
if (Channel) {
|
||||
ADDENV("REMUX_CHANNEL_ID=%s", *Channel->GetChannelID().ToString());
|
||||
ADDENV("REMUX_CHANNEL_NAME=%s", Channel->Name());
|
||||
ADDENV("REMUX_VTYPE=%d", Channel->Vtype());
|
||||
if (Channel->Vpid())
|
||||
ADDENV("REMUX_VPID=%d", Channel->Vpid());
|
||||
if (Channel->Ppid() != Channel->Vpid())
|
||||
ADDENV("REMUX_PPID=%d", Channel->Ppid());
|
||||
if (Channel->Tpid())
|
||||
ADDENV("REMUX_TPID=%d", Channel->Tpid());
|
||||
}
|
||||
else if (PatPmt) {
|
||||
ADDENV("REMUX_VTYPE=%d", PatPmt->Vtype());
|
||||
if (PatPmt->Vpid())
|
||||
ADDENV("REMUX_VPID=%d", PatPmt->Vpid());
|
||||
if (PatPmt->Ppid() != PatPmt->Vpid())
|
||||
ADDENV("REMUX_PPID=%d", PatPmt->Ppid());
|
||||
}
|
||||
|
||||
std::string buffer;
|
||||
if (Apids && *Apids) {
|
||||
@@ -94,9 +102,16 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
|
||||
buffer.clear();
|
||||
for (const int *pid = Apids; *pid; pid++) {
|
||||
int j;
|
||||
for (j = 0; Channel->Apid(j) && Channel->Apid(j) != *pid; j++)
|
||||
;
|
||||
(buffer += Channel->Alang(j)) += (*(pid + 1) ? " " : "");
|
||||
if (Channel) {
|
||||
for (j = 0; Channel->Apid(j) && Channel->Apid(j) != *pid; j++)
|
||||
;
|
||||
(buffer += Channel->Alang(j)) += (*(pid + 1) ? " " : "");
|
||||
}
|
||||
else if (PatPmt) {
|
||||
for (j = 0; PatPmt->Apid(j) && PatPmt->Apid(j) != *pid; j++)
|
||||
;
|
||||
(buffer += PatPmt->Alang(j)) += (*(pid + 1) ? " " : "");
|
||||
}
|
||||
}
|
||||
ADDENV("REMUX_ALANG=%s", buffer.c_str());
|
||||
}
|
||||
@@ -110,14 +125,21 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
|
||||
buffer.clear();
|
||||
for (const int *pid = Dpids; *pid; pid++) {
|
||||
int j;
|
||||
for (j = 0; Channel->Dpid(j) && Channel->Dpid(j) != *pid; j++)
|
||||
;
|
||||
(buffer += Channel->Dlang(j)) += (*(pid + 1) ? " " : "");
|
||||
if (Channel) {
|
||||
for (j = 0; Channel->Dpid(j) && Channel->Dpid(j) != *pid; j++)
|
||||
;
|
||||
(buffer += Channel->Dlang(j)) += (*(pid + 1) ? " " : "");
|
||||
}
|
||||
else if (PatPmt) {
|
||||
for (j = 0; PatPmt->Dpid(j) && PatPmt->Dpid(j) != *pid; j++)
|
||||
;
|
||||
(buffer += PatPmt->Dlang(j)) += (*(pid + 1) ? " " : "");
|
||||
}
|
||||
}
|
||||
ADDENV("REMUX_DLANG=%s", buffer.c_str());
|
||||
}
|
||||
|
||||
if (Channel->Spid(0)) {
|
||||
if (Channel && Channel->Spid(0)) {
|
||||
buffer.clear();
|
||||
for (const int *pid = Channel->Spids(); *pid; pid++)
|
||||
(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) ? " " : "");
|
||||
ADDENV("REMUX_SLANG=%s", buffer.c_str());
|
||||
}
|
||||
else if (PatPmt && PatPmt->Spid(0)) {
|
||||
buffer.clear();
|
||||
for (const int *pid = PatPmt->Spids(); *pid; pid++)
|
||||
(buffer += (const char *) itoa(*pid)) += (*(pid + 1) ? " " : "");
|
||||
ADDENV("REMUX_SPID=%s", buffer.c_str());
|
||||
|
||||
buffer.clear();
|
||||
for (int j = 0; PatPmt->Spid(j); j++)
|
||||
(buffer += PatPmt->Slang(j)) += (PatPmt->Spid(j + 1) ? " " : "");
|
||||
ADDENV("REMUX_SLANG=%s", buffer.c_str());
|
||||
}
|
||||
|
||||
if (Connection) {
|
||||
// add vars for a CGI like interface
|
||||
@@ -270,7 +303,7 @@ void cTSExt::Action(void)
|
||||
dsyslog("streamdev-server: buffer full while reading from externremux");
|
||||
|
||||
if (result == -1) {
|
||||
if (errno != EINTR) {
|
||||
if (errno != EINTR && errno != EAGAIN) {
|
||||
LOG_ERROR_STR("read failed");
|
||||
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):
|
||||
m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE, TS_SIZE * 2)),
|
||||
m_Remux(new cTSExt(m_ResultBuffer, Connection, Channel, Apids, Dpids))
|
||||
m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE)),
|
||||
m_Remux(new cTSExt(m_ResultBuffer, Connection, Channel, NULL, Apids, Dpids))
|
||||
{
|
||||
m_ResultBuffer->SetTimeouts(500, 100);
|
||||
}
|
||||
cExternRemux::cExternRemux(const cServerConnection *Connection, const cPatPmtParser *PatPmt, const int *Apids, const int *Dpids):
|
||||
m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE)),
|
||||
m_Remux(new cTSExt(m_ResultBuffer, Connection, NULL, PatPmt, Apids, Dpids))
|
||||
{
|
||||
m_ResultBuffer->SetTimeouts(500, 100);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <string>
|
||||
|
||||
class cChannel;
|
||||
class cPatPmtParser;
|
||||
class cServerConnection;
|
||||
|
||||
namespace Streamdev {
|
||||
@@ -19,6 +20,7 @@ private:
|
||||
|
||||
public:
|
||||
cExternRemux(const cServerConnection *Connection, const cChannel *Channel, const int *APids, const int *Dpids);
|
||||
cExternRemux(const cServerConnection *Connection, const cPatPmtParser *PatPmt, const int *APids, const int *Dpids);
|
||||
virtual ~cExternRemux();
|
||||
|
||||
int Put(const uchar *Data, int Count);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#ifdef STREAMDEV_PS
|
||||
|
||||
#include "remux/ts2ps.h"
|
||||
#include "server/streamer.h"
|
||||
#include <vdr/channels.h>
|
||||
@@ -216,3 +218,4 @@ uchar *cTS2PSRemux::Get(int &Count)
|
||||
return resultData;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#ifdef STREAMDEV_PS
|
||||
|
||||
#ifndef VDR_STREAMDEV_TS2PSREMUX_H
|
||||
#define VDR_STREAMDEV_TS2PSREMUX_H
|
||||
|
||||
@@ -34,3 +36,5 @@ public:
|
||||
} // namespace Streamdev
|
||||
|
||||
#endif // VDR_STREAMDEV_TS2PSREMUX_H
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
#
|
||||
# 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.
|
||||
# This name will be used in the '-P...' option of VDR to load the plugin.
|
||||
# By default the main source file also carries this name.
|
||||
#
|
||||
|
||||
PLUGIN = streamdev-server
|
||||
|
||||
### The name of the shared object file:
|
||||
|
||||
SOFILE = libvdr-$(PLUGIN).so
|
||||
|
||||
### Includes and Defines (add further entries here):
|
||||
|
||||
DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
|
||||
@@ -22,13 +26,12 @@ SERVEROBJS = $(PLUGIN).o \
|
||||
componentVTP.o connectionVTP.o \
|
||||
componentHTTP.o connectionHTTP.o menuHTTP.o \
|
||||
componentIGMP.o connectionIGMP.o \
|
||||
streamer.o livestreamer.o livefilter.o recplayer.o \
|
||||
suspend.o setup.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
|
||||
all: $(SOFILE) i18n
|
||||
|
||||
### Implicit rules:
|
||||
|
||||
@@ -39,44 +42,48 @@ all: libvdr-$(PLUGIN).so i18n
|
||||
|
||||
MAKEDEP = $(CXX) -MM -MG
|
||||
DEPFILE = .dependencies
|
||||
|
||||
$(DEPFILE): Makefile
|
||||
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(SERVEROBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) > $@
|
||||
@$(MAKEDEP) $(CXXFLAGS) $(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))))))
|
||||
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
|
||||
|
||||
%.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 $@ $^
|
||||
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)
|
||||
msgmerge -U --no-wrap --no-location --backup=none -q $@ $<
|
||||
msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $<
|
||||
@touch $@
|
||||
|
||||
$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
|
||||
@mkdir -p $(dir $@)
|
||||
cp $< $@
|
||||
$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
|
||||
install -D -m644 $< $@
|
||||
|
||||
i18n: $(I18Nmsgs)
|
||||
.PHONY: i18n
|
||||
i18n: $(I18Nmo) $(I18Npot)
|
||||
|
||||
install-i18n: $(I18Nmsgs)
|
||||
|
||||
### Targets:
|
||||
|
||||
libvdr-$(PLUGIN).so: $(SERVEROBJS) $(COMMONOBJS) \
|
||||
$(SOFILE): $(SERVEROBJS) $(COMMONOBJS) \
|
||||
../tools/sockettools.a ../remux/remux.a ../libdvbmpeg/libdvbmpegtools.a
|
||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $^ -o $@
|
||||
|
||||
%.so:
|
||||
$(CXX) $(CXXFLAGS) -shared $^ -o $@
|
||||
@cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION)
|
||||
install-lib: $(SOFILE)
|
||||
install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION)
|
||||
|
||||
install: install-lib install-i18n
|
||||
|
||||
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/connectionIGMP.h"
|
||||
#include "server/server.h"
|
||||
#include "server/setup.h"
|
||||
|
||||
#ifndef IGMP_ALL_HOSTS
|
||||
@@ -37,7 +38,6 @@
|
||||
class cMulticastGroup: public cListObject
|
||||
{
|
||||
public:
|
||||
cConnectionIGMP *connection;
|
||||
in_addr_t group;
|
||||
in_addr_t reporter;
|
||||
struct timeval timeout;
|
||||
@@ -48,7 +48,6 @@ public:
|
||||
};
|
||||
|
||||
cMulticastGroup::cMulticastGroup(in_addr_t Group) :
|
||||
connection(NULL),
|
||||
group(Group),
|
||||
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
|
||||
#endif
|
||||
{
|
||||
cMulticastGroup *group = m_Groups.First();
|
||||
while (group && group->group != Group)
|
||||
@@ -118,7 +121,12 @@ bool cComponentIGMP::Initialize(void)
|
||||
{
|
||||
if (cServerComponent::Initialize() && IGMPMembership(IGMP_ALL_ROUTER))
|
||||
{
|
||||
#if APIVERSNUM >= 20300
|
||||
LOCK_CHANNELS_READ;
|
||||
for (const cChannel *channel = Channels->First(); channel; channel = Channels->Next(channel))
|
||||
#else
|
||||
for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
|
||||
#endif
|
||||
{
|
||||
if (channel->GroupSep())
|
||||
continue;
|
||||
@@ -147,7 +155,12 @@ void cComponentIGMP::Destruct(void)
|
||||
Cancel(-1);
|
||||
m_CondWait.Signal();
|
||||
Cancel(2);
|
||||
#if APIVERSNUM >= 20300
|
||||
LOCK_CHANNELS_READ;
|
||||
for (const cChannel *channel = Channels->First(); channel; channel = Channels->Next(channel))
|
||||
#else
|
||||
for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
|
||||
#endif
|
||||
{
|
||||
if (channel->GroupSep())
|
||||
continue;
|
||||
@@ -235,10 +248,7 @@ cServerConnection* cComponentIGMP::ProcessMessage(struct igmp *Igmp, in_addr_t G
|
||||
group = new cMulticastGroup(Group);
|
||||
m_Groups.Add(group);
|
||||
}
|
||||
if (!group->connection) {
|
||||
IGMPStartMulticast(group);
|
||||
conn = group->connection;
|
||||
}
|
||||
conn = IGMPStartMulticast(group);
|
||||
IGMPStartTimer(group, Sender);
|
||||
if (Igmp->igmp_type == IGMP_V1_MEMBERSHIP_REPORT)
|
||||
IGMPStartV1HostTimer(group);
|
||||
@@ -430,20 +440,49 @@ void cComponentIGMP::IGMPSendGroupQuery(cMulticastGroup* Group)
|
||||
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);
|
||||
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);
|
||||
Group->connection = (cConnectionIGMP*) NewClient();
|
||||
if (!Group->connection->SetChannel(channel, Group->group)) {
|
||||
DELETENULL(Group->connection);
|
||||
#endif
|
||||
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
||||
#if APIVERSNUM >= 20300
|
||||
const cServerConnection *s = clients.First();
|
||||
#else
|
||||
cServerConnection *s = clients.First();
|
||||
#endif
|
||||
while (s) {
|
||||
if (s->RemoteIpAddr() == Group->group)
|
||||
break;
|
||||
s = clients.Next(s);
|
||||
}
|
||||
if (!s) {
|
||||
conn = NewClient();
|
||||
if (!((cConnectionIGMP *)conn)->SetChannel(channel, Group->group)) {
|
||||
DELETENULL(conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
void cComponentIGMP::IGMPStopMulticast(cMulticastGroup* Group)
|
||||
{
|
||||
if (Group->connection)
|
||||
Group->connection->Stop();
|
||||
cThreadLock lock;
|
||||
#if APIVERSNUM >= 20300
|
||||
cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
||||
#else
|
||||
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
||||
#endif
|
||||
for (cServerConnection *s = clients.First(); s; s = clients.Next(s)) {
|
||||
if (s->RemoteIpAddr() == Group->group)
|
||||
s->Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
#include <time.h>
|
||||
#include <vdr/thread.h>
|
||||
#include "server/component.h"
|
||||
#include "../common.h"
|
||||
|
||||
class cConnectionIGMP;
|
||||
class cMulticastGroup;
|
||||
|
||||
class cComponentIGMP: public cServerComponent, public cThread {
|
||||
@@ -24,7 +24,11 @@ private:
|
||||
bool m_Querier;
|
||||
cCondWait m_CondWait;
|
||||
|
||||
#if APIVERSNUM >= 20300
|
||||
cMulticastGroup* FindGroup(in_addr_t Group);
|
||||
#else
|
||||
cMulticastGroup* FindGroup(in_addr_t Group) const;
|
||||
#endif
|
||||
|
||||
/* Add or remove local host to multicast group */
|
||||
bool IGMPMembership(in_addr_t Group, bool Add = true);
|
||||
@@ -42,7 +46,7 @@ private:
|
||||
void IGMPStartRetransmitTimer(cMulticastGroup* Group);
|
||||
void IGMPClearRetransmitTimer(cMulticastGroup* Group);
|
||||
void IGMPSendGroupQuery(cMulticastGroup* Group);
|
||||
void IGMPStartMulticast(cMulticastGroup* Group);
|
||||
cServerConnection* IGMPStartMulticast(cMulticastGroup* Group);
|
||||
void IGMPStopMulticast(cMulticastGroup* Group);
|
||||
|
||||
virtual void Action();
|
||||
|
||||
@@ -9,59 +9,10 @@
|
||||
|
||||
#include <vdr/tools.h>
|
||||
#include <vdr/thread.h>
|
||||
#include <vdr/transfer.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
|
||||
class cSwitchLive {
|
||||
private:
|
||||
cMutex mutex;
|
||||
cCondWait switched;
|
||||
cDevice *device;
|
||||
const cChannel *channel;
|
||||
public:
|
||||
cDevice* Switch(cDevice *Device, const cChannel *Channel);
|
||||
void Switch(void);
|
||||
cSwitchLive(void);
|
||||
};
|
||||
|
||||
cSwitchLive::cSwitchLive(): device(NULL), channel(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
cDevice* cSwitchLive::Switch(cDevice *Device, const cChannel *Channel)
|
||||
{
|
||||
mutex.Lock();
|
||||
device = Device;
|
||||
channel = Channel;
|
||||
mutex.Unlock();
|
||||
switched.Wait();
|
||||
return device;
|
||||
}
|
||||
|
||||
void cSwitchLive::Switch(void)
|
||||
{
|
||||
mutex.Lock();
|
||||
if (channel && device) {
|
||||
cDevice::SetAvoidDevice(device);
|
||||
if (!Channels.SwitchTo(cDevice::CurrentChannel())) {
|
||||
if (StreamdevServerSetup.SuspendMode == smAlways) {
|
||||
Channels.SwitchTo(channel->Number());
|
||||
Skins.Message(mtInfo, tr("Streaming active"));
|
||||
}
|
||||
else {
|
||||
esyslog("streamdev: Can't receive channel %d (%s) from device %d. Moving live TV to other device failed (PrimaryDevice=%d, ActualDevice=%d)", channel->Number(), channel->Name(), device->CardIndex(), cDevice::PrimaryDevice()->CardIndex(), cDevice::ActualDevice()->CardIndex());
|
||||
device = NULL;
|
||||
}
|
||||
}
|
||||
// make sure we don't come in here next time
|
||||
channel = NULL;
|
||||
switched.Signal();
|
||||
}
|
||||
mutex.Unlock();
|
||||
}
|
||||
|
||||
cServerConnection::cServerConnection(const char *Protocol, int Type):
|
||||
cTBSocket(Type),
|
||||
m_Protocol(Protocol),
|
||||
@@ -69,14 +20,14 @@ cServerConnection::cServerConnection(const char *Protocol, int Type):
|
||||
m_Pending(false),
|
||||
m_ReadBytes(0),
|
||||
m_WriteBytes(0),
|
||||
m_WriteIndex(0)
|
||||
m_WriteIndex(0),
|
||||
m_Streamer(NULL)
|
||||
{
|
||||
m_SwitchLive = new cSwitchLive();
|
||||
}
|
||||
|
||||
cServerConnection::~cServerConnection()
|
||||
{
|
||||
delete m_SwitchLive;
|
||||
delete(m_Streamer);
|
||||
}
|
||||
|
||||
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)) {
|
||||
int temp = strtol(String, NULL, 10);
|
||||
if (temp == 0)
|
||||
temp = cDevice::CurrentChannel();
|
||||
#if APIVERSNUM >= 20300
|
||||
LOCK_CHANNELS_READ;
|
||||
if (temp >= 1 && temp <= Channels->MaxNumber())
|
||||
channel = Channels->GetByNumber(temp);
|
||||
#else
|
||||
if (temp >= 1 && temp <= Channels.MaxNumber())
|
||||
channel = Channels.GetByNumber(temp);
|
||||
#endif
|
||||
} else {
|
||||
#if APIVERSNUM >= 20300
|
||||
LOCK_CHANNELS_READ;
|
||||
channel = Channels->GetByChannelID(tChannelID::FromString(string));
|
||||
#else
|
||||
channel = Channels.GetByChannelID(tChannelID::FromString(string));
|
||||
#endif
|
||||
|
||||
if (channel == NULL) {
|
||||
int i = 1;
|
||||
#if APIVERSNUM >= 20300
|
||||
while ((channel = Channels->GetByNumber(i, 1)) != NULL) {
|
||||
#else
|
||||
while ((channel = Channels.GetByNumber(i, 1)) != NULL) {
|
||||
#endif
|
||||
if (String == channel->Name())
|
||||
break;
|
||||
|
||||
@@ -237,173 +205,14 @@ bool cServerConnection::Respond(const char *Message, bool Last, ...)
|
||||
return true;
|
||||
}
|
||||
|
||||
#if APIVERSNUM >= 10700
|
||||
static int GetClippedNumProvidedSystems(int AvailableBits, cDevice *Device)
|
||||
bool cServerConnection::Close()
|
||||
{
|
||||
int MaxNumProvidedSystems = (1 << AvailableBits) - 1;
|
||||
int NumProvidedSystems = Device->NumProvidedSystems();
|
||||
if (NumProvidedSystems > MaxNumProvidedSystems) {
|
||||
esyslog("ERROR: device %d supports %d modulation systems but cDevice::GetDevice() currently only supports %d delivery systems which should be fixed", Device->CardIndex() + 1, NumProvidedSystems, MaxNumProvidedSystems);
|
||||
NumProvidedSystems = MaxNumProvidedSystems;
|
||||
}
|
||||
else if (NumProvidedSystems <= 0) {
|
||||
esyslog("ERROR: device %d reported an invalid number (%d) of supported delivery systems - assuming 1", Device->CardIndex() + 1, NumProvidedSystems);
|
||||
NumProvidedSystems = 1;
|
||||
}
|
||||
return NumProvidedSystems;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* copy of cDevice::GetDevice(...) but without side effects (not detaching receivers)
|
||||
*/
|
||||
cDevice* cServerConnection::CheckDevice(const cChannel *Channel, int Priority, bool LiveView, const cDevice *AvoidDevice)
|
||||
{
|
||||
//cDevice *AvoidDevice = avoidDevice;
|
||||
//avoidDevice = NULL;
|
||||
// Collect the current priorities of all CAM slots that can decrypt the channel:
|
||||
int NumCamSlots = CamSlots.Count();
|
||||
int SlotPriority[NumCamSlots];
|
||||
int NumUsableSlots = 0;
|
||||
if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
|
||||
for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
|
||||
SlotPriority[CamSlot->Index()] = MAXPRIORITY + 1; // assumes it can't be used
|
||||
if (CamSlot->ModuleStatus() == msReady) {
|
||||
if (CamSlot->ProvidesCa(Channel->Caids())) {
|
||||
if (!ChannelCamRelations.CamChecked(Channel->GetChannelID(), CamSlot->SlotNumber())) {
|
||||
SlotPriority[CamSlot->Index()] = CamSlot->Priority();
|
||||
NumUsableSlots++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!NumUsableSlots)
|
||||
return NULL; // no CAM is able to decrypt this channel
|
||||
}
|
||||
|
||||
cDevice *d = NULL;
|
||||
//cCamSlot *s = NULL;
|
||||
|
||||
uint32_t Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
|
||||
for (int j = 0; j < NumCamSlots || !NumUsableSlots; j++) {
|
||||
if (NumUsableSlots && SlotPriority[j] > MAXPRIORITY)
|
||||
continue; // there is no CAM available in this slot
|
||||
for (int i = 0; i < cDevice::NumDevices(); i++) {
|
||||
cDevice *device = cDevice::GetDevice(i);
|
||||
if (device == AvoidDevice)
|
||||
continue; // we've been asked to skip this device
|
||||
if (Channel->Ca() && Channel->Ca() <= CA_DVB_MAX && Channel->Ca() != device->CardIndex() + 1)
|
||||
continue; // a specific card was requested, but not this one
|
||||
if (NumUsableSlots && !CamSlots.Get(j)->Assign(device, true))
|
||||
continue; // CAM slot can't be used with this device
|
||||
bool ndr;
|
||||
if (device->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
|
||||
if (NumUsableSlots && device->CamSlot() && device->CamSlot() != CamSlots.Get(j))
|
||||
ndr = true; // using a different CAM slot requires detaching receivers
|
||||
// Put together an integer number that reflects the "impact" using
|
||||
// this device would have on the overall system. Each condition is represented
|
||||
// by one bit in the number (or several bits, if the condition is actually
|
||||
// a numeric value). The sequence in which the conditions are listed corresponds
|
||||
// to their individual severity, where the one listed first will make the most
|
||||
// difference, because it results in the most significant bit of the result.
|
||||
uint32_t imp = 0;
|
||||
imp <<= 1; imp |= LiveView ? !device->IsPrimaryDevice() || ndr : 0; // prefer the primary device for live viewing if we don't need to detach existing receivers
|
||||
imp <<= 1; imp |= !device->Receiving() && (device != cTransferControl::ReceiverDevice() || device->IsPrimaryDevice()) || ndr; // use receiving devices if we don't need to detach existing receivers, but avoid primary device in local transfer mode
|
||||
imp <<= 1; imp |= device->Receiving(); // avoid devices that are receiving
|
||||
#if APIVERSNUM >= 10700
|
||||
imp <<= 4; imp |= GetClippedNumProvidedSystems(4, device) - 1; // avoid cards which support multiple delivery systems
|
||||
#endif
|
||||
imp <<= 1; imp |= device == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device
|
||||
imp <<= 8; imp |= min(max(device->Priority() + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
|
||||
imp <<= 8; imp |= min(max((NumUsableSlots ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
|
||||
imp <<= 1; imp |= ndr; // avoid devices if we need to detach existing receivers
|
||||
#if VDRVERSNUM < 10719
|
||||
imp <<= 1; imp |= device->IsPrimaryDevice(); // avoid the primary device
|
||||
#endif
|
||||
imp <<= 1; imp |= NumUsableSlots ? 0 : device->HasCi(); // avoid cards with Common Interface for FTA channels
|
||||
#if VDRVERSNUM < 10719
|
||||
imp <<= 1; imp |= device->HasDecoder(); // avoid full featured cards
|
||||
#else
|
||||
imp <<= 1; imp |= device->AvoidRecording(); // avoid SD full featured cards
|
||||
#endif
|
||||
imp <<= 1; imp |= NumUsableSlots ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
|
||||
#if VDRVERSNUM >= 10719
|
||||
imp <<= 1; imp |= device->IsPrimaryDevice(); // avoid the primary device
|
||||
#endif
|
||||
if (imp < Impact) {
|
||||
// This device has less impact than any previous one, so we take it.
|
||||
Impact = imp;
|
||||
d = device;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!NumUsableSlots)
|
||||
break; // no CAM necessary, so just one loop over the devices
|
||||
}
|
||||
return d;
|
||||
if (IsOpen())
|
||||
isyslog("streamdev-server: closing %s connection to %s:%d", Protocol(), RemoteIp().c_str(), RemotePort());
|
||||
return cTBSocket::Close();
|
||||
}
|
||||
|
||||
bool cServerConnection::UsedByLiveTV(cDevice *device)
|
||||
cString cServerConnection::ToText(char Delimiter) const
|
||||
{
|
||||
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();
|
||||
return cString::sprintf("%s%c%s:%d", Protocol(), Delimiter, RemoteIp().c_str(), RemotePort());
|
||||
}
|
||||
|
||||
@@ -7,15 +7,15 @@
|
||||
|
||||
#include "tools/socket.h"
|
||||
#include "common.h"
|
||||
#include "server/streamer.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
typedef std::map<std::string,std::string> tStrStrMap;
|
||||
typedef std::pair<std::string,std::string> tStrStr;
|
||||
|
||||
class cChannel;
|
||||
class cDevice;
|
||||
class cSwitchLive;
|
||||
|
||||
/* Basic capabilities of a straight text-based protocol, most functions
|
||||
virtual to support more complicated protocols */
|
||||
@@ -34,19 +34,10 @@ private:
|
||||
uint m_WriteBytes;
|
||||
uint m_WriteIndex;
|
||||
|
||||
cSwitchLive *m_SwitchLive;
|
||||
cStreamdevStreamer *m_Streamer;
|
||||
|
||||
tStrStrMap m_Headers;
|
||||
|
||||
/* Check if a device would be available for transfering the given
|
||||
channel. This call has no side effects except for temporarily
|
||||
detaching this connection's receivers. */
|
||||
cDevice *CheckDevice(const cChannel *Channel, int Priority, bool LiveView, const cDevice *AvoidDevice = NULL);
|
||||
|
||||
/* Test if device is in use as the transfer mode receiver device
|
||||
or a FF card, displaying live TV from internal tuner */
|
||||
static bool UsedByLiveTV(cDevice *device);
|
||||
|
||||
protected:
|
||||
/* Will be called when a command terminated by a newline has been
|
||||
received */
|
||||
@@ -63,6 +54,12 @@ protected:
|
||||
/* Add a request header */
|
||||
void SetHeader(const char *Name, const char *Value, const char *Prefix = "") { m_Headers.insert(tStrStr(std::string(Prefix) + Name, Value)); }
|
||||
|
||||
/* Set the streamer */
|
||||
void SetStreamer(cStreamdevStreamer* Streamer) { delete m_Streamer; m_Streamer = Streamer; }
|
||||
|
||||
/* Return the streamer */
|
||||
cStreamdevStreamer *Streamer() const { return m_Streamer; }
|
||||
|
||||
static const cChannel *ChannelFromString(const char *String, int *Apid = NULL, int *Dpid = NULL);
|
||||
|
||||
public:
|
||||
@@ -103,25 +100,17 @@ public:
|
||||
/* Will make the socket close after sending all queued output data */
|
||||
void DeferClose(void) { m_DeferClose = true; }
|
||||
|
||||
/* Will retrieve an unused device for transmitting data. Receivers have
|
||||
already been attached from the device if necessary. Use the returned
|
||||
cDevice in a following call to StartTransfer */
|
||||
cDevice *GetDevice(const cChannel *Channel, int Priority);
|
||||
|
||||
/* Test if a call to GetDevice would return a usable device. */
|
||||
bool ProvidesChannel(const cChannel *Channel, int Priority);
|
||||
|
||||
/* Do things which must be done in VDR's main loop */
|
||||
void MainThreadHook();
|
||||
/* Close the socket */
|
||||
virtual bool Close(void);
|
||||
|
||||
virtual void Flushed(void) {}
|
||||
|
||||
virtual void Detach(void) = 0;
|
||||
virtual void Attach(void) = 0;
|
||||
|
||||
/* This connections protocol name */
|
||||
virtual const char* Protocol(void) const { return m_Protocol; }
|
||||
|
||||
/* Text description of stream */
|
||||
virtual cString ToText(char Delimiter = ' ') const;
|
||||
|
||||
/* std::map with additional information */
|
||||
const tStrStrMap& Headers(void) const { return m_Headers; }
|
||||
};
|
||||
|
||||
@@ -3,6 +3,15 @@
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <vdr/thread.h>
|
||||
#include <vdr/recording.h>
|
||||
|
||||
#include "server/connectionHTTP.h"
|
||||
#include "server/menuHTTP.h"
|
||||
@@ -12,10 +21,12 @@
|
||||
cConnectionHTTP::cConnectionHTTP(void):
|
||||
cServerConnection("HTTP"),
|
||||
m_Status(hsRequest),
|
||||
m_LiveStreamer(NULL),
|
||||
m_Channel(NULL),
|
||||
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");
|
||||
m_Apid[0] = m_Apid[1] = 0;
|
||||
@@ -24,7 +35,9 @@ cConnectionHTTP::cConnectionHTTP(void):
|
||||
|
||||
cConnectionHTTP::~cConnectionHTTP()
|
||||
{
|
||||
delete m_LiveStreamer;
|
||||
SetStreamer(NULL);
|
||||
delete m_RecPlayer;
|
||||
delete m_MenuList;
|
||||
}
|
||||
|
||||
bool cConnectionHTTP::CanAuthenticate(void)
|
||||
@@ -48,9 +61,24 @@ bool cConnectionHTTP::Command(char *Cmd)
|
||||
*v = 0;
|
||||
SetHeader("REQUEST_METHOD", Cmd);
|
||||
q = strchr(p, '?');
|
||||
if (q)
|
||||
if (q) {
|
||||
*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);
|
||||
m_Status = hsHeaders;
|
||||
return true;
|
||||
@@ -133,97 +161,260 @@ bool cConnectionHTTP::ProcessRequest(void)
|
||||
}
|
||||
if (!authOk) {
|
||||
isyslog("streamdev-server: HTTP authorization required");
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 401 Authorization Required")
|
||||
&& Respond("WWW-authenticate: basic Realm=\"Streamdev-Server\")")
|
||||
&& Respond("");
|
||||
return HttpResponse(401, true, NULL, "WWW-authenticate: basic Realm=\"Streamdev-Server\"");
|
||||
}
|
||||
}
|
||||
|
||||
tStrStrMap::const_iterator it;
|
||||
it = m_Params.find("apid");
|
||||
if (it != m_Params.end())
|
||||
m_Apid[0] = atoi(it->second.c_str());
|
||||
it = m_Params.find("dpid");
|
||||
if (it != m_Params.end())
|
||||
m_Dpid[0] = atoi(it->second.c_str());
|
||||
|
||||
tStrStrMap::const_iterator it_method = Headers().find(REQUEST_METHOD);
|
||||
tStrStrMap::const_iterator it_pathinfo = Headers().find(PATH_INFO);
|
||||
if (it_method == Headers().end() || it_pathinfo == Headers().end()) {
|
||||
// should never happen
|
||||
esyslog("streamdev-server connectionHTTP: Missing method or pathinfo");
|
||||
} else if (it_method->second.compare("GET") == 0 && ProcessURI(it_pathinfo->second)) {
|
||||
if (m_ChannelList)
|
||||
return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
|
||||
if (m_MenuList)
|
||||
return Respond("%s", true, m_MenuList->HttpHeader().c_str());
|
||||
else if (m_Channel != NULL) {
|
||||
cDevice *device = NULL;
|
||||
if (ProvidesChannel(m_Channel, 0))
|
||||
device = GetDevice(m_Channel, 0);
|
||||
if (device != NULL) {
|
||||
device->SwitchChannel(m_Channel, false);
|
||||
m_LiveStreamer = new cStreamdevLiveStreamer(0, this);
|
||||
if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL)) {
|
||||
m_LiveStreamer->SetDevice(device);
|
||||
if (cStreamdevLiveStreamer::ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority)) {
|
||||
cStreamdevLiveStreamer* liveStreamer = new cStreamdevLiveStreamer(this, m_Channel, StreamdevServerSetup.HTTPPriority, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
|
||||
if (liveStreamer->GetDevice()) {
|
||||
SetStreamer(liveStreamer);
|
||||
if (!SetDSCP())
|
||||
LOG_ERROR_STR("unable to set DSCP sockopt");
|
||||
if (m_StreamType == stEXT) {
|
||||
return Respond("HTTP/1.0 200 OK");
|
||||
} else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) {
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: audio/mpeg")
|
||||
&& Respond("icy-name: %s", true, m_Channel->Name())
|
||||
&& Respond("");
|
||||
return HttpResponse(200, false, "audio/mpeg", "icy-name: %s", m_Channel->Name());
|
||||
} else if (ISRADIO(m_Channel)) {
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: audio/mpeg")
|
||||
&& Respond("");
|
||||
return HttpResponse(200, false, "audio/mpeg");
|
||||
} else {
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: video/mpeg")
|
||||
&& Respond("");
|
||||
return HttpResponse(200, false, "video/mpeg");
|
||||
}
|
||||
}
|
||||
DELETENULL(m_LiveStreamer);
|
||||
SetStreamer(NULL);
|
||||
delete liveStreamer;
|
||||
}
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 409 Channel not available")
|
||||
&& Respond("");
|
||||
return HttpResponse(503, true);
|
||||
}
|
||||
else if (m_RecPlayer != NULL) {
|
||||
Dprintf("GET recording\n");
|
||||
bool isPes = m_RecPlayer->getCurrentRecording()->IsPesRecording();
|
||||
// no remuxing for old PES recordings
|
||||
if (isPes && m_StreamType != stPES)
|
||||
return HttpResponse(503, true);
|
||||
|
||||
int64_t from, to;
|
||||
bool hasRange = ParseRange(from, to);
|
||||
|
||||
cStreamdevRecStreamer* recStreamer;
|
||||
if (from == 0 && hasRange && m_ReplayFakeRange) {
|
||||
recStreamer = new cStreamdevRecStreamer(this, m_RecPlayer, m_StreamType, (int64_t) 0L, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
|
||||
from += m_ReplayPos;
|
||||
if (to >= 0)
|
||||
to += m_ReplayPos;
|
||||
}
|
||||
else
|
||||
recStreamer = new cStreamdevRecStreamer(this, m_RecPlayer, m_StreamType, m_ReplayPos, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
|
||||
SetStreamer(recStreamer);
|
||||
|
||||
if (m_StreamType == stEXT)
|
||||
return Respond("HTTP/1.0 200 OK");
|
||||
else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0]))
|
||||
return HttpResponse(200, false, "audio/mpeg");
|
||||
|
||||
const char* contentType = (isPes || m_RecPlayer->getPatPmtData()->Vpid()) ? "video/mpeg" : "audio/mpeg";
|
||||
// range not supported when remuxing
|
||||
if (m_StreamType != stTS && !isPes)
|
||||
return HttpResponse(200, false, contentType);
|
||||
|
||||
uint64_t total = recStreamer->GetLength();
|
||||
if (hasRange) {
|
||||
int64_t length = recStreamer->SetRange(from, to);
|
||||
Dprintf("range response: %lld-%lld/%lld, len %lld\n", (long long)from, (long long)to, (long long)total, (long long)length);
|
||||
if (length < 0L)
|
||||
return HttpResponse(416, true, contentType, "Accept-Ranges: bytes\r\nContent-Range: bytes */%llu", (unsigned long long) total);
|
||||
else
|
||||
return HttpResponse(206, false, contentType, "Accept-Ranges: bytes\r\nContent-Range: bytes %lld-%lld/%llu\r\nContent-Length: %lld", (long long) from, (long long) to, (unsigned long long) total, (long long) length);
|
||||
}
|
||||
else
|
||||
return HttpResponse(200, false, contentType, "Accept-Ranges: bytes");
|
||||
}
|
||||
else {
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 404 not found")
|
||||
&& Respond("");
|
||||
return HttpResponse(404, true);
|
||||
}
|
||||
} else if (it_method->second.compare("HEAD") == 0 && ProcessURI(it_pathinfo->second)) {
|
||||
DeferClose();
|
||||
if (m_ChannelList)
|
||||
return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
|
||||
if (m_MenuList) {
|
||||
DeferClose();
|
||||
return Respond("%s", true, m_MenuList->HttpHeader().c_str());
|
||||
}
|
||||
else if (m_Channel != NULL) {
|
||||
if (ProvidesChannel(m_Channel, 0)) {
|
||||
if (cStreamdevLiveStreamer::ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority)) {
|
||||
if (m_StreamType == stEXT) {
|
||||
// TODO
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("");
|
||||
cStreamdevLiveStreamer *liveStreamer = new cStreamdevLiveStreamer(this, m_Channel, IDLEPRIORITY, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
|
||||
SetStreamer(liveStreamer);
|
||||
return Respond("HTTP/1.0 200 OK");
|
||||
} else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) {
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: audio/mpeg")
|
||||
&& Respond("icy-name: %s", true, m_Channel->Name())
|
||||
&& Respond("");
|
||||
return HttpResponse(200, true, "audio/mpeg", "icy-name: %s", m_Channel->Name());
|
||||
} else if (ISRADIO(m_Channel)) {
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: audio/mpeg")
|
||||
&& Respond("");
|
||||
return HttpResponse(200, true, "audio/mpeg");
|
||||
} else {
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: video/mpeg")
|
||||
&& Respond("");
|
||||
return HttpResponse(200, true, "video/mpeg");
|
||||
}
|
||||
}
|
||||
return Respond("HTTP/1.0 409 Channel not available")
|
||||
&& Respond("");
|
||||
return HttpResponse(503, true);
|
||||
}
|
||||
else if (m_RecPlayer != NULL) {
|
||||
Dprintf("HEAD recording\n");
|
||||
bool isPes = m_RecPlayer->getCurrentRecording()->IsPesRecording();
|
||||
// no remuxing for old PES recordings
|
||||
if (isPes && m_StreamType != stPES)
|
||||
return HttpResponse(503, true);
|
||||
|
||||
int64_t from, to;
|
||||
bool hasRange = ParseRange(from, to);
|
||||
|
||||
cStreamdevRecStreamer* recStreamer;
|
||||
if (from == 0 && hasRange && m_ReplayFakeRange) {
|
||||
recStreamer = new cStreamdevRecStreamer(this, m_RecPlayer, m_StreamType, m_ReplayPos, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
|
||||
from += m_ReplayPos;
|
||||
if (to >= 0)
|
||||
to += m_ReplayPos;
|
||||
}
|
||||
else
|
||||
recStreamer = new cStreamdevRecStreamer(this, m_RecPlayer, m_StreamType, m_ReplayPos, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
|
||||
SetStreamer(recStreamer);
|
||||
|
||||
if (m_StreamType == stEXT)
|
||||
return Respond("HTTP/1.0 200 OK");
|
||||
else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0]))
|
||||
return HttpResponse(200, true, "audio/mpeg");
|
||||
|
||||
const char* contentType = (isPes || m_RecPlayer->getPatPmtData()->Vpid()) ? "video/mpeg" : "audio/mpeg";
|
||||
// range not supported when remuxing
|
||||
if (m_StreamType != stTS && !isPes)
|
||||
return HttpResponse(200, false, contentType);
|
||||
|
||||
uint64_t total = recStreamer->GetLength();
|
||||
if (hasRange) {
|
||||
int64_t length = recStreamer->SetRange(from, to);
|
||||
if (length < 0L)
|
||||
return HttpResponse(416, true, contentType, "Accept-Ranges: bytes\r\nContent-Range: bytes */%llu", (unsigned long long) total);
|
||||
else
|
||||
return HttpResponse(206, true, contentType, "Accept-Ranges: bytes\r\nContent-Range: bytes %lld-%lld/%llu\r\nContent-Length: %lld", (long long) from, (long long) to, (unsigned long long) total, (long long) length);
|
||||
}
|
||||
else
|
||||
return HttpResponse(200, true, contentType, "Accept-Ranges: bytes");
|
||||
}
|
||||
else {
|
||||
return Respond("HTTP/1.0 404 not found")
|
||||
&& Respond("");
|
||||
return HttpResponse(404, true);
|
||||
}
|
||||
}
|
||||
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 400 Bad Request")
|
||||
&& Respond("");
|
||||
return HttpResponse(400, true);
|
||||
}
|
||||
|
||||
static const char *AAA[] = {
|
||||
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
||||
};
|
||||
static const char *MMM[] = {
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||
};
|
||||
|
||||
bool cConnectionHTTP::HttpResponse(int Code, bool Last, const char* ContentType, const char* Headers, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, Headers);
|
||||
#if APIVERSNUM >= 10728
|
||||
cString headers = cString::vsprintf(Headers, ap);
|
||||
#else
|
||||
cString headers = cString::sprintf(Headers, ap);
|
||||
#endif
|
||||
va_end(ap);
|
||||
|
||||
bool rc;
|
||||
if (Last)
|
||||
DeferClose();
|
||||
switch (Code)
|
||||
{
|
||||
case 200: rc = Respond("HTTP/1.1 200 OK"); break;
|
||||
case 206: rc = Respond("HTTP/1.1 206 Partial Content"); break;
|
||||
case 400: rc = Respond("HTTP/1.1 400 Bad Request"); break;
|
||||
case 401: rc = Respond("HTTP/1.1 401 Authorization Required"); break;
|
||||
case 404: rc = Respond("HTTP/1.1 404 Not Found"); break;
|
||||
case 416: rc = Respond("HTTP/1.1 416 Requested range not satisfiable"); break;
|
||||
case 503: rc = Respond("HTTP/1.1 503 Service Unavailable"); break;
|
||||
default: rc = Respond("HTTP/1.1 500 Internal Server Error");
|
||||
}
|
||||
if (rc && ContentType)
|
||||
rc = Respond("Content-Type: %s", true, ContentType);
|
||||
|
||||
if (rc)
|
||||
rc = Respond("Connection: close")
|
||||
&& Respond("Pragma: no-cache")
|
||||
&& Respond("Cache-Control: no-cache")
|
||||
&& Respond("Server: VDR-%s / streamdev-server-%s", true, VDRVERSION, VERSION);
|
||||
|
||||
time_t t = time(NULL);
|
||||
struct tm *gmt = gmtime(&t);
|
||||
if (rc && gmt) {
|
||||
char buf[] = "Date: AAA, DD MMM YYYY HH:MM:SS GMT";
|
||||
if (snprintf(buf, sizeof(buf), "Date: %s, %.2d %s %.4d %.2d:%.2d:%.2d GMT", AAA[gmt->tm_wday], gmt->tm_mday, MMM[gmt->tm_mon], gmt->tm_year + 1900, gmt->tm_hour, gmt->tm_min, gmt->tm_sec) == sizeof(buf) - 1)
|
||||
rc = Respond(buf);
|
||||
}
|
||||
|
||||
if (rc && strlen(Headers) > 0)
|
||||
rc = Respond(headers);
|
||||
|
||||
tStrStrMap::iterator it = m_Params.begin();
|
||||
while (rc && it != m_Params.end()) {
|
||||
static const char DLNA_POSTFIX[] = ".dlna.org";
|
||||
if (it->first.rfind(DLNA_POSTFIX) + sizeof(DLNA_POSTFIX) - 1 == it->first.length())
|
||||
rc = Respond("%s: %s", true, it->first.c_str(), it->second.c_str());
|
||||
++it;
|
||||
}
|
||||
return rc && Respond("");
|
||||
}
|
||||
|
||||
bool cConnectionHTTP::ParseRange(int64_t &From, int64_t &To) const
|
||||
{
|
||||
const static std::string RANGE("HTTP_RANGE");
|
||||
From = To = 0L;
|
||||
tStrStrMap::const_iterator it = Headers().find(RANGE);
|
||||
if (it != Headers().end()) {
|
||||
size_t b = it->second.find("bytes=");
|
||||
if (b != std::string::npos) {
|
||||
char* e = NULL;
|
||||
const char* r = it->second.c_str() + b + sizeof("bytes=") - 1;
|
||||
if (strchr(r, ',') != NULL)
|
||||
esyslog("streamdev-server cConnectionHTTP::GetRange: Multi-ranges not supported");
|
||||
From = strtol(r, &e, 10);
|
||||
if (r != e) {
|
||||
if (From < 0L) {
|
||||
To = -1L;
|
||||
return *e == 0 || *e == ',';
|
||||
}
|
||||
else if (*e == '-') {
|
||||
r = e + 1;
|
||||
if (*r == 0 || *e == ',') {
|
||||
To = -1L;
|
||||
return true;
|
||||
}
|
||||
To = strtol(r, &e, 10);
|
||||
return r != e && To >= From &&
|
||||
(*e == 0 || *e == ',');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cConnectionHTTP::Flushed(void)
|
||||
@@ -231,21 +422,21 @@ void cConnectionHTTP::Flushed(void)
|
||||
if (m_Status != hsBody)
|
||||
return;
|
||||
|
||||
if (m_ChannelList) {
|
||||
if (m_ChannelList->HasNext()) {
|
||||
if (!Respond("%s", true, m_ChannelList->Next().c_str()))
|
||||
if (m_MenuList) {
|
||||
if (m_MenuList->HasNext()) {
|
||||
if (!Respond("%s", true, m_MenuList->Next().c_str()))
|
||||
DeferClose();
|
||||
}
|
||||
else {
|
||||
DELETENULL(m_ChannelList);
|
||||
DELETENULL(m_MenuList);
|
||||
m_Status = hsFinished;
|
||||
DeferClose();
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (m_Channel != NULL) {
|
||||
else if (Streamer()) {
|
||||
Dprintf("streamer start\n");
|
||||
m_LiveStreamer->Start(this);
|
||||
Streamer()->Start(this);
|
||||
m_Status = hsFinished;
|
||||
}
|
||||
else {
|
||||
@@ -255,57 +446,61 @@ 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;
|
||||
cChannelIterator *iterator = NULL;
|
||||
cItemIterator *iterator = NULL;
|
||||
|
||||
const static std::string GROUP("group");
|
||||
if (Filebase.compare("tree") == 0) {
|
||||
const cChannel* c = NULL;
|
||||
size_t groupIndex = query.find("group=");
|
||||
if (groupIndex != std::string::npos)
|
||||
c = cChannelList::GetGroup(atoi(query.c_str() + groupIndex + 6));
|
||||
iterator = new cListTree(c);
|
||||
tStrStrMap::const_iterator it = m_Params.find(GROUP);
|
||||
iterator = new cListTree(it == m_Params.end() ? NULL : it->second.c_str());
|
||||
groupTarget = Filebase + Fileext;
|
||||
} else if (Filebase.compare("groups") == 0) {
|
||||
iterator = new cListGroups();
|
||||
groupTarget = (std::string) "group" + Fileext;
|
||||
} else if (Filebase.compare("group") == 0) {
|
||||
const cChannel* c = NULL;
|
||||
size_t groupIndex = query.find("group=");
|
||||
if (groupIndex != std::string::npos)
|
||||
c = cChannelList::GetGroup(atoi(query.c_str() + groupIndex + 6));
|
||||
iterator = new cListGroup(c);
|
||||
tStrStrMap::const_iterator it = m_Params.find(GROUP);
|
||||
iterator = new cListGroup(it == m_Params.end() ? NULL : it->second.c_str());
|
||||
} else if (Filebase.compare("channels") == 0) {
|
||||
iterator = new cListChannels();
|
||||
} else if (Filebase.compare("all") == 0 ||
|
||||
(Filebase.empty() && Fileext.empty())) {
|
||||
iterator = new cListAll();
|
||||
} else if (Filebase.compare("recordings") == 0) {
|
||||
iterator = new cRecordingsIterator(m_StreamType);
|
||||
}
|
||||
|
||||
if (iterator) {
|
||||
// assemble base url: http://host/path/
|
||||
std::string base;
|
||||
const static std::string HOST("HTTP_HOST");
|
||||
tStrStrMap::const_iterator it = Headers().find(HOST);
|
||||
if (it != Headers().end())
|
||||
base = "http://" + it->second + "/";
|
||||
else
|
||||
base = (std::string) "http://" + LocalIp() + ":" +
|
||||
(const char*) itoa(StreamdevServerSetup.HTTPServerPort) + "/";
|
||||
base += Path;
|
||||
|
||||
if (Filebase.empty() || Fileext.compare(".htm") == 0 || Fileext.compare(".html") == 0) {
|
||||
std::string self = Filebase + Fileext;
|
||||
if (!query.empty())
|
||||
self += '?' + query;
|
||||
return new cHtmlChannelList(iterator, m_StreamType, self.c_str(), groupTarget.c_str());
|
||||
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) {
|
||||
std::string base;
|
||||
tStrStrMap::const_iterator it = Headers().find(HOST);
|
||||
if (it != Headers().end())
|
||||
base = "http://" + it->second + "/";
|
||||
else
|
||||
base = (std::string) "http://" + LocalIp() + ":" +
|
||||
(const char*) itoa(StreamdevServerSetup.HTTPServerPort) + "/";
|
||||
base += Path;
|
||||
return new cM3uChannelList(iterator, base.c_str());
|
||||
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 {
|
||||
delete iterator;
|
||||
}
|
||||
@@ -313,6 +508,93 @@ cChannelList* cConnectionHTTP::ChannelListFromString(const std::string& Path, co
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RecPlayer* cConnectionHTTP::RecPlayerFromString(const char *FileBase, const char *FileExt)
|
||||
{
|
||||
RecPlayer *recPlayer = NULL;
|
||||
|
||||
if (strcasecmp(FileExt, ".rec") != 0)
|
||||
return NULL;
|
||||
|
||||
char *p = NULL;
|
||||
unsigned long l = strtoul(FileBase, &p, 0);
|
||||
if (p != FileBase && l > 0L) {
|
||||
if (*p == ':') {
|
||||
// get recording by dev:inode
|
||||
ino_t inode = (ino_t) strtoull(p + 1, &p, 0);
|
||||
if (*p == 0 && inode > 0) {
|
||||
struct stat st;
|
||||
#if APIVERSNUM >= 20300
|
||||
LOCK_RECORDINGS_READ;
|
||||
for (const cRecording *rec = Recordings->First(); rec; rec = Recordings->Next(rec)) {
|
||||
#else
|
||||
cThreadLock RecordingsLock(&Recordings);
|
||||
for (cRecording *rec = Recordings.First(); rec; rec = Recordings.Next(rec)) {
|
||||
#endif
|
||||
if (stat(rec->FileName(), &st) == 0 && st.st_dev == (dev_t) l && st.st_ino == inode)
|
||||
recPlayer = new RecPlayer(rec->FileName());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (*p == 0) {
|
||||
// get recording by index
|
||||
#if APIVERSNUM >= 20300
|
||||
LOCK_RECORDINGS_READ;
|
||||
const cRecording *rec = Recordings->Get((int) l - 1);
|
||||
#else
|
||||
cThreadLock RecordingsLock(&Recordings);
|
||||
cRecording *rec = Recordings.Get((int) l - 1);
|
||||
#endif
|
||||
if (rec)
|
||||
recPlayer = new RecPlayer(rec->FileName());
|
||||
}
|
||||
|
||||
if (recPlayer) {
|
||||
const char *pos = NULL;
|
||||
tStrStrMap::const_iterator it = m_Params.begin();
|
||||
while (it != m_Params.end()) {
|
||||
if (it->first == "pos") {
|
||||
pos = it->second.c_str();
|
||||
break;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
if (pos) {
|
||||
// With prefix "full_" we try to fool players
|
||||
// by replying with a content range starting
|
||||
// at the requested position instead of 0.
|
||||
// This is a heavy violation of standards.
|
||||
// Use at your own risk!
|
||||
if (strncasecmp(pos, "full_", 5) == 0) {
|
||||
m_ReplayFakeRange = true;
|
||||
pos += 5;
|
||||
}
|
||||
if (strncasecmp(pos, "resume", 6) == 0) {
|
||||
int id = pos[6] == '.' ? atoi(pos + 7) : 0;
|
||||
m_ReplayPos = recPlayer->positionFromResume(id);
|
||||
}
|
||||
else if (strncasecmp(pos, "mark.", 5) == 0) {
|
||||
int index = atoi(pos + 5);
|
||||
m_ReplayPos = recPlayer->positionFromMark(index);
|
||||
}
|
||||
else if (strncasecmp(pos, "time.", 5) == 0) {
|
||||
int seconds = atoi(pos + 5);
|
||||
m_ReplayPos = recPlayer->positionFromTime(seconds);
|
||||
}
|
||||
else if (strncasecmp(pos, "frame.", 6) == 0) {
|
||||
int frame = atoi(pos + 6);
|
||||
m_ReplayPos = recPlayer->positionFromFrameNumber(frame);
|
||||
}
|
||||
else {
|
||||
m_ReplayPos = atol(pos);
|
||||
if (m_ReplayPos > 0L && m_ReplayPos < 100L)
|
||||
m_ReplayPos = recPlayer->positionFromPercent((int) m_ReplayPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return recPlayer;
|
||||
}
|
||||
|
||||
bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
|
||||
{
|
||||
std::string filespec, fileext;
|
||||
@@ -320,11 +602,24 @@ bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
|
||||
|
||||
if (file_pos != std::string::npos) {
|
||||
size_t ext_pos = PathInfo.rfind('.');
|
||||
// file basename with leading / stripped off
|
||||
filespec = PathInfo.substr(file_pos + 1, ext_pos - file_pos - 1);
|
||||
if (ext_pos != std::string::npos)
|
||||
if (ext_pos != std::string::npos) {
|
||||
// file extension including leading .
|
||||
fileext = PathInfo.substr(ext_pos);
|
||||
const char *ext = fileext.c_str();
|
||||
// ignore dummy file extensions
|
||||
if (strcasecmp(ext, ".ts") == 0 ||
|
||||
strcasecmp(ext, ".vdr") == 0 ||
|
||||
strcasecmp(ext, ".vob") == 0) {
|
||||
size_t ext_end = ext_pos;
|
||||
if (ext_pos > 0)
|
||||
ext_pos = PathInfo.rfind('.', ext_pos - 1);
|
||||
if (ext_pos == std::string::npos)
|
||||
ext_pos = ext_end;
|
||||
fileext = PathInfo.substr(ext_pos, ext_end - ext_pos);
|
||||
}
|
||||
}
|
||||
// file basename with leading / stripped off
|
||||
filespec = PathInfo.substr(file_pos + 1, ext_pos - file_pos - 1);
|
||||
}
|
||||
if (fileext.length() > 5) {
|
||||
//probably not an extension
|
||||
@@ -335,10 +630,12 @@ bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
|
||||
// Streamtype with leading / stripped off
|
||||
std::string type = PathInfo.substr(1, PathInfo.find_first_of("/;", 1) - 1);
|
||||
const char* pType = type.c_str();
|
||||
if (strcasecmp(pType, "PS") == 0) {
|
||||
m_StreamType = stPS;
|
||||
} else if (strcasecmp(pType, "PES") == 0) {
|
||||
if (strcasecmp(pType, "PES") == 0) {
|
||||
m_StreamType = stPES;
|
||||
#ifdef STREAMDEV_PS
|
||||
} else if (strcasecmp(pType, "PS") == 0) {
|
||||
m_StreamType = stPS;
|
||||
#endif
|
||||
} else if (strcasecmp(pType, "TS") == 0) {
|
||||
m_StreamType = stTS;
|
||||
} 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());
|
||||
|
||||
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");
|
||||
return true;
|
||||
} else if ((m_RecPlayer = RecPlayerFromString(filespec.c_str(), fileext.c_str())) != NULL) {
|
||||
Dprintf("Recording %s found\n", m_RecPlayer->getCurrentRecording()->Name());
|
||||
return true;
|
||||
} else if ((m_Channel = ChannelFromString(filespec.c_str(), &m_Apid[0], &m_Dpid[0])) != NULL) {
|
||||
Dprintf("Channel found. Apid/Dpid is %d/%d\n", m_Apid[0], m_Dpid[0]);
|
||||
return true;
|
||||
@@ -359,3 +659,8 @@ bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
|
||||
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 "server/livestreamer.h"
|
||||
#include "server/recstreamer.h"
|
||||
|
||||
#include <map>
|
||||
#include <tools/select.h>
|
||||
|
||||
class cChannel;
|
||||
class cStreamdevLiveStreamer;
|
||||
class cChannelList;
|
||||
class cMenuList;
|
||||
|
||||
class cConnectionHTTP: public cServerConnection {
|
||||
private:
|
||||
@@ -26,17 +26,32 @@ private:
|
||||
|
||||
std::string m_Authorization;
|
||||
eHTTPStatus m_Status;
|
||||
tStrStrMap m_Params;
|
||||
eStreamType m_StreamType;
|
||||
// job: transfer
|
||||
cStreamdevLiveStreamer *m_LiveStreamer;
|
||||
const cChannel *m_Channel;
|
||||
int m_Apid[2];
|
||||
int m_Dpid[2];
|
||||
eStreamType m_StreamType;
|
||||
// job: replay
|
||||
RecPlayer *m_RecPlayer;
|
||||
int64_t m_ReplayPos;
|
||||
bool m_ReplayFakeRange;
|
||||
// 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 HttpResponse(int Code, bool Last, const char* ContentType = NULL, const char* Headers = "", ...);
|
||||
//__attribute__ ((format (printf, 5, 6)));
|
||||
/**
|
||||
* Extract byte range from HTTP Range header. Returns false if no valid
|
||||
* range is found. The contents of From and To are undefined in this
|
||||
* case. From may be negative in which case To is undefined.
|
||||
* TODO: support for multiple ranges.
|
||||
*/
|
||||
bool ParseRange(int64_t &From, int64_t &To) const;
|
||||
protected:
|
||||
bool ProcessRequest(void);
|
||||
|
||||
@@ -44,8 +59,7 @@ public:
|
||||
cConnectionHTTP(void);
|
||||
virtual ~cConnectionHTTP();
|
||||
|
||||
virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); }
|
||||
virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); }
|
||||
virtual cString ToText(char Delimiter = ' ') const;
|
||||
|
||||
virtual bool CanAuthenticate(void);
|
||||
|
||||
@@ -57,7 +71,7 @@ public:
|
||||
|
||||
inline bool cConnectionHTTP::Abort(void) const
|
||||
{
|
||||
return m_LiveStreamer && m_LiveStreamer->Abort();
|
||||
return !IsOpen() || (Streamer() && Streamer()->Abort());
|
||||
}
|
||||
|
||||
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
cConnectionIGMP::cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType) :
|
||||
cServerConnection(Name, SOCK_DGRAM),
|
||||
m_LiveStreamer(NULL),
|
||||
m_ClientPort(ClientPort),
|
||||
m_StreamType(StreamType),
|
||||
m_Channel(NULL)
|
||||
@@ -20,10 +19,13 @@ cConnectionIGMP::cConnectionIGMP(const char* Name, int ClientPort, eStreamType S
|
||||
|
||||
cConnectionIGMP::~cConnectionIGMP()
|
||||
{
|
||||
delete m_LiveStreamer;
|
||||
}
|
||||
|
||||
#if APIVERSNUM >= 20300
|
||||
bool cConnectionIGMP::SetChannel(const cChannel *Channel, in_addr_t Dst)
|
||||
#else
|
||||
bool cConnectionIGMP::SetChannel(cChannel *Channel, in_addr_t Dst)
|
||||
#endif
|
||||
{
|
||||
if (Channel) {
|
||||
m_Channel = Channel;
|
||||
@@ -42,32 +44,34 @@ bool cConnectionIGMP::SetChannel(cChannel *Channel, in_addr_t Dst)
|
||||
|
||||
void cConnectionIGMP::Welcome()
|
||||
{
|
||||
cDevice *device = NULL;
|
||||
if (ProvidesChannel(m_Channel, 0))
|
||||
device = GetDevice(m_Channel, 0);
|
||||
if (device != NULL) {
|
||||
device->SwitchChannel(m_Channel, false);
|
||||
m_LiveStreamer = new cStreamdevLiveStreamer(0, this);
|
||||
if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType)) {
|
||||
m_LiveStreamer->SetDevice(device);
|
||||
if (cStreamdevLiveStreamer::ProvidesChannel(m_Channel, StreamdevServerSetup.IGMPPriority)) {
|
||||
cStreamdevLiveStreamer * liveStreamer = new cStreamdevLiveStreamer(this, m_Channel, StreamdevServerSetup.IGMPPriority, m_StreamType);
|
||||
if (liveStreamer->GetDevice()) {
|
||||
SetStreamer(liveStreamer);
|
||||
if (!SetDSCP())
|
||||
LOG_ERROR_STR("unable to set DSCP sockopt");
|
||||
Dprintf("streamer start\n");
|
||||
m_LiveStreamer->Start(this);
|
||||
liveStreamer->Start(this);
|
||||
}
|
||||
else {
|
||||
SetStreamer(NULL);
|
||||
delete liveStreamer;
|
||||
esyslog("streamdev-server IGMP: SetChannel failed");
|
||||
DELETENULL(m_LiveStreamer);
|
||||
}
|
||||
}
|
||||
else
|
||||
esyslog("streamdev-server IGMP: GetDevice failed");
|
||||
esyslog("streamdev-server IGMP: SwitchDevice failed");
|
||||
}
|
||||
|
||||
void cConnectionIGMP::Stop()
|
||||
bool cConnectionIGMP::Close()
|
||||
{
|
||||
if (m_LiveStreamer) {
|
||||
m_LiveStreamer->Stop();
|
||||
DELETENULL(m_LiveStreamer);
|
||||
}
|
||||
if (Streamer())
|
||||
Streamer()->Stop();
|
||||
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 {
|
||||
private:
|
||||
cStreamdevLiveStreamer *m_LiveStreamer;
|
||||
int m_ClientPort;
|
||||
eStreamType m_StreamType;
|
||||
#if APIVERSNUM >= 20300
|
||||
const cChannel *m_Channel;
|
||||
#else
|
||||
cChannel *m_Channel;
|
||||
#endif
|
||||
|
||||
public:
|
||||
cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType);
|
||||
virtual ~cConnectionIGMP();
|
||||
|
||||
#if APIVERSNUM >= 20300
|
||||
bool SetChannel(const cChannel *Channel, in_addr_t Dst);
|
||||
#else
|
||||
bool SetChannel(cChannel *Channel, in_addr_t Dst);
|
||||
#endif
|
||||
virtual void Welcome(void);
|
||||
void Stop();
|
||||
virtual cString ToText(char Delimiter = ' ') const;
|
||||
|
||||
/* Not used here */
|
||||
virtual bool Command(char *Cmd) { return false; }
|
||||
|
||||
virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); }
|
||||
virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); }
|
||||
virtual bool Close(void);
|
||||
|
||||
virtual bool Abort(void) const;
|
||||
};
|
||||
|
||||
inline bool cConnectionIGMP::Abort(void) const
|
||||
{
|
||||
return !m_LiveStreamer || m_LiveStreamer->Abort();
|
||||
return !IsOpen() || !Streamer() || Streamer()->Abort();
|
||||
}
|
||||
|
||||
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,6 @@
|
||||
#include "server/recplayer.h"
|
||||
|
||||
class cTBSocket;
|
||||
class cStreamdevLiveStreamer;
|
||||
class cStreamdevFilterStreamer;
|
||||
class cLSTEHandler;
|
||||
class cLSTCHandler;
|
||||
@@ -20,7 +19,6 @@ class cConnectionVTP: public cServerConnection {
|
||||
|
||||
private:
|
||||
cTBSocket *m_LiveSocket;
|
||||
cStreamdevLiveStreamer *m_LiveStreamer;
|
||||
cTBSocket *m_FilterSocket;
|
||||
cStreamdevFilterStreamer *m_FilterStreamer;
|
||||
cTBSocket *m_RecSocket;
|
||||
@@ -28,7 +26,9 @@ private:
|
||||
|
||||
char *m_LastCommand;
|
||||
eStreamType m_StreamType;
|
||||
unsigned int m_ClientVersion;
|
||||
bool m_FiltersSupport;
|
||||
bool m_LoopPrevention;
|
||||
RecPlayer *m_RecPlayer;
|
||||
|
||||
// Priority is only known in PROV command
|
||||
@@ -53,12 +53,15 @@ public:
|
||||
virtual void Welcome(void);
|
||||
virtual void Reject(void);
|
||||
|
||||
virtual cString ToText(char Delimiter = ' ') const;
|
||||
|
||||
virtual bool Abort(void) const;
|
||||
virtual void Detach(void);
|
||||
virtual void Attach(void);
|
||||
|
||||
virtual bool Command(char *Cmd);
|
||||
bool CmdCAPS(char *Opts);
|
||||
bool CmdVERS(char *Opts);
|
||||
bool CmdPROV(char *Opts);
|
||||
bool CmdPORT(char *Opts);
|
||||
bool CmdREAD(char *Opts);
|
||||
|
||||
@@ -2,18 +2,52 @@
|
||||
* $Id: livefilter.c,v 1.7 2009/02/13 13:02:40 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include <vdr/filter.h>
|
||||
|
||||
#include "server/livefilter.h"
|
||||
#include "server/streamer.h"
|
||||
#include "common.h"
|
||||
|
||||
#ifndef TS_SYNC_BYTE
|
||||
# define TS_SYNC_BYTE 0x47
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void cStreamdevLiveFilter::SetStatus(bool On)
|
||||
{
|
||||
m_On = On;
|
||||
cFilter::SetStatus(On);
|
||||
}
|
||||
|
||||
void cStreamdevLiveFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
|
||||
{
|
||||
uchar buffer[TS_SIZE];
|
||||
@@ -32,8 +66,86 @@ void cStreamdevLiveFilter::Process(u_short Pid, u_char Tid, const u_char *Data,
|
||||
length -= chunk;
|
||||
pos += chunk;
|
||||
|
||||
int p = m_Streamer->Put(buffer, TS_SIZE);
|
||||
if (p != TS_SIZE)
|
||||
m_Streamer->ReportOverflow(TS_SIZE - p);
|
||||
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)
|
||||
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
|
||||
#define VDR_STREAMEV_LIVEFILTER_H
|
||||
|
||||
#include <vdr/config.h>
|
||||
#include "server/streamer.h"
|
||||
|
||||
#include <vdr/filter.h>
|
||||
class cDevice;
|
||||
class cStreamdevLiveFilter;
|
||||
|
||||
class cStreamdevStreamer;
|
||||
|
||||
class cStreamdevLiveFilter: public cFilter {
|
||||
class cStreamdevFilterStreamer: public cStreamdevStreamer {
|
||||
private:
|
||||
cStreamdevStreamer *m_Streamer;
|
||||
cDevice *m_Device;
|
||||
cStreamdevLiveFilter *m_Filter;
|
||||
cStreamdevBuffer *m_ReceiveBuffer;
|
||||
|
||||
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:
|
||||
cStreamdevLiveFilter(cStreamdevStreamer *Streamer);
|
||||
cStreamdevFilterStreamer();
|
||||
virtual ~cStreamdevFilterStreamer();
|
||||
|
||||
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);
|
||||
}
|
||||
void SetDevice(cDevice *Device);
|
||||
bool SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On);
|
||||
|
||||
virtual bool IsReceiving(void) const;
|
||||
void Receive(uchar *Data);
|
||||
|
||||
virtual void Attach(void);
|
||||
virtual void Detach(void);
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMEV_LIVEFILTER_H
|
||||
|
||||
@@ -8,45 +8,47 @@
|
||||
#include "remux/ts2es.h"
|
||||
#include "remux/extern.h"
|
||||
|
||||
#include <vdr/ringbuffer.h>
|
||||
#include <vdr/transfer.h>
|
||||
|
||||
#include "server/livestreamer.h"
|
||||
#include "server/livefilter.h"
|
||||
#include "server/setup.h"
|
||||
#include "common.h"
|
||||
|
||||
using namespace Streamdev;
|
||||
|
||||
// device occupied timeout to prevent VDR main loop to immediately switch back
|
||||
// when streamdev switched the live TV channel.
|
||||
// Note that there is still a gap between the GetDevice() and SetOccupied()
|
||||
// calls where the VDR main loop could strike
|
||||
#define STREAMDEVTUNETIMEOUT 5
|
||||
|
||||
// --- cStreamdevLiveReceiver -------------------------------------------------
|
||||
|
||||
class cStreamdevLiveReceiver: public cReceiver {
|
||||
friend class cStreamdevStreamer;
|
||||
|
||||
private:
|
||||
cStreamdevStreamer *m_Streamer;
|
||||
cStreamdevLiveStreamer *m_Streamer;
|
||||
|
||||
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);
|
||||
#endif
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, const cChannel *Channel,
|
||||
int Priority, const int *Pids):
|
||||
#if APIVERSNUM >= 10712
|
||||
cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevLiveStreamer *Streamer, const cChannel *Channel, int Priority, const int *Pids):
|
||||
cReceiver(Channel, Priority),
|
||||
#else
|
||||
cReceiver(Channel->GetChannelID(), Priority, 0, Pids),
|
||||
#endif
|
||||
m_Streamer(Streamer)
|
||||
{
|
||||
#if APIVERSNUM >= 10712
|
||||
// clears all PIDs but channel remains set
|
||||
SetPids(NULL);
|
||||
AddPids(Pids);
|
||||
#endif
|
||||
}
|
||||
|
||||
cStreamdevLiveReceiver::~cStreamdevLiveReceiver()
|
||||
@@ -55,16 +57,12 @@ cStreamdevLiveReceiver::~cStreamdevLiveReceiver()
|
||||
Detach();
|
||||
}
|
||||
|
||||
#if APIVERSNUM >= 20300
|
||||
void cStreamdevLiveReceiver::Receive(const uchar *Data, int Length) {
|
||||
#else
|
||||
void cStreamdevLiveReceiver::Receive(uchar *Data, int Length) {
|
||||
int p = m_Streamer->Receive(Data, Length);
|
||||
if (p != Length)
|
||||
m_Streamer->ReportOverflow(Length - p);
|
||||
}
|
||||
|
||||
inline void cStreamdevLiveReceiver::Activate(bool On)
|
||||
{
|
||||
Dprintf("LiveReceiver->Activate(%d)\n", On);
|
||||
m_Streamer->Activate(On);
|
||||
#endif
|
||||
m_Streamer->Receive(Data, Length);
|
||||
}
|
||||
|
||||
// --- cStreamdevPatFilter ----------------------------------------------------
|
||||
@@ -260,7 +258,12 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
|
||||
SI::PAT::Association assoc;
|
||||
for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); ) {
|
||||
if (!assoc.isNITPid()) {
|
||||
#if APIVERSNUM >= 20300
|
||||
LOCK_CHANNELS_READ;
|
||||
const cChannel *Channel = Channels->GetByServiceID(Source(), Transponder(), assoc.getServiceId());
|
||||
#else
|
||||
const cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId());
|
||||
#endif
|
||||
if (Channel && (Channel == m_Channel)) {
|
||||
int prevPmtPid = pmtPid;
|
||||
if (0 != (pmtPid = assoc.getPid())) {
|
||||
@@ -344,29 +347,36 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
|
||||
|
||||
// --- 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),
|
||||
m_Priority(Priority),
|
||||
m_NumPids(0),
|
||||
m_StreamType(stTSPIDS),
|
||||
m_Channel(NULL),
|
||||
m_Channel(Channel),
|
||||
m_Device(NULL),
|
||||
m_Receiver(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()
|
||||
{
|
||||
Dprintf("Desctructing Live streamer\n");
|
||||
Stop();
|
||||
if(m_PatFilter) {
|
||||
Detach();
|
||||
DELETENULL(m_PatFilter);
|
||||
}
|
||||
DELETENULL(m_PatFilter);
|
||||
DELETENULL(m_Receiver);
|
||||
delete m_Remux;
|
||||
delete m_ReceiveBuffer;
|
||||
}
|
||||
|
||||
bool cStreamdevLiveStreamer::HasPid(int Pid)
|
||||
@@ -446,27 +456,45 @@ bool cStreamdevLiveStreamer::SetPids(int Pid, const int *Pids1, const int *Pids2
|
||||
void cStreamdevLiveStreamer::SetPriority(int Priority)
|
||||
{
|
||||
m_Priority = Priority;
|
||||
StartReceiver();
|
||||
#if VDRVERSNUM >= 20104
|
||||
cThreadLock ThreadLock(m_Device);
|
||||
if (m_Receiver)
|
||||
m_Receiver->SetPriority(Priority);
|
||||
else
|
||||
#endif
|
||||
StartReceiver();
|
||||
}
|
||||
|
||||
void cStreamdevLiveStreamer::GetSignal(int *DevNum, int *Strength, int *Quality) const
|
||||
{
|
||||
if (m_Device) {
|
||||
*DevNum = m_Device->DeviceNumber() + 1;
|
||||
#if APIVERSNUM >= 10719
|
||||
*Strength = m_Device->SignalStrength();
|
||||
*Quality = m_Device->SignalQuality();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
cReceiver *current = m_Receiver;
|
||||
m_Receiver = new cStreamdevLiveReceiver(this, m_Channel, m_Priority, m_Pids);
|
||||
cThreadLock ThreadLock(m_Device);
|
||||
m_Receiver = new cStreamdevLiveReceiver(this, m_Channel, m_Priority, m_Pids);
|
||||
if (IsRunning())
|
||||
Attach();
|
||||
delete current;
|
||||
@@ -475,17 +503,15 @@ void cStreamdevLiveStreamer::StartReceiver(void)
|
||||
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");
|
||||
//printf("ca pid: %d\n", Channel->Ca());
|
||||
m_Channel = Channel;
|
||||
m_StreamType = StreamType;
|
||||
|
||||
const int *Apids = Apid ? Apid : m_Channel->Apids();
|
||||
const int *Dpids = Dpid ? Dpid : m_Channel->Dpids();
|
||||
|
||||
switch (m_StreamType) {
|
||||
switch (StreamType) {
|
||||
case stES:
|
||||
{
|
||||
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];
|
||||
else if (Dpid && Dpid[0])
|
||||
pid = Dpid[0];
|
||||
m_Remux = new cTS2ESRemux(pid);
|
||||
SetRemux(new cTS2ESRemux(pid));
|
||||
return SetPids(pid);
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
#ifdef STREAMDEV_PS
|
||||
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());
|
||||
#endif
|
||||
|
||||
case stEXT:
|
||||
m_Remux = new cExternRemux(Connection(), m_Channel, Apids, Dpids);
|
||||
SetRemux(new cExternRemux(Connection(), m_Channel, Apids, Dpids));
|
||||
// fall through
|
||||
case stTS:
|
||||
// This should never happen, but ...
|
||||
@@ -524,12 +552,40 @@ bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType Str
|
||||
|
||||
case stTSPIDS:
|
||||
Dprintf("pid streaming mode\n");
|
||||
// No PIDs requested yet. Start receiver anyway to occupy device
|
||||
StartReceiver(true);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#if APIVERSNUM >= 20300
|
||||
void cStreamdevLiveStreamer::Receive(const uchar *Data, int Length)
|
||||
#else
|
||||
void cStreamdevLiveStreamer::Receive(uchar *Data, int Length)
|
||||
#endif
|
||||
{
|
||||
int p = m_ReceiveBuffer->PutTS(Data, Length);
|
||||
if (p != Length)
|
||||
m_ReceiveBuffer->ReportOverflow(Length - p);
|
||||
}
|
||||
|
||||
void cStreamdevLiveStreamer::Action(void)
|
||||
{
|
||||
if (StreamdevServerSetup.LiveBufferMs) {
|
||||
// wait for first data block
|
||||
int count = 0;
|
||||
while (Running()) {
|
||||
if (m_ReceiveBuffer->Get(count) != NULL) {
|
||||
cCondWait::SleepMs(StreamdevServerSetup.LiveBufferMs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
cStreamdevStreamer::Action();
|
||||
}
|
||||
|
||||
int cStreamdevLiveStreamer::Put(const uchar *Data, int Count)
|
||||
{
|
||||
// insert si data
|
||||
@@ -537,34 +593,12 @@ int cStreamdevLiveStreamer::Put(const uchar *Data, int Count)
|
||||
int siCount;
|
||||
uchar *siData = m_PatFilter->Get(siCount);
|
||||
if (siData) {
|
||||
if (m_Remux)
|
||||
siCount = m_Remux->Put(siData, siCount);
|
||||
else
|
||||
siCount = cStreamdevStreamer::Put(siData, siCount);
|
||||
siCount = cStreamdevStreamer::Put(siData, siCount);
|
||||
if (siCount)
|
||||
m_PatFilter->Del(siCount);
|
||||
}
|
||||
}
|
||||
if (m_Remux)
|
||||
return m_Remux->Put(Data, Count);
|
||||
else
|
||||
return cStreamdevStreamer::Put(Data, Count);
|
||||
}
|
||||
|
||||
uchar *cStreamdevLiveStreamer::Get(int &Count)
|
||||
{
|
||||
if (m_Remux)
|
||||
return m_Remux->Get(Count);
|
||||
else
|
||||
return cStreamdevStreamer::Get(Count);
|
||||
}
|
||||
|
||||
void cStreamdevLiveStreamer::Del(int Count)
|
||||
{
|
||||
if (m_Remux)
|
||||
m_Remux->Del(Count);
|
||||
else
|
||||
cStreamdevStreamer::Del(Count);
|
||||
return cStreamdevStreamer::Put(Data, Count);
|
||||
}
|
||||
|
||||
void cStreamdevLiveStreamer::Attach(void)
|
||||
@@ -572,7 +606,8 @@ void cStreamdevLiveStreamer::Attach(void)
|
||||
Dprintf("cStreamdevLiveStreamer::Attach()\n");
|
||||
if (m_Device) {
|
||||
if (m_Receiver) {
|
||||
m_Device->Detach(m_Receiver);
|
||||
if (m_Receiver->IsAttached())
|
||||
m_Device->Detach(m_Receiver);
|
||||
m_Device->AttachReceiver(m_Receiver);
|
||||
}
|
||||
if (m_PatFilter) {
|
||||
@@ -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 result;
|
||||
@@ -609,105 +739,3 @@ std::string cStreamdevLiveStreamer::Report(void)
|
||||
result += "\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
// --- cStreamdevFilterStreamer -------------------------------------------------
|
||||
|
||||
cStreamdevFilterStreamer::cStreamdevFilterStreamer():
|
||||
cStreamdevStreamer("streamdev-filterstreaming"),
|
||||
m_Device(NULL),
|
||||
m_Filter(NULL)/*,
|
||||
m_Channel(NULL)*/
|
||||
{
|
||||
}
|
||||
|
||||
cStreamdevFilterStreamer::~cStreamdevFilterStreamer()
|
||||
{
|
||||
Dprintf("Desctructing Filter streamer\n");
|
||||
Detach();
|
||||
m_Device = NULL;
|
||||
DELETENULL(m_Filter);
|
||||
Stop();
|
||||
}
|
||||
|
||||
void cStreamdevFilterStreamer::Attach(void)
|
||||
{
|
||||
Dprintf("cStreamdevFilterStreamer::Attach()\n");
|
||||
LOCK_THREAD;
|
||||
if(m_Device && m_Filter)
|
||||
m_Device->AttachFilter(m_Filter);
|
||||
}
|
||||
|
||||
void cStreamdevFilterStreamer::Detach(void)
|
||||
{
|
||||
Dprintf("cStreamdevFilterStreamer::Detach()\n");
|
||||
LOCK_THREAD;
|
||||
if(m_Device && m_Filter)
|
||||
m_Device->Detach(m_Filter);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void cStreamdevFilterStreamer::SetChannel(const cChannel *Channel)
|
||||
{
|
||||
LOCK_THREAD;
|
||||
Dprintf("cStreamdevFilterStreamer::SetChannel(%s : %s)", Channel?Channel->Name():"<null>",
|
||||
Channel ? *Channel->GetChannelID().ToString() : "");
|
||||
m_Channel = Channel;
|
||||
}
|
||||
#endif
|
||||
|
||||
void cStreamdevFilterStreamer::SetDevice(cDevice *Device)
|
||||
{
|
||||
Dprintf("cStreamdevFilterStreamer::SetDevice()\n");
|
||||
LOCK_THREAD;
|
||||
Detach();
|
||||
m_Device = Device;
|
||||
//m_Channel = NULL;
|
||||
Attach();
|
||||
}
|
||||
|
||||
bool cStreamdevFilterStreamer::SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On)
|
||||
{
|
||||
Dprintf("cStreamdevFilterStreamer::SetFilter(%u,0x%x,0x%x,%s)\n", Pid, Tid, Mask, On?"On":"Off");
|
||||
|
||||
if(!m_Device)
|
||||
return false;
|
||||
|
||||
if (On) {
|
||||
if (m_Filter == NULL) {
|
||||
m_Filter = new cStreamdevLiveFilter(this);
|
||||
Dprintf("attaching filter to device\n");
|
||||
Attach();
|
||||
}
|
||||
m_Filter->Set(Pid, Tid, Mask);
|
||||
} else if (m_Filter != NULL)
|
||||
m_Filter->Del(Pid, Tid, Mask);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void cStreamdevFilterStreamer::ChannelSwitch(const cDevice *Device, int ChannelNumber) {
|
||||
LOCK_THREAD;
|
||||
if(Device == m_Device) {
|
||||
if(ChannelNumber > 0) {
|
||||
cChannel *ch = Channels.GetByNumber(ChannelNumber);
|
||||
if(ch != NULL) {
|
||||
if(m_Filter != NULL &&
|
||||
m_Channel != NULL &&
|
||||
(! TRANSPONDER(ch, m_Channel))) {
|
||||
|
||||
isyslog("***** LiveFilterStreamer: transponder changed ! %s",
|
||||
*ch->GetChannelID().ToString());
|
||||
|
||||
uchar buffer[TS_SIZE] = {TS_SYNC_BYTE, 0xff, 0xff, 0xff, 0x7f, 0};
|
||||
strcpy((char*)(buffer + 5), ch->GetChannelID().ToString());
|
||||
int p = Put(buffer, TS_SIZE);
|
||||
if (p != TS_SIZE)
|
||||
ReportOverflow(TS_SIZE - p);
|
||||
}
|
||||
m_Channel = ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2,82 +2,88 @@
|
||||
#define VDR_STREAMDEV_LIVESTREAMER_H
|
||||
|
||||
#include <vdr/config.h>
|
||||
#include <vdr/status.h>
|
||||
#include <vdr/receiver.h>
|
||||
|
||||
#include "server/streamer.h"
|
||||
#include "server/streamdev-server.h"
|
||||
#include "common.h"
|
||||
|
||||
namespace Streamdev {
|
||||
class cTSRemux;
|
||||
}
|
||||
#define LIVEBUFSIZE (20000 * TS_SIZE)
|
||||
|
||||
class cStreamdevPatFilter;
|
||||
class cStreamdevLiveReceiver;
|
||||
|
||||
// --- cStreamdevLiveStreamer -------------------------------------------------
|
||||
|
||||
class cStreamdevLiveStreamer: public cStreamdevStreamer {
|
||||
class cStreamdevLiveStreamer: public cStreamdevStreamer, public cMainThreadHookSubscriber
|
||||
#if VDRVERSNUM >= 20104
|
||||
, public cStatus
|
||||
#endif
|
||||
{
|
||||
private:
|
||||
int m_Priority;
|
||||
int m_Pids[MAXRECEIVEPIDS + 1];
|
||||
int m_NumPids;
|
||||
eStreamType m_StreamType;
|
||||
int m_Caids[MAXCAIDS + 1];
|
||||
const cChannel *m_Channel;
|
||||
cDevice *m_Device;
|
||||
cStreamdevLiveReceiver *m_Receiver;
|
||||
cStreamdevBuffer *m_ReceiveBuffer;
|
||||
cStreamdevPatFilter *m_PatFilter;
|
||||
Streamdev::cTSRemux *m_Remux;
|
||||
bool m_SwitchLive;
|
||||
|
||||
void StartReceiver(void);
|
||||
void StartReceiver(bool Force = false);
|
||||
bool HasPid(int Pid);
|
||||
|
||||
public:
|
||||
cStreamdevLiveStreamer(int Priority, const cServerConnection *Connection);
|
||||
virtual ~cStreamdevLiveStreamer();
|
||||
/* 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);
|
||||
|
||||
void SetDevice(cDevice *Device) { m_Device = Device; }
|
||||
bool SetPid(int Pid, bool On);
|
||||
bool SetPids(int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL);
|
||||
bool SetChannel(const cChannel *Channel, eStreamType StreamType, const int* Apid = NULL, const int* Dpid = NULL);
|
||||
void SetPriority(int Priority);
|
||||
void GetSignal(int *DevNum, int *Strength, int *Quality) const;
|
||||
/* Find a suitable device and tune it to the requested channel. */
|
||||
cDevice *SwitchDevice(const cChannel *Channel, int Priority);
|
||||
|
||||
bool SetChannel(eStreamType StreamType, const int* Apid = NULL, const int* Dpid = NULL);
|
||||
|
||||
protected:
|
||||
virtual uchar* GetFromReceiver(int &Count) { return m_ReceiveBuffer->Get(Count); }
|
||||
virtual void DelFromReceiver(int Count) { m_ReceiveBuffer->Del(Count); }
|
||||
|
||||
virtual int Put(const uchar *Data, int Count);
|
||||
virtual uchar *Get(int &Count);
|
||||
virtual void Del(int Count);
|
||||
virtual void Action(void);
|
||||
|
||||
virtual void ChannelChange(const cChannel *Channel);
|
||||
|
||||
public:
|
||||
cStreamdevLiveStreamer(const cServerConnection *Connection, const cChannel *Channel, int Priority, eStreamType StreamType, const int* Apid = NULL, const int* Dpid = NULL);
|
||||
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 Detach(void);
|
||||
|
||||
cDevice *GetDevice() const { return m_Device; }
|
||||
|
||||
/* Test if a call to GetDevice would return a usable device. */
|
||||
static bool ProvidesChannel(const cChannel *Channel, int Priority);
|
||||
|
||||
/* Do things which must be done in VDR's main loop */
|
||||
void MainThreadHook();
|
||||
|
||||
// Statistical purposes:
|
||||
virtual std::string Report(void);
|
||||
};
|
||||
|
||||
|
||||
// --- cStreamdevFilterStreamer -------------------------------------------------
|
||||
|
||||
//#include <vdr/status.h>
|
||||
|
||||
class cStreamdevLiveFilter;
|
||||
|
||||
class cStreamdevFilterStreamer: public cStreamdevStreamer /*, public cStatus*/ {
|
||||
private:
|
||||
cDevice *m_Device;
|
||||
cStreamdevLiveFilter *m_Filter;
|
||||
//const cChannel *m_Channel;
|
||||
|
||||
public:
|
||||
cStreamdevFilterStreamer();
|
||||
virtual ~cStreamdevFilterStreamer();
|
||||
|
||||
void SetDevice(cDevice *Device);
|
||||
//void SetChannel(const cChannel *Channel);
|
||||
bool SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On);
|
||||
|
||||
virtual void Attach(void);
|
||||
virtual void Detach(void);
|
||||
|
||||
// cStatus message handlers
|
||||
//virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber);
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_LIVESTREAMER_H
|
||||
|
||||
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 "server/menuHTTP.h"
|
||||
|
||||
//**************************** cChannelIterator **************
|
||||
cChannelIterator::cChannelIterator(const cChannel *First): channel(First)
|
||||
{}
|
||||
|
||||
const cChannel* cChannelIterator::Next()
|
||||
//**************************** cRecordingIterator **************
|
||||
#if APIVERSNUM >= 20300
|
||||
cRecordingsIterator::cRecordingsIterator(eStreamType StreamType)
|
||||
#else
|
||||
cRecordingsIterator::cRecordingsIterator(eStreamType StreamType): RecordingsLock(&Recordings)
|
||||
#endif
|
||||
{
|
||||
const cChannel *current = channel;
|
||||
channel = NextChannel(channel);
|
||||
streamType = StreamType;
|
||||
#if APIVERSNUM >= 20300
|
||||
LOCK_RECORDINGS_READ;
|
||||
first = NextSuitable(Recordings->First());
|
||||
#else
|
||||
first = NextSuitable(Recordings.First());
|
||||
#endif
|
||||
current = NULL;
|
||||
}
|
||||
|
||||
const cRecording* cRecordingsIterator::NextSuitable(const cRecording *Recording)
|
||||
{
|
||||
while (Recording)
|
||||
{
|
||||
bool isPes = Recording->IsPesRecording();
|
||||
if (!isPes || (isPes && streamType == stPES))
|
||||
break;
|
||||
#if APIVERSNUM >= 20300
|
||||
LOCK_RECORDINGS_READ;
|
||||
Recording = Recordings->Next(Recording);
|
||||
#else
|
||||
Recording = Recordings.Next(Recording);
|
||||
#endif
|
||||
}
|
||||
return Recording;
|
||||
}
|
||||
|
||||
bool cRecordingsIterator::Next()
|
||||
{
|
||||
#if APIVERSNUM >= 20300
|
||||
LOCK_RECORDINGS_READ;
|
||||
#endif
|
||||
if (first)
|
||||
{
|
||||
current = first;
|
||||
first = NULL;
|
||||
}
|
||||
else
|
||||
#if APIVERSNUM >= 20300
|
||||
current = NextSuitable(Recordings->Next(current));
|
||||
#else
|
||||
current = NextSuitable(Recordings.Next(current));
|
||||
#endif
|
||||
return current;
|
||||
}
|
||||
|
||||
const cString cRecordingsIterator::ItemRessource() const
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(current->FileName(), &st) == 0)
|
||||
return cString::sprintf("%lu:%llu.rec", (unsigned long) st.st_dev, (unsigned long long) st.st_ino);
|
||||
return "";
|
||||
}
|
||||
|
||||
//**************************** cChannelIterator **************
|
||||
cChannelIterator::cChannelIterator(const cChannel *First)
|
||||
{
|
||||
first = First;
|
||||
current = NULL;
|
||||
}
|
||||
|
||||
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(): cChannelIterator(Channels.First())
|
||||
cListAll::cListAll(): cChannelIterator(FirstChannel())
|
||||
{}
|
||||
|
||||
const cChannel* cListAll::NextChannel(const cChannel *Channel)
|
||||
{
|
||||
#if APIVERSNUM >= 20300
|
||||
LOCK_CHANNELS_READ;
|
||||
if (Channel)
|
||||
Channel = SkipFakeGroups(Channels->Next(Channel));
|
||||
#else
|
||||
if (Channel)
|
||||
Channel = SkipFakeGroups(Channels.Next(Channel));
|
||||
#endif
|
||||
return Channel;
|
||||
}
|
||||
|
||||
//**************************** cListChannels **************
|
||||
cListChannels::cListChannels(): cChannelIterator(Channels.Get(Channels.GetNextNormal(-1)))
|
||||
cListChannels::cListChannels(): cChannelIterator(NextNormal())
|
||||
{}
|
||||
|
||||
const cChannel* cListChannels::NextChannel(const cChannel *Channel)
|
||||
{
|
||||
#if APIVERSNUM >= 20300
|
||||
LOCK_CHANNELS_READ;
|
||||
if (Channel)
|
||||
Channel = Channels->Get(Channels->GetNextNormal(Channel->Index()));
|
||||
#else
|
||||
if (Channel)
|
||||
Channel = Channels.Get(Channels.GetNextNormal(Channel->Index()));
|
||||
#endif
|
||||
return Channel;
|
||||
}
|
||||
|
||||
// ********************* cListGroups ****************
|
||||
cListGroups::cListGroups(): cChannelIterator(Channels.Get(Channels.GetNextGroup(-1)))
|
||||
cListGroups::cListGroups(): cChannelIterator(NextGroup())
|
||||
{}
|
||||
|
||||
const cChannel* cListGroups::NextChannel(const cChannel *Channel)
|
||||
{
|
||||
#if APIVERSNUM >= 20300
|
||||
LOCK_CHANNELS_READ;
|
||||
if (Channel)
|
||||
Channel = Channels->Get(Channels->GetNextGroup(Channel->Index()));
|
||||
#else
|
||||
if (Channel)
|
||||
Channel = Channels.Get(Channels.GetNextGroup(Channel->Index()));
|
||||
#endif
|
||||
return Channel;
|
||||
}
|
||||
//
|
||||
// ********************* cListGroup ****************
|
||||
cListGroup::cListGroup(const cChannel *Group): cChannelIterator(GetNextChannelInGroup(Group))
|
||||
cListGroup::cListGroup(const char *GroupId): cChannelIterator(GetNextChannelInGroup(GetGroup(GroupId)))
|
||||
{}
|
||||
|
||||
const cChannel* cListGroup::GetNextChannelInGroup(const cChannel *Channel)
|
||||
{
|
||||
#if APIVERSNUM >= 20300
|
||||
LOCK_CHANNELS_READ;
|
||||
if (Channel)
|
||||
Channel = SkipFakeGroups(Channels->Next(Channel));
|
||||
#else
|
||||
if (Channel)
|
||||
Channel = SkipFakeGroups(Channels.Next(Channel));
|
||||
#endif
|
||||
return Channel && !Channel->GroupSep() ? Channel : NULL;
|
||||
}
|
||||
|
||||
@@ -62,67 +248,65 @@ const cChannel* cListGroup::NextChannel(const cChannel *Channel)
|
||||
}
|
||||
//
|
||||
// ********************* 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));
|
||||
#endif
|
||||
}
|
||||
|
||||
const cChannel* cListTree::NextChannel(const cChannel *Channel)
|
||||
{
|
||||
if (currentGroup == selectedGroup)
|
||||
{
|
||||
#if APIVERSNUM >= 20300
|
||||
LOCK_CHANNELS_READ;
|
||||
if (Channel)
|
||||
Channel = SkipFakeGroups(Channels->Next(Channel));
|
||||
#else
|
||||
if (Channel)
|
||||
Channel = SkipFakeGroups(Channels.Next(Channel));
|
||||
#endif
|
||||
if (Channel && Channel->GroupSep())
|
||||
currentGroup = Channel;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if APIVERSNUM >= 20300
|
||||
LOCK_CHANNELS_READ;
|
||||
if (Channel)
|
||||
Channel = Channels->Get(Channels->GetNextGroup(Channel->Index()));
|
||||
#else
|
||||
if (Channel)
|
||||
Channel = Channels.Get(Channels.GetNextGroup(Channel->Index()));
|
||||
#endif
|
||||
currentGroup = Channel;
|
||||
}
|
||||
return Channel;
|
||||
}
|
||||
|
||||
// ******************** cChannelList ******************
|
||||
cChannelList::cChannelList(cChannelIterator *Iterator) : iterator(Iterator)
|
||||
// ******************** cMenuList ******************
|
||||
cMenuList::cMenuList(cItemIterator *Iterator) : iterator(Iterator)
|
||||
{}
|
||||
|
||||
cChannelList::~cChannelList()
|
||||
cMenuList::~cMenuList()
|
||||
{
|
||||
delete iterator;
|
||||
}
|
||||
|
||||
int cChannelList::GetGroupIndex(const cChannel *Group)
|
||||
{
|
||||
int index = 0;
|
||||
for (int curr = Channels.GetNextGroup(-1); curr >= 0; curr = Channels.GetNextGroup(curr))
|
||||
{
|
||||
if (Channels.Get(curr) == Group)
|
||||
return index;
|
||||
index++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
const cChannel* cChannelList::GetGroup(int Index)
|
||||
{
|
||||
int group = Channels.GetNextGroup(-1);
|
||||
while (Index-- && group >= 0)
|
||||
group = Channels.GetNextGroup(group);
|
||||
return group >= 0 ? Channels.Get(group) : NULL;
|
||||
}
|
||||
|
||||
// ******************** cHtmlChannelList ******************
|
||||
const char* cHtmlChannelList::menu =
|
||||
// ******************** cHtmlMenuList ******************
|
||||
const char* cHtmlMenuList::menu =
|
||||
"[<a href=\"/\">Home</a> (<a href=\"all.html\" tvid=\"RED\">no script</a>)] "
|
||||
"[<a href=\"tree.html\" tvid=\"GREEN\">Tree View</a>] "
|
||||
"[<a href=\"groups.html\" tvid=\"YELLOW\">Groups</a> (<a href=\"groups.m3u\">Playlist</a>)] "
|
||||
"[<a href=\"channels.html\" tvid=\"BLUE\">Channels</a> (<a href=\"channels.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.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"
|
||||
"<!--\n"
|
||||
"a:link, a:visited, a:hover, a:active, a:focus { color:#333399; }\n"
|
||||
@@ -138,7 +322,7 @@ const char* cHtmlChannelList::css =
|
||||
"-->\n"
|
||||
"</style>";
|
||||
|
||||
const char* cHtmlChannelList::js =
|
||||
const char* cHtmlMenuList::js =
|
||||
"<script language=\"JavaScript\">\n"
|
||||
"<!--\n"
|
||||
|
||||
@@ -199,13 +383,15 @@ const char* cHtmlChannelList::js =
|
||||
"</script>";
|
||||
|
||||
|
||||
std::string cHtmlChannelList::StreamTypeMenu()
|
||||
std::string cHtmlMenuList::StreamTypeMenu()
|
||||
{
|
||||
std::string typeMenu;
|
||||
typeMenu += (streamType == stTS ? (std::string) "[TS] " :
|
||||
(std::string) "[<a href=\"/TS/" + self + "\">TS</a>] ");
|
||||
#ifdef STREAMDEV_PS
|
||||
typeMenu += (streamType == stPS ? (std::string) "[PS] " :
|
||||
(std::string) "[<a href=\"/PS/" + self + "\">PS</a>] ");
|
||||
#endif
|
||||
typeMenu += (streamType == stPES ? (std::string) "[PES] " :
|
||||
(std::string) "[<a href=\"/PES/" + self + "\">PES</a>] ");
|
||||
typeMenu += (streamType == stES ? (std::string) "[ES] " :
|
||||
@@ -215,27 +401,29 @@ std::string cHtmlChannelList::StreamTypeMenu()
|
||||
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;
|
||||
self = strdup(Self);
|
||||
rss = strdup(Rss);
|
||||
groupTarget = (GroupTarget && *GroupTarget) ? strdup(GroupTarget) : NULL;
|
||||
htmlState = hsRoot;
|
||||
current = NULL;
|
||||
onItem = true;
|
||||
}
|
||||
|
||||
cHtmlChannelList::~cHtmlChannelList()
|
||||
cHtmlMenuList::~cHtmlMenuList()
|
||||
{
|
||||
free((void *) self);
|
||||
free((void *) rss);
|
||||
free((void *) groupTarget);
|
||||
}
|
||||
|
||||
bool cHtmlChannelList::HasNext()
|
||||
bool cHtmlMenuList::HasNext()
|
||||
{
|
||||
return htmlState != hsPageBottom;
|
||||
}
|
||||
|
||||
std::string cHtmlChannelList::Next()
|
||||
std::string cHtmlMenuList::Next()
|
||||
{
|
||||
switch (htmlState)
|
||||
{
|
||||
@@ -252,39 +440,39 @@ std::string cHtmlChannelList::Next()
|
||||
htmlState = hsPageTop;
|
||||
break;
|
||||
case hsPageTop:
|
||||
current = NextChannel();
|
||||
htmlState = current ? (current->GroupSep() ? hsGroupTop : hsPlainTop) : hsPageBottom;
|
||||
onItem = NextItem();
|
||||
htmlState = onItem ? (IsGroup() ? hsGroupTop : hsPlainTop) : hsPageBottom;
|
||||
break;
|
||||
case hsPlainTop:
|
||||
htmlState = hsPlainItem;
|
||||
break;
|
||||
case hsPlainItem:
|
||||
current = NextChannel();
|
||||
htmlState = current && !current->GroupSep() ? hsPlainItem : hsPlainBottom;
|
||||
onItem = NextItem();
|
||||
htmlState = onItem && !IsGroup() ? hsPlainItem : hsPlainBottom;
|
||||
break;
|
||||
case hsPlainBottom:
|
||||
htmlState = current ? hsGroupTop : hsPageBottom;
|
||||
htmlState = onItem ? hsGroupTop : hsPageBottom;
|
||||
break;
|
||||
case hsGroupTop:
|
||||
current = NextChannel();
|
||||
htmlState = current && !current->GroupSep() ? hsItemsTop : hsGroupBottom;
|
||||
onItem = NextItem();
|
||||
htmlState = onItem && !IsGroup() ? hsItemsTop : hsGroupBottom;
|
||||
break;
|
||||
case hsItemsTop:
|
||||
htmlState = hsItem;
|
||||
break;
|
||||
case hsItem:
|
||||
current = NextChannel();
|
||||
htmlState = current && !current->GroupSep() ? hsItem : hsItemsBottom;
|
||||
onItem = NextItem();
|
||||
htmlState = onItem && !IsGroup() ? hsItem : hsItemsBottom;
|
||||
break;
|
||||
case hsItemsBottom:
|
||||
htmlState = hsGroupBottom;
|
||||
break;
|
||||
case hsGroupBottom:
|
||||
htmlState = current ? hsGroupTop : hsPageBottom;
|
||||
htmlState = onItem ? hsGroupTop : hsPageBottom;
|
||||
break;
|
||||
case hsPageBottom:
|
||||
default:
|
||||
esyslog("streamdev-server cHtmlChannelList: invalid call to Next()");
|
||||
esyslog("streamdev-server cHtmlMenuList: invalid call to Next()");
|
||||
break;
|
||||
}
|
||||
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>";
|
||||
}
|
||||
|
||||
std::string cHtmlChannelList::PageBottom()
|
||||
std::string cHtmlMenuList::PageBottom()
|
||||
{
|
||||
return (std::string) "";
|
||||
}
|
||||
|
||||
std::string cHtmlChannelList::GroupTitle()
|
||||
std::string cHtmlMenuList::GroupTitle()
|
||||
{
|
||||
if (groupTarget)
|
||||
{
|
||||
return (std::string) "<a href=\"" + groupTarget + "?group=" +
|
||||
(const char*) itoa(cChannelList::GetGroupIndex(current)) +
|
||||
"\">" + current->Name() + "</a>";
|
||||
return (std::string) "<a href=\"" + groupTarget + "?group=" + (const char*) ItemId() + "\">" +
|
||||
ItemTitle() + "</a>";
|
||||
}
|
||||
else
|
||||
{
|
||||
return (std::string) current->Name();
|
||||
return (std::string) ItemTitle();
|
||||
}
|
||||
}
|
||||
|
||||
std::string cHtmlChannelList::ItemText()
|
||||
std::string cHtmlMenuList::ItemText()
|
||||
{
|
||||
std::string line;
|
||||
std::string suffix;
|
||||
|
||||
switch (streamType) {
|
||||
case stTS: suffix = (std::string) ".ts"; break;
|
||||
#ifdef STREAMDEV_PS
|
||||
case stPS: suffix = (std::string) ".vob"; break;
|
||||
#endif
|
||||
// for Network Media Tank
|
||||
case stPES: suffix = (std::string) ".vdr"; break;
|
||||
default: suffix = "";
|
||||
}
|
||||
line += (std::string) "<li value=\"" + (const char*) itoa(current->Number()) + "\">";
|
||||
line += (std::string) "<a href=\"" + (std::string) current->GetChannelID().ToString() + suffix + "\"";
|
||||
line += (std::string) "<li value=\"" + (const char*) ItemId() + "\">";
|
||||
line += (std::string) "<a href=\"" + (const char*) ItemRessource() + suffix + "\"";
|
||||
|
||||
// for Network Media Tank
|
||||
line += (std::string) " vod ";
|
||||
if (current->Number() < 1000)
|
||||
line += (std::string) " tvid=\"" + (const char*) itoa(current->Number()) + "\"";
|
||||
if (strlen(ItemId()) < 4)
|
||||
line += (std::string) " tvid=\"" + (const char*) ItemId() + "\"";
|
||||
|
||||
line += (std::string) ">" + current->Name() + "</a>";
|
||||
line += (std::string) ">" + ItemTitle() + "</a>";
|
||||
|
||||
int count = 0;
|
||||
for (int i = 0; current->Apid(i) != 0; ++i, ++count)
|
||||
;
|
||||
for (int i = 0; current->Dpid(i) != 0; ++i, ++count)
|
||||
;
|
||||
|
||||
if (count > 1)
|
||||
// TS always streams all PIDs
|
||||
if (streamType != stTS)
|
||||
{
|
||||
int index = 1;
|
||||
for (int i = 0; current->Apid(i) != 0; ++i, ++index) {
|
||||
line += (std::string) " <a href=\"" + (std::string) current->GetChannelID().ToString() +
|
||||
"+" + (const char*)itoa(index) + suffix + "\" class=\"apid\" vod>" + current->Alang(i) + "</a>";
|
||||
}
|
||||
for (int i = 0; current->Dpid(i) != 0; ++i, ++index) {
|
||||
line += (std::string) " <a href=\"" + (std::string) current->GetChannelID().ToString() +
|
||||
"+" + (const char*)itoa(index) + suffix + "\" class=\"dpid\" vod>" + current->Dlang(i) + "</a>";
|
||||
}
|
||||
const char* lang;
|
||||
std::string pids;
|
||||
for (int i = 0; (lang = Alang(i)) != NULL; ++i, ++index) {
|
||||
pids += (std::string) " <a href=\"" + (const char*) ItemRessource() +
|
||||
"+" + (const char*)itoa(index) + suffix + "\" class=\"apid\" vod>" + (const char*) lang + "</a>";
|
||||
}
|
||||
for (int i = 0; (lang = Dlang(i)) != NULL; ++i, ++index) {
|
||||
pids += (std::string) " <a href=\"" + (const char*) ItemRessource() +
|
||||
"+" + (const char*)itoa(index) + suffix + "\" class=\"dpid\" vod>" + (const char*) lang + "</a>";
|
||||
}
|
||||
// always show audio PIDs for stES to select audio only
|
||||
if (index > 2 || streamType == stES)
|
||||
line += pids;
|
||||
}
|
||||
line += "</li>";
|
||||
return line;
|
||||
}
|
||||
|
||||
// ******************** cM3uChannelList ******************
|
||||
cM3uChannelList::cM3uChannelList(cChannelIterator *Iterator, const char* Base)
|
||||
: cChannelList(Iterator),
|
||||
// ******************** cM3uMenuList ******************
|
||||
cM3uMenuList::cM3uMenuList(cItemIterator *Iterator, const char* Base)
|
||||
: cMenuList(Iterator),
|
||||
m_IConv(cCharSetConv::SystemCharacterTable(), "UTF-8")
|
||||
{
|
||||
base = strdup(Base);
|
||||
m3uState = msFirst;
|
||||
}
|
||||
|
||||
cM3uChannelList::~cM3uChannelList()
|
||||
cM3uMenuList::~cM3uMenuList()
|
||||
{
|
||||
free(base);
|
||||
}
|
||||
|
||||
bool cM3uChannelList::HasNext()
|
||||
bool cM3uMenuList::HasNext()
|
||||
{
|
||||
return m3uState != msLast;
|
||||
}
|
||||
|
||||
std::string cM3uChannelList::Next()
|
||||
std::string cM3uMenuList::Next()
|
||||
{
|
||||
if (m3uState == msFirst)
|
||||
{
|
||||
@@ -409,26 +598,83 @@ std::string cM3uChannelList::Next()
|
||||
return "#EXTM3U";
|
||||
}
|
||||
|
||||
const cChannel *channel = NextChannel();
|
||||
if (!channel)
|
||||
if (!NextItem())
|
||||
{
|
||||
m3uState = msLast;
|
||||
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" +
|
||||
base + "group.m3u?group=" +
|
||||
(const char*) itoa(cChannelList::GetGroupIndex(channel));
|
||||
base + "group.m3u?group=" + (const char*) ItemId();
|
||||
}
|
||||
else
|
||||
{
|
||||
return (std::string) "#EXTINF:-1," +
|
||||
(const char*) itoa(channel->Number()) + " " + name + "\r\n" +
|
||||
base + (std::string) channel->GetChannelID().ToString();
|
||||
(const char*) ItemId() + " " + name + "\r\n" +
|
||||
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 "../common.h"
|
||||
#include <vdr/recording.h>
|
||||
|
||||
class cChannel;
|
||||
|
||||
// ******************** cChannelIterator ******************
|
||||
class cChannelIterator
|
||||
// ******************** cItemIterator ******************
|
||||
class cItemIterator
|
||||
{
|
||||
public:
|
||||
virtual bool Next() = 0;
|
||||
virtual bool IsGroup() const = 0;
|
||||
virtual const cString ItemId() const = 0;
|
||||
virtual const char* ItemTitle() const = 0;
|
||||
virtual const cString ItemRessource() const = 0;
|
||||
virtual const char* Alang(int i) const = 0;
|
||||
virtual const char* Dlang(int i) const = 0;
|
||||
virtual ~cItemIterator() {};
|
||||
};
|
||||
|
||||
class cRecordingsIterator: public cItemIterator
|
||||
{
|
||||
private:
|
||||
const cChannel *channel;
|
||||
eStreamType streamType;
|
||||
const cRecording *first;
|
||||
const cRecording *current;
|
||||
cThreadLock RecordingsLock;
|
||||
protected:
|
||||
virtual const cRecording* NextSuitable(const cRecording *Recording);
|
||||
public:
|
||||
virtual bool Next();
|
||||
virtual bool IsGroup() const { return false; }
|
||||
virtual const cString ItemId() const { return current ? itoa(current->Index() + 1) : "0"; }
|
||||
virtual const char* ItemTitle() const { return current ? current->Title() : ""; }
|
||||
virtual const cString ItemRessource() const;
|
||||
virtual const char* Alang(int i) const { return NULL; }
|
||||
virtual const char* Dlang(int i) const { return NULL; }
|
||||
cRecordingsIterator(eStreamType StreamType);
|
||||
virtual ~cRecordingsIterator() {};
|
||||
};
|
||||
|
||||
class cChannelIterator: public cItemIterator
|
||||
{
|
||||
private:
|
||||
const cChannel *first;
|
||||
const cChannel *current;
|
||||
protected:
|
||||
virtual const cChannel* FirstChannel();
|
||||
virtual const cChannel* NextNormal();
|
||||
virtual const cChannel* NextGroup();
|
||||
virtual const cChannel* NextChannel(const cChannel *Channel) = 0;
|
||||
static inline const cChannel* SkipFakeGroups(const cChannel *Channel);
|
||||
// Helper which returns the group by its index
|
||||
static const cChannel* GetGroup(const char* GroupId);
|
||||
public:
|
||||
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);
|
||||
virtual ~cChannelIterator() {};
|
||||
};
|
||||
@@ -54,7 +101,7 @@ class cListGroup: public cChannelIterator
|
||||
protected:
|
||||
virtual const cChannel* NextChannel(const cChannel *Channel);
|
||||
public:
|
||||
cListGroup(const cChannel *Group);
|
||||
cListGroup(const char *GroupId);
|
||||
virtual ~cListGroup() {};
|
||||
};
|
||||
|
||||
@@ -66,31 +113,32 @@ class cListTree: public cChannelIterator
|
||||
protected:
|
||||
virtual const cChannel* NextChannel(const cChannel *Channel);
|
||||
public:
|
||||
cListTree(const cChannel *SelectedGroup);
|
||||
cListTree(const char *SelectedGroupId);
|
||||
virtual ~cListTree() {};
|
||||
};
|
||||
|
||||
// ******************** cChannelList ******************
|
||||
class cChannelList
|
||||
// ******************** cMenuList ******************
|
||||
class cMenuList
|
||||
{
|
||||
private:
|
||||
cChannelIterator *iterator;
|
||||
cItemIterator *iterator;
|
||||
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:
|
||||
// Helper which returns the group index
|
||||
static int GetGroupIndex(const cChannel* Group);
|
||||
// Helper which returns the group by its index
|
||||
static const cChannel* GetGroup(int Index);
|
||||
|
||||
virtual std::string HttpHeader() { return "HTTP/1.0 200 OK\r\n"; };
|
||||
virtual bool HasNext() = 0;
|
||||
virtual std::string Next() = 0;
|
||||
cChannelList(cChannelIterator *Iterator);
|
||||
virtual ~cChannelList();
|
||||
cMenuList(cItemIterator *Iterator);
|
||||
virtual ~cMenuList();
|
||||
};
|
||||
|
||||
class cHtmlChannelList: public cChannelList
|
||||
class cHtmlMenuList: public cMenuList
|
||||
{
|
||||
private:
|
||||
static const char* menu;
|
||||
@@ -104,9 +152,10 @@ class cHtmlChannelList: public cChannelList
|
||||
hsItemsTop, hsItem, hsItemsBottom
|
||||
};
|
||||
eHtmlState htmlState;
|
||||
const cChannel *current;
|
||||
bool onItem;
|
||||
eStreamType streamType;
|
||||
const char* self;
|
||||
const char* rss;
|
||||
const char* groupTarget;
|
||||
|
||||
std::string StreamTypeMenu();
|
||||
@@ -117,18 +166,18 @@ class cHtmlChannelList: public cChannelList
|
||||
std::string PageBottom();
|
||||
public:
|
||||
virtual std::string HttpHeader() {
|
||||
return cChannelList::HttpHeader()
|
||||
return cMenuList::HttpHeader()
|
||||
+ "Content-type: text/html; charset="
|
||||
+ (cCharSetConv::SystemCharacterTable() ? cCharSetConv::SystemCharacterTable() : "UTF-8")
|
||||
+ "\r\n";
|
||||
}
|
||||
virtual bool HasNext();
|
||||
virtual std::string Next();
|
||||
cHtmlChannelList(cChannelIterator *Iterator, eStreamType StreamType, const char *Self, const char *GroupTarget);
|
||||
virtual ~cHtmlChannelList();
|
||||
cHtmlMenuList(cItemIterator *Iterator, eStreamType StreamType, const char *Self, const char *Rss, const char *GroupTarget);
|
||||
virtual ~cHtmlMenuList();
|
||||
};
|
||||
|
||||
class cM3uChannelList: public cChannelList
|
||||
class cM3uMenuList: public cMenuList
|
||||
{
|
||||
private:
|
||||
char *base;
|
||||
@@ -136,17 +185,41 @@ class cM3uChannelList: public cChannelList
|
||||
eM3uState m3uState;
|
||||
cCharSetConv m_IConv;
|
||||
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 std::string Next();
|
||||
cM3uChannelList(cChannelIterator *Iterator, const char* Base);
|
||||
virtual ~cM3uChannelList();
|
||||
cM3uMenuList(cItemIterator *Iterator, const char* Base);
|
||||
virtual ~cM3uMenuList();
|
||||
};
|
||||
|
||||
class cRssMenuList: public cMenuList
|
||||
{
|
||||
private:
|
||||
char *base;
|
||||
char *html;
|
||||
enum eRssState { msFirst, msContinue, msLast };
|
||||
eRssState rssState;
|
||||
cCharSetConv m_IConv;
|
||||
public:
|
||||
virtual std::string HttpHeader() { return cMenuList::HttpHeader() + "Content-type: application/rss+xml\r\n"; };
|
||||
virtual bool HasNext();
|
||||
virtual std::string Next();
|
||||
|
||||
cRssMenuList(cItemIterator *Iterator, const char *Base, const char *Html);
|
||||
virtual ~cRssMenuList();
|
||||
};
|
||||
|
||||
inline const cChannel* cChannelIterator::SkipFakeGroups(const cChannel* Group)
|
||||
{
|
||||
#if APIVERSNUM >= 20300
|
||||
LOCK_CHANNELS_READ;
|
||||
#endif
|
||||
while (Group && Group->GroupSep() && !*Group->Name())
|
||||
#if APIVERSNUM >= 20300
|
||||
Group = Channels->Next(Group);
|
||||
#else
|
||||
Group = Channels.Next(Group);
|
||||
#endif
|
||||
return Group;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,45 +5,43 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:06+0200\n"
|
||||
"Project-Id-Version: streamdev\n"
|
||||
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
|
||||
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
||||
"Last-Translator: Frank Schmirler <vdrdev@schmirler.de>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"Language-Team: German <vdr@linuxtv.org>\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "VDR Streaming Server"
|
||||
|
||||
msgid "Streaming active"
|
||||
msgstr "Streamen im Gange"
|
||||
|
||||
msgid "Suspend Live TV"
|
||||
msgstr "Live-TV pausieren"
|
||||
msgid "Streamdev Connections"
|
||||
msgstr "Streamdev Verbindungen"
|
||||
|
||||
msgid "Offer suspend mode"
|
||||
msgstr "Pausieren anbieten"
|
||||
msgid "Disconnect"
|
||||
msgstr "Trennen"
|
||||
|
||||
msgid "Always suspended"
|
||||
msgstr "Immer pausiert"
|
||||
|
||||
msgid "Never suspended"
|
||||
msgstr "Nie pausiert"
|
||||
msgid "Suspend"
|
||||
msgstr "Pausieren"
|
||||
|
||||
msgid "Common Settings"
|
||||
msgstr "Allgemeines"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Hauptmenüeintrag verstecken"
|
||||
|
||||
msgid "Start with Live TV suspended"
|
||||
msgstr "Live-TV beim Start pausieren"
|
||||
|
||||
msgid "Maximum Number of Clients"
|
||||
msgstr "Maximalanzahl an Clients"
|
||||
|
||||
msgid "Suspend behaviour"
|
||||
msgstr "Pausierverhalten"
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Client darf pausieren"
|
||||
msgid "Live TV buffer delay (ms)"
|
||||
msgstr "Live-TV Pufferdauer (ms)"
|
||||
|
||||
msgid "VDR-to-VDR Server"
|
||||
msgstr "VDR-zu-VDR Server"
|
||||
@@ -57,6 +55,15 @@ msgstr "Port des VDR-zu-VDR Servers"
|
||||
msgid "Bind to IP"
|
||||
msgstr "Binde an IP"
|
||||
|
||||
msgid "Legacy Client Priority"
|
||||
msgstr "Priorität für alte Clients"
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Client darf pausieren"
|
||||
|
||||
msgid "Loop Prevention"
|
||||
msgstr "Schleifen verhindern"
|
||||
|
||||
msgid "HTTP Server"
|
||||
msgstr "HTTP Server"
|
||||
|
||||
@@ -66,6 +73,9 @@ msgstr "HTTP Server starten"
|
||||
msgid "HTTP Server Port"
|
||||
msgstr "Port des HTTP Servers"
|
||||
|
||||
msgid "Priority"
|
||||
msgstr "Priorität"
|
||||
|
||||
msgid "HTTP Streamtype"
|
||||
msgstr "HTTP Streamtyp"
|
||||
|
||||
@@ -81,3 +91,5 @@ msgstr "Port des Multicast Clients"
|
||||
msgid "Multicast Streamtype"
|
||||
msgstr "Multicast Streamtyp"
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "VDR Streaming Server"
|
||||
|
||||
@@ -6,44 +6,42 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:06+0200\n"
|
||||
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
|
||||
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
|
||||
"Last-Translator: Javier Bradineras <jbradi@hotmail.com>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"Language-Team: Spanish <vdr@linuxtv.org>\n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "Servidor de transmisiones del VDR"
|
||||
|
||||
msgid "Streaming active"
|
||||
msgstr "Trasmisión activa"
|
||||
|
||||
msgid "Suspend Live TV"
|
||||
msgstr "Suspender TV en vivo"
|
||||
msgid "Streamdev Connections"
|
||||
msgstr ""
|
||||
|
||||
msgid "Offer suspend mode"
|
||||
msgstr "Ofrecer modo de suspensión"
|
||||
msgid "Disconnect"
|
||||
msgstr ""
|
||||
|
||||
msgid "Always suspended"
|
||||
msgstr "Siempre suspendido"
|
||||
|
||||
msgid "Never suspended"
|
||||
msgstr "Nunca suspendido"
|
||||
msgid "Suspend"
|
||||
msgstr "Suspender"
|
||||
|
||||
msgid "Common Settings"
|
||||
msgstr "Configuración común"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr ""
|
||||
|
||||
msgid "Start with Live TV suspended"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum Number of Clients"
|
||||
msgstr "Numero máximo de clientes"
|
||||
|
||||
msgid "Suspend behaviour"
|
||||
msgstr "Comportamiento de la suspensión"
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Permitir suspender al cliente"
|
||||
msgid "Live TV buffer delay (ms)"
|
||||
msgstr ""
|
||||
|
||||
msgid "VDR-to-VDR Server"
|
||||
msgstr "Servidor VDR-a-VDR"
|
||||
@@ -57,6 +55,15 @@ msgstr "Puerto del Servidor VDR-a-VDR"
|
||||
msgid "Bind to IP"
|
||||
msgstr "IP asociada"
|
||||
|
||||
msgid "Legacy Client Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Permitir suspender al cliente"
|
||||
|
||||
msgid "Loop Prevention"
|
||||
msgstr ""
|
||||
|
||||
msgid "HTTP Server"
|
||||
msgstr "Servidor HTTP"
|
||||
|
||||
@@ -66,6 +73,9 @@ msgstr "Iniciar Servidor HTTP"
|
||||
msgid "HTTP Server Port"
|
||||
msgstr "Puerto del Servidor HTTP"
|
||||
|
||||
msgid "Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "HTTP Streamtype"
|
||||
msgstr "Tipo de flujo HTTP"
|
||||
|
||||
@@ -81,4 +91,5 @@ msgstr "Puerto del Cliente Multicast"
|
||||
msgid "Multicast Streamtype"
|
||||
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.
|
||||
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
||||
# This file is distributed under the same license as the VDR streamdev package.
|
||||
# Rolf Ahrenberg <rahrenbe@cc.hut.fi>, 2008
|
||||
# Rolf Ahrenberg, 2008-
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:06+0200\n"
|
||||
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
|
||||
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
||||
"Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"Last-Translator: Rolf Ahrenberg\n"
|
||||
"Language-Team: Finnish <vdr@linuxtv.org>\n"
|
||||
"Language: fi\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"
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "VDR-suoratoistopalvelin"
|
||||
|
||||
msgid "Streaming active"
|
||||
msgstr "Suoratoistopalvelin aktiivinen"
|
||||
|
||||
msgid "Suspend Live TV"
|
||||
msgstr "Pysäytä suora TV-lähetys"
|
||||
msgid "Streamdev Connections"
|
||||
msgstr "Suoratoistoyhteydet"
|
||||
|
||||
msgid "Offer suspend mode"
|
||||
msgstr "tyrkytä"
|
||||
msgid "Disconnect"
|
||||
msgstr "Katkaise"
|
||||
|
||||
msgid "Always suspended"
|
||||
msgstr "aina"
|
||||
|
||||
msgid "Never suspended"
|
||||
msgstr "ei koskaan"
|
||||
msgid "Suspend"
|
||||
msgstr "Pysäytä"
|
||||
|
||||
msgid "Common Settings"
|
||||
msgstr "Yleiset asetukset"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Piilota valinta päävalikosta"
|
||||
|
||||
msgid "Start with Live TV suspended"
|
||||
msgstr "Käynnistä Live-katselu pysäytettynä"
|
||||
|
||||
msgid "Maximum Number of Clients"
|
||||
msgstr "Suurin sallittu asiakkaiden määrä"
|
||||
msgstr "Suurin sallittu asiakkaiden määrä"
|
||||
|
||||
msgid "Suspend behaviour"
|
||||
msgstr "Pysäytystoiminto"
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Asiakas saa pysäyttää palvelimen"
|
||||
msgid "Live TV buffer delay (ms)"
|
||||
msgstr ""
|
||||
|
||||
msgid "VDR-to-VDR Server"
|
||||
msgstr "VDR-palvelin"
|
||||
|
||||
msgid "Start VDR-to-VDR Server"
|
||||
msgstr "Käynnistä VDR-palvelin"
|
||||
msgstr "Käynnistä VDR-palvelin"
|
||||
|
||||
msgid "VDR-to-VDR Server Port"
|
||||
msgstr "VDR-palvelimen portti"
|
||||
@@ -57,27 +55,41 @@ msgstr "VDR-palvelimen portti"
|
||||
msgid "Bind to IP"
|
||||
msgstr "Sido osoitteeseen"
|
||||
|
||||
msgid "Legacy Client Priority"
|
||||
msgstr "Legacy-asiakkaan prioriteetti"
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Asiakas saa pysäyttää palvelimen"
|
||||
|
||||
msgid "Loop Prevention"
|
||||
msgstr "Estä asiakaslaitesilmukat"
|
||||
|
||||
msgid "HTTP Server"
|
||||
msgstr "HTTP-palvelin"
|
||||
|
||||
msgid "Start HTTP Server"
|
||||
msgstr "Käynnistä HTTP-palvelin"
|
||||
msgstr "Käynnistä HTTP-palvelin"
|
||||
|
||||
msgid "HTTP Server Port"
|
||||
msgstr "HTTP-palvelimen portti"
|
||||
|
||||
msgid "Priority"
|
||||
msgstr "Prioriteetti"
|
||||
|
||||
msgid "HTTP Streamtype"
|
||||
msgstr "HTTP-lähetysmuoto"
|
||||
msgstr "HTTP-lähetysmuoto"
|
||||
|
||||
msgid "Multicast Streaming Server"
|
||||
msgstr "Multicast-suoratoistopalvelin"
|
||||
|
||||
msgid "Start IGMP Server"
|
||||
msgstr "Käynnistä IGMP-palvelin"
|
||||
msgstr "Käynnistä IGMP-palvelin"
|
||||
|
||||
msgid "Multicast Client Port"
|
||||
msgstr "Multicast-portti"
|
||||
|
||||
msgid "Multicast Streamtype"
|
||||
msgstr "Multicast-lähetysmuoto"
|
||||
msgstr "Multicast-lähetysmuoto"
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "VDR-suoratoistopalvelin"
|
||||
|
||||
@@ -6,44 +6,42 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:06+0200\n"
|
||||
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
|
||||
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
||||
"Last-Translator: micky979 <micky979@free.fr>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"Language-Team: French <vdr@linuxtv.org>\n"
|
||||
"Language: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "Serveur de streaming VDR"
|
||||
|
||||
msgid "Streaming active"
|
||||
msgstr "Streaming actif"
|
||||
|
||||
msgid "Suspend Live TV"
|
||||
msgstr "Suspendre Live TV"
|
||||
msgid "Streamdev Connections"
|
||||
msgstr ""
|
||||
|
||||
msgid "Offer suspend mode"
|
||||
msgstr "Offrir le mode suspendre"
|
||||
msgid "Disconnect"
|
||||
msgstr ""
|
||||
|
||||
msgid "Always suspended"
|
||||
msgstr "Toujours suspendre"
|
||||
|
||||
msgid "Never suspended"
|
||||
msgstr "Jamais suspendre"
|
||||
msgid "Suspend"
|
||||
msgstr "Suspendre"
|
||||
|
||||
msgid "Common Settings"
|
||||
msgstr "Paramètres communs"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr ""
|
||||
|
||||
msgid "Start with Live TV suspended"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum Number of Clients"
|
||||
msgstr "Nombre maximun de clients"
|
||||
|
||||
msgid "Suspend behaviour"
|
||||
msgstr "Suspendre"
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Le client peut suspendre"
|
||||
msgid "Live TV buffer delay (ms)"
|
||||
msgstr ""
|
||||
|
||||
msgid "VDR-to-VDR Server"
|
||||
msgstr "VDR-to-VDR Serveur"
|
||||
@@ -57,6 +55,15 @@ msgstr "Port du serveur VDR-to-VDR"
|
||||
msgid "Bind to IP"
|
||||
msgstr "Attacher aux IP"
|
||||
|
||||
msgid "Legacy Client Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Le client peut suspendre"
|
||||
|
||||
msgid "Loop Prevention"
|
||||
msgstr ""
|
||||
|
||||
msgid "HTTP Server"
|
||||
msgstr "Serveur HTTP"
|
||||
|
||||
@@ -66,6 +73,9 @@ msgstr "D
|
||||
msgid "HTTP Server Port"
|
||||
msgstr "Port du serveur HTTP"
|
||||
|
||||
msgid "Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "HTTP Streamtype"
|
||||
msgstr "Type de Streaming HTTP"
|
||||
|
||||
@@ -81,3 +91,5 @@ msgstr ""
|
||||
msgid "Multicast Streamtype"
|
||||
msgstr ""
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "Serveur de streaming VDR"
|
||||
|
||||
@@ -8,44 +8,42 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:06+0200\n"
|
||||
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
|
||||
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
|
||||
"PO-Revision-Date: 2012-06-12 19:57+0100\n"
|
||||
"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"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "Server trasmissione VDR"
|
||||
|
||||
msgid "Streaming active"
|
||||
msgstr "Trasmissione attiva"
|
||||
|
||||
msgid "Suspend Live TV"
|
||||
msgstr "Sospendi TV dal vivo"
|
||||
msgid "Streamdev Connections"
|
||||
msgstr "Connessioni Streamdev"
|
||||
|
||||
msgid "Offer suspend mode"
|
||||
msgstr "Offri mod. sospensione"
|
||||
msgid "Disconnect"
|
||||
msgstr "Disconnetti"
|
||||
|
||||
msgid "Always suspended"
|
||||
msgstr "Sempre sospeso"
|
||||
|
||||
msgid "Never suspended"
|
||||
msgstr "Mai sospeso"
|
||||
msgid "Suspend"
|
||||
msgstr "Sospendi"
|
||||
|
||||
msgid "Common Settings"
|
||||
msgstr "Impostazioni comuni"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Nascondi voce menu principale"
|
||||
|
||||
msgid "Start with Live TV suspended"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum Number of Clients"
|
||||
msgstr "Numero massimo di Client"
|
||||
|
||||
msgid "Suspend behaviour"
|
||||
msgstr "Tipo sospensione"
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Permetti sospensione al Client"
|
||||
msgid "Live TV buffer delay (ms)"
|
||||
msgstr ""
|
||||
|
||||
msgid "VDR-to-VDR Server"
|
||||
msgstr "Server VDR-a-VDR"
|
||||
@@ -59,6 +57,15 @@ msgstr "Porta Server VDR-a-VDR"
|
||||
msgid "Bind to IP"
|
||||
msgstr "IP associati"
|
||||
|
||||
msgid "Legacy Client Priority"
|
||||
msgstr "Priorità nativa client"
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Permetti sospensione al Client"
|
||||
|
||||
msgid "Loop Prevention"
|
||||
msgstr "Evita ciclo"
|
||||
|
||||
msgid "HTTP Server"
|
||||
msgstr "Server HTTP"
|
||||
|
||||
@@ -68,6 +75,9 @@ msgstr "Avvia Server HTTP"
|
||||
msgid "HTTP Server Port"
|
||||
msgstr "Porta Server HTTP"
|
||||
|
||||
msgid "Priority"
|
||||
msgstr "Priorità"
|
||||
|
||||
msgid "HTTP Streamtype"
|
||||
msgstr "Tipo flusso HTTP"
|
||||
|
||||
@@ -83,3 +93,5 @@ msgstr "Porta Client Multicast"
|
||||
msgid "Multicast Streamtype"
|
||||
msgstr "Tipo flusso Multicast"
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "Server trasmissione VDR"
|
||||
|
||||
@@ -6,44 +6,42 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:06+0200\n"
|
||||
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
|
||||
"PO-Revision-Date: 2009-11-26 21:57+0200\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"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "VDR transliavimo serveris"
|
||||
|
||||
msgid "Streaming active"
|
||||
msgstr "Transliavimas vyksta"
|
||||
|
||||
msgid "Suspend Live TV"
|
||||
msgstr "Pristabdyti Live TV"
|
||||
msgid "Streamdev Connections"
|
||||
msgstr ""
|
||||
|
||||
msgid "Offer suspend mode"
|
||||
msgstr "Klausti dėl sustabdymo"
|
||||
msgid "Disconnect"
|
||||
msgstr ""
|
||||
|
||||
msgid "Always suspended"
|
||||
msgstr "Visada stabdyti"
|
||||
|
||||
msgid "Never suspended"
|
||||
msgstr "Niekada nestabdyti"
|
||||
msgid "Suspend"
|
||||
msgstr "Pristabdyti"
|
||||
|
||||
msgid "Common Settings"
|
||||
msgstr "Bendri nustatymai"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr ""
|
||||
|
||||
msgid "Start with Live TV suspended"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum Number of Clients"
|
||||
msgstr "Maksimalus klientų skaičius"
|
||||
|
||||
msgid "Suspend behaviour"
|
||||
msgstr "Pristabdyti veikimą"
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Klientas gali pristabdyti"
|
||||
msgid "Live TV buffer delay (ms)"
|
||||
msgstr ""
|
||||
|
||||
msgid "VDR-to-VDR Server"
|
||||
msgstr "VDR-su-VDR Serveris"
|
||||
@@ -57,6 +55,15 @@ msgstr "VDR-su-VDR Serverio portas"
|
||||
msgid "Bind to IP"
|
||||
msgstr "Pririšti IP"
|
||||
|
||||
msgid "Legacy Client Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Klientas gali pristabdyti"
|
||||
|
||||
msgid "Loop Prevention"
|
||||
msgstr ""
|
||||
|
||||
msgid "HTTP Server"
|
||||
msgstr "HTTP Serveris"
|
||||
|
||||
@@ -66,6 +73,9 @@ msgstr "Paleisti HTTP serverį"
|
||||
msgid "HTTP Server Port"
|
||||
msgstr "HTTP serverio portas"
|
||||
|
||||
msgid "Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "HTTP Streamtype"
|
||||
msgstr "HTTP transliavimo tipas"
|
||||
|
||||
@@ -81,3 +91,5 @@ msgstr "Multicast kliento portas"
|
||||
msgid "Multicast Streamtype"
|
||||
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 ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:06+0200\n"
|
||||
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
|
||||
"PO-Revision-Date: 2008-06-26 15:36+0100\n"
|
||||
"Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"Language-Team: Russian <vdr@linuxtv.org>\n"
|
||||
"Language: ru\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-5\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "VDR Streaming áÕàÒÕà"
|
||||
|
||||
msgid "Streaming active"
|
||||
msgstr "ÁâàØÜØÝÓ ÐÚâØÒÕÝ"
|
||||
|
||||
msgid "Suspend Live TV"
|
||||
msgstr "¾áâÐÝÞÒÚÐ Live TV"
|
||||
msgid "Streamdev Connections"
|
||||
msgstr ""
|
||||
|
||||
msgid "Offer suspend mode"
|
||||
msgstr "¿àÕÔÛÐÓÐâì ÞáâÐÝÞÒÚã"
|
||||
msgid "Disconnect"
|
||||
msgstr ""
|
||||
|
||||
msgid "Always suspended"
|
||||
msgstr "²áÕÓÔÐ ÞáâÐÝÞÒÛÕÝ"
|
||||
|
||||
msgid "Never suspended"
|
||||
msgstr "½ØÚÞÓÔÐ ÝÕ ÞáâÐÝÞÒÛÕÝ"
|
||||
msgid "Suspend"
|
||||
msgstr "¾áâÐÝÞÒÚÐ"
|
||||
|
||||
msgid "Common Settings"
|
||||
msgstr "½ÐáâàÞÙÚØ"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr ""
|
||||
|
||||
msgid "Start with Live TV suspended"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum Number of Clients"
|
||||
msgstr "¼ÐÚá. ÚÞÛØçÕáâÒÞ ÚÛØÕÝâÞÒ"
|
||||
|
||||
msgid "Suspend behaviour"
|
||||
msgstr "¿ÞÒÕÔÕÝØÕ ÞáâÐÝÞÒÚØ"
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "ºÛØÕÝâ ÜÞÖÕâ ÞáâÐÝÐÒÛØÒÐâì"
|
||||
msgid "Live TV buffer delay (ms)"
|
||||
msgstr ""
|
||||
|
||||
msgid "VDR-to-VDR Server"
|
||||
msgstr "VDR-to-VDR áÕàÒÕà"
|
||||
@@ -57,6 +55,15 @@ msgstr "VDR-to-VDR
|
||||
msgid "Bind to IP"
|
||||
msgstr "¿àØáÞÕÔØÝØâìáï Ú IP"
|
||||
|
||||
msgid "Legacy Client Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "ºÛØÕÝâ ÜÞÖÕâ ÞáâÐÝÐÒÛØÒÐâì"
|
||||
|
||||
msgid "Loop Prevention"
|
||||
msgstr ""
|
||||
|
||||
msgid "HTTP Server"
|
||||
msgstr "HTTP áÕàÒÕà"
|
||||
|
||||
@@ -66,6 +73,9 @@ msgstr "
|
||||
msgid "HTTP Server Port"
|
||||
msgstr "HTTP áÕàÒÕà ¿Þàâ"
|
||||
|
||||
msgid "Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "HTTP Streamtype"
|
||||
msgstr "ÂØß HTTP ßÞâÞÚÐ"
|
||||
|
||||
@@ -81,3 +91,5 @@ msgstr ""
|
||||
msgid "Multicast Streamtype"
|
||||
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 ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev_SK\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:06+0200\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Report-Msgid-Bugs-To: <vdrdev@schmirler.de>\n"
|
||||
"POT-Creation-Date: 2014-05-05 22:46+0200\n"
|
||||
"PO-Revision-Date: 2013-11-22 23:39+0100\n"
|
||||
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
|
||||
"Language-Team: Slovak <hrala.milan@gmail.com>\n"
|
||||
"Language: sk\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=iso-8859-2\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Poedit-Language: Slovak\n"
|
||||
"X-Poedit-Country: SLOVAKIA\n"
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "VDR prúdový server"
|
||||
|
||||
msgid "Streaming active"
|
||||
msgstr "streamovanie aktivne"
|
||||
msgstr "Streamovanie aktívne"
|
||||
|
||||
msgid "Suspend Live TV"
|
||||
msgstr "Pozastavenie ¾ivého vysielania"
|
||||
msgid "Streamdev Connections"
|
||||
msgstr "Streamdev spojenia"
|
||||
|
||||
msgid "Offer suspend mode"
|
||||
msgstr "Výber re¾ímu pozastavenia"
|
||||
msgid "Disconnect"
|
||||
msgstr "Odpoji»"
|
||||
|
||||
msgid "Always suspended"
|
||||
msgstr "V¾dy pozastavi»"
|
||||
|
||||
msgid "Never suspended"
|
||||
msgstr "Nikdy nepozastavi»"
|
||||
msgid "Suspend"
|
||||
msgstr "Pozastavi»"
|
||||
|
||||
msgid "Common Settings"
|
||||
msgstr "V¹eobecné nastavenia"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Schova» v hlavnom menu"
|
||||
|
||||
msgid "Start with Live TV suspended"
|
||||
msgstr "Pozastavi» Live TV pri ¹tarte"
|
||||
|
||||
msgid "Maximum Number of Clients"
|
||||
msgstr "Maximály poèet klientov"
|
||||
|
||||
msgid "Suspend behaviour"
|
||||
msgstr "Správanie preru¹enia"
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Klient mô¾e pozastavi»"
|
||||
msgid "Live TV buffer delay (ms)"
|
||||
msgstr ""
|
||||
|
||||
msgid "VDR-to-VDR Server"
|
||||
msgstr "VDR-do-VDR server"
|
||||
msgstr "Prenos z VDR do VDR"
|
||||
|
||||
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"
|
||||
msgstr "Port serveru pre VDR-do-VDR"
|
||||
msgstr "Port servera prenosu z VDR do VDR"
|
||||
|
||||
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"
|
||||
msgstr "server HTTP"
|
||||
msgstr "HTTP server "
|
||||
|
||||
msgid "Start HTTP Server"
|
||||
msgstr "Spusti» HTTP Server"
|
||||
|
||||
msgid "HTTP Server Port"
|
||||
msgstr "Port serveru HTTP"
|
||||
msgstr "Port HTTP servera"
|
||||
|
||||
msgid "Priority"
|
||||
msgstr "Priorita"
|
||||
|
||||
msgid "HTTP Streamtype"
|
||||
msgstr "typ prúdu HTTP"
|
||||
msgstr "Typ HTTP streamu"
|
||||
|
||||
msgid "Multicast Streaming Server"
|
||||
msgstr "Multicast prúdový server"
|
||||
msgstr "Streamovanie Multicastového servera"
|
||||
|
||||
msgid "Start IGMP Server"
|
||||
msgstr "Spusti» IGMP Server"
|
||||
|
||||
msgid "Multicast Client Port"
|
||||
msgstr "Port klienta Multicast"
|
||||
msgstr "Port Multicast klienta"
|
||||
|
||||
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
|
||||
#include "vdr/device.h"
|
||||
|
||||
#undef _XOPEN_SOURCE
|
||||
#define _XOPEN_SOURCE 600
|
||||
#include <fcntl.h>
|
||||
|
||||
RecPlayer::RecPlayer(cRecording* rec)
|
||||
RecPlayer::RecPlayer(const char* FileName)
|
||||
{
|
||||
file = NULL;
|
||||
fileOpen = 0;
|
||||
lastPosition = 0;
|
||||
recording = rec;
|
||||
recording = new cRecording(FileName);
|
||||
for(int i = 1; i < 1000; i++) segments[i] = NULL;
|
||||
|
||||
// FIXME find out max file path / name lengths
|
||||
|
||||
#if VDRVERSNUM >= 10703 || defined(TSPLAY_PATCH_VERSION)
|
||||
indexFile = new cIndexFile(recording->FileName(), false, rec->IsPesRecording());
|
||||
#else
|
||||
indexFile = new cIndexFile(recording->FileName(), false);
|
||||
#endif
|
||||
indexFile = new cIndexFile(recording->FileName(), false, recording->IsPesRecording());
|
||||
if (!indexFile) esyslog("ERROR: Streamdev: Failed to create indexfile!");
|
||||
|
||||
scan();
|
||||
|
||||
parser = new cPatPmtParser();
|
||||
unsigned char buffer[2 * TS_SIZE];
|
||||
unsigned long l = getBlock(buffer, 0UL, sizeof(buffer));
|
||||
if (!l || !parser->ParsePatPmt(buffer, (int) l))
|
||||
esyslog("ERROR: Streamdev: Failed to parse PAT/PMT");
|
||||
}
|
||||
|
||||
void RecPlayer::scan()
|
||||
@@ -61,24 +64,18 @@ void RecPlayer::scan()
|
||||
for(i = 1; i < 1000; i++)
|
||||
{
|
||||
|
||||
#if APIVERSNUM < 10703 || !defined(TSPLAY_PATCH_VERSION)
|
||||
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
|
||||
//log->log("RecPlayer", Log::DEBUG, "FILENAME: %s", fileName);
|
||||
file = fopen(fileName, "r");
|
||||
#else
|
||||
snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), i);
|
||||
file = fopen(fileName, "r");
|
||||
if (!file) {
|
||||
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
|
||||
file = fopen(fileName, "r");
|
||||
}
|
||||
#endif
|
||||
if (!file) break;
|
||||
|
||||
segments[i] = new Segment();
|
||||
segments[i]->start = totalLength;
|
||||
fseek(file, 0, SEEK_END);
|
||||
totalLength += ftell(file);
|
||||
totalLength += ftello(file);
|
||||
totalFrames = indexFile->Last();
|
||||
//log->log("RecPlayer", Log::DEBUG, "File %i found, totalLength now %llu, numFrames = %lu", i, totalLength, totalFrames);
|
||||
segments[i]->end = totalLength;
|
||||
@@ -94,6 +91,9 @@ RecPlayer::~RecPlayer()
|
||||
int i = 1;
|
||||
while(segments[i++]) delete segments[i];
|
||||
if (file) fclose(file);
|
||||
delete indexFile;
|
||||
delete recording;
|
||||
delete parser;
|
||||
}
|
||||
|
||||
int RecPlayer::openFile(int index)
|
||||
@@ -102,7 +102,6 @@ int RecPlayer::openFile(int index)
|
||||
|
||||
char fileName[2048];
|
||||
|
||||
#if APIVERSNUM >= 10703 || defined(TSPLAY_PATCH_VERSION)
|
||||
snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), index);
|
||||
isyslog("openFile called for index %i string:%s", index, fileName);
|
||||
|
||||
@@ -112,7 +111,6 @@ int RecPlayer::openFile(int index)
|
||||
fileOpen = index;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), index);
|
||||
isyslog("openFile called for index %i string:%s", index, fileName);
|
||||
@@ -178,7 +176,7 @@ unsigned long RecPlayer::getBlock(unsigned char* buffer, uint64_t position, unsi
|
||||
uint32_t yetToGet = amount;
|
||||
uint32_t got = 0;
|
||||
uint32_t getFromThisSegment = 0;
|
||||
uint32_t filePosition;
|
||||
uint64_t filePosition;
|
||||
|
||||
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.
|
||||
|
||||
// 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;
|
||||
currentPosition += getFromThisSegment;
|
||||
@@ -221,17 +219,47 @@ cRecording* RecPlayer::getCurrentRecording()
|
||||
return recording;
|
||||
}
|
||||
|
||||
#if VDRVERSNUM < 10732
|
||||
#define ALIGNED_POS(x) (positionFromFrameNumber(indexFile->GetNextIFrame(x, 1)))
|
||||
#else
|
||||
#define ALIGNED_POS(x) (positionFromFrameNumber(indexFile->GetClosestIFrame(x)))
|
||||
#endif
|
||||
uint64_t RecPlayer::positionFromResume(int ResumeID)
|
||||
{
|
||||
int resumeBackup = Setup.ResumeID;
|
||||
Setup.ResumeID = ResumeID;
|
||||
cResumeFile resume(recording->FileName(), recording->IsPesRecording());
|
||||
Setup.ResumeID = resumeBackup;
|
||||
return ALIGNED_POS(resume.Read());
|
||||
}
|
||||
|
||||
uint64_t RecPlayer::positionFromMark(int MarkIndex)
|
||||
{
|
||||
cMarks marks;
|
||||
if (marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording()) && marks.Count()) {
|
||||
cMark *mark = marks.cConfig<cMark>::Get(MarkIndex);
|
||||
if (mark)
|
||||
return ALIGNED_POS(mark->Position());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t RecPlayer::positionFromTime(int Seconds)
|
||||
{
|
||||
return ALIGNED_POS(SecondsToFrames(Seconds, recording->FramesPerSecond()));
|
||||
}
|
||||
|
||||
uint64_t RecPlayer::positionFromPercent(int Percent)
|
||||
{
|
||||
return ALIGNED_POS(getLengthFrames() * Percent / 100L);
|
||||
}
|
||||
|
||||
uint64_t RecPlayer::positionFromFrameNumber(uint32_t frameNumber)
|
||||
{
|
||||
if (!indexFile) return 0;
|
||||
|
||||
#if VDRVERSNUM >= 10703 || defined(TSPLAY_PATCH_VERSION)
|
||||
uint16_t retFileNumber;
|
||||
off_t retFileOffset;
|
||||
#else
|
||||
uchar retFileNumber;
|
||||
int retFileOffset;
|
||||
#endif
|
||||
|
||||
if (!indexFile->Get((int)frameNumber, &retFileNumber, &retFileOffset))
|
||||
{
|
||||
@@ -262,7 +290,7 @@ uint32_t RecPlayer::frameNumberFromPosition(uint64_t position)
|
||||
if ((position >= segments[segmentNumber]->start) && (position < segments[segmentNumber]->end)) break;
|
||||
// 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);
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <vdr/recording.h>
|
||||
#include <vdr/remux.h>
|
||||
|
||||
#include "server/streamer.h"
|
||||
|
||||
@@ -36,7 +37,7 @@ class Segment
|
||||
class RecPlayer
|
||||
{
|
||||
public:
|
||||
RecPlayer(cRecording* rec);
|
||||
RecPlayer(const char* FileName);
|
||||
~RecPlayer();
|
||||
uint64_t getLengthBytes();
|
||||
uint32_t getLengthFrames();
|
||||
@@ -44,7 +45,12 @@ class RecPlayer
|
||||
int openFile(int index);
|
||||
uint64_t getLastPosition();
|
||||
cRecording* getCurrentRecording();
|
||||
const cPatPmtParser* getPatPmtData() { return parser; }
|
||||
void scan();
|
||||
uint64_t positionFromResume(int ResumeID);
|
||||
uint64_t positionFromMark(int MarkIndex);
|
||||
uint64_t positionFromTime(int Seconds);
|
||||
uint64_t positionFromPercent(int Percent);
|
||||
uint64_t positionFromFrameNumber(uint32_t frameNumber);
|
||||
uint32_t frameNumberFromPosition(uint64_t position);
|
||||
bool getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength);
|
||||
@@ -52,6 +58,7 @@ class RecPlayer
|
||||
private:
|
||||
cRecording* recording;
|
||||
cIndexFile* indexFile;
|
||||
cPatPmtParser* parser;
|
||||
FILE* file;
|
||||
int fileOpen;
|
||||
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,9 +152,8 @@ void cStreamdevServer::Action(void)
|
||||
|
||||
cServerConnection *next = m_Clients.Next(s);
|
||||
if (!result) {
|
||||
isyslog("streamdev: closing streamdev connection to %s:%d",
|
||||
s->RemoteIp().c_str(), s->RemotePort());
|
||||
s->Close();
|
||||
if (s->IsOpen())
|
||||
s->Close();
|
||||
Lock();
|
||||
m_Clients.Del(s);
|
||||
Unlock();
|
||||
@@ -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);
|
||||
for (cServerConnection *s = m_Clients.First(); s; s = m_Clients.Next(s))
|
||||
s->MainThreadHook();
|
||||
Lock.Lock(m_Instance);
|
||||
return m_Clients;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,12 @@ public:
|
||||
static void Initialize(void);
|
||||
static void Destruct(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)
|
||||
|
||||
@@ -10,16 +10,22 @@
|
||||
cStreamdevServerSetup StreamdevServerSetup;
|
||||
|
||||
cStreamdevServerSetup::cStreamdevServerSetup(void) {
|
||||
HideMenuEntry = false;
|
||||
MaxClients = 5;
|
||||
StartSuspended = ssAuto;
|
||||
LiveBufferMs = 0;
|
||||
StartVTPServer = true;
|
||||
VTPServerPort = 2004;
|
||||
VTPPriority = 0;
|
||||
LoopPrevention = false;
|
||||
StartHTTPServer = true;
|
||||
HTTPServerPort = 3000;
|
||||
HTTPPriority = 0;
|
||||
HTTPStreamType = stTS;
|
||||
StartIGMPServer = false;
|
||||
IGMPClientPort = 1234;
|
||||
IGMPPriority = 0;
|
||||
IGMPStreamType = stTS;
|
||||
SuspendMode = smAlways;
|
||||
AllowSuspend = false;
|
||||
strcpy(VTPBindIP, "0.0.0.0");
|
||||
strcpy(HTTPBindIP, "0.0.0.0");
|
||||
@@ -27,19 +33,25 @@ cStreamdevServerSetup::cStreamdevServerSetup(void) {
|
||||
}
|
||||
|
||||
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, "ServerPort") == 0) VTPServerPort = atoi(Value);
|
||||
else if (strcmp(Name, "VTPPriority") == 0) VTPPriority = atoi(Value);
|
||||
else if (strcmp(Name, "VTPBindIP") == 0) strcpy(VTPBindIP, Value);
|
||||
else if (strcmp(Name, "LoopPrevention") == 0) LoopPrevention = atoi(Value);
|
||||
else if (strcmp(Name, "StartHTTPServer") == 0) StartHTTPServer = atoi(Value);
|
||||
else if (strcmp(Name, "HTTPServerPort") == 0) HTTPServerPort = atoi(Value);
|
||||
else if (strcmp(Name, "HTTPPriority") == 0) HTTPPriority = atoi(Value);
|
||||
else if (strcmp(Name, "HTTPStreamType") == 0) HTTPStreamType = atoi(Value);
|
||||
else if (strcmp(Name, "HTTPBindIP") == 0) strcpy(HTTPBindIP, Value);
|
||||
else if (strcmp(Name, "StartIGMPServer") == 0) StartIGMPServer = atoi(Value);
|
||||
else if (strcmp(Name, "IGMPClientPort") == 0) IGMPClientPort = atoi(Value);
|
||||
else if (strcmp(Name, "IGMPPriority") == 0) IGMPPriority = atoi(Value);
|
||||
else if (strcmp(Name, "IGMPStreamType") == 0) IGMPStreamType = atoi(Value);
|
||||
else if (strcmp(Name, "IGMPBindIP") == 0) strcpy(IGMPBindIP, Value);
|
||||
else if (strcmp(Name, "SuspendMode") == 0) SuspendMode = atoi(Value);
|
||||
else if (strcmp(Name, "AllowSuspend") == 0) AllowSuspend = atoi(Value);
|
||||
else return false;
|
||||
return true;
|
||||
@@ -53,12 +65,6 @@ const char* cStreamdevServerMenuSetupPage::StreamTypes[st_Count - 1] = {
|
||||
"EXT"
|
||||
};
|
||||
|
||||
const char* cStreamdevServerMenuSetupPage::SuspendModes[sm_Count] = {
|
||||
trNOOP("Offer suspend mode"),
|
||||
trNOOP("Always suspended"),
|
||||
trNOOP("Never suspended")
|
||||
};
|
||||
|
||||
cStreamdevServerMenuSetupPage::cStreamdevServerMenuSetupPage(void) {
|
||||
m_NewSetup = StreamdevServerSetup;
|
||||
|
||||
@@ -69,34 +75,44 @@ cStreamdevServerMenuSetupPage::~cStreamdevServerMenuSetupPage() {
|
||||
}
|
||||
|
||||
void cStreamdevServerMenuSetupPage::Set(void) {
|
||||
static const char* modes[sm_Count];
|
||||
for (int i = 0; i < sm_Count; i++)
|
||||
modes[i] = tr(SuspendModes[i]);
|
||||
static const char *StartSuspendedItems[ss_Count] =
|
||||
{
|
||||
trVDR("no"),
|
||||
trVDR("yes"),
|
||||
trVDR("auto")
|
||||
};
|
||||
|
||||
int current = Current();
|
||||
Clear();
|
||||
AddCategory (tr("Common Settings"));
|
||||
Add(new cMenuEditBoolItem(tr("Hide Mainmenu Entry"), &m_NewSetup.HideMenuEntry));
|
||||
Add(new cMenuEditStraItem(tr("Start with Live TV suspended"), &m_NewSetup.StartSuspended, ss_Count, StartSuspendedItems));
|
||||
Add(new cMenuEditIntItem (tr("Maximum Number of Clients"), &m_NewSetup.MaxClients, 0, 100));
|
||||
Add(new cMenuEditIntItem (tr("Live TV buffer delay (ms)"), &m_NewSetup.LiveBufferMs, 0, 1500));
|
||||
|
||||
Add(new cMenuEditStraItem(tr("Suspend behaviour"), &m_NewSetup.SuspendMode, sm_Count, modes));
|
||||
if (m_NewSetup.SuspendMode == smOffer)
|
||||
Add(new cMenuEditBoolItem(tr("Client may suspend"), &m_NewSetup.AllowSuspend));
|
||||
|
||||
AddCategory (tr("VDR-to-VDR Server"));
|
||||
Add(new cMenuEditBoolItem(tr("Start VDR-to-VDR Server"), &m_NewSetup.StartVTPServer));
|
||||
Add(new cMenuEditIntItem (tr("VDR-to-VDR Server Port"), &m_NewSetup.VTPServerPort, 0, 65535));
|
||||
Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.VTPBindIP));
|
||||
Add(new cMenuEditIntItem (tr("Legacy Client Priority"), &m_NewSetup.VTPPriority, MINPRIORITY, MAXPRIORITY));
|
||||
Add(new cMenuEditBoolItem(tr("Client may suspend"), &m_NewSetup.AllowSuspend));
|
||||
if (cPluginManager::CallFirstService(LOOP_PREVENTION_SERVICE))
|
||||
Add(new cMenuEditBoolItem(tr("Loop Prevention"), &m_NewSetup.LoopPrevention));
|
||||
|
||||
AddCategory (tr("HTTP Server"));
|
||||
Add(new cMenuEditBoolItem(tr("Start HTTP Server"), &m_NewSetup.StartHTTPServer));
|
||||
Add(new cMenuEditIntItem (tr("HTTP Server Port"), &m_NewSetup.HTTPServerPort, 0, 65535));
|
||||
Add(new cMenuEditStraItem(tr("HTTP Streamtype"), &m_NewSetup.HTTPStreamType, st_Count - 1, StreamTypes));
|
||||
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"));
|
||||
Add(new cMenuEditBoolItem(tr("Start IGMP Server"), &m_NewSetup.StartIGMPServer));
|
||||
Add(new cMenuEditIntItem (tr("Multicast Client Port"), &m_NewSetup.IGMPClientPort, 0, 65535));
|
||||
Add(new cMenuEditStraItem(tr("Multicast Streamtype"), &m_NewSetup.IGMPStreamType, st_Count - 1, StreamTypes));
|
||||
Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.IGMPBindIP));
|
||||
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));
|
||||
Display();
|
||||
}
|
||||
@@ -126,19 +142,25 @@ void cStreamdevServerMenuSetupPage::Store(void) {
|
||||
cStreamdevServer::Destruct();
|
||||
}
|
||||
|
||||
SetupStore("HideMenuEntry", m_NewSetup.HideMenuEntry);
|
||||
SetupStore("MaxClients", m_NewSetup.MaxClients);
|
||||
SetupStore("StartSuspended", m_NewSetup.StartSuspended);
|
||||
SetupStore("LiveBufferMs", m_NewSetup.LiveBufferMs);
|
||||
SetupStore("StartServer", m_NewSetup.StartVTPServer);
|
||||
SetupStore("ServerPort", m_NewSetup.VTPServerPort);
|
||||
SetupStore("VTPBindIP", m_NewSetup.VTPBindIP);
|
||||
SetupStore("VTPPriority", m_NewSetup.VTPPriority);
|
||||
SetupStore("LoopPrevention", m_NewSetup.LoopPrevention);
|
||||
SetupStore("StartHTTPServer", m_NewSetup.StartHTTPServer);
|
||||
SetupStore("HTTPServerPort", m_NewSetup.HTTPServerPort);
|
||||
SetupStore("HTTPStreamType", m_NewSetup.HTTPStreamType);
|
||||
SetupStore("HTTPBindIP", m_NewSetup.HTTPBindIP);
|
||||
SetupStore("HTTPPriority", m_NewSetup.HTTPPriority);
|
||||
SetupStore("HTTPStreamType", m_NewSetup.HTTPStreamType);
|
||||
SetupStore("StartIGMPServer", m_NewSetup.StartIGMPServer);
|
||||
SetupStore("IGMPClientPort", m_NewSetup.IGMPClientPort);
|
||||
SetupStore("IGMPStreamType", m_NewSetup.IGMPStreamType);
|
||||
SetupStore("IGMPBindIP", m_NewSetup.IGMPBindIP);
|
||||
SetupStore("SuspendMode", m_NewSetup.SuspendMode);
|
||||
SetupStore("IGMPPriority", m_NewSetup.IGMPPriority);
|
||||
SetupStore("IGMPStreamType", m_NewSetup.IGMPStreamType);
|
||||
SetupStore("AllowSuspend", m_NewSetup.AllowSuspend);
|
||||
|
||||
StreamdevServerSetup = m_NewSetup;
|
||||
@@ -146,11 +168,3 @@ void cStreamdevServerMenuSetupPage::Store(void) {
|
||||
if (restart)
|
||||
cStreamdevServer::Initialize();
|
||||
}
|
||||
|
||||
eOSState cStreamdevServerMenuSetupPage::ProcessKey(eKeys Key) {
|
||||
int oldMode = m_NewSetup.SuspendMode;
|
||||
eOSState state = cMenuSetupPage::ProcessKey(Key);
|
||||
if (oldMode != m_NewSetup.SuspendMode)
|
||||
Set();
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -7,25 +7,38 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
enum eStartSuspended {
|
||||
ssNo,
|
||||
ssYes,
|
||||
ssAuto,
|
||||
ss_Count
|
||||
};
|
||||
|
||||
struct cStreamdevServerSetup {
|
||||
cStreamdevServerSetup(void);
|
||||
|
||||
bool SetupParse(const char *Name, const char *Value);
|
||||
|
||||
int HideMenuEntry;
|
||||
int MaxClients;
|
||||
int StartSuspended;
|
||||
int LiveBufferMs;
|
||||
int StartVTPServer;
|
||||
int VTPServerPort;
|
||||
char VTPBindIP[20];
|
||||
int VTPPriority;
|
||||
int AllowSuspend;
|
||||
int LoopPrevention;
|
||||
int StartHTTPServer;
|
||||
int HTTPServerPort;
|
||||
int HTTPPriority;
|
||||
int HTTPStreamType;
|
||||
char HTTPBindIP[20];
|
||||
int StartIGMPServer;
|
||||
int IGMPClientPort;
|
||||
int IGMPPriority;
|
||||
int IGMPStreamType;
|
||||
char IGMPBindIP[20];
|
||||
int SuspendMode;
|
||||
int AllowSuspend;
|
||||
};
|
||||
|
||||
extern cStreamdevServerSetup StreamdevServerSetup;
|
||||
@@ -33,14 +46,12 @@ extern cStreamdevServerSetup StreamdevServerSetup;
|
||||
class cStreamdevServerMenuSetupPage: public cMenuSetupPage {
|
||||
private:
|
||||
static const char* StreamTypes[];
|
||||
static const char* SuspendModes[];
|
||||
cStreamdevServerSetup m_NewSetup;
|
||||
|
||||
void AddCategory(const char *Title);
|
||||
void Set();
|
||||
protected:
|
||||
virtual void Store(void);
|
||||
virtual eOSState ProcessKey(eKeys Key);
|
||||
|
||||
public:
|
||||
cStreamdevServerMenuSetupPage(void);
|
||||
|
||||
@@ -9,18 +9,47 @@
|
||||
#include <getopt.h>
|
||||
#include <vdr/tools.h>
|
||||
#include "streamdev-server.h"
|
||||
#include "server/menu.h"
|
||||
#include "server/setup.h"
|
||||
#include "server/server.h"
|
||||
#include "server/suspend.h"
|
||||
|
||||
#if !defined(APIVERSNUM) || APIVERSNUM < 10516
|
||||
#error "VDR-1.5.16 API version or greater is required!"
|
||||
#if !defined(APIVERSNUM) || APIVERSNUM < 10725
|
||||
#error "VDR-1.7.25 or greater required to compile server! Use 'make client' to compile client only."
|
||||
#endif
|
||||
|
||||
cList<cMainThreadHookSubscriber> cMainThreadHookSubscriber::m_Subscribers;
|
||||
cMutex cMainThreadHookSubscriber::m_Mutex;
|
||||
|
||||
#if APIVERSNUM >= 20300
|
||||
cList<cMainThreadHookSubscriber>& cMainThreadHookSubscriber::Subscribers(cMutexLock& Lock)
|
||||
#else
|
||||
const cList<cMainThreadHookSubscriber>& cMainThreadHookSubscriber::Subscribers(cMutexLock& Lock)
|
||||
#endif
|
||||
{
|
||||
Lock.Lock(&m_Mutex);
|
||||
return m_Subscribers;
|
||||
}
|
||||
|
||||
cMainThreadHookSubscriber::cMainThreadHookSubscriber()
|
||||
{
|
||||
m_Mutex.Lock();
|
||||
m_Subscribers.Add(this);
|
||||
m_Mutex.Unlock();
|
||||
}
|
||||
|
||||
cMainThreadHookSubscriber::~cMainThreadHookSubscriber()
|
||||
{
|
||||
m_Mutex.Lock();
|
||||
m_Subscribers.Del(this, false);
|
||||
m_Mutex.Unlock();
|
||||
}
|
||||
|
||||
const char *cPluginStreamdevServer::DESCRIPTION = trNOOP("VDR Streaming Server");
|
||||
|
||||
cPluginStreamdevServer::cPluginStreamdevServer(void)
|
||||
{
|
||||
m_Suspend = false;
|
||||
}
|
||||
|
||||
cPluginStreamdevServer::~cPluginStreamdevServer()
|
||||
@@ -98,6 +127,9 @@ bool cPluginStreamdevServer::Start(void)
|
||||
|
||||
cStreamdevServer::Initialize();
|
||||
|
||||
m_Suspend = StreamdevServerSetup.StartSuspended == ssAuto ?
|
||||
!cDevice::PrimaryDevice()->HasDecoder() :
|
||||
StreamdevServerSetup.StartSuspended;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -119,20 +151,29 @@ cString cPluginStreamdevServer::Active(void)
|
||||
|
||||
const char *cPluginStreamdevServer::MainMenuEntry(void)
|
||||
{
|
||||
if (StreamdevServerSetup.SuspendMode == smOffer && !cSuspendCtl::IsActive())
|
||||
return tr("Suspend Live TV");
|
||||
return NULL;
|
||||
return !StreamdevServerSetup.HideMenuEntry ? tr("Streamdev Connections") : NULL;
|
||||
}
|
||||
|
||||
cOsdObject *cPluginStreamdevServer::MainMenuAction(void)
|
||||
{
|
||||
cControl::Launch(new cSuspendCtl);
|
||||
return NULL;
|
||||
return new cStreamdevServerMenu();
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -145,4 +186,90 @@ bool cPluginStreamdevServer::SetupParse(const char *Name, const char *Value)
|
||||
return StreamdevServerSetup.SetupParse(Name, Value);
|
||||
}
|
||||
|
||||
const char **cPluginStreamdevServer::SVDRPHelpPages(void)
|
||||
{
|
||||
static const char *HelpPages[]=
|
||||
{
|
||||
"LSTC\n"
|
||||
" List connected clients\n",
|
||||
"DISC client_id\n"
|
||||
" Disconnect a client\n",
|
||||
NULL
|
||||
};
|
||||
return HelpPages;
|
||||
}
|
||||
|
||||
cString cPluginStreamdevServer::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
|
||||
{
|
||||
|
||||
cString reply = NULL;
|
||||
if (!strcasecmp(Command, "LSTC"))
|
||||
{
|
||||
reply = "";
|
||||
cThreadLock lock;
|
||||
#if APIVERSNUM >= 20300
|
||||
cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
||||
#else
|
||||
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
||||
#endif
|
||||
cServerConnection *s = clients.First();
|
||||
if (!s)
|
||||
{
|
||||
reply = "no client connected";
|
||||
ReplyCode = 550;
|
||||
} else
|
||||
{
|
||||
for (; s; s = clients.Next(s))
|
||||
{
|
||||
reply = cString::sprintf("%s%p: %s\n", (const char*) reply, s, (const char *) s->ToText());
|
||||
}
|
||||
ReplyCode = 250;
|
||||
}
|
||||
} else if (!strcasecmp(Command, "DISC"))
|
||||
{
|
||||
void *client = NULL;
|
||||
if (sscanf(Option, "%p", &client) != 1 || !client)
|
||||
{
|
||||
reply = "invalid client handle";
|
||||
ReplyCode = 501;
|
||||
} else
|
||||
{
|
||||
cThreadLock lock;
|
||||
#if APIVERSNUM >= 20300
|
||||
cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
||||
#else
|
||||
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
||||
#endif
|
||||
cServerConnection *s = clients.First();
|
||||
for (; s && s != client; s = clients.Next(s));
|
||||
|
||||
if (!s)
|
||||
{
|
||||
reply = "client not found";
|
||||
ReplyCode = 501;
|
||||
} else
|
||||
{
|
||||
s->Close();
|
||||
reply = "client disconnected";
|
||||
ReplyCode = 250;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
bool cPluginStreamdevServer::Service(const char *Id, void *Data)
|
||||
{
|
||||
if (strcmp(Id, "StreamdevServer::ClientCount-v1.0") == 0) {
|
||||
if (Data) {
|
||||
int *count = (int *) Data;
|
||||
cThreadLock lock;
|
||||
*count = cStreamdevServer::Clients(lock).Count();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
VDRPLUGINCREATOR(cPluginStreamdevServer); // Don't touch this!
|
||||
|
||||
@@ -7,11 +7,30 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <vdr/tools.h>
|
||||
#include <vdr/plugin.h>
|
||||
|
||||
class cMainThreadHookSubscriber: public cListObject {
|
||||
private:
|
||||
static cList<cMainThreadHookSubscriber> m_Subscribers;
|
||||
static cMutex m_Mutex;
|
||||
public:
|
||||
#if APIVERSNUM >= 20300
|
||||
static cList<cMainThreadHookSubscriber>& Subscribers(cMutexLock& Lock);
|
||||
#else
|
||||
static const cList<cMainThreadHookSubscriber>& Subscribers(cMutexLock& Lock);
|
||||
#endif
|
||||
|
||||
virtual void MainThreadHook() = 0;
|
||||
|
||||
cMainThreadHookSubscriber();
|
||||
virtual ~cMainThreadHookSubscriber();
|
||||
};
|
||||
|
||||
class cPluginStreamdevServer : public cPlugin {
|
||||
private:
|
||||
static const char *DESCRIPTION;
|
||||
bool m_Suspend;
|
||||
|
||||
public:
|
||||
cPluginStreamdevServer(void);
|
||||
@@ -29,6 +48,9 @@ public:
|
||||
virtual void MainThreadHook(void);
|
||||
virtual cMenuSetupPage *SetupMenu(void);
|
||||
virtual bool SetupParse(const char *Name, const char *Value);
|
||||
virtual const char **SVDRPHelpPages(void);
|
||||
virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
|
||||
virtual bool Service(const char *Id, void *Data = NULL);
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEVSERVER_H
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "server/streamer.h"
|
||||
#include "server/suspend.h"
|
||||
#include "server/setup.h"
|
||||
#include "tools/socket.h"
|
||||
#include "tools/select.h"
|
||||
#include "common.h"
|
||||
@@ -47,15 +45,18 @@ void cStreamdevWriter::Action(void)
|
||||
int count, offset = 0;
|
||||
int timeout = 0;
|
||||
|
||||
#if APIVERSNUM >= 10705
|
||||
SetPriority(-3);
|
||||
#endif
|
||||
sel.Clear();
|
||||
sel.Add(*m_Socket, true);
|
||||
while (Running()) {
|
||||
if (block == NULL) {
|
||||
block = m_Streamer->Get(count);
|
||||
offset = 0;
|
||||
// still no data - are we done?
|
||||
if (block == NULL && !m_Streamer->IsReceiving() && timeout++ > 20) {
|
||||
esyslog("streamdev-server: streamer done - writer exiting");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (block != NULL) {
|
||||
@@ -102,72 +103,82 @@ void cStreamdevWriter::Action(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
m_Socket->Close();
|
||||
Dprintf("Max. Transmit Blocksize was: %d\n", max);
|
||||
}
|
||||
|
||||
// --- cRemuxDummy ------------------------------------------------------------
|
||||
|
||||
class cRemuxDummy: public Streamdev::cTSRemux {
|
||||
private:
|
||||
cStreamdevBuffer m_Buffer;
|
||||
public:
|
||||
cRemuxDummy();
|
||||
virtual int Put(const uchar *Data, int Count) { return m_Buffer.Put(Data, Count); }
|
||||
virtual uchar *Get(int& Count) { return m_Buffer.Get(Count); }
|
||||
virtual void Del(int Count) { return m_Buffer.Del(Count); }
|
||||
};
|
||||
|
||||
cRemuxDummy::cRemuxDummy(): m_Buffer(WRITERBUFSIZE, TS_SIZE * 2)
|
||||
{
|
||||
m_Buffer.SetTimeouts(100, 100);
|
||||
}
|
||||
|
||||
// --- cStreamdevStreamer -----------------------------------------------------
|
||||
|
||||
cStreamdevStreamer::cStreamdevStreamer(const char *Name, const cServerConnection *Connection):
|
||||
cThread(Name),
|
||||
m_Connection(Connection),
|
||||
m_Writer(NULL),
|
||||
m_RingBuffer(new cStreamdevBuffer(STREAMERBUFSIZE, TS_SIZE * 2,
|
||||
true, "streamdev-streamer")),
|
||||
m_SendBuffer(new cStreamdevBuffer(WRITERBUFSIZE, TS_SIZE * 2))
|
||||
m_Remux(new cRemuxDummy()),
|
||||
m_Writer(NULL)
|
||||
{
|
||||
m_RingBuffer->SetTimeouts(0, 100);
|
||||
m_SendBuffer->SetTimeouts(100, 100);
|
||||
}
|
||||
|
||||
cStreamdevStreamer::~cStreamdevStreamer()
|
||||
{
|
||||
Dprintf("Desctructing streamer\n");
|
||||
delete m_RingBuffer;
|
||||
delete m_SendBuffer;
|
||||
delete m_Remux;
|
||||
}
|
||||
|
||||
void cStreamdevStreamer::Start(cTBSocket *Socket)
|
||||
{
|
||||
Dprintf("start streamer\n");
|
||||
Dprintf("start writer\n");
|
||||
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();
|
||||
}
|
||||
Attach();
|
||||
}
|
||||
|
||||
void cStreamdevStreamer::Stop(void)
|
||||
{
|
||||
Detach();
|
||||
if (Running()) {
|
||||
Dprintf("stopping streamer\n");
|
||||
Dprintf("stop streamer\n");
|
||||
Cancel(3);
|
||||
}
|
||||
if (m_Writer) {
|
||||
Detach();
|
||||
DELETENULL(m_Writer);
|
||||
}
|
||||
Dprintf("stop writer\n");
|
||||
DELETENULL(m_Writer);
|
||||
}
|
||||
|
||||
void cStreamdevStreamer::Action(void)
|
||||
{
|
||||
#if APIVERSNUM >= 10705
|
||||
SetPriority(-3);
|
||||
#endif
|
||||
while (Running()) {
|
||||
int got;
|
||||
uchar *block = m_RingBuffer->Get(got);
|
||||
uchar *block = GetFromReceiver(got);
|
||||
|
||||
if (block) {
|
||||
int count = Put(block, got);
|
||||
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/tools.h>
|
||||
|
||||
#include "remux/tsremux.h"
|
||||
|
||||
class cTBSocket;
|
||||
class cStreamdevStreamer;
|
||||
class cServerConnection;
|
||||
@@ -17,7 +19,6 @@ class cServerConnection;
|
||||
#define TS_SIZE 188
|
||||
#endif
|
||||
|
||||
#define STREAMERBUFSIZE (20000 * TS_SIZE)
|
||||
#define WRITERBUFSIZE (20000 * TS_SIZE)
|
||||
|
||||
// --- cStreamdevBuffer -------------------------------------------------------
|
||||
@@ -66,14 +67,18 @@ public:
|
||||
class cStreamdevStreamer: public cThread {
|
||||
private:
|
||||
const cServerConnection *m_Connection;
|
||||
cStreamdevWriter *m_Writer;
|
||||
cStreamdevBuffer *m_RingBuffer;
|
||||
cStreamdevBuffer *m_SendBuffer;
|
||||
Streamdev::cTSRemux *m_Remux;
|
||||
cStreamdevWriter *m_Writer;
|
||||
cStreamdevBuffer *m_SendBuffer;
|
||||
|
||||
protected:
|
||||
virtual uchar* GetFromReceiver(int &Count) = 0;
|
||||
virtual void DelFromReceiver(int Count) = 0;
|
||||
virtual int Put(const uchar *Data, int Count);
|
||||
virtual void Action(void);
|
||||
|
||||
bool IsRunning(void) const { return m_Writer; }
|
||||
void SetRemux(Streamdev::cTSRemux *Remux) { delete m_Remux; m_Remux = Remux; }
|
||||
|
||||
public:
|
||||
cStreamdevStreamer(const char *Name, const cServerConnection *Connection = NULL);
|
||||
@@ -83,18 +88,16 @@ public:
|
||||
|
||||
virtual void Start(cTBSocket *Socket);
|
||||
virtual void Stop(void);
|
||||
virtual bool IsReceiving(void) const = 0;
|
||||
bool Abort(void);
|
||||
|
||||
void Activate(bool On);
|
||||
int Receive(uchar *Data, int Length) { return m_RingBuffer->PutTS(Data, Length); }
|
||||
void ReportOverflow(int Bytes) { m_RingBuffer->ReportOverflow(Bytes); }
|
||||
|
||||
virtual int Put(const uchar *Data, int Count) { return m_SendBuffer->PutTS(Data, Count); }
|
||||
virtual uchar *Get(int &Count) { return m_SendBuffer->Get(Count); }
|
||||
virtual void Del(int Count) { m_SendBuffer->Del(Count); }
|
||||
uchar *Get(int &Count) { return m_Remux->Get(Count); }
|
||||
void Del(int Count) { m_Remux->Del(Count); }
|
||||
|
||||
virtual void Detach(void) {}
|
||||
virtual void Attach(void) {}
|
||||
|
||||
virtual cString ToText() const { return ""; };
|
||||
};
|
||||
|
||||
inline bool cStreamdevStreamer::Abort(void)
|
||||
|
||||
@@ -39,7 +39,7 @@ void cSuspendLive::Action(void) {
|
||||
bool cSuspendCtl::m_Active = false;
|
||||
|
||||
cSuspendCtl::cSuspendCtl(void):
|
||||
cControl(m_Suspend = new cSuspendLive) {
|
||||
cControl(m_Suspend = new cSuspendLive, 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/select.h"
|
||||
|
||||
#include <vdr/tools.h>
|
||||
#include <string.h>
|
||||
@@ -27,7 +28,7 @@ cTBSocket::~cTBSocket() {
|
||||
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;
|
||||
int socket;
|
||||
|
||||
@@ -45,13 +46,36 @@ bool cTBSocket::Connect(const std::string &Host, unsigned int Port) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TimeoutMs > 0 && ::fcntl(socket, F_SETFL, O_NONBLOCK) == -1) {
|
||||
::close(socket);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_RemoteAddr.sin_family = AF_INET;
|
||||
m_RemoteAddr.sin_port = htons(Port);
|
||||
m_RemoteAddr.sin_addr.s_addr = inet_addr(Host.c_str());
|
||||
if (::connect(socket, (struct sockaddr*)&m_RemoteAddr,
|
||||
sizeof(m_RemoteAddr)) == -1) {
|
||||
::close(socket);
|
||||
return false;
|
||||
if (::connect(socket, (struct sockaddr*)&m_RemoteAddr, sizeof(m_RemoteAddr)) == -1) {
|
||||
if (TimeoutMs > 0 && errno == EINPROGRESS) {
|
||||
int so_error;
|
||||
socklen_t len = sizeof(so_error);
|
||||
cTBSelect select;
|
||||
select.Add(socket);
|
||||
if (select.Select(TimeoutMs) == -1 ||
|
||||
::getsockopt(socket, SOL_SOCKET, SO_ERROR, &so_error, &len) == -1) {
|
||||
::close(socket);
|
||||
return false;
|
||||
}
|
||||
if (so_error) {
|
||||
errno = so_error;
|
||||
::close(socket);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
::close(socket);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Type == SOCK_STREAM) {
|
||||
|
||||
@@ -32,11 +32,13 @@ public:
|
||||
Reimplemented for TCP/IPv4 sockets. */
|
||||
virtual ssize_t SysWrite(const void *Buffer, size_t Length) const;
|
||||
|
||||
/* Connect() tries to connect an available local socket to the port given
|
||||
by Port of the target host given by Host in numbers-and-dots notation
|
||||
(i.e. "212.43.45.21"). Returns true if the connection attempt was
|
||||
successful and false otherwise, setting errno appropriately. */
|
||||
virtual bool Connect(const std::string &Host, uint Port);
|
||||
/* Connect() tries to connect an available local socket within TimeoutMs
|
||||
milliseconds to the port given by Port of the target host given by
|
||||
Host in numbers-and-dots notation (i.e. "212.43.45.21"). A TimeoutMs
|
||||
of 0 will disable non-blocking IO for the connect call. Returns true
|
||||
if the connection attempt was successful and false otherwise, setting
|
||||
errno appropriately. */
|
||||
virtual bool Connect(const std::string &Host, uint Port, uint TimeoutMs = 0);
|
||||
|
||||
/* 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
|
||||
@@ -60,7 +62,7 @@ public:
|
||||
/* Accept() returns a newly created cTBSocket, which is connected to the
|
||||
first connection request on the queue of pending connections of a
|
||||
listening socket. If no connection request was pending, or if any other
|
||||
error occured, the resulting cTBSocket is closed. */
|
||||
error occurred, the resulting cTBSocket is closed. */
|
||||
virtual cTBSocket Accept(void) const;
|
||||
|
||||
/* 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) {
|
||||
cTBSelect sel;
|
||||
int ms, offs;
|
||||
|
||||
cTimeMs starttime;
|
||||
ms = TimeoutMs;
|
||||
|
||||
offs = 0;
|
||||
sel.Clear();
|
||||
sel.Add(m_Filed, true);
|
||||
while (Length > 0) {
|
||||
int b;
|
||||
|
||||
ms = TimeoutMs - starttime.Elapsed();
|
||||
if (ms <= 0) {
|
||||
errno = ETIMEDOUT;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sel.Select(ms) == -1)
|
||||
return false;
|
||||
|
||||
@@ -76,11 +81,6 @@ bool cTBSource::TimedWrite(const void *Buffer, size_t Length, uint TimeoutMs) {
|
||||
Length -= b;
|
||||
}
|
||||
|
||||
ms = TimeoutMs - starttime.Elapsed();
|
||||
if (ms <= 0) {
|
||||
errno = ETIMEDOUT;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
/* Close() resets the source to the uninitialized state (IsOpen() == false)
|
||||
and must be called by any derivations after really closing the source.
|
||||
Returns true on success and false on error, setting errno appropriately.
|
||||
The object is in closed state afterwards, even if an error occured. */
|
||||
The object is in closed state afterwards, even if an error occurred. */
|
||||
virtual bool Close(void);
|
||||
|
||||
/* Read() reads at most Length bytes into the storage pointed to by Buffer,
|
||||
|
||||
Reference in New Issue
Block a user