Compare commits
115 Commits
v0_4
...
servermenu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7175d7de91 | ||
|
|
94aef85adc | ||
|
|
9b91301d94 | ||
|
|
7347e24123 | ||
|
|
c652e8fa81 | ||
|
|
ce1583a756 | ||
|
|
20c931ac6d | ||
|
|
f9224d0618 | ||
|
|
30674fb6c8 | ||
|
|
8850e63da5 | ||
|
|
6c9c6ca77f | ||
|
|
9cb72968f6 | ||
|
|
b9d2f66295 | ||
|
|
e0e14bb322 | ||
|
|
077398fdef | ||
|
|
bdaea38b86 | ||
|
|
6b633dbfa2 | ||
|
|
615f101b9d | ||
|
|
2fae067cfe | ||
|
|
475e7bbd6a | ||
|
|
4c0888d483 | ||
|
|
24a1638ddd | ||
|
|
cc74cba396 | ||
|
|
2c8377d42a | ||
|
|
673fbae2e2 | ||
|
|
f73665a0bd | ||
|
|
f6299b4584 | ||
|
|
09e0a0176b | ||
|
|
19e8cf2a28 | ||
|
|
17bb6390f9 | ||
|
|
58a4f3dbe5 | ||
|
|
1a933d6cc9 | ||
|
|
c06970123c | ||
|
|
2cb0ba6cf1 | ||
|
|
ee7929bd39 | ||
|
|
dc41eecffd | ||
|
|
30930d5c74 | ||
|
|
3a2936a140 | ||
|
|
06d5418e42 | ||
|
|
0783799826 | ||
|
|
316a1f884f | ||
|
|
9fbc166848 | ||
|
|
abf8a62fe9 | ||
|
|
a35675490d | ||
|
|
0c07109974 | ||
|
|
6ea5efe939 | ||
|
|
0a860a1e3e | ||
|
|
c7d19619db | ||
|
|
c0a7f1610a | ||
|
|
edc3ad1c8e | ||
|
|
ebacf759ab | ||
|
|
1a1141e4a1 | ||
|
|
7517b874d8 | ||
|
|
6f984b87e3 | ||
|
|
8a128f7c57 | ||
|
|
2f027a6ffc | ||
|
|
dd7cc838ad | ||
|
|
93c9aa9af0 | ||
|
|
c882a991cc | ||
|
|
b44c9ac3f5 | ||
|
|
b3c72deaed | ||
|
|
d794d83854 | ||
|
|
b5bd22e7bd | ||
|
|
e99ea00348 | ||
|
|
73e30fd5ca | ||
|
|
ac40edfd24 | ||
|
|
6da7cf90d9 | ||
|
|
1cf41fb16f | ||
|
|
6fb88f8d99 | ||
|
|
0e9e22c23d | ||
|
|
1474cdf799 | ||
|
|
4ab256caea | ||
|
|
4f3a6aae88 | ||
|
|
d5fa0a49f8 | ||
|
|
8469a4d894 | ||
|
|
41763e7710 | ||
|
|
c8cf217c01 | ||
|
|
5644d3c03f | ||
|
|
fe77c5dccb | ||
|
|
dd09bfbb45 | ||
|
|
460d5f0689 | ||
|
|
052a94db5a | ||
|
|
7b8e396f77 | ||
|
|
64bf3e5ecf | ||
|
|
44a71ffe8a | ||
|
|
008e7c8510 | ||
|
|
64ff2c08be | ||
|
|
3d16ba0840 | ||
|
|
78410ea576 | ||
|
|
c26b89f9c2 | ||
|
|
37f2adf02b | ||
|
|
479d5c4b48 | ||
|
|
0e0b4b587d | ||
|
|
9af6ceb007 | ||
|
|
41cf7a5848 | ||
|
|
30aa3b0610 | ||
|
|
ca043780a7 | ||
|
|
cd8d7fbd6c | ||
|
|
459e41e810 | ||
|
|
7184adef83 | ||
|
|
ac2e992305 | ||
|
|
c364a3396d | ||
|
|
4a5af4f489 | ||
|
|
86c82c1381 | ||
|
|
992444cb67 | ||
|
|
c5b5a4cbeb | ||
|
|
42474b8431 | ||
|
|
924cf6e4d0 | ||
|
|
90bae5b011 | ||
|
|
9ecbab8882 | ||
|
|
685f43c6ec | ||
|
|
b2bea54b34 | ||
|
|
ecbde41090 | ||
|
|
dc796dfe74 | ||
|
|
34a62eb2ef |
128
CONTRIBUTORS
128
CONTRIBUTORS
@@ -1,6 +1,10 @@
|
||||
Special thanks go to the following persons (if you think your name is missing
|
||||
here, please send an email to vdrdev@schmirler.de):
|
||||
|
||||
Klaus Schmidinger
|
||||
for VDR as a whole
|
||||
for permission to use VDR 1.6.0 cRemux code for PES remuxing
|
||||
|
||||
Sascha Volkenandt, the original author,
|
||||
for this great plugin
|
||||
|
||||
@@ -8,14 +12,14 @@ The Metzler Brothers
|
||||
as a lot of code has been taken from their libdvbmpeg package
|
||||
|
||||
Angelus (DOm)
|
||||
for providing italian language texts
|
||||
for providing Italian language texts
|
||||
for reporting problems with the Elchi-Patch
|
||||
|
||||
Michal
|
||||
for sending a patch to select the HTTP streamtype via remote
|
||||
|
||||
Rolf Ahrenberg
|
||||
for providing finnish language texts
|
||||
for providing Finnish language texts
|
||||
for adding externremux.sh commandline parameter
|
||||
for silencing compiler warnings
|
||||
for adding PAT, PMT, PCR and EIT to HTTP TS streams
|
||||
@@ -25,6 +29,16 @@ Rolf Ahrenberg
|
||||
for a TS PAT repacker based on Petri Laine's VDR TS recording patch
|
||||
for making it possible to pass parameters to externremux.sh
|
||||
for removing pre VDR 1.4 legacy code
|
||||
for adding gettext support
|
||||
for fixing output format of some debug messages
|
||||
for replacing private members by cThread::Running()/Active()
|
||||
for improving externremux script termination
|
||||
for fixing PAT repacker version field
|
||||
for improving LIMIKUUTIO and PARENTALRATING patch detection
|
||||
for suggesting to include the charset in HTTP replies
|
||||
for requesting replacement of asprintf calls
|
||||
for suggesting to change the URL path from EXTERN to EXT
|
||||
for suggesting increased thread priorities for cStreamdevWriter/Streamer
|
||||
|
||||
Rantanen Teemu
|
||||
for providing vdr-incompletesections.diff
|
||||
@@ -43,6 +57,8 @@ Udo Richter
|
||||
for fixing streamdev-server shutdown
|
||||
for speeding up cPluginStreamdevServer::Active()
|
||||
for adapting to VDR 1.5.0 API
|
||||
for adapting to VDR 1.7.1
|
||||
for proper tsplay-0.2 patch detection
|
||||
|
||||
greenman
|
||||
for reporting that the log could get flooded on connection failures.
|
||||
@@ -68,9 +84,117 @@ alexw
|
||||
|
||||
Olli Lammi
|
||||
for fixing a busy wait when client isn't accepting data fast enough
|
||||
for suggesting signaling instead of sleeping when writing to buffers
|
||||
|
||||
Joerg Pulz
|
||||
for his FreeBSD compatibility patch
|
||||
|
||||
tobi
|
||||
for pointing to unused files in the libdvbmpeg directory
|
||||
|
||||
Diego Pierotto
|
||||
for providing Italian language texts
|
||||
|
||||
micky979
|
||||
for providing French language texts
|
||||
|
||||
Tiroler
|
||||
for reporting a problem when switching between encrypted channels
|
||||
|
||||
Pixelpeter
|
||||
for an initial fix to the "switching between ecncrypted channels" problem
|
||||
|
||||
Anssi Hannula
|
||||
for the vdr-1.6.0-intcamdevices patch
|
||||
for fixing insecure format strings in LSTX handlers
|
||||
|
||||
wirbel
|
||||
for pointing out that section filtering is optional for VDR devices
|
||||
for reporting a problem with Makefile defines in VDR 1.7.4+
|
||||
|
||||
Jori Hamalainen
|
||||
for extensive testing while making stream compatible to Network Media Tank
|
||||
for adding Network Media Tank browser support to HTML pages
|
||||
|
||||
owagner
|
||||
for pointing out a problem with the encrypted channel switching fix
|
||||
for suggesting use of SO_KEEPALIVE socket option to detect dead sockets
|
||||
|
||||
Joachim König-Baltes
|
||||
for fixing Min/MaxPriority parsing
|
||||
|
||||
Artem Makhutov
|
||||
for suggesting and heavy testing IGMP based multicast streaming
|
||||
|
||||
Alwin Esch
|
||||
for adding XBMC support by extending VTP capabilities
|
||||
for adding VDR 1.7.11 parental rating support for VTP LSTE command
|
||||
for adding the DELT FORCE option to delete running timers
|
||||
|
||||
BBlack
|
||||
for reporting that updating recordings list on CmdPLAY is a bad idea
|
||||
|
||||
Milan Hrala
|
||||
for providing Slovak language texts
|
||||
|
||||
Valdemaras Pipiras
|
||||
for providing Lithuanian language texts
|
||||
|
||||
sk8ter
|
||||
for fixing failures when switching between two encrypted channels
|
||||
|
||||
lhanisch
|
||||
for fixing a memory leak in cStreamdevPatFilter::GetPid
|
||||
|
||||
Eric Valette
|
||||
for adding support for EnhancedAC3
|
||||
|
||||
carel
|
||||
for reporting "plugin doesn't honor APIVERSION" error in new Makefile
|
||||
for helping to find a way to cleanly shutdown externremux with mencoder
|
||||
for reporting that GetClippedNumProvidedSystems is no longer up-to-date
|
||||
|
||||
wolfi.m
|
||||
for reporting a typo in externremux quality parameter value
|
||||
|
||||
Norman Thiel
|
||||
for reporting a wrong URL path in m3u playlists
|
||||
|
||||
vel_tins
|
||||
for reporting that externremux x264 uses value of ABR for VBR
|
||||
for various suggestions to improve externremux.sh
|
||||
|
||||
Matthias Prill
|
||||
for reporting a compiler error with older libstdc++ versions
|
||||
|
||||
Timothy D. Lenz
|
||||
for reporting missing support for invisible channel groups in HTTP menu
|
||||
|
||||
Rainer Blickle
|
||||
for reporting that channel switches may interrupt live TV on the server
|
||||
|
||||
Gavin Hamill
|
||||
for reporting that ES/PS/PES no longer works
|
||||
|
||||
Michal Novotny
|
||||
for reporting that switching away live TV fails when "always suspended"
|
||||
|
||||
wtor
|
||||
for reporting that a client may interrupt replaying on FF cards
|
||||
for helping to debug channel switch issues on FF cards
|
||||
|
||||
Javier Bradineras
|
||||
for providing Spanish language texts
|
||||
|
||||
Pekko Tiitto
|
||||
for providing a git mirror of streamdev's lost CVS repository
|
||||
for suggesting to use mencoder params -alang and -msglevel in externremux
|
||||
|
||||
Lubo¨ Dole¸el
|
||||
for suggesting higher buffer sizes to fix some ringbuffer overflows
|
||||
|
||||
Ville Skyttä
|
||||
for updating the outdated COPYING file and FSF address
|
||||
for restricting VTP command RENR to liemikuutio patch < 1.32
|
||||
for fixing memory and filedescriptor leaks in libdvbmpeg
|
||||
for code cleanup and optimization
|
||||
|
||||
39
COPYING
39
COPYING
@@ -1,12 +1,12 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
@@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
@@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
@@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
@@ -225,7 +225,7 @@ impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
@@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
@@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
@@ -303,10 +303,9 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
@@ -336,5 +335,5 @@ necessary. Here is a sample; alter the names:
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
|
||||
210
HISTORY
210
HISTORY
@@ -1,5 +1,215 @@
|
||||
VDR Plugin 'streamdev' Revision History
|
||||
---------------------------------------
|
||||
|
||||
- code cleanup and optimization (thanks to Ville Skyttä)
|
||||
- properly shutdown IGMP timeout handler thread when the plugin is stopped.
|
||||
Fixes occasional segfaults on VDR exit.
|
||||
- fixed memory leak in libdvbmpeg read_pes (thanks to Ville Skyttä)
|
||||
- dropped several unused functions in libdvbmpeg
|
||||
- restricted VTP command RENR to liemikuutio patch < 1.32. Build fails with
|
||||
newer versions of this patch (thanks to Ville Skyttä)
|
||||
- updated outdated COPYING file and FSF address (thanks to Ville Skyttä)
|
||||
- include SDT and TDT in TS streams
|
||||
- the icy-name HTTP header sent with radio streams makes VLC pick the wrong
|
||||
demuxer. Send icy-name only for ES audio streams.
|
||||
- fixed regression of "live TV must be switched in VDR main thread" change:
|
||||
deadlock in IGMP streaming server when switching live TV.
|
||||
- streamdev-client returns true in its AvoidRecording() method introduced
|
||||
with VDR 1.7.19. Note however that the impact of NumProvidedSystems is
|
||||
higher.
|
||||
- updated device selection to code of VDR 1.7.19
|
||||
- adaption to VDR 1.7.12 cReceiver API change
|
||||
- increased WRITERBUFSIZE. Has been reported to fix some ringbuffer
|
||||
overflows (thanks to Lubo¨ Dole¸el)
|
||||
- check availability of channel if VTP command TUNE is called without
|
||||
prior PROV call (e.g. client side EPG scan)
|
||||
- added support for VDR 1.7.19 SignalStrength/SignalQuality
|
||||
- analog video channels use the same transponder and pid for different
|
||||
channels, so streamdev-client must always issue TUNE command
|
||||
- server must close the VTP connection also if filter stream is broken
|
||||
- fixed missing #ifdefs for new NumProvidedSystems setup option
|
||||
- new externremux.sh mencoder config options: audio pid by language code
|
||||
(-alang) and verbosity (-msglevel) (thanks to Pekko Tiitto)
|
||||
- writer must not spend too much time waiting in select() without checking
|
||||
if the thread has been cancelled
|
||||
- added Spanish translation (thanks to Javier Bradineras)
|
||||
- live TV must be switched in VDR main thread
|
||||
- dropped compatibility with VDR < 1.5.16
|
||||
- return value of streamdev-clients cDevice::NumProvidedSystems() now
|
||||
configurable in plugin setup
|
||||
|
||||
2011-02-11: Version 0.5.1
|
||||
|
||||
- updated copy of GetClippedNumProvidedSystems to the version used since
|
||||
VDR 1.7.15 (reported by carel@vdrportal)
|
||||
- fixed the code deciding if a device is in use for live TV or not. It did
|
||||
not work as expected for FF cards (reported by wtor@vdrportal)
|
||||
- increased client side timeout for TUNE command
|
||||
- more dsyslog messages to help troubleshouting channel switch issues
|
||||
- improved the channel switch code trying to move live TV to different card
|
||||
- make sure that a client doesn't interrupt replaying on server's FF card
|
||||
(reported by wtor@vdrportal)
|
||||
- switching away live TV failed even when "always suspended" (reported by
|
||||
Michal Novotny)
|
||||
- fixed regression: no receiver created for ES/PS/PES (reported by Gavin
|
||||
Hamill)
|
||||
- VTP no longer uses a static priority value for its server-side receivers.
|
||||
The server stores channel and priority requested with the PROV command and
|
||||
re-uses these values in a subsequent TUNE for the same channel. The new
|
||||
PRIO command is used to update the receiver's priority if necessary.
|
||||
- added parameter HEIGHT to externremux.sh
|
||||
- fixed syslog messages reporting local instead of remote IP and port
|
||||
- fixed regression of the GetDevice(...) change. Filter streaming to clients
|
||||
with a recent VDR version no longer worked.
|
||||
- log an error if externremux.sh is missing or not executable
|
||||
- since VDR 1.5.0 cDevice::GetDevice(...) is no longer a query only method.
|
||||
It detaches all receivers of the device it returns. So it is no longer
|
||||
suitable for testing the availability of a device. Added a copy of VDR's
|
||||
cDevice::GetDevice(...) without the detach receivers part as a workaround
|
||||
until a better solution is available
|
||||
- added dsyslog messages to help troubleshouting channel switch issues
|
||||
- VTP command SUSP didn't attach the player to the primary device
|
||||
- fixed incompatibilities with older make versions
|
||||
- replacing a connections receiver is now an atomic operation. Solves
|
||||
stuttering audio/video due to lost TS packets when adding/removing PIDs
|
||||
- disabled attribute warn_unused_result in libdvbmpeg
|
||||
- slightly increased thread priorities of cStreamdevWriter/Streamer
|
||||
(suggested by Rolf Ahrenberg)
|
||||
- fixed missing support for invisible channel groups (groups without name)
|
||||
in HTTP menu (reported by Timothy D. Lenz)
|
||||
- don't quote actual program call in externremux.sh, so you can run the
|
||||
program through e.g. nice or taskset just by extending the variable
|
||||
which holds the program name
|
||||
- in externremux.sh each mencoder audio and video codec has a dedicated
|
||||
variable for a default option string now. Still you can override each
|
||||
default option with an URL parameter
|
||||
- externremux.sh mencoder now uses scale parameter with negative height
|
||||
instead of -xy for scaling (suggested by vel_tins@vdrportal)
|
||||
- added FPS (frames per second) parameter to externremux.sh (suggested by
|
||||
vel_tins@vdrportal)
|
||||
- don't use std::map.at(). It's not available in older libstdc++ version
|
||||
(reported by Matthias Prill)
|
||||
- fixed extremux x264 using value of ABR for VBR (thanks to vel_tins@vdrportal)
|
||||
|
||||
2010-07-20: Version 0.5.0b
|
||||
|
||||
- fixed wrong URL path in m3u playlists (reported by Norman Thiel)
|
||||
|
||||
2010-07-20: Version 0.5.0a
|
||||
|
||||
- set externremux.sh executable in distribution archive
|
||||
- externremux quality value should be wlan54, not wlan45 (reported by
|
||||
wolfi.m@vdrportal)
|
||||
|
||||
2010-07-19: Version 0.5.0
|
||||
|
||||
- using SIGINT in externremux to kill mencoder works better than SIGTERM;
|
||||
especially x264 still needs a SIGKILL sometimes
|
||||
- added --remove-destination to cp commands installing plugins
|
||||
- fixed "plugin doesn't honor APIVERSION" (reported by carel@vdrportal)
|
||||
- updated Italian translation (thanks to Diego Pierotto)
|
||||
- config option "client may suspend" hidden if not applicable
|
||||
- updated and enhanced README
|
||||
- separated language resources of client and server
|
||||
- restructured build process
|
||||
- added support for HTTP method HEAD
|
||||
- rewrite of externremux.sh, including support for various URL parameters,
|
||||
logging and improved shutdown
|
||||
- start externremux script in a separate process group
|
||||
- changed HTTP URL path for externremux from EXTERN to EXT (suggested by
|
||||
Rolf Ahrenberg)
|
||||
- HTTP headers now have to be emitted by externremux script
|
||||
- pass channel related information and URL parameters to externremux script
|
||||
through environment
|
||||
- implement CGI like interface for externremux script
|
||||
- dropped "Synchronize EPG" feature. Please use epgsync-plugin instead
|
||||
(available from http://vdr.schmirler.de)
|
||||
- proper tsplay-0.2 patch detection. tsplay-0.1 is no longer recognized
|
||||
(thanks to Udo Richter)
|
||||
- added compatibility with VDR 1.6 tsplay-0.1 patch
|
||||
- added support for EnhancedAC3 (thanks to Eric Valette)
|
||||
- fixed a memory leak in cStreamdevPatFilter::GetPid (thanks to lhanisch)
|
||||
- length -1 is the correct value for streams in M3U playlists
|
||||
- switching between two encrypted channels on the same transponder didn't
|
||||
always work (thanks to sk8ter@vdrportal)
|
||||
- added DELT FORCE option to delete running timers (thanks to Alwin Esch)
|
||||
- added VDR 1.7.11 parental rating support for VTP LSTE command (thanks to
|
||||
Alwin Esch)
|
||||
- added Lithuanian translation (thanks to Valdemaras Pipiras)
|
||||
- fixed missing virtual destructor for cTSRemux
|
||||
- added defines for large file support to Makefile as required by VDR 1.7.4+
|
||||
(reported by wirbel@vdrportal)
|
||||
- added Slovak translation (thanks to Milan Hrala)
|
||||
- fixed regression from fix for switching between encrypted channels. It was
|
||||
no longer possible to receive multiple (FTA) streams from the same
|
||||
transponder
|
||||
- silenced warnings concerning asprintf (requested by Rolf Ahrenberg)
|
||||
- don't update recordings list on CmdPLAY (reported by BBlack)
|
||||
- cleaned up common.h / common.c
|
||||
- dropped cStreamdevMenuSetupPage
|
||||
- report charset in HTTP replies (suggested by Rolf Ahrenberg)
|
||||
- use SO_KEEPALIVE option on all sockets do detect dead sockets (thanks to
|
||||
owagner)
|
||||
- enable PatFilter for externremux, so VLC can be used as remuxer or client
|
||||
- fixed insecure format strings in LSTX handlers (thanks to Anssi Hannula)
|
||||
- updated Finish translation (thanks to Rolf Ahrenberg)
|
||||
- removed redefinitions in includes - caused problems in older compilers
|
||||
- fixed ts2ps.h defines
|
||||
- fixed missing virtual for cTS2PESRemux destructor
|
||||
- silenced format mismatch warning on 64bit OS
|
||||
- added XBMC support by extending VTP capabilities (thanks to Alwin Esch)
|
||||
- now there's a common baseclass for all remuxers, make use of it
|
||||
- added cDevice::NumProvidedSystems() which was introduced in VDR 1.7.0
|
||||
- added namespace to remuxers
|
||||
- increased WRITERBUFSIZE - buffer was too small for high bandwidth content
|
||||
- removed cStreamdevStreamer::m_Running
|
||||
- eliminated potential busy waits in remuxers
|
||||
- updated cTSRemux static helpers to code of their VDR 1.6.0 counterparts
|
||||
- re-enabled PES vor VDR 1.7.3+. Streamdev now uses a copy of VDR 1.6.0's
|
||||
cRemux for TS to PES remuxing.
|
||||
- make sure that only complete TS packets are written to ringbuffers
|
||||
- use signaling instead of sleeps when writing to ringbuffers
|
||||
- optimized cStreamdevPatFilter PAT packet initialization
|
||||
- fixed cStreamdevPatFilter not processing PATs with length > TS_SIZE - 5
|
||||
- use a small ringbuffer for cStreamdevPatFilter instead of writing to
|
||||
cStreamdevStreamers SendBuffer as two threads mustn't write to the same
|
||||
ringbuffer
|
||||
- added missing call to StopSectionHandler which could cause crashes when
|
||||
shutting down VDR
|
||||
- added IGMP based multicast streaming
|
||||
- ignore trailing blank lines in HTTP requests
|
||||
- fixed parsing Min/MaxPriority from config (thanks to Joachim König-Baltes)
|
||||
- updated Finnish translation (thanks to Rolf Ahrenberg)
|
||||
- added Min/MaxPriority parameters. Can be used to keep client VDR from
|
||||
using streamdev e.g. when recording
|
||||
- disabled PES for VDR 1.7.3+
|
||||
- added Network Media Tank browser support to HTML pages (thanks to Jori
|
||||
Hamalainen)
|
||||
- minor fixes of PAT repacker
|
||||
- repack and send every PAT packet we receive
|
||||
- fixed null pointer in server.c when cConnection::Accept() failes
|
||||
- consider Pids from channels.conf when HTTP TS streaming. Section filtering
|
||||
is an optional feature for VDR devices, so we must not rely on the PMT
|
||||
alone (pointed out by wirbel@vdrportal)
|
||||
- improved externremux script termination (thanks to Rolf Ahrenberg)
|
||||
- use cThread::Running()/Active() instead of private members (thanks to
|
||||
Rolf Ahrenberg)
|
||||
- fixed output format of some debug messages (thanks to Rolf Ahrenberg)
|
||||
- added HTTP authentication
|
||||
- compatibility for VDR 1.7.1 (thanks to Udo Richter)
|
||||
- added vdr-1.6.0-intcamdevices.patch (thanks to Anssi Hannula)
|
||||
- fixed problem when switching from one encrypted channel to an other
|
||||
(reported by Tiroler@vdrportal, initial bugfix by pixelpeter@vdrportal,
|
||||
another fix by owagner@vdrportal)
|
||||
- added preprocessor directive for ancient gcc
|
||||
- added Russian translation (thanks to Oleg Roitburd)
|
||||
- fixed assignment of externremux.sh's default location (reported by plautze)
|
||||
- added French translation (thanks to micky979)
|
||||
- added Italian translation (thanks to Diego Pierotto)
|
||||
- added gettext support (thanks to Rolf Ahrenberg)
|
||||
- added vdr-1.6.0-ignore_missing_cam patch
|
||||
- dropped obsolete respect_ca patch
|
||||
- removed legacy code for < VDR 1.5.9 (thanks to Rolf Ahrenberg)
|
||||
|
||||
2008-04-07: Branched v0_4
|
||||
|
||||
|
||||
129
Makefile
129
Makefile
@@ -1,36 +1,59 @@
|
||||
#
|
||||
# Makefile for a Video Disk Recorder plugin
|
||||
#
|
||||
# $Id: Makefile,v 1.15 2008/04/07 14:50:32 schmirl Exp $
|
||||
# $Id: Makefile,v 1.23 2010/08/02 10:36:59 schmirl Exp $
|
||||
|
||||
# The official name of this plugin.
|
||||
# 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 C++ compiler and options:
|
||||
|
||||
CXX ?= g++
|
||||
CXXFLAGS ?= -fPIC -g -O2 -Wall -Woverloaded-virtual -Wno-parentheses
|
||||
|
||||
### The directory environment:
|
||||
|
||||
VDRDIR = ../../..
|
||||
LIBDIR = ../../lib
|
||||
TMPDIR = /tmp
|
||||
|
||||
### Allow user defined options to overwrite defaults:
|
||||
|
||||
-include $(VDRDIR)/Make.config
|
||||
|
||||
### 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:
|
||||
|
||||
@@ -39,74 +62,35 @@ PACKAGE = vdr-$(ARCHIVE)
|
||||
|
||||
### Includes and Defines (add further entries here):
|
||||
|
||||
INCLUDES += -I$(VDRDIR)/include -I.
|
||||
INCLUDES += -I$(VDRDIR)/include -I..
|
||||
|
||||
DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
|
||||
DEFINES += -D_GNU_SOURCE
|
||||
|
||||
### The object files (add further files here):
|
||||
|
||||
COMMONOBJS = common.o i18n.o \
|
||||
\
|
||||
tools/source.o tools/select.o tools/socket.o tools/tools.o
|
||||
|
||||
CLIENTOBJS = $(PLUGIN)-client.o \
|
||||
\
|
||||
client/socket.o client/device.o client/setup.o \
|
||||
client/filter.o
|
||||
|
||||
|
||||
SERVEROBJS = $(PLUGIN)-server.o \
|
||||
\
|
||||
server/server.o server/connectionVTP.o server/connectionHTTP.o \
|
||||
server/componentHTTP.o server/componentVTP.o server/connection.o \
|
||||
server/component.o server/suspend.o server/setup.o server/streamer.o \
|
||||
server/livestreamer.o server/livefilter.o server/menuHTTP.o \
|
||||
\
|
||||
remux/tsremux.o remux/ts2ps.o remux/ts2es.o remux/extern.o
|
||||
|
||||
ifdef DEBUG
|
||||
DEFINES += -DDEBUG
|
||||
DEFINES += -DDEBUG
|
||||
endif
|
||||
ifdef STREAMDEV_DEBUG
|
||||
DEFINES += -DDEBUG
|
||||
endif
|
||||
|
||||
### The main target:
|
||||
|
||||
.PHONY: all dist clean
|
||||
all: libvdr-$(PLUGIN)-client.so libvdr-$(PLUGIN)-server.so
|
||||
|
||||
### Implicit rules:
|
||||
|
||||
%.o: %.c
|
||||
$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
|
||||
|
||||
# Dependencies:
|
||||
|
||||
MAKEDEP = $(CXX) -MM -MG
|
||||
DEPFILE = .dependencies
|
||||
ifdef GCC3
|
||||
$(DEPFILE): Makefile
|
||||
@rm -f $@
|
||||
@for i in $(CLIENTOBJS:%.o=%.c) $(SERVEROBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) ; do \
|
||||
$(MAKEDEP) $(DEFINES) $(INCLUDES) -MT "`dirname $$i`/`basename $$i .c`.o" $$i >>$@ ; \
|
||||
done
|
||||
else
|
||||
$(DEPFILE): Makefile
|
||||
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(CLIENTOBJS:%.o=%.c) $(SERVEROBJS:%.o=%.c) \
|
||||
$(COMMONOBJS:%.o=%.c) > $@
|
||||
endif
|
||||
|
||||
-include $(DEPFILE)
|
||||
.PHONY: all client server dist clean
|
||||
all: client server
|
||||
|
||||
### Targets:
|
||||
|
||||
libdvbmpeg/libdvbmpegtools.a: libdvbmpeg/*.c libdvbmpeg/*.h
|
||||
$(MAKE) -C ./libdvbmpeg libdvbmpegtools.a
|
||||
client:
|
||||
$(MAKE) -C ./tools
|
||||
$(MAKE) -C ./client
|
||||
# installs to $(LIBDIR)/libvdr-streamdev-client.so.$(APIVERSION)
|
||||
|
||||
libvdr-$(PLUGIN)-client.so: $(CLIENTOBJS) $(COMMONOBJS) libdvbmpeg/libdvbmpegtools.a
|
||||
libvdr-$(PLUGIN)-server.so: $(SERVEROBJS) $(COMMONOBJS) libdvbmpeg/libdvbmpegtools.a
|
||||
|
||||
%.so:
|
||||
$(CXX) $(CXXFLAGS) -shared $^ -o $@
|
||||
@cp $@ $(LIBDIR)/$@.$(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)
|
||||
@@ -117,5 +101,8 @@ dist: clean
|
||||
@echo Distribution package created as $(PACKAGE).tgz
|
||||
|
||||
clean:
|
||||
@-rm -f $(COMMONOBJS) $(CLIENTOBJS) $(SERVEROBJS) $(DEPFILE) *.so *.tgz core* *~
|
||||
$(MAKE) -C ./tools clean
|
||||
$(MAKE) -C ./libdvbmpeg clean
|
||||
$(MAKE) -C ./remux clean
|
||||
$(MAKE) -C ./client clean
|
||||
$(MAKE) -C ./server clean
|
||||
|
||||
371
README
371
README
@@ -15,18 +15,20 @@ Contents:
|
||||
|
||||
1. Description
|
||||
2. Installation
|
||||
2.1 VDR 1.3.X and older
|
||||
2.2 VDR 1.4.X and above
|
||||
2.3 Updating from streamdev 0.3.x
|
||||
2.1 Compatibility
|
||||
2.2 Compiling
|
||||
2.3 Updating
|
||||
3. Usage
|
||||
3.1 Usage HTTP server
|
||||
3.2 Usage VDR-to-VDR server
|
||||
3.3 Usage VDR-to-VDR client
|
||||
3.2 Usage IGMP multicast server
|
||||
3.3 Usage VDR-to-VDR server
|
||||
3.4 Usage VDR-to-VDR client
|
||||
4. Other useful Plugins
|
||||
4.1 Plugins for VDR-to-VDR clients
|
||||
4.2 Plugins for Server
|
||||
4.3 Alternatives
|
||||
5. Known Problems
|
||||
5. externremux.sh
|
||||
6. Known Problems
|
||||
|
||||
|
||||
1. Description:
|
||||
@@ -61,7 +63,7 @@ the PROTOCOL file.
|
||||
2. Installation:
|
||||
----------------
|
||||
|
||||
Let's say streamdev's version is 0.4.0 and vdr's version is 1.X.X. If you
|
||||
Let's say streamdev's version is 0.5.0 and vdr's version is 1.X.X. If you
|
||||
use anything else please exchange the version numbers appropriately (this
|
||||
way I don't have to update this section all the times;) ).
|
||||
|
||||
@@ -78,56 +80,93 @@ If you want to drive additional Input-Devices (with different sources) on the
|
||||
client, you can merge the channels.conf files. VDR will detect if the local
|
||||
device or the network device can receive the channels.
|
||||
|
||||
Last, but not least you have to copy the streamdev folder into the
|
||||
"plugins/streamdev" subfolder of VDR's config-directory (which is equal to your
|
||||
video-directory if not specified otherwise). For example, if you didn't specify
|
||||
a separate config-directory, and specified your video directory as "/video0",
|
||||
the directory has to be copied to /video0/plugins/streamdev.
|
||||
Last, but not least you have to copy the streamdev-server folder into the
|
||||
"plugins/streamdev-server" subfolder of VDR's config-directory (which is equal
|
||||
to your video-directory if not specified otherwise). For example, if you didn't
|
||||
specify a separate config-directory, and set your video directory to "/video0",
|
||||
the directory has to be copied to /video0/plugins/streamdev-server.
|
||||
|
||||
The directory contains a file named streamdevhosts.conf which you must adjust
|
||||
to your needs. The syntax is the same as for svdrphosts.conf, so please consult
|
||||
VDR's documentation on how to fill that file, if you can't do it on-the-fly.
|
||||
|
||||
There's also a sample externremux.sh script in this directory. It is used by
|
||||
streamdev's external remux feature. The sample script uses mencoder. Please
|
||||
check the script for further information. You can specify a different script
|
||||
location with the -r parameter. The VDR commandline would then include a
|
||||
streamdev's external remux feature. The sample script uses mencoder by default.
|
||||
Please check the script for further information. You can specify a different
|
||||
script location with the -r parameter. The VDR commandline would then include a
|
||||
"-P 'streamdev-server -r /usr/local/bin/remux.sh'". Note the additional quotes,
|
||||
as otherwise -r will be passed to VDR and not to streamdev.
|
||||
|
||||
|
||||
2.1 VDR 1.3.X and older:
|
||||
------------------------
|
||||
2.1 Compatibility:
|
||||
------------------
|
||||
|
||||
This version is not compatible to VDR releases older than 1.4.0. You will
|
||||
probably need one of the streamdev-0.3.x releases.
|
||||
This version is not compatible to VDR releases older than 1.5.9. Take one of
|
||||
the streamdev-0.4.x releases if you are running at least VDR 1.4.x. For older
|
||||
VDRs you will probably need one of the streamdev-0.3.x releases.
|
||||
|
||||
2.2 VDR 1.4.X and above:
|
||||
------------------------
|
||||
2.2 Compiling:
|
||||
--------------
|
||||
|
||||
cd vdr-1.X.X/PLUGINS/src
|
||||
tar xvfz vdr-streamdev-0.4.0.tgz
|
||||
ln -s streamdev-0.4.0 streamdev
|
||||
cp -r streamdev/streamdev VDRCONFDIR/plugins/
|
||||
cd ../..
|
||||
make [options, if necessary] vdr
|
||||
make [options, if necessary] plugins
|
||||
cd vdr-1.X.X/PLUGINS/src
|
||||
tar xvfz vdr-streamdev-0.5.0.tgz
|
||||
ln -s streamdev-0.5.0 streamdev
|
||||
cp -r streamdev/streamdev-server VDRCONFDIR/plugins/
|
||||
cd ../..
|
||||
make [options, if necessary] vdr
|
||||
make [options, if necessary] plugins
|
||||
|
||||
2.3 Updating from streamdev 0.3.x
|
||||
----------------------------------
|
||||
To build only the plugin, change into the streamdev source folder and issue
|
||||
make
|
||||
|
||||
Starting with streamdev 0.4.0, all additional files are kept in a directory
|
||||
called "streamdev" inside VDR's plugin config directory. This affects in
|
||||
particular the file "streamdevhosts.conf". You will have to move it to its
|
||||
To build only streamdev-server or only streamdev-client, use
|
||||
make server
|
||||
make client
|
||||
|
||||
2.3 Updating:
|
||||
--------------
|
||||
|
||||
If you are updating streamdev from an earlier release, you might have to
|
||||
perform some additional steps. Check which version you've been running before,
|
||||
then read below for the necessary changes.
|
||||
|
||||
* Location of files:
|
||||
--------------------
|
||||
(Affected: 0.3.x, 0.4.x, 0.4.0pre, 0.5.0pre)
|
||||
|
||||
Starting with streamdev 0.5.0, all additional files are kept in a directory
|
||||
called "streamdev-server" inside VDR's plugin config directory. It is the new
|
||||
default location of externremux.sh and the new place where streamdev-server
|
||||
expects the file "streamdevhosts.conf". You will have to move this file to its
|
||||
new location:
|
||||
|
||||
mv VDRCONFDIR/plugins/streamdevhosts.conf VDRCONFDIR/plugins/streamdev/
|
||||
streamdev 0.3.x:
|
||||
mv VDRCONFDIR/plugins/streamdevhosts.conf VDRCONFDIR/plugins/streamdev-server/
|
||||
|
||||
(Directory VDRCONFDIR/plugins/streamdev already exists, as you copied the
|
||||
whole folder from the sources directory as suggested above, right?)
|
||||
streamdev 0.4.x, 0.4.0pre and 0.5.0pre:
|
||||
mv VDRCONFDIR/plugins/streamdev VDRCONFDIR/plugins/streamdev-server/
|
||||
|
||||
The new default location for externremux.sh is also in this directory.
|
||||
Now check the contents of streamdevhosts.conf. Does it contain a "0.0.0.0/0"
|
||||
entry? If your VDR machine is connected to the Internet, this line gives
|
||||
*anyone* full access to streamdev, unless you took some other measures to
|
||||
prevent this (e.g. firewall). You might want to remove this line and enable
|
||||
HTTP authentication instead.
|
||||
|
||||
* Handling of externremux script:
|
||||
---------------------------------
|
||||
(Affected: 0.3.x, 0.4.0pre, 0.5.0pre)
|
||||
|
||||
Streamdev server's externremux script became responsible for emitting all HTTP
|
||||
headers. A quick and dirty extension to your current script would be:
|
||||
|
||||
echo -ne 'Content-type: video/mpeg\r\n'
|
||||
echo -ne '\r\n'
|
||||
|
||||
However I encourage you to try the new externremux.sh script shipped with the
|
||||
streamdev source distribution.
|
||||
|
||||
To emphasize the required change in externremux, the URL path for passing the
|
||||
stream through externremux has changed from EXTERN to EXT.
|
||||
|
||||
3. Usage:
|
||||
---------
|
||||
@@ -136,10 +175,16 @@ Start the server core itself by specifying -Pstreamdev-server on your VDR
|
||||
commandline. To use the client core, specify -Pstreamdev-client. Both parts
|
||||
can run in one VDR instance, if necessary.
|
||||
|
||||
On the server, the main menu entry "Streamdev Connections" gives you a list
|
||||
of currently connected clients. Use the "red" key to terminate a connection.
|
||||
Note that depending on connection type and client, the client might re-connect
|
||||
sooner or later. Depending on the server setup, the "blue" key might be enabled
|
||||
as well. Please read below.
|
||||
|
||||
The parameter "Suspend behaviour" allows you to specify how the server should
|
||||
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
|
||||
primary device (i.e. disrupt live-tv). If set to "Offer suspend mode", you
|
||||
enable the "blue" key in the server's main menu. It will put the server into
|
||||
"Suspend Mode" (a picture is displayed on TV). Then, a client may switch the
|
||||
primary card to wherever it likes to. While watching TV (Suspend deactivated),
|
||||
the client may not switch the transponder on the primary device. If you set
|
||||
@@ -151,10 +196,10 @@ suspend" to yes, the client can suspend the server remotely (this only applies
|
||||
if "Offer suspend mode" is selected).
|
||||
|
||||
NOTE: This mainly applies to One-Card-Systems, since with multiple cards there
|
||||
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.
|
||||
is no need to switch transponders on the primary interface, if on of the other
|
||||
cards is idle (i.e. if it is not blocked by a recording). If all cards are in
|
||||
use (i.e. when something is recorded, or by multiple clients), this applies to
|
||||
Multiple-Card-Systems as well.
|
||||
|
||||
3.1 Usage HTTP server:
|
||||
----------------------
|
||||
@@ -166,15 +211,15 @@ listen to with the parameter "HTTP Server Port". The parameter "HTTP Streamtype"
|
||||
allows you to specify a default stream type, which is used if no specific type
|
||||
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)
|
||||
EXTERN Pass stream through external script (e.g. for converting with mencoder)
|
||||
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)
|
||||
|
||||
Assuming that you leave the default port (3000), point your web browser to
|
||||
|
||||
http://hostname:3000/
|
||||
http://hostname:3000/
|
||||
|
||||
You will be presented a menu with links to various channel lists, including M3U
|
||||
playlist formats.
|
||||
@@ -182,29 +227,106 @@ playlist formats.
|
||||
If you don't want to use the HTML menu or the M3U playlists, you can access the
|
||||
streams directly like this:
|
||||
|
||||
http://hostname:3000/3
|
||||
http://hostname:3000/S19.2E-0-12480-898
|
||||
http://hostname:3000/3
|
||||
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.
|
||||
|
||||
http://hostname:3000/TS/3
|
||||
http://hostname:3000/PES/S19.2E-0-12480-898
|
||||
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 'EXTERN'. You need to specify
|
||||
Possible values are 'PES', 'TS', 'PS', 'ES' and 'EXT'. You need to specify
|
||||
the ES format explicitly if you want to listen to radio channels. Play them
|
||||
back i.e. with mpg123.
|
||||
|
||||
mpg123 http://hostname:3000/ES/200
|
||||
mpg123 http://hostname:3000/ES/200
|
||||
|
||||
With 'EXTERN' you can also add a parameter which is passed as argument to the
|
||||
externremux script.
|
||||
With 'EXT' you can also add parameters which are passed as arguments to the
|
||||
externremux script (e.g. http://hostname:3000/EXT;param1=value1;param2=value2/3)
|
||||
Check your externremux.sh script for the parameters it understands. For details
|
||||
on how to modify or write your own externremux.sh, please see the chapter upon
|
||||
externremux.sh further down.
|
||||
|
||||
http://hostname:3000/EXTERN;some_parameter/3
|
||||
If you want to access streamdev's HTTP server from the Internet, do *not* grant
|
||||
access for anyone by allowing any IP in "streamdevhosts.conf". Instead, pass the
|
||||
"-a" commandline option to streamdev-server. It takes a username and a password
|
||||
as argument. Clients with an IP not accepted by "streamdevhosts.conf" will then
|
||||
have to login. The VDR commandline will have to look like this:
|
||||
|
||||
3.2 Usage VDR-to-VDR server:
|
||||
vdr ... -P 'streamdev-server -a vdr:secret' ...
|
||||
|
||||
Note the single quotes, as otherwise "-a" will be passed to VDR and not to
|
||||
streamdev-server. The login ("vdr" in the example above) doesn't have to exist
|
||||
as a system account.
|
||||
|
||||
3.2 Usage IGMP multicast server:
|
||||
--------------------------------
|
||||
|
||||
IGMP based multicast streaming is often used by settop boxes to receive IP TV.
|
||||
Streamdev's multicast server allows you to feed live TV from VDR to such a
|
||||
settop box. VLC is known to work well if you look for a software client.
|
||||
|
||||
The advantage of multicasting is that the actual stream is sent out only once,
|
||||
regardless of how many clients want to receive it. The downside is, that you
|
||||
cannot simply multicast across network boundaries. You need multicast routers.
|
||||
For multicast streaming over the public Internet you would even need to register
|
||||
for your own IP range. So don't even think of multicasting via Internet with
|
||||
streamdev! Streamdev will send the stream only to one local ethernet segment and
|
||||
all clients must be connected to this same segment. There must not be a router
|
||||
inbetween. Also note that the client must not run on the streamdev-server
|
||||
machine.
|
||||
|
||||
Each channel is offered on a different multicast IP. Channel 1 is available from
|
||||
multicast IP 239.255.0.1, channel 2 from 239.255.0.2 and so on. The upper limit
|
||||
is 239.255.254.255 which corresponds to channel 65279 (239.255.255.0/24 is
|
||||
reserved according to RFC-2365).
|
||||
|
||||
Before you can use streamdev's multicast server, you might need to patch VDR.
|
||||
Binding an IGMP socket is a privileged operation, so you must start VDR as root.
|
||||
If you pass the -u option to VDR, it will drop almost all priviledges before
|
||||
streamdev is even loaded. Apply vdr-cap_net_raw.diff to keep VDR from dropping
|
||||
the CAP_NET_RAW capability required to bind the IGMP socket. The patch is part
|
||||
of streamdev's source distribution. Check the patches subdirectory. There's no
|
||||
need to patch VDR if it is kept running as root (not recommended).
|
||||
|
||||
The multicast server is disabled by default. Enter the streamdev-server setup
|
||||
menu to enable it and - IMPORTANT - bind the multicast server to the IP of your
|
||||
VDR server's LAN ethernet card. The multicast server will refuse to start with
|
||||
the default bind adresse "0.0.0.0".
|
||||
|
||||
Now edit your streamdevhosts.conf. To allow streaming of all channels, it must
|
||||
contain "239.255.0.0/16". Note that you cannot limit connections by client IP
|
||||
here. You can however restrict which channels are allowed to be multicasted.
|
||||
Enter individual multicast IPs instead of "239.255.0.0/16".
|
||||
|
||||
By default, the linux kernel will refuse to join more than 20 multicast groups.
|
||||
You might want to increase this up to "number_of_channels + 1". Note that it's
|
||||
"number_of_channels", not "maximum_channel_number".
|
||||
|
||||
#First 100 channels:
|
||||
bash# sysctl -w net.ipv4.igmp_max_memberships=101
|
||||
|
||||
#All channels:
|
||||
bash# COUNT=$(grep -c '^[^:]' PATH_TO_YOUR/channels.conf)
|
||||
bash# sysctl -w net.ipv4.igmp_max_memberships=$((COUNT + 1))
|
||||
|
||||
You need to run the sysctl command *before* VDR is started. The setting is lost
|
||||
after the next reboot. Check the documentation of your Linux distro on how to
|
||||
make the setting persist (i.e. have your distro change the value for you as
|
||||
part of the boot procedure). Most likely /etc/sysctl.conf is your friend.
|
||||
|
||||
A multicast server never knows how many clients are actually receiving a stream.
|
||||
If a client signals that it leaves a multicast group, the server has to query
|
||||
for other listeners before it can stop the stream. This may delay zapping from
|
||||
one transponder to an other. The client will probably requests the new channel
|
||||
before the previous stream has been stopped. If there's no free DVB card, VDR
|
||||
won't be able to fulfill the request until a DVB card becomes available and the
|
||||
client resends the request.
|
||||
|
||||
3.3 Usage VDR-to-VDR server:
|
||||
----------------------------
|
||||
|
||||
You can activate the VDR-to-VDR server part in the PlugIn's Setup Menu. It is
|
||||
@@ -213,7 +335,7 @@ port where you want the server to listen for incoming connections. The server
|
||||
will be activated when you push the OK button inside the setup menu, so there's
|
||||
no need to restart VDR.
|
||||
|
||||
3.3 Usage VDR-to-VDR client:
|
||||
3.4 Usage VDR-to-VDR client:
|
||||
----------------------------
|
||||
|
||||
Streamdev-client adds a "Suspend Server" item to VDR's mainmenu. With the
|
||||
@@ -260,14 +382,46 @@ With "Filter Streaming" enabled, the client will receive meta information like
|
||||
EPG data and service information, just as if the client had its own DVB card.
|
||||
Link channels and even a client-side EPG scan have been reported to work.
|
||||
|
||||
The last parameter, "Synchronize EPG", will have the client synchronize it's
|
||||
program table with the server every now and then, but not regularly. This
|
||||
happens when starting the client, and everytime VDR does its housekeeping
|
||||
tasks. The only thing that's guaranteed is, that there will be a minimum
|
||||
interval of ten seconds between each EPG synchronization. With "Filter
|
||||
Streaming" this option has been obsoleted. If you still need to synchronize
|
||||
EPG as additional information is available from the server, you should use the
|
||||
epgsync-plugin instead (http://vdr.schmirler.de).
|
||||
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 are running at least VDR 1.7.0, you can also configure the "Broadcast
|
||||
Systems / Cost" of the streamdev-client device. On a pure streamdev-client only
|
||||
system it doesn't matter what you configure here. But if your client is equipped
|
||||
with a DVB card, you should read on. VDR always prefers the cheapest device
|
||||
in terms of supported broadcast systems and modulations. A DVB-S2 card supports
|
||||
two broadcast systems (DVB-S and DVB-S2). From VDR 1.7.15 on, the supported
|
||||
modulations are counted as well (QPSK, QAM32/64/128/256, VSB8/16, TURBO_FEC).
|
||||
So for a DVB-S2 card which does QPSK you'll get a total cost of three. A DVB-C
|
||||
card (one broadcast system) which can do QAM32,QAM64,QAM128,QAM256 would give
|
||||
you a total of five. Check your log for "frontend ... provides ... with ..."
|
||||
messages to find out the cost of your DVB cards. Then pick a suitable value for
|
||||
streamdev-client. With equal costs, VDR will usually prefer the DVB card and
|
||||
take streamdev for recordings. If streamdev's costs are higher, live TV will
|
||||
use your DVB card until a recordings kicks in. Then the recording will take the
|
||||
DVB card and live TV will be shifted to streamdev (you'll notice a short
|
||||
interruption of live TV).
|
||||
|
||||
Note that streamdev-client acts similar to a DVB card. It is possible to receive
|
||||
multiple channels simultaneously, but only from the same transponder. Just add
|
||||
additional instances of streamdev-client and you will be able to receive as many
|
||||
transponders at a time. The same trick allows a client to receive channels from
|
||||
different servers. To create an additional instance, copy the streamdev-client
|
||||
binary to a different name (e.g. streamdev-client2):
|
||||
|
||||
cd VDRPLUGINLIBDIR
|
||||
cp libvdr-streamdev-client.so.1.X.X libvdr-streamdev-client2.so.1.X.X
|
||||
|
||||
Now add -Pstreamdev-client2 to the VDR commandline. In the VDR plugin setup
|
||||
a second streamdev-client entry should show up. Both instances have to be
|
||||
configured individually.
|
||||
|
||||
|
||||
4. Other useful Plugins:
|
||||
------------------------
|
||||
@@ -311,11 +465,80 @@ With its networking option, xineliboutput provides an alternative to streamdev.
|
||||
You will get the picture of the server VDR, including its OSD. However you
|
||||
won't get independent clients, as they all share the same output.
|
||||
|
||||
5. Known Problems:
|
||||
5. externremux.sh:
|
||||
------------------
|
||||
|
||||
* In VDR-to-VDR setup, the availability of a channel is checked with a different
|
||||
priority than the actual channel switch. The later always uses priority 0.
|
||||
Usually a channel switch for live TV has priority 0 anyway, so it is not a
|
||||
problem here. However timers usually have a higher priority. Either avoid
|
||||
client side recordings or set the priority of client side timers to 0.
|
||||
When selecting streamtype "EXT", the TS stream from VDR is passed through an
|
||||
external program for further processing. By default a script installed at
|
||||
VDRCONFDIR/plugins/streamdev/externremux.sh is expected, however you may
|
||||
specify a different location as parameter -r to the streamdev-server plugin
|
||||
(see chapter upon Installation above).
|
||||
|
||||
The TS stream is passed to the script on stdin, the resulting stream is expected
|
||||
on stdout. The following parameters are passed to the script in the environment:
|
||||
|
||||
* Information on the channel:
|
||||
REMUX_CHANNEL_ID VDR channel ID
|
||||
REMUX_CHANNEL_NAME Channel name
|
||||
REMUX_VTYPE Video type (2 for MPEG-2)
|
||||
REMUX_VPID Video PID (undefined if audio only)
|
||||
REMUX_PPID PCR PID (undefined if equal to VPID)
|
||||
REMUX_TPID Teletext PID (undefined if not available)
|
||||
REMUX_APID Space separated list of audio pids
|
||||
REMUX_ALANG Space separated list of audio languages
|
||||
REMUX_DPID Space separated list of dolby pids
|
||||
REMUX_DLANG Space separated list of dolby languages
|
||||
REMUX_SPID Space separated list of subtitle pids
|
||||
REMUX_SLANG Space separated list of subtitle languages
|
||||
REMUX_PARAM_* All (user supplied) parameters (e.g. REMUX_PARAM_x)
|
||||
|
||||
* Information on the connection (CGI like)
|
||||
REMOTE_ADDR Client IP
|
||||
SERVER_NAME Local IP
|
||||
SERVER_PORT Local port
|
||||
SERVER_PROTOCOL Streamdev protocol (HTTP, VTP, IGMP)
|
||||
SERVER_SOFTWARE Streamdev version
|
||||
All HTTP headers converted to uppercase, '-' replaced by '_' (e.g. USER_AGENT)
|
||||
|
||||
The script should perform the following steps (pseudocode):
|
||||
|
||||
if (SERVER_PROTOCOL == HTTP)
|
||||
write headers (including Content-Type) to STDOUT
|
||||
write empty line to STDOUT
|
||||
if (REQUEST_METHOD == HEAD)
|
||||
exit
|
||||
endif
|
||||
endif
|
||||
while (read STDIN)
|
||||
remux to STDOUT
|
||||
wend
|
||||
|
||||
onSIGINT/SIGKILL: cleanup and exit
|
||||
|
||||
|
||||
6. Known Problems:
|
||||
------------------
|
||||
|
||||
* There have been reports that channel switching with VDR 1.5.x/1.6.x clients
|
||||
sometimes fails. Current version includes a workaround which seems to work, but
|
||||
YMMV ;)
|
||||
|
||||
* Viewing encrypted channels became an issue with VDR's new CAM handling code.
|
||||
Streamdev doesn't provide a (dummy) CAM, so out of the box, VDR won't ever try
|
||||
to receive encrypted channels from streamdev. Pick one of the following
|
||||
solutions to work around the problem:
|
||||
|
||||
1. Force VDR to use streamdev. Open the channels menu on the client (or edit its
|
||||
channels.conf if you know how to do this) and set the CA field of all channels
|
||||
that only the server can decrypt to streamdev's device index. Usually streamdev
|
||||
will get number 9 or 10. Streamdev logs the actual device number when starting
|
||||
up. So please consider the logs for the correct value. Remember to fill in
|
||||
hexadecimal values if you are using an editor to modify your channels.conf
|
||||
(number 10 becomes an "a", number 11 a "b", ...).
|
||||
|
||||
2. Apply either patch "patches/vdr-1.6.0-intcamdevices.patch" or patch
|
||||
"patches/vdr-1.6.0-ignore_missing_cam.diff" to your client VDR. Intcamdevices
|
||||
is the clean solution, but it modifies the VDR API. So you will need to
|
||||
recompile all of your plugins. The ignore_missing_cam patch is trivial, no need
|
||||
to recompile other plugins. However it is not suitable for clients with a DVB
|
||||
card of their own.
|
||||
|
||||
84
client/Makefile
Normal file
84
client/Makefile
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* *~
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: device.c,v 1.18 2008/04/07 14:50:32 schmirl Exp $
|
||||
* $Id: device.c,v 1.27 2010/08/18 10:26:55 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include "client/device.h"
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "tools/select.h"
|
||||
|
||||
#include <vdr/config.h>
|
||||
#include <vdr/channels.h>
|
||||
#include <vdr/ringbuffer.h>
|
||||
#include <vdr/eit.h>
|
||||
@@ -32,10 +33,8 @@ cStreamdevDevice::cStreamdevDevice(void) {
|
||||
|
||||
m_Device = this;
|
||||
m_Pids = 0;
|
||||
m_Priority = -1;
|
||||
m_DvrClosed = true;
|
||||
|
||||
if (StreamdevClientSetup.SyncEPG)
|
||||
ClientSocket.SynchronizeEPG();
|
||||
}
|
||||
|
||||
cStreamdevDevice::~cStreamdevDevice() {
|
||||
@@ -50,10 +49,16 @@ cStreamdevDevice::~cStreamdevDevice() {
|
||||
|
||||
Cancel(3);
|
||||
|
||||
StopSectionHandler();
|
||||
DELETENULL(m_Filters);
|
||||
DELETENULL(m_TSBuffer);
|
||||
}
|
||||
|
||||
#if APIVERSNUM >= 10700
|
||||
int cStreamdevDevice::NumProvidedSystems(void) const
|
||||
{ return StreamdevClientSetup.NumProvidedSystems; }
|
||||
#endif
|
||||
|
||||
bool cStreamdevDevice::ProvidesSource(int Source) const {
|
||||
Dprintf("ProvidesSource, Source=%d\n", Source);
|
||||
return true;
|
||||
@@ -69,7 +74,9 @@ bool cStreamdevDevice::IsTunedToTransponder(const cChannel *Channel)
|
||||
{
|
||||
bool res = false;
|
||||
if (ClientSocket.DataSocket(siLive) != NULL
|
||||
&& TRANSPONDER(Channel, m_Channel))
|
||||
&& TRANSPONDER(Channel, m_Channel)
|
||||
&& Channel->Ca() == CA_FTA
|
||||
&& m_Channel->Ca() == CA_FTA)
|
||||
res = true;
|
||||
return res;
|
||||
}
|
||||
@@ -85,6 +92,19 @@ bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
|
||||
|
||||
Dprintf("ProvidesChannel, Channel=%s, Prio=%d\n", Channel->Name(), Priority);
|
||||
|
||||
if (StreamdevClientSetup.MinPriority <= StreamdevClientSetup.MaxPriority)
|
||||
{
|
||||
if (Priority < StreamdevClientSetup.MinPriority ||
|
||||
Priority > StreamdevClientSetup.MaxPriority)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Priority < StreamdevClientSetup.MinPriority &&
|
||||
Priority > StreamdevClientSetup.MaxPriority)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ClientSocket.DataSocket(siLive) != NULL
|
||||
&& TRANSPONDER(Channel, m_Channel))
|
||||
res = true;
|
||||
@@ -92,7 +112,7 @@ bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
|
||||
res = prio && ClientSocket.ProvidesChannel(Channel, Priority);
|
||||
ndr = true;
|
||||
}
|
||||
|
||||
|
||||
if (NeedsDetachReceivers)
|
||||
*NeedsDetachReceivers = ndr;
|
||||
Dprintf("prov res = %d, ndr = %d\n", res, ndr);
|
||||
@@ -103,12 +123,17 @@ bool cStreamdevDevice::SetChannelDevice(const cChannel *Channel,
|
||||
bool LiveView) {
|
||||
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))
|
||||
&& TRANSPONDER(Channel, m_Channel)
|
||||
&& Channel->Ca() == CA_FTA
|
||||
&& m_Channel->Ca() == CA_FTA)
|
||||
return true;
|
||||
|
||||
DetachAllReceivers();
|
||||
@@ -123,6 +148,8 @@ bool cStreamdevDevice::SetPid(cPidHandle *Handle, int Type, bool On) {
|
||||
Handle->used);
|
||||
LOCK_THREAD;
|
||||
|
||||
m_UpdatePriority = ClientSocket.SupportsPrio();
|
||||
|
||||
if (On && !m_TSBuffer) {
|
||||
Dprintf("SetPid: no data connection -> OpenDvr()");
|
||||
OpenDvrInt();
|
||||
@@ -190,16 +217,11 @@ void cStreamdevDevice::CloseDvrInt(void) {
|
||||
}
|
||||
|
||||
Dprintf("cStreamdevDevice::CloseDvrInt(): Closing DVR connection\n");
|
||||
#if VDRVERSNUM < 10500
|
||||
DELETENULL(m_TSBuffer);
|
||||
ClientSocket.CloseDvr();
|
||||
#else
|
||||
// Hack for VDR 1.5.x clients (sometimes sending ABRT after TUNE)
|
||||
// TODO: Find a clean solution to fix this
|
||||
ClientSocket.SetChannelDevice(m_Channel);
|
||||
ClientSocket.CloseDvr();
|
||||
DELETENULL(m_TSBuffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
void cStreamdevDevice::CloseDvr(void) {
|
||||
@@ -289,3 +311,31 @@ bool cStreamdevDevice::ReInit(void) {
|
||||
return StreamdevClientSetup.StartClient ? Init() : true;
|
||||
}
|
||||
|
||||
void cStreamdevDevice::UpdatePriority(void) {
|
||||
if (m_Device) {
|
||||
m_Device->Lock();
|
||||
if (m_Device->m_UpdatePriority && ClientSocket.DataSocket(siLive)) {
|
||||
int Priority = m_Device->Priority();
|
||||
if (m_Device == cDevice::ActualDevice() && Priority < Setup.PrimaryLimit)
|
||||
Priority = Setup.PrimaryLimit;
|
||||
if (m_Device->m_Priority != Priority && ClientSocket.SetPriority(Priority))
|
||||
m_Device->m_Priority = Priority;
|
||||
}
|
||||
m_Device->Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
int cStreamdevDevice::SignalStrength(void) const {
|
||||
int strength = -1;
|
||||
if (ClientSocket.DataSocket(siLive) != NULL)
|
||||
ClientSocket.GetSignal(&strength, NULL);
|
||||
return strength;
|
||||
}
|
||||
|
||||
int cStreamdevDevice::SignalQuality(void) const {
|
||||
int quality = -1;
|
||||
if (ClientSocket.DataSocket(siLive) != NULL)
|
||||
ClientSocket.GetSignal(NULL, &quality);
|
||||
return quality;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: device.h,v 1.7 2008/04/07 14:40:39 schmirl Exp $
|
||||
* $Id: device.h,v 1.10 2010/08/18 10:26:55 schmirl Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_DEVICE_H
|
||||
@@ -15,13 +15,14 @@ class cTBString;
|
||||
#define CMD_LOCK_OBJ(x) cMutexLock CmdLock((cMutex*)&(x)->m_Mutex)
|
||||
|
||||
class cStreamdevDevice: public cDevice {
|
||||
friend class cRemoteRecordings;
|
||||
|
||||
private:
|
||||
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;
|
||||
@@ -49,12 +50,22 @@ public:
|
||||
cStreamdevDevice(void);
|
||||
virtual ~cStreamdevDevice();
|
||||
|
||||
virtual bool HasInternalCam(void) { return true; }
|
||||
virtual bool ProvidesSource(int Source) const;
|
||||
virtual bool ProvidesTransponder(const cChannel *Channel) const;
|
||||
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1,
|
||||
bool *NeedsDetachReceivers = NULL) const;
|
||||
#if APIVERSNUM >= 10700
|
||||
virtual int NumProvidedSystems(void) const;
|
||||
#endif
|
||||
#if APIVERSNUM >= 10719
|
||||
virtual bool AvoidRecording(void) const { return true; }
|
||||
#endif
|
||||
virtual bool IsTunedToTransponder(const cChannel *Channel);
|
||||
virtual int SignalStrength(void) const;
|
||||
virtual int SignalQuality(void) const;
|
||||
|
||||
static void UpdatePriority(void);
|
||||
static bool Init(void);
|
||||
static bool ReInit(void);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: filter.c,v 1.12 2008/04/07 14:27:28 schmirl Exp $
|
||||
* $Id: filter.c,v 1.14 2009/02/13 13:02:39 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include "client/filter.h"
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <vdr/device.h>
|
||||
|
||||
#define PID_MASK_HI 0x1F
|
||||
// --- cStreamdevFilter ------------------------------------------------------
|
||||
|
||||
class cStreamdevFilter: public cListObject {
|
||||
@@ -226,6 +227,7 @@ void cStreamdevFilters::Action(void) {
|
||||
u_short pid = (((u_short)block[1] & PID_MASK_HI) << 8) | block[2];
|
||||
u_char tid = block[3];
|
||||
bool Pusi = block[1] & 0x40;
|
||||
// proprietary extension
|
||||
int len = block[4];
|
||||
#if 0
|
||||
if (block[1] == 0xff &&
|
||||
|
||||
52
client/po/de_DE.po
Normal file
52
client/po/de_DE.po
Normal file
@@ -0,0 +1,52 @@
|
||||
# VDR streamdev plugin language source file.
|
||||
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
||||
# This file is distributed under the same license as the VDR streamdev package.
|
||||
# Frank Schmirler <vdrdev@schmirler.de>, 2008
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2011-02-16 08:49+0100\n"
|
||||
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
||||
"Last-Translator: Frank Schmirler <vdrdev@schmirler.de>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VTP Streaming Client"
|
||||
msgstr "VTP Streaming Client"
|
||||
|
||||
msgid "Suspend Server"
|
||||
msgstr "Server pausieren"
|
||||
|
||||
msgid "Server is suspended"
|
||||
msgstr "Server ist pausiert"
|
||||
|
||||
msgid "Couldn't suspend Server!"
|
||||
msgstr "Konnte Server nicht pausieren!"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Hauptmenüeintrag verstecken"
|
||||
|
||||
msgid "Start Client"
|
||||
msgstr "Client starten"
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "IP der Gegenseite"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "Port der Gegenseite"
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "Filter-Daten streamen"
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr "Minimale Priorität"
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr "Maximale Priorität"
|
||||
|
||||
msgid "Broadcast Systems / Cost"
|
||||
msgstr "Empfangssysteme / Kosten"
|
||||
51
client/po/es_ES.po
Normal file
51
client/po/es_ES.po
Normal file
@@ -0,0 +1,51 @@
|
||||
# VDR streamdev plugin language source file.
|
||||
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
||||
# This file is distributed under the same license as the VDR streamdev package.
|
||||
# Javier Bradineras <jbradi@hotmail.com>, 2011
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
||||
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
|
||||
"Last-Translator: Javier Bradineras <jbradi@hotmail.com>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VTP Streaming Client"
|
||||
msgstr "Cliente trasmisión VTP"
|
||||
|
||||
msgid "Suspend Server"
|
||||
msgstr "Suspender servidor"
|
||||
|
||||
msgid "Server is suspended"
|
||||
msgstr "Servidor en suspensión"
|
||||
|
||||
msgid "Couldn't suspend Server!"
|
||||
msgstr "Imposible suspender el servidor!"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Ocultar entrada en menú principal"
|
||||
|
||||
msgid "Start Client"
|
||||
msgstr "Iniciar Cliente"
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "Indicar IP del Servidor"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "Indicar puerto remoto del Servidor"
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "Filtrar transmisión"
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr "Prioridad mínima"
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr "Prioridad máxima"
|
||||
|
||||
|
||||
50
client/po/fi_FI.po
Normal file
50
client/po/fi_FI.po
Normal file
@@ -0,0 +1,50 @@
|
||||
# VDR streamdev plugin language source file.
|
||||
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
||||
# This file is distributed under the same license as the VDR streamdev package.
|
||||
# Rolf Ahrenberg <rahrenbe@cc.hut.fi>, 2008
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
||||
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
||||
"Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VTP Streaming Client"
|
||||
msgstr "VTP-suoratoistoasiakas"
|
||||
|
||||
msgid "Suspend Server"
|
||||
msgstr "Pysäytä palvelin"
|
||||
|
||||
msgid "Server is suspended"
|
||||
msgstr "Palvelin on pysäytetty"
|
||||
|
||||
msgid "Couldn't suspend Server!"
|
||||
msgstr "Palvelinta ei onnistuttu pysäyttämään!"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Piilota valinta päävalikosta"
|
||||
|
||||
msgid "Start Client"
|
||||
msgstr "Käynnistä VDR-asiakas"
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "Etäkoneen IP-osoite"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "Etäkoneen portti"
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "Suodatetun tiedon suoratoisto"
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr "Pienin prioriteetti"
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr "Suurin prioriteetti"
|
||||
|
||||
50
client/po/fr_FR.po
Normal file
50
client/po/fr_FR.po
Normal file
@@ -0,0 +1,50 @@
|
||||
# VDR streamdev plugin language source file.
|
||||
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
||||
# This file is distributed under the same license as the VDR streamdev package.
|
||||
# Frank Schmirler <vdrdev@schmirler.de>, 2008
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
||||
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
||||
"Last-Translator: micky979 <micky979@free.fr>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VTP Streaming Client"
|
||||
msgstr "Client de streaming VTP"
|
||||
|
||||
msgid "Suspend Server"
|
||||
msgstr "Suspendre le serveur"
|
||||
|
||||
msgid "Server is suspended"
|
||||
msgstr "Le serveur est suspendu"
|
||||
|
||||
msgid "Couldn't suspend Server!"
|
||||
msgstr "Impossible de suspendre le serveur!"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Masquer dans le menu principal"
|
||||
|
||||
msgid "Start Client"
|
||||
msgstr "Démarrage du client"
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "Adresse IP du serveur"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "Port du serveur"
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "Filtre streaming"
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr ""
|
||||
|
||||
52
client/po/it_IT.po
Normal file
52
client/po/it_IT.po
Normal file
@@ -0,0 +1,52 @@
|
||||
# VDR streamdev plugin language source file.
|
||||
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
||||
# This file is distributed under the same license as the VDR streamdev package.
|
||||
# Alberto Carraro <bertocar@tin.it>, 2001
|
||||
# Antonio Ospite <ospite@studenti.unina.it>, 2003
|
||||
# Sean Carlos <seanc@libero.it>, 2005
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
||||
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
|
||||
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VTP Streaming Client"
|
||||
msgstr "Client trasmissione VTP"
|
||||
|
||||
msgid "Suspend Server"
|
||||
msgstr "Sospendi Server"
|
||||
|
||||
msgid "Server is suspended"
|
||||
msgstr "Server sospeso"
|
||||
|
||||
msgid "Couldn't suspend Server!"
|
||||
msgstr "Impossibile sospendere il server!"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Nascondi voce menu principale"
|
||||
|
||||
msgid "Start Client"
|
||||
msgstr "Avvia Client"
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "Indirizzo IP del Server"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "Porta Server Remoto"
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "Filtra trasmissione"
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr "Priorità minima"
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr "Priorità massima"
|
||||
|
||||
50
client/po/lt_LT.po
Normal file
50
client/po/lt_LT.po
Normal file
@@ -0,0 +1,50 @@
|
||||
# VDR streamdev plugin language source file.
|
||||
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
||||
# This file is distributed under the same license as the VDR streamdev package.
|
||||
# Frank Schmirler <vdrdev@schmirler.de>, 2008
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
||||
"PO-Revision-Date: 2009-11-26 21:57+0200\n"
|
||||
"Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
|
||||
"Language-Team: Lietuvių\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VTP Streaming Client"
|
||||
msgstr "VTP transliavimo standartas"
|
||||
|
||||
msgid "Suspend Server"
|
||||
msgstr "Sustabdyti serverį"
|
||||
|
||||
msgid "Server is suspended"
|
||||
msgstr "Serveris sustabdytas"
|
||||
|
||||
msgid "Couldn't suspend Server!"
|
||||
msgstr "Negali sustabdyti serverio!"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Paslėpti pagrindinio meniu įrašą"
|
||||
|
||||
msgid "Start Client"
|
||||
msgstr "Paleisti klientą"
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "Nuotolinis IP adresas"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "Nuotolinis portas"
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "Filtruoti transliavimą"
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr "Minimalus prioritetas"
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr "Maksimalus prioritetas"
|
||||
|
||||
50
client/po/ru_RU.po
Normal file
50
client/po/ru_RU.po
Normal file
@@ -0,0 +1,50 @@
|
||||
# VDR streamdev plugin language source file.
|
||||
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
||||
# This file is distributed under the same license as the VDR streamdev package.
|
||||
# Frank Schmirler <vdrdev@schmirler.de>, 2008
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
||||
"PO-Revision-Date: 2008-06-26 15:36+0100\n"
|
||||
"Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-5\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VTP Streaming Client"
|
||||
msgstr "VTP Streaming ÚÛØÕÝâ"
|
||||
|
||||
msgid "Suspend Server"
|
||||
msgstr "¾áâÐÝÞÒØâì áÕàÒÕà"
|
||||
|
||||
msgid "Server is suspended"
|
||||
msgstr "ÁÕàÒÕà ÞáâÐÝÞÒÛÕÝ"
|
||||
|
||||
msgid "Couldn't suspend Server!"
|
||||
msgstr "ÝÕ ÜÞÓã ÞáâÐÝÞÒØâì áÕàÒÕà"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "ÁßàïâÐâì Ò ÓÛÐÒÝÞÜ ÜÕÝî"
|
||||
|
||||
msgid "Start Client"
|
||||
msgstr "ÁâÐàâ ÚÛØÕÝâÐ"
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "ÃÔÐÛÕÝÝëÙ IP"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "ÃÔÐÛÕÝÝëÙ ßÞàâ"
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "ÄØÛìâà ßÞâÞÚÐ"
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr ""
|
||||
|
||||
52
client/po/sk_SK.po
Normal file
52
client/po/sk_SK.po
Normal file
@@ -0,0 +1,52 @@
|
||||
# VDR streamdev plugin language source file.
|
||||
# Copyright (C) 2009 streamdev development team. See http://streamdev.vdr-developer.org
|
||||
# This file is distributed under the same license as the VDR streamdev package.
|
||||
# Milan Hrala <hrala.milan@gmail.com>, 2009
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev_SK\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2010-06-14 13:05+0200\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
|
||||
"Language-Team: Slovak <hrala.milan@gmail.com>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=iso-8859-2\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Poedit-Language: Slovak\n"
|
||||
"X-Poedit-Country: SLOVAKIA\n"
|
||||
|
||||
msgid "VTP Streaming Client"
|
||||
msgstr "VTP prúdový klient"
|
||||
|
||||
msgid "Suspend Server"
|
||||
msgstr "Server pozastavený"
|
||||
|
||||
msgid "Server is suspended"
|
||||
msgstr "Server je doèasne preru¹ený"
|
||||
|
||||
msgid "Couldn't suspend Server!"
|
||||
msgstr "Nepodarilo sa pozastavi» Server!"
|
||||
|
||||
msgid "Hide Mainmenu Entry"
|
||||
msgstr "Schova» polo¾ku v hlavnom menu"
|
||||
|
||||
msgid "Start Client"
|
||||
msgstr "Spusti» Klienta"
|
||||
|
||||
msgid "Remote IP"
|
||||
msgstr "Vzdialená IP"
|
||||
|
||||
msgid "Remote Port"
|
||||
msgstr "Vzdialený port"
|
||||
|
||||
msgid "Filter Streaming"
|
||||
msgstr "filtrova» prúdy"
|
||||
|
||||
msgid "Minimum Priority"
|
||||
msgstr "minimálna priorita"
|
||||
|
||||
msgid "Maximum Priority"
|
||||
msgstr "maximálna priorita"
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
/*
|
||||
* $Id: setup.c,v 1.5 2008/04/07 14:50:32 schmirl Exp $
|
||||
* $Id: setup.c,v 1.10 2010/06/08 05:55:17 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include <vdr/menuitems.h>
|
||||
|
||||
#include "client/setup.h"
|
||||
#include "client/device.h"
|
||||
#include "i18n.h"
|
||||
|
||||
cStreamdevClientSetup StreamdevClientSetup;
|
||||
|
||||
@@ -14,8 +13,12 @@ cStreamdevClientSetup::cStreamdevClientSetup(void) {
|
||||
StartClient = false;
|
||||
RemotePort = 2004;
|
||||
StreamFilters = false;
|
||||
SyncEPG = false;
|
||||
HideMenuEntry = false;
|
||||
MinPriority = -1;
|
||||
MaxPriority = MAXPRIORITY;
|
||||
#if APIVERSNUM >= 10700
|
||||
NumProvidedSystems = 1;
|
||||
#endif
|
||||
strcpy(RemoteIp, "");
|
||||
}
|
||||
|
||||
@@ -29,8 +32,12 @@ bool cStreamdevClientSetup::SetupParse(const char *Name, const char *Value) {
|
||||
}
|
||||
else if (strcmp(Name, "RemotePort") == 0) RemotePort = atoi(Value);
|
||||
else if (strcmp(Name, "StreamFilters") == 0) StreamFilters = atoi(Value);
|
||||
else if (strcmp(Name, "SyncEPG") == 0) SyncEPG = atoi(Value);
|
||||
else if (strcmp(Name, "HideMenuEntry") == 0) HideMenuEntry = atoi(Value);
|
||||
else if (strcmp(Name, "MinPriority") == 0) MinPriority = atoi(Value);
|
||||
else if (strcmp(Name, "MaxPriority") == 0) MaxPriority = atoi(Value);
|
||||
#if APIVERSNUM >= 10700
|
||||
else if (strcmp(Name, "NumProvidedSystems") == 0) NumProvidedSystems = atoi(Value);
|
||||
#endif
|
||||
else return false;
|
||||
return true;
|
||||
}
|
||||
@@ -38,12 +45,18 @@ bool cStreamdevClientSetup::SetupParse(const char *Name, const char *Value) {
|
||||
cStreamdevClientMenuSetupPage::cStreamdevClientMenuSetupPage(void) {
|
||||
m_NewSetup = StreamdevClientSetup;
|
||||
|
||||
AddBoolEdit (tr("Hide Mainmenu Entry"),m_NewSetup.HideMenuEntry);
|
||||
AddBoolEdit (tr("Start Client"), m_NewSetup.StartClient);
|
||||
AddIpEdit (tr("Remote IP"), m_NewSetup.RemoteIp);
|
||||
AddShortEdit(tr("Remote Port"), m_NewSetup.RemotePort);
|
||||
AddBoolEdit (tr("Filter Streaming"), m_NewSetup.StreamFilters);
|
||||
AddBoolEdit (tr("Synchronize EPG"), m_NewSetup.SyncEPG);
|
||||
Add(new cMenuEditBoolItem(tr("Hide Mainmenu Entry"), &m_NewSetup.HideMenuEntry));
|
||||
Add(new cMenuEditBoolItem(tr("Start Client"), &m_NewSetup.StartClient));
|
||||
Add(new cMenuEditIpItem (tr("Remote IP"), m_NewSetup.RemoteIp));
|
||||
Add(new cMenuEditIntItem (tr("Remote Port"), &m_NewSetup.RemotePort, 0, 65535));
|
||||
Add(new 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 APIVERSNUM >= 10715
|
||||
Add(new cMenuEditIntItem (tr("Broadcast Systems / Cost"), &m_NewSetup.NumProvidedSystems, 1, 15));
|
||||
#elif APIVERSNUM >= 10700
|
||||
Add(new cMenuEditIntItem (tr("Broadcast Systems / Cost"), &m_NewSetup.NumProvidedSystems, 1, 4));
|
||||
#endif
|
||||
SetCurrent(Get(0));
|
||||
}
|
||||
|
||||
@@ -63,8 +76,12 @@ void cStreamdevClientMenuSetupPage::Store(void) {
|
||||
SetupStore("RemoteIp", m_NewSetup.RemoteIp);
|
||||
SetupStore("RemotePort", m_NewSetup.RemotePort);
|
||||
SetupStore("StreamFilters", m_NewSetup.StreamFilters);
|
||||
SetupStore("SyncEPG", m_NewSetup.SyncEPG);
|
||||
SetupStore("HideMenuEntry", m_NewSetup.HideMenuEntry);
|
||||
SetupStore("MinPriority", m_NewSetup.MinPriority);
|
||||
SetupStore("MaxPriority", m_NewSetup.MaxPriority);
|
||||
#if APIVERSNUM >= 10700
|
||||
SetupStore("NumProvidedSystems", m_NewSetup.NumProvidedSystems);
|
||||
#endif
|
||||
|
||||
StreamdevClientSetup = m_NewSetup;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: setup.h,v 1.4 2008/04/07 14:50:32 schmirl Exp $
|
||||
* $Id: setup.h,v 1.7 2010/06/08 05:55:17 schmirl Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_SETUPCLIENT_H
|
||||
@@ -16,13 +16,17 @@ struct cStreamdevClientSetup {
|
||||
char RemoteIp[20];
|
||||
int RemotePort;
|
||||
int StreamFilters;
|
||||
int SyncEPG;
|
||||
int HideMenuEntry;
|
||||
int MinPriority;
|
||||
int MaxPriority;
|
||||
#if APIVERSNUM >= 10700
|
||||
int NumProvidedSystems;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern cStreamdevClientSetup StreamdevClientSetup;
|
||||
|
||||
class cStreamdevClientMenuSetupPage: public cStreamdevMenuSetupPage {
|
||||
class cStreamdevClientMenuSetupPage: public cMenuSetupPage {
|
||||
private:
|
||||
cStreamdevClientSetup m_NewSetup;
|
||||
|
||||
|
||||
111
client/socket.c
111
client/socket.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: socket.c,v 1.11 2008/04/07 14:40:40 schmirl Exp $
|
||||
* $Id: socket.c,v 1.15 2010/08/18 10:26:55 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include <tools/select.h>
|
||||
@@ -14,13 +14,16 @@
|
||||
#include "client/socket.h"
|
||||
#include "client/setup.h"
|
||||
#include "common.h"
|
||||
#include "i18n.h"
|
||||
|
||||
cClientSocket ClientSocket;
|
||||
|
||||
cClientSocket::cClientSocket(void)
|
||||
{
|
||||
memset(m_DataSockets, 0, sizeof(cTBSocket*) * si_Count);
|
||||
m_Prio = false;
|
||||
m_LastSignalUpdate = 0;
|
||||
m_LastSignalStrength = -1;
|
||||
m_LastSignalQuality = -1;
|
||||
Reset();
|
||||
}
|
||||
|
||||
@@ -143,8 +146,14 @@ bool cClientSocket::CheckConnection(void) {
|
||||
if(Command("CAPS FILTERS", 220))
|
||||
Filters = ",FILTERS";
|
||||
|
||||
isyslog("Streamdev: Connected to server %s:%d using capabilities TSPIDS%s",
|
||||
RemoteIp().c_str(), RemotePort(), Filters);
|
||||
const char *Prio = "";
|
||||
if(Command("CAPS PRIO", 220)) {
|
||||
Prio = ",PRIO";
|
||||
m_Prio = true;
|
||||
}
|
||||
|
||||
isyslog("Streamdev: Connected to server %s:%d using capabilities TSPIDS%s%s",
|
||||
RemoteIp().c_str(), RemotePort(), Filters, Prio);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -242,16 +251,54 @@ bool cClientSocket::SetChannelDevice(const cChannel *Channel) {
|
||||
CMD_LOCK;
|
||||
|
||||
std::string command = (std::string)"TUNE "
|
||||
+ (const char*)Channel->GetChannelID().ToString();
|
||||
if (!Command(command, 220)) {
|
||||
+ (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());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_LastSignalUpdate = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::SetPriority(int Priority) {
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
std::string command = (std::string)"PRIO " + (const char*)itoa(Priority);
|
||||
if (!Command(command, 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("Streamdev: Failed to update priority on %s:%d", RemoteIp().c_str(),
|
||||
RemotePort());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::GetSignal(int *SignalStrength, int *SignalQuality) {
|
||||
if (!CheckConnection()) return -1;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (m_LastSignalUpdate != time(NULL)) {
|
||||
std::string buffer;
|
||||
if (!Command("SGNL") || !Expect(220, &buffer)
|
||||
|| sscanf(buffer.c_str(), "%*d %*d %d:%d", &m_LastSignalStrength, &m_LastSignalQuality) != 2) {
|
||||
m_LastSignalStrength = -1;
|
||||
m_LastSignalQuality = -1;
|
||||
}
|
||||
m_LastSignalUpdate = time(NULL);
|
||||
}
|
||||
if (SignalStrength)
|
||||
*SignalStrength = m_LastSignalStrength;
|
||||
if (SignalQuality)
|
||||
*SignalQuality = m_LastSignalQuality;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool cClientSocket::SetPid(int Pid, bool On) {
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
@@ -260,8 +307,8 @@ bool cClientSocket::SetPid(int Pid, bool On) {
|
||||
std::string command = (std::string)(On ? "ADDP " : "DELP ") + (const char*)itoa(Pid);
|
||||
if (!Command(command, 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("Streamdev: Pid %d not available from %s:%d", Pid, LocalIp().c_str(),
|
||||
LocalPort());
|
||||
esyslog("Streamdev: Pid %d not available from %s:%d", Pid, RemoteIp().c_str(),
|
||||
RemotePort());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -277,7 +324,7 @@ bool cClientSocket::SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On) {
|
||||
if (!Command(command, 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("Streamdev: Filter %hu, %hhu, %hhu not available from %s:%d",
|
||||
Pid, Tid, Mask, LocalIp().c_str(), LocalPort());
|
||||
Pid, Tid, Mask, RemoteIp().c_str(), RemotePort());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -301,52 +348,6 @@ bool cClientSocket::CloseDvr(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::SynchronizeEPG(void) {
|
||||
std::string buffer;
|
||||
bool result;
|
||||
FILE *epgfd;
|
||||
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
isyslog("Streamdev: Synchronizing EPG from server\n");
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (!Command("LSTE"))
|
||||
return false;
|
||||
|
||||
if ((epgfd = tmpfile()) == NULL) {
|
||||
esyslog("ERROR: Streamdev: Error while processing EPG data: %s",
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
while ((result = Expect(215, &buffer))) {
|
||||
if (buffer[3] == ' ') break;
|
||||
fputs(buffer.c_str() + 4, epgfd);
|
||||
fputc('\n', epgfd);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't fetch EPG data from %s:%d",
|
||||
RemoteIp().c_str(), RemotePort());
|
||||
fclose(epgfd);
|
||||
return false;
|
||||
}
|
||||
|
||||
rewind(epgfd);
|
||||
if (cSchedules::Read(epgfd))
|
||||
cSchedules::Cleanup(true);
|
||||
else {
|
||||
esyslog("ERROR: Streamdev: Parsing EPG data failed");
|
||||
fclose(epgfd);
|
||||
return false;
|
||||
}
|
||||
fclose(epgfd);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::Quit(void) {
|
||||
bool res;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: socket.h,v 1.6 2008/04/07 14:40:40 schmirl Exp $
|
||||
* $Id: socket.h,v 1.8 2010/08/18 10:26:55 schmirl Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_CLIENT_CONNECTION_H
|
||||
@@ -20,7 +20,11 @@ private:
|
||||
cTBSocket *m_DataSockets[si_Count];
|
||||
cMutex m_Mutex;
|
||||
char m_Buffer[BUFSIZ + 1]; // various uses
|
||||
bool m_Prio; // server supports command PRIO
|
||||
|
||||
time_t m_LastSignalUpdate;
|
||||
int m_LastSignalStrength;
|
||||
int m_LastSignalQuality;
|
||||
protected:
|
||||
/* Send Command, and return true if the command results in Expected.
|
||||
Returns false on failure, setting errno appropriately if it has been
|
||||
@@ -45,10 +49,12 @@ public:
|
||||
bool CreateDataConnection(eSocketId Id);
|
||||
bool CloseDataConnection(eSocketId Id);
|
||||
bool SetChannelDevice(const cChannel *Channel);
|
||||
bool SupportsPrio() { return m_Prio; }
|
||||
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 CloseDvr(void);
|
||||
bool SynchronizeEPG(void);
|
||||
bool SuspendServer(void);
|
||||
bool Quit(void);
|
||||
|
||||
|
||||
@@ -3,19 +3,18 @@
|
||||
*
|
||||
* See the README file for copyright information and how to reach the author.
|
||||
*
|
||||
* $Id: streamdev-client.c,v 1.5 2008/04/07 14:50:32 schmirl Exp $
|
||||
* $Id: streamdev-client.c,v 1.3 2010/08/18 10:26:56 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include "streamdev-client.h"
|
||||
#include "client/device.h"
|
||||
#include "client/setup.h"
|
||||
#include "i18n.h"
|
||||
|
||||
#if VDRVERSNUM < 10400
|
||||
#error "VDR-1.4.0 or greater is required"
|
||||
#if !defined(APIVERSNUM) || APIVERSNUM < 10516
|
||||
#error "VDR-1.5.16 API version or greater is required!"
|
||||
#endif
|
||||
|
||||
const char *cPluginStreamdevClient::DESCRIPTION = "VTP Streaming Client";
|
||||
const char *cPluginStreamdevClient::DESCRIPTION = trNOOP("VTP Streaming Client");
|
||||
|
||||
cPluginStreamdevClient::cPluginStreamdevClient(void) {
|
||||
}
|
||||
@@ -28,17 +27,9 @@ const char *cPluginStreamdevClient::Description(void) {
|
||||
}
|
||||
|
||||
bool cPluginStreamdevClient::Start(void) {
|
||||
i18n_name = Name();
|
||||
RegisterI18n(Phrases);
|
||||
|
||||
I18nRegister(PLUGIN_NAME_I18N);
|
||||
cStreamdevDevice::Init();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cPluginStreamdevClient::Housekeeping(void) {
|
||||
if (StreamdevClientSetup.StartClient && StreamdevClientSetup.SyncEPG)
|
||||
ClientSocket.SynchronizeEPG();
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *cPluginStreamdevClient::MainMenuEntry(void) {
|
||||
@@ -61,4 +52,8 @@ bool cPluginStreamdevClient::SetupParse(const char *Name, const char *Value) {
|
||||
return StreamdevClientSetup.SetupParse(Name, Value);
|
||||
}
|
||||
|
||||
void cPluginStreamdevClient::MainThreadHook(void) {
|
||||
cStreamdevDevice::UpdatePriority();
|
||||
}
|
||||
|
||||
VDRPLUGINCREATOR(cPluginStreamdevClient); // Don't touch this!
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: streamdev-client.h,v 1.1 2004/12/30 22:43:59 lordjaxom Exp $
|
||||
* $Id: streamdev-client.h,v 1.3 2010/08/18 10:26:56 schmirl Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEVCLIENT_H
|
||||
@@ -19,11 +19,11 @@ public:
|
||||
virtual const char *Version(void) { return VERSION; }
|
||||
virtual const char *Description(void);
|
||||
virtual bool Start(void);
|
||||
virtual void Housekeeping(void);
|
||||
virtual const char *MainMenuEntry(void);
|
||||
virtual cOsdObject *MainMenuAction(void);
|
||||
virtual cMenuSetupPage *SetupMenu(void);
|
||||
virtual bool SetupParse(const char *Name, const char *Value);
|
||||
virtual void MainThreadHook(void);
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEVCLIENT_H
|
||||
140
common.c
140
common.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: common.c,v 1.7 2008/04/07 14:27:27 schmirl Exp $
|
||||
* $Id: common.c,v 1.12 2010/07/19 13:49:24 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include <vdr/channels.h>
|
||||
@@ -7,146 +7,12 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "tools/select.h"
|
||||
#include "i18n.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
const char *VERSION = "0.4.0-pre";
|
||||
const char *VERSION = "0.5.1-git";
|
||||
|
||||
const char *StreamTypes[st_Count] = {
|
||||
"TS",
|
||||
"PES",
|
||||
"PS",
|
||||
"ES",
|
||||
"Extern",
|
||||
"", // used internally only
|
||||
};
|
||||
|
||||
const char *SuspendModes[sm_Count] = {
|
||||
"Offer suspend mode",
|
||||
"Always suspended",
|
||||
"Never suspended"
|
||||
};
|
||||
|
||||
const char IpCharacters[] = "0123456789.";
|
||||
|
||||
char *GetNextLine(char *String, uint Length, uint &Offset) {
|
||||
char *last, *first;
|
||||
|
||||
first = String + Offset;
|
||||
for (last = first; last < String + Length; ++last) {
|
||||
if (*last == '\012') {
|
||||
if (*(last - 1) == '\015')
|
||||
*(last - 1) = '\0';
|
||||
|
||||
*last++ = '\0';
|
||||
Dprintf("IN: |%s|\n", first);
|
||||
Offset = last - String;
|
||||
return first;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const cChannel *ChannelFromString(const char *String, int *Apid) {
|
||||
const cChannel *channel = NULL;
|
||||
char *string = strdup(String);
|
||||
char *ptr, *end;
|
||||
int apididx = 0;
|
||||
|
||||
if ((ptr = strrchr(string, '+')) != NULL) {
|
||||
*(ptr++) = '\0';
|
||||
apididx = strtoul(ptr, &end, 10);
|
||||
Dprintf("found apididx: %d\n", apididx);
|
||||
}
|
||||
|
||||
if (isnumber(string)) {
|
||||
int temp = strtol(String, NULL, 10);
|
||||
if (temp >= 1 && temp <= Channels.MaxNumber())
|
||||
channel = Channels.GetByNumber(temp);
|
||||
} else {
|
||||
channel = Channels.GetByChannelID(tChannelID::FromString(string));
|
||||
|
||||
if (channel == NULL) {
|
||||
int i = 1;
|
||||
while ((channel = Channels.GetByNumber(i, 1)) != NULL) {
|
||||
if (String == channel->Name())
|
||||
break;
|
||||
|
||||
i = channel->Number() + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (channel != NULL && apididx > 0) {
|
||||
int apid = 0, index = 1;
|
||||
|
||||
for (int i = 0; channel->Apid(i) != 0; ++i, ++index) {
|
||||
if (index == apididx) {
|
||||
apid = channel->Apid(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (apid == 0) {
|
||||
for (int i = 0; channel->Dpid(i) != 0; ++i, ++index) {
|
||||
if (index == apididx) {
|
||||
apid = channel->Dpid(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Apid != NULL)
|
||||
*Apid = apid;
|
||||
}
|
||||
|
||||
free(string);
|
||||
return channel;
|
||||
}
|
||||
|
||||
void cStreamdevMenuSetupPage::AddCategory(const char *Title) {
|
||||
char *buffer = NULL;
|
||||
|
||||
asprintf(&buffer, "--- %s -------------------------------------------------"
|
||||
"---------------", Title );
|
||||
|
||||
cOsdItem *item = new cOsdItem(buffer);
|
||||
free(buffer);
|
||||
item->SetSelectable(false);
|
||||
Add(item);
|
||||
}
|
||||
|
||||
void cStreamdevMenuSetupPage::AddBoolEdit(const char *Title, int &Value) {
|
||||
Add(new cMenuEditBoolItem(Title, &Value));
|
||||
}
|
||||
|
||||
void cStreamdevMenuSetupPage::AddIpEdit(const char *Title, char *Value) {
|
||||
Add(new cMenuEditIpItem(Title, Value));
|
||||
}
|
||||
|
||||
void cStreamdevMenuSetupPage::AddShortEdit(const char *Title, int &Value) {
|
||||
AddRangeEdit(Title, Value, 0, 65535);
|
||||
}
|
||||
|
||||
void cStreamdevMenuSetupPage::AddRangeEdit(const char *Title, int &Value,
|
||||
int Min, int Max) {
|
||||
Add(new cMenuEditIntItem(Title, &Value, Min, Max));
|
||||
}
|
||||
|
||||
void cStreamdevMenuSetupPage::AddSuspEdit(const char *Title, int &Value) {
|
||||
static const char *SuspendModesTR[sm_Count] = { NULL };
|
||||
|
||||
if (SuspendModesTR[0] == NULL) {
|
||||
for (int i = 0; i < sm_Count; ++i)
|
||||
SuspendModesTR[i] = tr(SuspendModes[i]);
|
||||
}
|
||||
|
||||
Add(new cMenuEditStraItem(Title, &Value, sm_Count, SuspendModesTR));
|
||||
}
|
||||
void cStreamdevMenuSetupPage::AddTypeEdit(const char *Title, int &Value) {
|
||||
Add(new cMenuEditStraItem(Title, &Value, st_CountSetup, StreamTypes));
|
||||
}
|
||||
const char cMenuEditIpItem::IpCharacters[] = "0123456789.";
|
||||
|
||||
cMenuEditIpItem::cMenuEditIpItem(const char *Name, char *Value):
|
||||
cMenuEditItem(Name) {
|
||||
|
||||
51
common.h
51
common.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: common.h,v 1.11 2008/04/07 14:40:39 schmirl Exp $
|
||||
* $Id: common.h,v 1.16 2010/07/19 13:49:24 schmirl Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_COMMON_H
|
||||
@@ -23,42 +23,27 @@
|
||||
# define Dprintf(x...)
|
||||
#endif
|
||||
|
||||
# define TRANSPONDER(c1, c2) (c1->Transponder() == c2->Transponder())
|
||||
#if APIVERSNUM >= 10714
|
||||
#define TRANSPONDER(c1, c2) (c1->Transponder() == c2->Transponder() && !c1->IsSourceType('V'))
|
||||
#else
|
||||
#define TRANSPONDER(c1, c2) (c1->Transponder() == c2->Transponder())
|
||||
#endif
|
||||
|
||||
# define MAXPARSEBUFFER KILOBYTE(16)
|
||||
#define MAXPARSEBUFFER KILOBYTE(16)
|
||||
|
||||
/* Check if a channel is a radio station. */
|
||||
#define ISRADIO(x) ((x)->Vpid()==0||(x)->Vpid()==1||(x)->Vpid()==0x1fff)
|
||||
|
||||
class cChannel;
|
||||
|
||||
char *GetNextLine(char *String, uint Length, uint &Offset);
|
||||
|
||||
const cChannel *ChannelFromString(const char *String, int *Apid = NULL);
|
||||
|
||||
/* Disable logging if BUFCOUNT buffer overflows occur within BUFOVERTIME
|
||||
milliseconds. Enable logging again if there is no error within BUFOVERTIME
|
||||
milliseconds. */
|
||||
#define BUFOVERTIME 5000
|
||||
#define BUFOVERCOUNT 100
|
||||
|
||||
#define POLLFAIL esyslog("Streamdev: Polling failed: %s", strerror(errno))
|
||||
#define WRITEFAIL esyslog("Streamdev: Writing failed: %s", strerror(errno))
|
||||
#define READFAIL esyslog("Streamdev: Reading failed: %s", strerror(errno))
|
||||
#define CHECKPOLL(x) if ((x)<0){POLLFAIL; return false;}
|
||||
#define CHECKWRITE(x) if ((x)<0) { WRITEFAIL; return false; }
|
||||
#define CHECKREAD(x) if ((x)<0) { READFAIL; return false; }
|
||||
|
||||
enum eStreamType {
|
||||
stTS,
|
||||
stPES,
|
||||
stPS,
|
||||
stES,
|
||||
stExtern,
|
||||
stEXT,
|
||||
stTSPIDS,
|
||||
|
||||
#define st_CountSetup (stExtern+1)
|
||||
#define st_Count (stTSPIDS+1)
|
||||
st_Count
|
||||
};
|
||||
|
||||
enum eSuspendMode {
|
||||
@@ -72,29 +57,15 @@ enum eSocketId {
|
||||
siLive,
|
||||
siReplay,
|
||||
siLiveFilter,
|
||||
siDataRespond,
|
||||
si_Count
|
||||
};
|
||||
|
||||
extern const char *VERSION;
|
||||
extern const char *StreamTypes[st_Count];
|
||||
extern const char *SuspendModes[sm_Count];
|
||||
extern const char IpCharacters[];
|
||||
|
||||
class cStreamdevMenuSetupPage: public cMenuSetupPage {
|
||||
protected:
|
||||
void AddCategory(const char *Title);
|
||||
virtual void Store(void) = 0;
|
||||
|
||||
void AddBoolEdit(const char *Title, int &Value);
|
||||
void AddIpEdit(const char *Title, char *Value);
|
||||
void AddShortEdit(const char *Title, int &Value);
|
||||
void AddRangeEdit(const char *Title, int &Value, int Min, int Max);
|
||||
void AddSuspEdit(const char *Title, int &Value);
|
||||
void AddTypeEdit(const char *Title, int &Value);
|
||||
};
|
||||
|
||||
class cMenuEditIpItem: public cMenuEditItem {
|
||||
private:
|
||||
static const char IpCharacters[];
|
||||
char *value;
|
||||
int curNum;
|
||||
int pos;
|
||||
|
||||
786
i18n.c
786
i18n.c
@@ -1,786 +0,0 @@
|
||||
/*
|
||||
* $Id: i18n.c,v 1.8 2008/04/07 14:50:32 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
const char *i18n_name = NULL;
|
||||
|
||||
const tI18nPhrase Phrases[] = {
|
||||
{ "VDR Streaming Server", // English
|
||||
"VDR Streaming Server", // Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"VDR-suoratoistopalvelin", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika / Greek
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "VTP Streaming Client", // English
|
||||
"VTP Streaming Client", // Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"VTP-suoratoistoasiakas ", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika / Greek
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Start VDR-to-VDR Server", // English
|
||||
"VDR-zu-VDR Server starten", // Deutsch
|
||||
"", // Slovenski
|
||||
"Avvia il Server VDR-toVDR", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Käynnistä VDR-palvelin", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika / Greek
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Start HTTP Server", // English
|
||||
"HTTP Server starten", // Deutsch
|
||||
"", // Slovenski
|
||||
"Avvia il Server HTTP", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Käynnistä HTTP-palvelin", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "HTTP Streamtype", // English
|
||||
"HTTP Streamtyp", // Deutsch
|
||||
"", // Slovenski
|
||||
"Tipo di Stream HTTP", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"HTTP-lähetysmuoto", // Suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Start Client", // English
|
||||
"Client starten", // Deutsch
|
||||
"", // Slovenski
|
||||
"Avvia il Client", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Käynnistä VDR-asiakas", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "VDR-to-VDR Server Port", // English
|
||||
"Port des VDR-zu-VDR Servers", // Deutsch
|
||||
"", // Slovenski
|
||||
"Porta del Server VDR-to-VDR", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"VDR-palvelimen portti", // Suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "HTTP Server Port", // English
|
||||
"Port des HTTP Servers", // Deutsch
|
||||
"", // Slovenski
|
||||
"Porta del Server HTTP", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"HTTP-palvelimen portti", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Maximum Number of Clients", // English
|
||||
"Maximalanzahl an Clients", // Deutsch
|
||||
"", // Slovenski
|
||||
"Numero Massimo di Client", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Suurin sallittu asiakkaiden määrä", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Remote IP", // English
|
||||
"IP der Gegenseite", // Deutsch
|
||||
"", // Slovenski
|
||||
"Indirizzo IP del Server", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Etäkoneen IP-osoite", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Remote Port", // English
|
||||
"Port der Gegenseite", // Deutsch
|
||||
"", // Slovenski
|
||||
"Porta del Server Remoto", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Etäkoneen portti", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Remote Streamtype", // English
|
||||
"Streamtyp von Gegenseite", // Deutsch
|
||||
"", // Slovenski
|
||||
"Tipo di Stream", // Italiano (oppure Flusso ?)
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Etäkoneen lähetysmuoto", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Common Settings", // English
|
||||
"Allgemeines", // Deutsch
|
||||
"", // Slovenski
|
||||
"Settaggi Comuni", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Yleiset asetukset", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "VDR-to-VDR Server", // English
|
||||
"VDR-zu-VDR Server", // Deutsch
|
||||
"", // Slovenski
|
||||
"Server VDR-to-VDR", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"VDR-palvelin", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "HTTP Server", // English
|
||||
"HTTP Server", // Deutsch
|
||||
"", // Slovenski
|
||||
"Server HTTP", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"HTTP-palvelin", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "VDR-to-VDR Client", // English
|
||||
"VDR-zu-VDR Client", // Deutsch
|
||||
"", // Slovenski
|
||||
"Client VDR-to-VDR", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"VDR-asiakas", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Please restart VDR to activate changes", // English
|
||||
"Bitte starten Sie für die Änderungen VDR neu", // Deutsch
|
||||
"", // Slovenski
|
||||
"Riavviare VDR per attivare i cambiamenti", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Aktivoi muutokset käynnistämällä VDR uudelleen", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Synchronize EPG", // English
|
||||
"EPG synchronisieren", // Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Päivitä ohjelmaopas", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Suspend Live TV", // English
|
||||
"Live-TV pausieren", // Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Pysäytä suora TV-lähetys", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Suspend behaviour", // English
|
||||
"Pausierverhalten", // Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Pysäytystoiminto", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Offer suspend mode", // English
|
||||
"Pausieren anbieten", // Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"tyrkytä", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Always suspended", // English
|
||||
"Immer pausiert", // Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"aina", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Never suspended", // English
|
||||
"Nie pausiert", // Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"ei koskaan", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Suspend Server", // English
|
||||
"Server pausieren", // Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Pysäytä palvelin", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Server is suspended", // English
|
||||
"Server ist pausiert", // Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Palvelin on pysäytetty", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Couldn't suspend Server!", // English
|
||||
"Konnte Server nicht pausieren!", // Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Palvelinta ei onnistuttu pysäyttämään!", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Client may suspend", // English
|
||||
"Client darf pausieren", // Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Asiakas saa pysäyttää palvelimen", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Bind to IP", // English
|
||||
"", // Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Sido osoitteeseen", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Filter Streaming", // English
|
||||
"", // Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Suodatetun tiedon suoratoisto", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Streaming active", // English
|
||||
"Streamen im Gange", // Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Suoratoistopalvelin aktiivinen", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ "Hide Mainmenu Entry", // English
|
||||
"Hauptmenüeintrag verstecken", // Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Piilota valinta päävalikosta", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
"", // Russian
|
||||
"", // Hrvatski
|
||||
"", // Eesti
|
||||
"", // Dansk
|
||||
"", // Czech
|
||||
#if VDRVERSNUM >= 10502
|
||||
"", // Türkçe
|
||||
#endif
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
16
i18n.h
16
i18n.h
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* $Id: i18n.h,v 1.1 2004/12/30 22:43:58 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_I18N_H
|
||||
#define VDR_STREAMDEV_I18N_H
|
||||
|
||||
#include <vdr/i18n.h>
|
||||
|
||||
extern const char *i18n_name;
|
||||
extern const tI18nPhrase Phrases[];
|
||||
|
||||
#undef tr
|
||||
#define tr(s) I18nTranslate(s, i18n_name)
|
||||
|
||||
#endif // VDR_STREAMDEV_I18N_H
|
||||
@@ -1,25 +1,38 @@
|
||||
INCS = -I.
|
||||
CFLAGS = -g -Wall -O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -fPIC
|
||||
MFLAG = -M
|
||||
OBJS = ctools.o ringbuffy.o remux.o transform.o
|
||||
SRC = $(wildcard *.c)
|
||||
#
|
||||
# Makefile for a Video Disk Recorder plugin
|
||||
#
|
||||
# $Id: Makefile,v 1.5 2010/07/30 10:49:28 schmirl Exp $
|
||||
|
||||
DESTDIR = /usr/local
|
||||
### The object files (add further files here):
|
||||
|
||||
OBJS = ctools.o remux.o ringbuffy.o transform.o
|
||||
|
||||
### Disable attribute warn_unused_result
|
||||
|
||||
DEFINES += -U_FORTIFY_SOURCE
|
||||
|
||||
### The main target:
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
- rm -f *.o *~ *.a .depend
|
||||
|
||||
libdvbmpegtools.a: $(OBJS)
|
||||
ar -rcs libdvbmpegtools.a $(OBJS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c $(CFLAGS) $(INCS) $(DEFINES) $<
|
||||
### Implicit rules:
|
||||
|
||||
.depend:
|
||||
$(CXX) $(DEFINES) $(MFLAG) $(SRC) $(INCS)> .depend
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
|
||||
|
||||
### Dependencies:
|
||||
|
||||
MAKEDEP = $(CC) -MM -MG
|
||||
DEPFILE = .dependencies
|
||||
|
||||
-include .depend
|
||||
$(DEPFILE): Makefile
|
||||
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
|
||||
|
||||
-include $(DEPFILE)
|
||||
|
||||
### Targets:
|
||||
|
||||
clean:
|
||||
@-rm -f $(OBJS) $(DEPFILE) *.a core* *~
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
|
||||
@@ -596,7 +596,10 @@ int read_pes(int f, pes_packet *p){
|
||||
|
||||
if (p->length >0){
|
||||
buf = (uint8_t *) malloc(p->length);
|
||||
if((neof = save_read(f,buf,p->length))< p->length) return -1;
|
||||
if((neof = save_read(f,buf,p->length))< p->length){
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
cread_pes((char *)buf,p);
|
||||
free(buf);
|
||||
} else return 0;
|
||||
@@ -1876,528 +1879,3 @@ int write_ps_header(uint8_t *buf,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define MAX_BASE 80
|
||||
#define MAX_PATH 256
|
||||
#define MAX_EXT 10
|
||||
|
||||
int break_up_filename(char *name, char *base_name, char *path, char *ext)
|
||||
{
|
||||
int l,i,sstop,sstart;
|
||||
|
||||
l = strlen(name);
|
||||
sstop = l;
|
||||
sstart = -1;
|
||||
for( i= l-1; i >= 0; i--){
|
||||
if (sstop == l && name[i] == '.') sstop = i;
|
||||
if (sstart<0 && name[i] == '/') sstart = i+1;
|
||||
}
|
||||
if (sstart < 0) sstart = 0;
|
||||
if (sstop-sstart < MAX_BASE){
|
||||
strncpy(base_name, name+sstart, sstop-sstart);
|
||||
base_name[sstop-sstart]=0;
|
||||
if(sstart > 0){
|
||||
if( l - sstop + sstart < MAX_PATH){
|
||||
strncpy(path, name, sstart);
|
||||
path[sstart] = 0;
|
||||
} else {
|
||||
fprintf(stderr,"PATH too long\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
} else {
|
||||
strcpy(path, "./");
|
||||
}
|
||||
|
||||
if(sstop < l){
|
||||
if( l - sstop -1 < MAX_EXT){
|
||||
strncpy(ext, name+sstop+1, l-sstop-1);
|
||||
ext[l-sstop-1]=0;
|
||||
} else {
|
||||
fprintf(stderr,"Extension too long\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
} else {
|
||||
strcpy(ext, "");
|
||||
}
|
||||
|
||||
} else {
|
||||
fprintf(stderr,"Name too long\n");
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
printf("%d %d\n",sstart, sstop);
|
||||
printf("%s %d\n",name, strlen(name));
|
||||
printf("%s %d\n",base_name, strlen(base_name));
|
||||
printf("%s %d\n",path,strlen(path));
|
||||
printf("%s %d\n",ext,strlen(ext));
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int seek_mpg_start(uint8_t *buf, int size)
|
||||
{
|
||||
int found = 0;
|
||||
int c=0;
|
||||
int seq = 0;
|
||||
int mpeg = 0;
|
||||
int mark = 0;
|
||||
|
||||
while ( !seq ){
|
||||
while (found != 4){
|
||||
switch (found) {
|
||||
case 0:
|
||||
if ( buf[c] == 0x00 ) found++;
|
||||
c++;
|
||||
break;
|
||||
case 1:
|
||||
if ( buf[c] == 0x00 ) found++;
|
||||
else found = 0;
|
||||
c++;
|
||||
break;
|
||||
case 2:
|
||||
if ( buf[c] == 0x01 ) found++;
|
||||
else found = 0;
|
||||
if ( buf[c] == 0x00 ) found = 2;
|
||||
c++;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if ( (buf[c] & 0xe0) == 0xe0 ) found++;
|
||||
else found = 0;
|
||||
c++;
|
||||
break;
|
||||
}
|
||||
if (c >= size) return -1;
|
||||
}
|
||||
|
||||
if (found == 4){
|
||||
mark = c-4;
|
||||
c+=2;
|
||||
if (c >= size) return -1;
|
||||
|
||||
if ( (buf[c] & 0xC0) == 0x80 ){
|
||||
mpeg = 2;
|
||||
c += 2;
|
||||
if (c >= size) return -1;
|
||||
c += buf[c]+1;
|
||||
if (c >= size) return -1;
|
||||
} else {
|
||||
mpeg = 1;
|
||||
while( buf[c] == 0xFF ) {
|
||||
c++;
|
||||
if (c >= size) return -1;
|
||||
}
|
||||
if ( (buf[c] & 0xC0) == 0x40) c+=2;
|
||||
if (c >= size) return -1;
|
||||
if ( (buf[c] & 0x30) ){
|
||||
if ( (buf[c] & 0x30) == 0x20) c+=5;
|
||||
else c+=10;
|
||||
} else c++;
|
||||
if (c >= size) return -1;
|
||||
}
|
||||
|
||||
if ( buf[c] == 0x00 &&
|
||||
buf[c+1] == 0x00 &&
|
||||
buf[c+2] == 0x01 &&
|
||||
buf[c+3] == 0xB3 )
|
||||
seq = 1;
|
||||
}
|
||||
found = 0;
|
||||
}
|
||||
|
||||
return size-mark;
|
||||
}
|
||||
|
||||
|
||||
void write_mpg(int fstart, uint64_t length, int fdin, int fdout)
|
||||
{
|
||||
// uint8_t mpeg_end[4] = { 0x00, 0x00, 0x01, 0xB9 };
|
||||
uint8_t *buf;
|
||||
uint64_t l=0;
|
||||
uint64_t count = 0;
|
||||
struct stat sb;
|
||||
int buf_size;
|
||||
|
||||
fstat (fdout, &sb);
|
||||
buf_size = sb.st_blksize;
|
||||
|
||||
buf = (uint8_t *) alloca (buf_size + sizeof (int));
|
||||
|
||||
lseek(fdin, fstart, SEEK_SET);
|
||||
|
||||
while ( count < length && (l = read(fdin,buf,buf_size)) >= 0){
|
||||
if (l > 0) count+=l;
|
||||
write(fdout,buf,l);
|
||||
printf("written %02.2f%%\r",(100.*count)/length);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
//write( fdout, mpeg_end, 4);
|
||||
}
|
||||
|
||||
|
||||
#define CHECKBUF (1024*1024)
|
||||
#define ONE_GIG (1024UL*1024UL*1024UL)
|
||||
void split_mpg(char *name, uint64_t size)
|
||||
{
|
||||
char base_name[MAX_BASE];
|
||||
char path[MAX_PATH];
|
||||
char ext[MAX_EXT];
|
||||
char new_name[256];
|
||||
uint8_t buf[CHECKBUF];
|
||||
int fdin;
|
||||
int fdout;
|
||||
uint64_t length = 0;
|
||||
uint64_t last;
|
||||
int i;
|
||||
int mark, csize;
|
||||
struct stat sb;
|
||||
|
||||
if (break_up_filename(name,base_name,path,ext) < 0) exit(1);
|
||||
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
if ( (fdin = open(name, O_RDONLY)) < 0){
|
||||
#else
|
||||
if ( (fdin = open(name, O_RDONLY|O_LARGEFILE)) < 0){
|
||||
#endif
|
||||
fprintf(stderr,"Can't open %s\n",name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fstat (fdin, &sb);
|
||||
|
||||
length = sb.st_size;
|
||||
if ( length < ONE_GIG )
|
||||
printf("Filelength = %2.2f MB\n", length/1024./1024.);
|
||||
else
|
||||
printf("Filelength = %2.2f GB\n", length/1024./1024./1024.);
|
||||
|
||||
if ( length < size ) length = size;
|
||||
|
||||
printf("Splitting %s into Files with size <= %2.2f MB\n",name,
|
||||
size/1024./1024.);
|
||||
|
||||
csize = CHECKBUF;
|
||||
read(fdin, buf, csize);
|
||||
if ( (mark = seek_mpg_start(buf,csize)) < 0){
|
||||
fprintf(stderr,"Couldn't find sequence header\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
last = csize-mark;
|
||||
|
||||
for ( i = 0 ; i < length/size; i++){
|
||||
csize = CHECKBUF;
|
||||
|
||||
if (csize > length-last) csize = length-last;
|
||||
lseek(fdin, last+size-csize, SEEK_SET);
|
||||
read(fdin, buf, csize);
|
||||
if ( (mark = seek_mpg_start(buf,csize)) < 0){
|
||||
fprintf(stderr,"Couldn't find sequence header\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sprintf(new_name,"%s-%03d.%s",base_name,i,ext);
|
||||
printf("writing %s\n",new_name);
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC,
|
||||
#else
|
||||
if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC
|
||||
|O_LARGEFILE,
|
||||
#endif
|
||||
S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|
|
||||
S_IROTH|S_IWOTH)) < 0){
|
||||
fprintf(stderr,"Can't open %s\n",new_name);
|
||||
exit(1);
|
||||
}
|
||||
write_mpg(last, size-mark, fdin, fdout);
|
||||
last = last + size - mark;
|
||||
}
|
||||
sprintf(new_name,"%s-%03d.%s",base_name,i,ext);
|
||||
printf("writing %s\n",new_name);
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC,
|
||||
#else
|
||||
if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC
|
||||
|O_LARGEFILE,
|
||||
#endif
|
||||
S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|
|
||||
S_IROTH|S_IWOTH)) < 0){
|
||||
fprintf(stderr,"Can't open %s\n",new_name);
|
||||
exit(1);
|
||||
}
|
||||
write_mpg(last, length-last, fdin, fdout);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void cut_mpg(char *name, uint64_t size)
|
||||
{
|
||||
char base_name[MAX_BASE];
|
||||
char path[MAX_PATH];
|
||||
char ext[MAX_EXT];
|
||||
char new_name[256];
|
||||
uint8_t buf[CHECKBUF];
|
||||
int fdin;
|
||||
int fdout;
|
||||
uint64_t length = 0;
|
||||
uint64_t last;
|
||||
int mark, csize;
|
||||
struct stat sb;
|
||||
|
||||
if (break_up_filename(name,base_name,path,ext) < 0) exit(1);
|
||||
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
if ( (fdin = open(name, O_RDONLY)) < 0){
|
||||
#else
|
||||
if ( (fdin = open(name, O_RDONLY|O_LARGEFILE)) < 0){
|
||||
#endif
|
||||
fprintf(stderr,"Can't open %s\n",name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fstat (fdin, &sb);
|
||||
|
||||
length = sb.st_size;
|
||||
if ( length < ONE_GIG )
|
||||
printf("Filelength = %2.2f MB\n", length/1024./1024.);
|
||||
else
|
||||
printf("Filelength = %2.2f GB\n", length/1024./1024./1024.);
|
||||
|
||||
if ( length < size ) length = size;
|
||||
|
||||
printf("Splitting %s into 2 Files with length %.2f MB and %.2f MB\n",
|
||||
name, size/1024./1024., (length-size)/1024./1024.);
|
||||
|
||||
csize = CHECKBUF;
|
||||
read(fdin, buf, csize);
|
||||
if ( (mark = seek_mpg_start(buf,csize)) < 0){
|
||||
fprintf(stderr,"Couldn't find sequence header\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
last = csize-mark;
|
||||
|
||||
if (csize > length-last) csize = length-last;
|
||||
lseek(fdin, last+size-csize, SEEK_SET);
|
||||
read(fdin, buf, csize);
|
||||
if ( (mark = seek_mpg_start(buf,csize)) < 0){
|
||||
fprintf(stderr,"Couldn't find sequence header\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sprintf(new_name,"%s-1.%s",base_name,ext);
|
||||
printf("writing %s\n",new_name);
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC,
|
||||
#else
|
||||
if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC
|
||||
|O_LARGEFILE,
|
||||
#endif
|
||||
S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|
|
||||
S_IROTH|S_IWOTH)) < 0){
|
||||
fprintf(stderr,"Can't open %s\n",new_name);
|
||||
exit(1);
|
||||
}
|
||||
write_mpg(last, size-mark, fdin, fdout);
|
||||
last = last + size - mark;
|
||||
|
||||
sprintf(new_name,"%s-2.%s",base_name,ext);
|
||||
printf("writing %s\n",new_name);
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC,
|
||||
#else
|
||||
if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC
|
||||
|O_LARGEFILE,
|
||||
#endif
|
||||
S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|
|
||||
S_IROTH|S_IWOTH)) < 0){
|
||||
fprintf(stderr,"Can't open %s\n",new_name);
|
||||
exit(1);
|
||||
}
|
||||
write_mpg(last, length-last, fdin, fdout);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void write_all (int fd, const char *data, int length)
|
||||
{
|
||||
int r;
|
||||
|
||||
while (length) {
|
||||
if ((r = write(fd, data, length)) > 0) {
|
||||
data += r;
|
||||
length -= r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void read_all (int fd, char *data, int length)
|
||||
{
|
||||
int c = 0;
|
||||
|
||||
while(1) {
|
||||
if( read(fd, data+c, 1) == 1) {
|
||||
c++;
|
||||
if(data[c-1] == '\n') {
|
||||
data[c] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
fprintf (stderr, "Error reading socket\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
char *url2host (char *url, char **name, uint32_t *ip, uint32_t *port)
|
||||
{
|
||||
char *murl;
|
||||
struct hostent *hoste;
|
||||
struct in_addr haddr;
|
||||
int found_ip = 1;
|
||||
|
||||
if (!(strncmp(url, "http://", 7)))
|
||||
url += 7;
|
||||
|
||||
*name = strdup(url);
|
||||
if (!(*name)) {
|
||||
*name = NULL;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
murl = url;
|
||||
while (*murl && *murl != ':' && *murl != '/') {
|
||||
if ((*murl < '0' || *murl > '9') && *murl != '.')
|
||||
found_ip = 0;
|
||||
murl++;
|
||||
}
|
||||
|
||||
(*name)[murl - url] = 0;
|
||||
if (found_ip) {
|
||||
if ((*ip = inet_addr(*name)) == INADDR_NONE)
|
||||
return (NULL);
|
||||
} else {
|
||||
if (!(hoste = gethostbyname(*name)))
|
||||
return (NULL);
|
||||
memcpy (&haddr, hoste->h_addr, sizeof(haddr));
|
||||
*ip = haddr.s_addr;
|
||||
}
|
||||
|
||||
if (!*murl || *murl == '/') {
|
||||
*port = 80;
|
||||
return (murl);
|
||||
}
|
||||
*port = atoi(++murl);
|
||||
|
||||
while (*murl && *murl != '/')
|
||||
murl++;
|
||||
return (murl);
|
||||
}
|
||||
|
||||
#define ACCEPT "Accept: video/mpeg, video/x-mpegurl, */*\r\n"
|
||||
|
||||
int http_open (char *url)
|
||||
{
|
||||
char purl[1024], *host, req[1024], *sptr;
|
||||
uint32_t ip;
|
||||
uint32_t port;
|
||||
int sock;
|
||||
int reloc, relocnum = 0;
|
||||
struct sockaddr_in server;
|
||||
int mfd;
|
||||
|
||||
strncpy (purl, url, 1023);
|
||||
purl[1023] = '\0';
|
||||
|
||||
do {
|
||||
host = NULL;
|
||||
strcpy (req, "GET ");
|
||||
if (!(sptr = url2host(purl, &host, &ip, &port))) {
|
||||
fprintf (stderr, "Unknown host\n");
|
||||
exit (1);
|
||||
}
|
||||
strcat (req, sptr);
|
||||
sprintf (req + strlen(req),
|
||||
" HTTP/1.0\r\nUser-Agent: %s/%s\r\n",
|
||||
"whatever", "you want");
|
||||
if (host) {
|
||||
sprintf(req + strlen(req),
|
||||
"Host: %s:%u\r\n", host, port);
|
||||
free (host);
|
||||
}
|
||||
|
||||
strcat (req, ACCEPT);
|
||||
strcat (req, "\r\n");
|
||||
|
||||
server.sin_port = htons(port);
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_addr.s_addr = ip;
|
||||
|
||||
if ((sock = socket(PF_INET, SOCK_STREAM, 6)) < 0) {
|
||||
perror ("socket");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (connect(sock, (struct sockaddr *)&server,
|
||||
sizeof(server))) {
|
||||
perror ("connect");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
write_all (sock, req, strlen(req));
|
||||
if (!(mfd = fileno(fdopen(sock, "rb")))) {
|
||||
perror ("open");
|
||||
exit (1);
|
||||
}
|
||||
reloc = 0;
|
||||
purl[0] = '\0';
|
||||
read_all (mfd, req, 1023);
|
||||
if ((sptr = strchr(req, ' '))) {
|
||||
switch (sptr[1]) {
|
||||
case '2':
|
||||
break;
|
||||
case '3':
|
||||
reloc = 1;
|
||||
default:
|
||||
fprintf (stderr, "HTTP req failed:%s",
|
||||
sptr+1);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
do {
|
||||
read_all (mfd,req, 1023);
|
||||
if (!strncmp(req, "Location:", 9))
|
||||
strncpy (purl, req+10, 1023);
|
||||
} while (req[0] != '\r' && req[0] != '\n');
|
||||
} while (reloc && purl[0] && relocnum++ < 3);
|
||||
if (reloc) {
|
||||
fprintf (stderr, "Too many HTTP relocations.\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
extern int errno;
|
||||
const char * strerrno (void)
|
||||
{
|
||||
return strerror(errno);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
|
||||
@@ -387,16 +387,7 @@ extern "C" {
|
||||
uint8_t buffer2_scale,
|
||||
uint32_t buffer2_size);
|
||||
|
||||
|
||||
int seek_mpg_start(uint8_t *buf, int size);
|
||||
|
||||
|
||||
void split_mpg(char *name, uint64_t size);
|
||||
void cut_mpg(char *name, uint64_t size);
|
||||
int http_open (char *url);
|
||||
ssize_t save_read(int fd, void *buf, size_t count);
|
||||
|
||||
const char * strerrno(void);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
|
||||
@@ -388,10 +388,9 @@ int refill_buffy(Remux *rem)
|
||||
|
||||
|
||||
while ( acount > MAX_PLENGTH && vcount > MAX_PLENGTH && count < 10){
|
||||
int neof;
|
||||
count++;
|
||||
init_pes(&pes);
|
||||
if ((neof = read_pes(fin,&pes)) <= 0) return -1;
|
||||
if (read_pes(fin,&pes) <= 0) return -1;
|
||||
switch(pes.stream_id){
|
||||
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
|
||||
rem->apes++;
|
||||
@@ -702,22 +701,6 @@ void init_remux(Remux *rem, int fin, int fout, int mult)
|
||||
rem->time_off = 0;
|
||||
}
|
||||
|
||||
uint32_t bytes2pts(int bytes, int rate)
|
||||
{
|
||||
if (bytes < 0xFFFFFFFFUL/720000UL)
|
||||
return (uint32_t)(bytes*720000UL/rate);
|
||||
else
|
||||
return (uint32_t)(bytes/rate*720000UL);
|
||||
}
|
||||
|
||||
long pts2bytes( uint32_t pts, int rate)
|
||||
{
|
||||
if (pts < 0xEFFFFFFFUL/rate)
|
||||
return (pts*rate/720000);
|
||||
else
|
||||
return (pts* (rate/720000));
|
||||
}
|
||||
|
||||
int write_audio_pes( Remux *rem, uint8_t *buf, int *alength)
|
||||
{
|
||||
int add;
|
||||
@@ -1088,12 +1071,6 @@ struct remux_s{
|
||||
} REMUX;
|
||||
|
||||
|
||||
void init_REMUX(REMUX *rem)
|
||||
{
|
||||
rem->num_pbuf = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define REPACK 2048
|
||||
#define ABUF_SIZE REPACK*1024
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "ringbuffy.h"
|
||||
@@ -191,9 +192,8 @@ int ring_read_file(ringbuffy *rbuf, int fd, int count)
|
||||
}
|
||||
|
||||
int ring_rest(ringbuffy *rbuf){
|
||||
int diff, free, pos, rest;
|
||||
int diff, free, pos;
|
||||
pos = rbuf->read_pos;
|
||||
rest = rbuf->size - pos;
|
||||
diff = rbuf->write_pos - pos;
|
||||
free = (diff >= 0) ? diff : rbuf->size+diff;
|
||||
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef RINGBUFFY_H
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
|
||||
|
||||
#define MAX_PLENGTH 0xFFFF
|
||||
#define MMAX_PLENGTH (8*MAX_PLENGTH)
|
||||
#define MMAX_PLENGTH (64*MAX_PLENGTH)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
# The cannels.conf ca field can be used to bind a channel to a specific
|
||||
# device. The streamdev-client does not consider this information, so
|
||||
# there's no way to keep VDR from using streamdev for a specific
|
||||
# channel. Apply this patch if you need this feature.
|
||||
#
|
||||
# This fix should probably become part of streamdev. However as it
|
||||
# changes the behaviour of streamdev, I decided to keep it as a separate
|
||||
# patch until there is something like a new official streamdev release.
|
||||
#
|
||||
--- client/device.h.bak 2006-11-09 12:25:21.000000000 +0100
|
||||
+++ client/device.h 2006-11-09 12:26:57.000000000 +0100
|
||||
@@ -50,6 +50,7 @@
|
||||
cStreamdevDevice(void);
|
||||
virtual ~cStreamdevDevice();
|
||||
|
||||
+ virtual int ProvidesCa(const cChannel *Channel) const;
|
||||
virtual bool ProvidesSource(int Source) const;
|
||||
virtual bool ProvidesTransponder(const cChannel *Channel) const;
|
||||
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1,
|
||||
--- client/device.c.bak 2006-11-09 12:23:24.000000000 +0100
|
||||
+++ client/device.c 2006-11-09 12:35:48.000000000 +0100
|
||||
@@ -57,6 +57,12 @@
|
||||
#endif
|
||||
}
|
||||
|
||||
+int cStreamdevDevice::ProvidesCa(const cChannel *Channel) const
|
||||
+{
|
||||
+ // Encrypted is acceptable for now. Will ask the server later.
|
||||
+ return Channel->Ca() <= CA_DVB_MAX ? cDevice::ProvidesCa(Channel) : 1;
|
||||
+}
|
||||
+
|
||||
bool cStreamdevDevice::ProvidesSource(int Source) const {
|
||||
Dprintf("ProvidesSource, Source=%d\n", Source);
|
||||
return false;
|
||||
@@ -78,7 +84,7 @@
|
||||
if (ClientSocket.DataSocket(siLive) != NULL
|
||||
&& TRANSPONDER(Channel, m_Channel))
|
||||
res = true;
|
||||
- else {
|
||||
+ else if (ProvidesCa(Channel)) {
|
||||
res = prio && ClientSocket.ProvidesChannel(Channel, Priority);
|
||||
ndr = true;
|
||||
}
|
||||
13
patches/vdr-1.6.0-ignore_missing_cam.diff
Normal file
13
patches/vdr-1.6.0-ignore_missing_cam.diff
Normal file
@@ -0,0 +1,13 @@
|
||||
--- device.c.orig 2008-03-28 11:47:25.000000000 +0100
|
||||
+++ device.c 2008-03-28 11:47:09.000000000 +0100
|
||||
@@ -375,8 +375,8 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
- if (!NumUsableSlots)
|
||||
- return NULL; // no CAM is able to decrypt this channel
|
||||
+// if (!NumUsableSlots)
|
||||
+// return NULL; // no CAM is able to decrypt this channel
|
||||
}
|
||||
|
||||
bool NeedsDetachReceivers = false;
|
||||
78
patches/vdr-1.6.0-intcamdevices.patch
Normal file
78
patches/vdr-1.6.0-intcamdevices.patch
Normal file
@@ -0,0 +1,78 @@
|
||||
Index: vdr-1.6.0-nocamdevices/device.c
|
||||
===================================================================
|
||||
--- vdr-1.6.0-nocamdevices/device.c
|
||||
+++ vdr-1.6.0-nocamdevices/device.c 2008-04-27 18:55:37.000000000 +0300
|
||||
@@ -363,6 +363,7 @@
|
||||
int NumCamSlots = CamSlots.Count();
|
||||
int SlotPriority[NumCamSlots];
|
||||
int NumUsableSlots = 0;
|
||||
+ bool InternalCamNeeded = false;
|
||||
if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
|
||||
for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
|
||||
SlotPriority[CamSlot->Index()] = MAXPRIORITY + 1; // assumes it can't be used
|
||||
@@ -376,7 +377,7 @@
|
||||
}
|
||||
}
|
||||
if (!NumUsableSlots)
|
||||
- return NULL; // no CAM is able to decrypt this channel
|
||||
+ InternalCamNeeded = true; // no CAM is able to decrypt this channel
|
||||
}
|
||||
|
||||
bool NeedsDetachReceivers = false;
|
||||
@@ -392,11 +393,13 @@
|
||||
continue; // this device shall be temporarily avoided
|
||||
if (Channel->Ca() && Channel->Ca() <= CA_DVB_MAX && Channel->Ca() != device[i]->CardIndex() + 1)
|
||||
continue; // a specific card was requested, but not this one
|
||||
- if (NumUsableSlots && !CamSlots.Get(j)->Assign(device[i], true))
|
||||
+ if (InternalCamNeeded && !device[i]->HasInternalCam())
|
||||
+ continue; // no CAM is able to decrypt this channel and the device uses vdr handled CAMs
|
||||
+ if (NumUsableSlots && !device[i]->HasInternalCam() && !CamSlots.Get(j)->Assign(device[i], true))
|
||||
continue; // CAM slot can't be used with this device
|
||||
bool ndr;
|
||||
if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
|
||||
- if (NumUsableSlots && device[i]->CamSlot() && device[i]->CamSlot() != CamSlots.Get(j))
|
||||
+ if (NumUsableSlots && !device[i]->HasInternalCam() && device[i]->CamSlot() && device[i]->CamSlot() != CamSlots.Get(j))
|
||||
ndr = true; // using a different CAM slot requires detaching receivers
|
||||
// Put together an integer number that reflects the "impact" using
|
||||
// this device would have on the overall system. Each condition is represented
|
||||
@@ -410,18 +413,18 @@
|
||||
imp <<= 1; imp |= device[i]->Receiving(); // avoid devices that are receiving
|
||||
imp <<= 1; imp |= device[i] == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device
|
||||
imp <<= 8; imp |= min(max(device[i]->Priority() + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
|
||||
- imp <<= 8; imp |= min(max((NumUsableSlots ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
|
||||
+ imp <<= 8; imp |= min(max(((NumUsableSlots && !device[i]->HasInternalCam()) ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
|
||||
imp <<= 1; imp |= ndr; // avoid devices if we need to detach existing receivers
|
||||
imp <<= 1; imp |= device[i]->IsPrimaryDevice(); // avoid the primary device
|
||||
- imp <<= 1; imp |= NumUsableSlots ? 0 : device[i]->HasCi(); // avoid cards with Common Interface for FTA channels
|
||||
+ imp <<= 1; imp |= (NumUsableSlots || InternalCamNeeded) ? 0 : device[i]->HasCi(); // avoid cards with Common Interface for FTA channels
|
||||
imp <<= 1; imp |= device[i]->HasDecoder(); // avoid full featured cards
|
||||
- imp <<= 1; imp |= NumUsableSlots ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
|
||||
+ imp <<= 1; imp |= (NumUsableSlots && !device[i]->HasInternalCam()) ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
|
||||
if (imp < Impact) {
|
||||
// This device has less impact than any previous one, so we take it.
|
||||
Impact = imp;
|
||||
d = device[i];
|
||||
NeedsDetachReceivers = ndr;
|
||||
- if (NumUsableSlots)
|
||||
+ if (NumUsableSlots && !device[i]->HasInternalCam())
|
||||
s = CamSlots.Get(j);
|
||||
}
|
||||
}
|
||||
Index: vdr-1.6.0-nocamdevices/device.h
|
||||
===================================================================
|
||||
--- vdr-1.6.0-nocamdevices/device.h
|
||||
+++ vdr-1.6.0-nocamdevices/device.h 2008-04-27 18:55:49.000000000 +0300
|
||||
@@ -335,6 +335,12 @@
|
||||
public:
|
||||
virtual bool HasCi(void);
|
||||
///< Returns true if this device has a Common Interface.
|
||||
+ virtual bool HasInternalCam(void) { return false; }
|
||||
+ ///< Returns true if this device handles encrypted channels itself
|
||||
+ ///< without VDR assistance. This can be e.g. when the device is a
|
||||
+ ///< client that gets the stream from another VDR instance that has
|
||||
+ ///< already decrypted the stream. In this case ProvidesChannel()
|
||||
+ ///< shall check whether the channel can be decrypted.
|
||||
void SetCamSlot(cCamSlot *CamSlot);
|
||||
///< Sets the given CamSlot to be used with this device.
|
||||
cCamSlot *CamSlot(void) const { return camSlot; }
|
||||
|
||||
11
patches/vdr-cap_net_raw.diff
Normal file
11
patches/vdr-cap_net_raw.diff
Normal file
@@ -0,0 +1,11 @@
|
||||
--- vdr.c.orig 2009-02-13 09:45:55.000000000 +0100
|
||||
+++ vdr.c 2009-02-13 09:46:24.000000000 +0100
|
||||
@@ -115,7 +115,7 @@
|
||||
static bool SetCapSysTime(void)
|
||||
{
|
||||
// drop all capabilities except cap_sys_time
|
||||
- cap_t caps = cap_from_text("= cap_sys_time=ep");
|
||||
+ cap_t caps = cap_from_text("= cap_sys_time,cap_net_raw=ep");
|
||||
if (!caps) {
|
||||
fprintf(stderr, "vdr: cap_from_text failed: %s\n", strerror(errno));
|
||||
return false;
|
||||
34
remux/Makefile
Normal file
34
remux/Makefile
Normal file
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# Makefile for a Video Disk Recorder plugin
|
||||
#
|
||||
# $Id: Makefile,v 1.2 2010/07/19 13:49:28 schmirl Exp $
|
||||
|
||||
### The object files (add further files here):
|
||||
|
||||
OBJS = tsremux.o ts2es.o ts2pes.o ts2ps.o extern.o
|
||||
|
||||
### The main target:
|
||||
|
||||
.PHONY: clean
|
||||
remux.a: $(OBJS)
|
||||
ar -rcs remux.a $^
|
||||
|
||||
### Implicit rules:
|
||||
|
||||
%.o: %.c
|
||||
$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
|
||||
|
||||
### Dependencies:
|
||||
|
||||
MAKEDEP = $(CXX) -MM -MG
|
||||
DEPFILE = .dependencies
|
||||
|
||||
$(DEPFILE): Makefile
|
||||
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
|
||||
|
||||
-include $(DEPFILE)
|
||||
|
||||
### Targets:
|
||||
|
||||
clean:
|
||||
@-rm -f $(OBJS) $(DEPFILE) *.a core* *~
|
||||
178
remux/extern.c
178
remux/extern.c
@@ -1,13 +1,18 @@
|
||||
#include "remux/extern.h"
|
||||
#include "server/server.h"
|
||||
#include "server/connection.h"
|
||||
#include "server/streamer.h"
|
||||
#include <vdr/channels.h>
|
||||
#include <vdr/tools.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
const char *g_ExternRemux = EXTERNREMUXPATH;
|
||||
namespace Streamdev {
|
||||
|
||||
#define MAXENV 63
|
||||
|
||||
class cTSExt: public cThread {
|
||||
private:
|
||||
@@ -20,16 +25,19 @@ protected:
|
||||
virtual void Action(void);
|
||||
|
||||
public:
|
||||
cTSExt(cRingBufferLinear *ResultBuffer, std::string Parameter);
|
||||
cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connection, const cChannel *Channel, const int *Apids, const int *Dpids);
|
||||
virtual ~cTSExt();
|
||||
|
||||
void Put(const uchar *Data, int Count);
|
||||
};
|
||||
|
||||
cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, std::string Parameter):
|
||||
} // namespace Streamdev
|
||||
using namespace Streamdev;
|
||||
|
||||
cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connection, const cChannel *Channel, const int *Apids, const int *Dpids):
|
||||
m_ResultBuffer(ResultBuffer),
|
||||
m_Active(false),
|
||||
m_Process(0),
|
||||
m_Process(-1),
|
||||
m_Inpipe(0),
|
||||
m_Outpipe(0)
|
||||
{
|
||||
@@ -59,6 +67,118 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, std::string Parameter):
|
||||
|
||||
if (m_Process == 0) {
|
||||
// child process
|
||||
char *env[MAXENV + 1];
|
||||
int i = 0;
|
||||
|
||||
#define ADDENV(x...) if (asprintf(&env[i++], x) < 0) i--
|
||||
|
||||
// add channel ID, name and pids to environment
|
||||
ADDENV("REMUX_CHANNEL_ID=%s", *Channel->GetChannelID().ToString());
|
||||
ADDENV("REMUX_CHANNEL_NAME=%s", Channel->Name());
|
||||
#if APIVERSNUM >= 10701
|
||||
ADDENV("REMUX_VTYPE=%d", Channel->Vtype());
|
||||
#endif
|
||||
if (Channel->Vpid())
|
||||
ADDENV("REMUX_VPID=%d", Channel->Vpid());
|
||||
if (Channel->Ppid() != Channel->Vpid())
|
||||
ADDENV("REMUX_PPID=%d", Channel->Ppid());
|
||||
if (Channel->Tpid())
|
||||
ADDENV("REMUX_TPID=%d", Channel->Tpid());
|
||||
|
||||
std::string buffer;
|
||||
if (Apids && *Apids) {
|
||||
for (const int *pid = Apids; *pid; pid++)
|
||||
(buffer += (const char *) itoa(*pid)) += (*(pid + 1) ? " " : "");
|
||||
ADDENV("REMUX_APID=%s", buffer.c_str());
|
||||
|
||||
buffer.clear();
|
||||
for (const int *pid = Apids; *pid; pid++) {
|
||||
int j;
|
||||
for (j = 0; Channel->Apid(j) && Channel->Apid(j) != *pid; j++)
|
||||
;
|
||||
(buffer += Channel->Alang(j)) += (*(pid + 1) ? " " : "");
|
||||
}
|
||||
ADDENV("REMUX_ALANG=%s", buffer.c_str());
|
||||
}
|
||||
|
||||
if (Dpids && *Dpids) {
|
||||
buffer.clear();
|
||||
for (const int *pid = Dpids; *pid; pid++)
|
||||
(buffer += (const char *) itoa(*pid)) += (*(pid + 1) ? " " : "");
|
||||
ADDENV("REMUX_DPID=%s", buffer.c_str());
|
||||
|
||||
buffer.clear();
|
||||
for (const int *pid = Dpids; *pid; pid++) {
|
||||
int j;
|
||||
for (j = 0; Channel->Dpid(j) && Channel->Dpid(j) != *pid; j++)
|
||||
;
|
||||
(buffer += Channel->Dlang(j)) += (*(pid + 1) ? " " : "");
|
||||
}
|
||||
ADDENV("REMUX_DLANG=%s", buffer.c_str());
|
||||
}
|
||||
|
||||
if (Channel->Spid(0)) {
|
||||
buffer.clear();
|
||||
for (const int *pid = Channel->Spids(); *pid; pid++)
|
||||
(buffer += (const char *) itoa(*pid)) += (*(pid + 1) ? " " : "");
|
||||
ADDENV("REMUX_SPID=%s", buffer.c_str());
|
||||
|
||||
buffer.clear();
|
||||
for (int j = 0; Channel->Spid(j); j++)
|
||||
(buffer += Channel->Slang(j)) += (Channel->Spid(j + 1) ? " " : "");
|
||||
ADDENV("REMUX_SLANG=%s", buffer.c_str());
|
||||
}
|
||||
|
||||
if (Connection) {
|
||||
// add vars for a CGI like interface
|
||||
// the following vars are not implemented:
|
||||
// REMOTE_HOST, REMOTE_IDENT, REMOTE_USER
|
||||
// CONTENT_TYPE, CONTENT_LENGTH,
|
||||
// SCRIPT_NAME, PATH_TRANSLATED, GATEWAY_INTERFACE
|
||||
ADDENV("REMOTE_ADDR=%s", Connection->RemoteIp().c_str());
|
||||
ADDENV("SERVER_NAME=%s", Connection->LocalIp().c_str());
|
||||
ADDENV("SERVER_PORT=%d", Connection->LocalPort());
|
||||
ADDENV("SERVER_PROTOCOL=%s", Connection->Protocol());
|
||||
ADDENV("SERVER_SOFTWARE=%s", VERSION);
|
||||
|
||||
for (tStrStrMap::const_iterator it = Connection->Headers().begin(); it != Connection->Headers().end(); ++it) {
|
||||
if (i >= MAXENV) {
|
||||
esyslog("streamdev-server: Too many headers for externremux.sh");
|
||||
break;
|
||||
}
|
||||
ADDENV("%s=%s", it->first.c_str(), it->second.c_str());
|
||||
}
|
||||
|
||||
// look for section parameters: /path;param1=value1;param2=value2/
|
||||
std::string::size_type begin, end;
|
||||
const static std::string PATH_INFO("PATH_INFO");
|
||||
|
||||
tStrStrMap::const_iterator it_pathinfo = Connection->Headers().find(PATH_INFO);
|
||||
const std::string& path = it_pathinfo == Connection->Headers().end() ? "/" : it_pathinfo->second;
|
||||
begin = path.find(';', 0);
|
||||
begin = path.find_first_not_of(';', begin);
|
||||
end = path.find_first_of(";/", begin);
|
||||
while (begin != std::string::npos && path[begin] != '/') {
|
||||
std::string param = path.substr(begin, end - begin);
|
||||
std::string::size_type e = param.find('=');
|
||||
|
||||
if (i >= MAXENV) {
|
||||
esyslog("streamdev-server: Too many parameters for externremux.sh");
|
||||
break;
|
||||
}
|
||||
else if (e > 0 && e != std::string::npos) {
|
||||
ADDENV("REMUX_PARAM_%s", param.c_str());
|
||||
}
|
||||
else
|
||||
esyslog("streamdev-server: Invalid externremux.sh parameter %s", param.c_str());
|
||||
|
||||
begin = path.find_first_not_of(';', end);
|
||||
end = path.find_first_of(";/", begin);
|
||||
}
|
||||
}
|
||||
|
||||
env[i] = NULL;
|
||||
|
||||
dup2(inpipe[0], STDIN_FILENO);
|
||||
close(inpipe[1]);
|
||||
dup2(outpipe[1], STDOUT_FILENO);
|
||||
@@ -68,9 +188,20 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, std::string Parameter):
|
||||
for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
|
||||
close(i); //close all dup'ed filedescriptors
|
||||
|
||||
std::string cmd = std::string(g_ExternRemux) + " " + Parameter;
|
||||
execl("/bin/sh", "sh", "-c", cmd.c_str(), NULL);
|
||||
_exit(-1);
|
||||
if (setpgid(0, 0) == -1)
|
||||
esyslog("streamdev-server: externremux setpgid failed: %m");
|
||||
|
||||
if (access(opt_remux, X_OK) == -1) {
|
||||
esyslog("streamdev-server %s: %m", opt_remux);
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
if (execle("/bin/sh", "sh", "-c", opt_remux, NULL, env) == -1) {
|
||||
esyslog("streamdev-server: externremux script '%s' execution failed: %m", opt_remux);
|
||||
_exit(-1);
|
||||
}
|
||||
// should never be reached
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
close(inpipe[0]);
|
||||
@@ -85,16 +216,31 @@ cTSExt::~cTSExt()
|
||||
m_Active = false;
|
||||
Cancel(3);
|
||||
if (m_Process > 0) {
|
||||
// close pipes
|
||||
close(m_Outpipe);
|
||||
close(m_Inpipe);
|
||||
kill(m_Process, SIGTERM);
|
||||
for (int i = 0; waitpid(m_Process, NULL, WNOHANG) == 0; i++) {
|
||||
if (i == 20) {
|
||||
esyslog("streamdev-server: externremux process won't stop - killing it");
|
||||
kill(m_Process, SIGKILL);
|
||||
}
|
||||
cCondWait::SleepMs(100);
|
||||
// signal and wait for termination
|
||||
if (kill(m_Process, SIGINT) < 0) {
|
||||
esyslog("streamdev-server: externremux SIGINT failed: %m");
|
||||
}
|
||||
else {
|
||||
int i = 0;
|
||||
int retval;
|
||||
while ((retval = waitpid(m_Process, NULL, WNOHANG)) == 0) {
|
||||
|
||||
if ((++i % 20) == 0) {
|
||||
esyslog("streamdev-server: externremux process won't stop - killing it");
|
||||
kill(m_Process, SIGKILL);
|
||||
}
|
||||
cCondWait::SleepMs(100);
|
||||
}
|
||||
|
||||
if (retval < 0)
|
||||
esyslog("streamdev-server: externremux process waitpid failed: %m");
|
||||
else
|
||||
Dprintf("streamdev-server: externremux child (%d) exited as expected\n", m_Process);
|
||||
}
|
||||
m_Process = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,9 +296,9 @@ void cTSExt::Put(const uchar *Data, int Count)
|
||||
}
|
||||
}
|
||||
|
||||
cExternRemux::cExternRemux(int VPid, const int *APids, const int *Dpids, const int *SPids, std::string Parameter):
|
||||
cExternRemux::cExternRemux(const cServerConnection *Connection, const cChannel *Channel, const int *Apids, const int *Dpids):
|
||||
m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE, TS_SIZE * 2)),
|
||||
m_Remux(new cTSExt(m_ResultBuffer, Parameter))
|
||||
m_Remux(new cTSExt(m_ResultBuffer, Connection, Channel, Apids, Dpids))
|
||||
{
|
||||
m_ResultBuffer->SetTimeouts(500, 100);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
#include <vdr/ringbuffer.h>
|
||||
#include <string>
|
||||
|
||||
extern const char *g_ExternRemux;
|
||||
class cChannel;
|
||||
class cServerConnection;
|
||||
|
||||
namespace Streamdev {
|
||||
|
||||
class cTSExt;
|
||||
|
||||
@@ -15,7 +18,7 @@ private:
|
||||
cTSExt *m_Remux;
|
||||
|
||||
public:
|
||||
cExternRemux(int VPid, const int *APids, const int *Dpids, const int *SPids, std::string Parameter);
|
||||
cExternRemux(const cServerConnection *Connection, const cChannel *Channel, const int *APids, const int *Dpids);
|
||||
virtual ~cExternRemux();
|
||||
|
||||
int Put(const uchar *Data, int Count);
|
||||
@@ -23,4 +26,6 @@ public:
|
||||
void Del(int Count) { m_ResultBuffer->Del(Count); }
|
||||
};
|
||||
|
||||
} // namespace Streamdev
|
||||
|
||||
#endif // VDR_STREAMDEV_EXTERNREMUX_H
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#include "remux/ts2es.h"
|
||||
#include "server/streamer.h"
|
||||
#include "libdvbmpeg/transform.h"
|
||||
#include "common.h"
|
||||
#include <vdr/device.h>
|
||||
|
||||
// from VDR's remux.c
|
||||
#define MAXNONUSEFULDATA (10*1024*1024)
|
||||
|
||||
namespace Streamdev {
|
||||
|
||||
class cTS2ES: public ipack {
|
||||
friend void PutES(uint8_t *Buffer, int Size, void *Data);
|
||||
|
||||
@@ -32,6 +33,9 @@ void PutES(uint8_t *Buffer, int Size, void *Data)
|
||||
This->start = 1;
|
||||
}
|
||||
|
||||
} // namespace Streamdev
|
||||
using namespace Streamdev;
|
||||
|
||||
cTS2ES::cTS2ES(cRingBufferLinear *ResultBuffer)
|
||||
{
|
||||
m_ResultBuffer = ResultBuffer;
|
||||
@@ -75,10 +79,10 @@ void cTS2ES::PutTSPacket(const uint8_t *Buffer) {
|
||||
|
||||
cTS2ESRemux::cTS2ESRemux(int Pid):
|
||||
m_Pid(Pid),
|
||||
m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE, IPACKS)),
|
||||
m_ResultBuffer(new cStreamdevBuffer(WRITERBUFSIZE, IPACKS)),
|
||||
m_Remux(new cTS2ES(m_ResultBuffer))
|
||||
{
|
||||
m_ResultBuffer->SetTimeouts(0, 100);
|
||||
m_ResultBuffer->SetTimeouts(100, 100);
|
||||
}
|
||||
|
||||
cTS2ESRemux::~cTS2ESRemux()
|
||||
@@ -111,8 +115,10 @@ int cTS2ESRemux::Put(const uchar *Data, int Count)
|
||||
break;
|
||||
if (Data[i] != TS_SYNC_BYTE)
|
||||
break;
|
||||
if (m_ResultBuffer->Free() < 2 * IPACKS)
|
||||
if (m_ResultBuffer->Free() < 2 * IPACKS) {
|
||||
m_ResultBuffer->WaitForPut();
|
||||
break; // A cTS2ES might write one full packet and also a small rest
|
||||
}
|
||||
int pid = cTSRemux::GetPid(Data + i + 1);
|
||||
if (Data[i + 3] & 0x10) { // got payload
|
||||
if (m_Pid == pid)
|
||||
|
||||
@@ -2,15 +2,16 @@
|
||||
#define VDR_STREAMDEV_TS2ESREMUX_H
|
||||
|
||||
#include "remux/tsremux.h"
|
||||
#include <vdr/ringbuffer.h>
|
||||
#include "server/streamer.h"
|
||||
|
||||
namespace Streamdev {
|
||||
|
||||
class cTS2ES;
|
||||
class cRingBufferLinear;
|
||||
|
||||
class cTS2ESRemux: public cTSRemux {
|
||||
private:
|
||||
int m_Pid;
|
||||
cRingBufferLinear *m_ResultBuffer;
|
||||
cStreamdevBuffer *m_ResultBuffer;
|
||||
cTS2ES *m_Remux;
|
||||
|
||||
public:
|
||||
@@ -22,4 +23,6 @@ public:
|
||||
void Del(int Count) { m_ResultBuffer->Del(Count); }
|
||||
};
|
||||
|
||||
} // namespace Streamdev
|
||||
|
||||
#endif // VDR_STREAMDEV_TS2ESREMUX_H
|
||||
|
||||
2017
remux/ts2pes.c
Normal file
2017
remux/ts2pes.c
Normal file
File diff suppressed because it is too large
Load Diff
56
remux/ts2pes.h
Normal file
56
remux/ts2pes.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* ts2pes.h: A streaming MPEG2 remultiplexer
|
||||
*
|
||||
* This file is based on a copy of remux.h from Klaus Schmidinger's
|
||||
* VDR, version 1.6.0.
|
||||
*
|
||||
* $Id: ts2pes.h,v 1.4 2009/07/06 06:11:11 schmirl Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_TS2PES_H
|
||||
#define VDR_STREAMDEV_TS2PES_H
|
||||
|
||||
#include "remux/tsremux.h"
|
||||
#include "server/streamer.h"
|
||||
|
||||
#define MAXTRACKS 64
|
||||
|
||||
namespace Streamdev {
|
||||
|
||||
class cTS2PES;
|
||||
|
||||
class cTS2PESRemux: public cTSRemux {
|
||||
private:
|
||||
bool noVideo;
|
||||
bool synced;
|
||||
int skipped;
|
||||
cTS2PES *ts2pes[MAXTRACKS];
|
||||
int numTracks;
|
||||
cStreamdevBuffer *resultBuffer;
|
||||
int resultSkipped;
|
||||
public:
|
||||
cTS2PESRemux(int VPid, const int *APids, const int *DPids, const int *SPids);
|
||||
///< Creates a new remuxer for the given PIDs. VPid is the video PID, while
|
||||
///< APids, DPids and SPids are pointers to zero terminated lists of audio,
|
||||
///< dolby and subtitle PIDs (the pointers may be NULL if there is no such
|
||||
///< PID).
|
||||
virtual ~cTS2PESRemux();
|
||||
int Put(const uchar *Data, int Count);
|
||||
///< Puts at most Count bytes of Data into the remuxer.
|
||||
///< \return Returns the number of bytes actually consumed from Data.
|
||||
uchar *Get(int &Count);
|
||||
///< Gets all currently available data from the remuxer.
|
||||
///< \return Count contains the number of bytes the result points to, and
|
||||
void Del(int Count);
|
||||
///< Deletes Count bytes from the remuxer. Count must be the number returned
|
||||
///< from a previous call to Get(). Several calls to Del() with fractions of
|
||||
///< a previously returned Count may be made, but the total sum of all Count
|
||||
///< values must be exactly what the previous Get() has returned.
|
||||
void Clear(void);
|
||||
///< Clears the remuxer of all data it might still contain, keeping the PID
|
||||
///< settings as they are.
|
||||
};
|
||||
|
||||
} // namespace Streamdev
|
||||
|
||||
#endif // VDR_STREAMDEV_TS2PES_H
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <vdr/channels.h>
|
||||
#include <vdr/device.h>
|
||||
|
||||
namespace Streamdev {
|
||||
|
||||
class cTS2PS {
|
||||
friend void PutPES(uint8_t *Buffer, int Size, void *Data);
|
||||
|
||||
@@ -28,6 +30,9 @@ void PutPES(uint8_t *Buffer, int Size, void *Data)
|
||||
esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Size - n, Size);
|
||||
}
|
||||
|
||||
} // namespace Streamdev
|
||||
using namespace Streamdev;
|
||||
|
||||
cTS2PS::cTS2PS(cRingBufferLinear *ResultBuffer, int Pid, uint8_t AudioCid)
|
||||
{
|
||||
m_ResultBuffer = ResultBuffer;
|
||||
@@ -74,13 +79,13 @@ void cTS2PS::PutTSPacket(const uint8_t *Buffer)
|
||||
|
||||
cTS2PSRemux::cTS2PSRemux(int VPid, const int *APids, const int *DPids, const int *SPids):
|
||||
m_NumTracks(0),
|
||||
m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE, IPACKS)),
|
||||
m_ResultBuffer(new cStreamdevBuffer(WRITERBUFSIZE, IPACKS)),
|
||||
m_ResultSkipped(0),
|
||||
m_Skipped(0),
|
||||
m_Synced(false),
|
||||
m_IsRadio(VPid == 0 || VPid == 1 || VPid == 0x1FFF)
|
||||
{
|
||||
m_ResultBuffer->SetTimeouts(0, 100);
|
||||
m_ResultBuffer->SetTimeouts(100, 100);
|
||||
|
||||
if (VPid)
|
||||
m_Remux[m_NumTracks++] = new cTS2PS(m_ResultBuffer, VPid);
|
||||
@@ -124,8 +129,10 @@ int cTS2PSRemux::Put(const uchar *Data, int Count)
|
||||
break;
|
||||
if (Data[i] != TS_SYNC_BYTE)
|
||||
break;
|
||||
if (m_ResultBuffer->Free() < 2 * IPACKS)
|
||||
if (m_ResultBuffer->Free() < 2 * IPACKS) {
|
||||
m_ResultBuffer->WaitForPut();
|
||||
break; // A cTS2PS might write one full packet and also a small rest
|
||||
}
|
||||
int pid = GetPid(Data + i + 1);
|
||||
if (Data[i + 3] & 0x10) { // got payload
|
||||
for (int t = 0; t < m_NumTracks; t++) {
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
#ifndef VDR_STREAMDEV_TS2PESREMUX_H
|
||||
#define VDR_STREAMDEV_TS2PESREMUX_H
|
||||
#ifndef VDR_STREAMDEV_TS2PSREMUX_H
|
||||
#define VDR_STREAMDEV_TS2PSREMUX_H
|
||||
|
||||
#include "remux/tsremux.h"
|
||||
#include <vdr/remux.h>
|
||||
#include <vdr/ringbuffer.h>
|
||||
#include "server/streamer.h"
|
||||
|
||||
#ifndef MAXTRACKS
|
||||
#define MAXTRACKS 64
|
||||
#endif
|
||||
|
||||
namespace Streamdev {
|
||||
|
||||
class cTS2PS;
|
||||
|
||||
@@ -11,7 +16,7 @@ class cTS2PSRemux: public cTSRemux {
|
||||
private:
|
||||
int m_NumTracks;
|
||||
cTS2PS *m_Remux[MAXTRACKS];
|
||||
cRingBufferLinear *m_ResultBuffer;
|
||||
cStreamdevBuffer *m_ResultBuffer;
|
||||
int m_ResultSkipped;
|
||||
int m_Skipped;
|
||||
bool m_Synced;
|
||||
@@ -26,4 +31,6 @@ public:
|
||||
void Del(int Count) { m_ResultBuffer->Del(Count); }
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_TS2PESREMUX_H
|
||||
} // namespace Streamdev
|
||||
|
||||
#endif // VDR_STREAMDEV_TS2PSREMUX_H
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
#include "remux/tsremux.h"
|
||||
|
||||
#define SC_PICTURE 0x00 // "picture header"
|
||||
#define SC_PICTURE 0x00 // "picture header"
|
||||
#define PID_MASK_HI 0x1F
|
||||
#define VIDEO_STREAM_S 0xE0
|
||||
|
||||
using namespace Streamdev;
|
||||
|
||||
void cTSRemux::SetBrokenLink(uchar *Data, int Length)
|
||||
{
|
||||
if (Length > 9 && Data[0] == 0 && Data[1] == 0 && Data[2] == 1 && (Data[3] & 0xF0) == VIDEO_STREAM_S) {
|
||||
for (int i = Data[8] + 9; i < Length - 7; i++) { // +9 to skip video packet header
|
||||
int PesPayloadOffset = 0;
|
||||
if (AnalyzePesHeader(Data, Length, PesPayloadOffset) >= phMPEG1 && (Data[3] & 0xF0) == VIDEO_STREAM_S) {
|
||||
for (int i = PesPayloadOffset; i < Length - 7; i++) {
|
||||
if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1 && Data[i + 3] == 0xB8) {
|
||||
if (!(Data[i + 7] & 0x40)) // set flag only if GOP is not closed
|
||||
Data[i + 7] |= 0x20;
|
||||
@@ -39,17 +44,40 @@ int cTSRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &P
|
||||
// If the return value is -1 the packet was not completely in the buffer.
|
||||
int Length = GetPacketLength(Data, Count, Offset);
|
||||
if (Length > 0) {
|
||||
if (Length >= 8) {
|
||||
int i = Offset + 8; // the minimum length of the video packet header
|
||||
i += Data[i] + 1; // possible additional header bytes
|
||||
for (; i < Offset + Length - 5; i++) {
|
||||
if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
|
||||
switch (Data[i + 3]) {
|
||||
case SC_PICTURE: PictureType = (Data[i + 5] >> 3) & 0x07;
|
||||
return Length;
|
||||
int PesPayloadOffset = 0;
|
||||
if (AnalyzePesHeader(Data + Offset, Length, PesPayloadOffset) >= phMPEG1) {
|
||||
const uchar *p = Data + Offset + PesPayloadOffset + 2;
|
||||
const uchar *pLimit = Data + Offset + Length - 3;
|
||||
#ifdef TEST_cVideoRepacker
|
||||
// cVideoRepacker ensures that a new PES packet is started for a new sequence,
|
||||
// group or picture which allows us to easily skip scanning through a huge
|
||||
// amount of video data.
|
||||
if (p < pLimit) {
|
||||
if (p[-2] || p[-1] || p[0] != 0x01)
|
||||
pLimit = 0; // skip scanning: packet doesn't start with 0x000001
|
||||
else {
|
||||
switch (p[1]) {
|
||||
case SC_SEQUENCE:
|
||||
case SC_GROUP:
|
||||
case SC_PICTURE:
|
||||
break;
|
||||
default: // skip scanning: packet doesn't start a new sequence, group or picture
|
||||
pLimit = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
while (p < pLimit && (p = (const uchar *)memchr(p, 0x01, pLimit - p))) {
|
||||
if (!p[-2] && !p[-1]) { // found 0x000001
|
||||
switch (p[1]) {
|
||||
case SC_PICTURE: PictureType = (p[3] >> 3) & 0x07;
|
||||
return Length;
|
||||
}
|
||||
p += 4; // continue scanning after 0x01ssxxyy
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
p += 3; // continue scanning after 0x01xxyy
|
||||
}
|
||||
}
|
||||
PictureType = NO_PICTURE;
|
||||
return Length;
|
||||
|
||||
@@ -4,30 +4,25 @@
|
||||
#include "libdvbmpeg/transform.h"
|
||||
#include <vdr/remux.h>
|
||||
|
||||
#define RESULTBUFFERSIZE KILOBYTE(256)
|
||||
// Picture types:
|
||||
#define NO_PICTURE 0
|
||||
|
||||
namespace Streamdev {
|
||||
|
||||
class cTSRemux {
|
||||
protected:
|
||||
/*uchar m_ResultBuffer[RESULTBUFFERSIZE];
|
||||
int m_ResultCount;
|
||||
int m_ResultDelivered;
|
||||
int m_Synced;
|
||||
int m_Skipped;
|
||||
int m_Sync;
|
||||
|
||||
|
||||
virtual void PutTSPacket(int Pid, const uint8_t *Data) = 0;
|
||||
|
||||
public:
|
||||
cTSRemux(bool Sync = true);
|
||||
virtual ~cTSRemux();
|
||||
|
||||
virtual uchar *Process(const uchar *Data, int &Count, int &Result);*/
|
||||
virtual ~cTSRemux() {};
|
||||
|
||||
virtual int Put(const uchar *Data, int Count) = 0;
|
||||
virtual uchar *Get(int &Count) = 0;
|
||||
virtual void Del(int Count) = 0;
|
||||
|
||||
static void SetBrokenLink(uchar *Data, int Length);
|
||||
static int GetPid(const uchar *Data);
|
||||
static int GetPacketLength(const uchar *Data, int Count, int Offset);
|
||||
static int GetPacketLength(const uchar *Data, int Count, int Offset);
|
||||
static int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
|
||||
};
|
||||
|
||||
} // namespace Streamdev
|
||||
|
||||
#endif // VDR_STREAMDEV_TSREMUX_H
|
||||
|
||||
82
server/Makefile
Normal file
82
server/Makefile
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 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* *~
|
||||
@@ -1,13 +1,14 @@
|
||||
/*
|
||||
* $Id: component.c,v 1.3 2005/05/09 20:22:29 lordjaxom Exp $
|
||||
* $Id: component.c,v 1.4 2009/02/13 10:39:22 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include "server/component.h"
|
||||
#include "server/connection.h"
|
||||
|
||||
cServerComponent::cServerComponent(const char *Protocol, const char *ListenIp,
|
||||
uint ListenPort):
|
||||
uint ListenPort, int Type, int IpProto):
|
||||
m_Protocol(Protocol),
|
||||
m_Listen(Type, IpProto),
|
||||
m_ListenIp(ListenIp),
|
||||
m_ListenPort(ListenPort)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: component.h,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $
|
||||
* $Id: component.h,v 1.3 2009/02/13 10:39:22 schmirl Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_SERVERS_COMPONENT_H
|
||||
@@ -17,8 +17,8 @@ class cServerConnection;
|
||||
|
||||
class cServerComponent: public cListObject {
|
||||
private:
|
||||
cTBSocket m_Listen;
|
||||
const char *m_Protocol;
|
||||
cTBSocket m_Listen;
|
||||
const char *m_ListenIp;
|
||||
uint m_ListenPort;
|
||||
|
||||
@@ -27,7 +27,7 @@ protected:
|
||||
virtual cServerConnection *NewClient(void) = 0;
|
||||
|
||||
public:
|
||||
cServerComponent(const char *Protocol, const char *ListenIp, uint ListenPort);
|
||||
cServerComponent(const char *Protocol, const char *ListenIp, uint ListenPort, int Type = SOCK_STREAM, int IpProto = 0);
|
||||
virtual ~cServerComponent();
|
||||
|
||||
/* Starts listening on the specified Port, override if you want to do things
|
||||
|
||||
461
server/componentIGMP.c
Normal file
461
server/componentIGMP.c
Normal file
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
* $Id: componentIGMP.c,v 1.2 2009/07/03 21:44:19 schmirl Exp $
|
||||
*/
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/igmp.h>
|
||||
|
||||
#include "server/componentIGMP.h"
|
||||
#include "server/connectionIGMP.h"
|
||||
#include "server/server.h"
|
||||
#include "server/setup.h"
|
||||
|
||||
#ifndef IGMP_ALL_HOSTS
|
||||
#define IGMP_ALL_HOSTS htonl(0xE0000001L)
|
||||
#endif
|
||||
#ifndef IGMP_ALL_ROUTER
|
||||
#define IGMP_ALL_ROUTER htonl(0xE0000002L)
|
||||
#endif
|
||||
|
||||
// IGMP parameters according to RFC2236. All time values in seconds.
|
||||
#define IGMP_ROBUSTNESS 2
|
||||
#define IGMP_QUERY_INTERVAL 125
|
||||
#define IGMP_QUERY_RESPONSE_INTERVAL 10
|
||||
#define IGMP_GROUP_MEMBERSHIP_INTERVAL (2 * IGMP_QUERY_INTERVAL + IGMP_QUERY_RESPONSE_INTERVAL)
|
||||
#define IGMP_OTHER_QUERIER_PRESENT_INTERVAL (2 * IGMP_QUERY_INTERVAL + IGMP_QUERY_RESPONSE_INTERVAL / 2)
|
||||
#define IGMP_STARTUP_QUERY_INTERVAL (IGMP_QUERY_INTERVAL / 4)
|
||||
#define IGMP_STARTUP_QUERY_COUNT IGMP_ROBUSTNESS
|
||||
// This value is 1/10 sec. RFC default is 10. Reduced to minimum to free unused channels ASAP
|
||||
#define IGMP_LAST_MEMBER_QUERY_INTERVAL_TS 1
|
||||
#define IGMP_LAST_MEMBER_QUERY_COUNT IGMP_ROBUSTNESS
|
||||
|
||||
// operations on struct timeval
|
||||
#define TV_CMP(a, cmp, b) (a.tv_sec == b.tv_sec ? a.tv_usec cmp b.tv_usec : a.tv_sec cmp b.tv_sec)
|
||||
#define TV_SET(tv) (tv.tv_sec || tv.tv_usec)
|
||||
#define TV_CLR(tv) memset(&tv, 0, sizeof(tv))
|
||||
#define TV_CPY(dst, src) memcpy(&dst, &src, sizeof(dst))
|
||||
#define TV_ADD(dst, ts) dst.tv_sec += ts / 10; dst.tv_usec += (ts % 10) * 100000; if (dst.tv_usec >= 1000000) { dst.tv_usec -= 1000000; dst.tv_sec++; }
|
||||
|
||||
class cMulticastGroup: public cListObject
|
||||
{
|
||||
public:
|
||||
in_addr_t group;
|
||||
in_addr_t reporter;
|
||||
struct timeval timeout;
|
||||
struct timeval v1timer;
|
||||
struct timeval retransmit;
|
||||
|
||||
cMulticastGroup(in_addr_t Group);
|
||||
};
|
||||
|
||||
cMulticastGroup::cMulticastGroup(in_addr_t Group) :
|
||||
group(Group),
|
||||
reporter(0)
|
||||
{
|
||||
TV_CLR(timeout);
|
||||
TV_CLR(v1timer);
|
||||
TV_CLR(retransmit);
|
||||
}
|
||||
|
||||
void logIGMP(uint8_t type, struct in_addr Src, struct in_addr Dst, struct in_addr Grp)
|
||||
{
|
||||
const char* msg;
|
||||
switch (type) {
|
||||
case IGMP_MEMBERSHIP_QUERY: msg = "membership query"; break;
|
||||
case IGMP_V1_MEMBERSHIP_REPORT: msg = "V1 membership report"; break;
|
||||
case IGMP_V2_MEMBERSHIP_REPORT: msg = "V2 membership report"; break;
|
||||
case IGMP_V2_LEAVE_GROUP: msg = "leave group"; break;
|
||||
default: msg = "unknown"; break;
|
||||
}
|
||||
char* s = strdup(inet_ntoa(Src));
|
||||
char* d = strdup(inet_ntoa(Dst));
|
||||
dsyslog("streamdev-server IGMP: Received %s from %s (dst %s) for %s", msg, s, d, inet_ntoa(Grp));
|
||||
free(s);
|
||||
free(d);
|
||||
}
|
||||
|
||||
/* Taken from http://tools.ietf.org/html/rfc1071 */
|
||||
uint16_t inetChecksum(uint16_t *addr, int count)
|
||||
{
|
||||
uint32_t sum = 0;
|
||||
while (count > 1) {
|
||||
sum += *addr++;
|
||||
count -= 2;
|
||||
}
|
||||
|
||||
if( count > 0 )
|
||||
sum += * (uint8_t *) addr;
|
||||
|
||||
while (sum>>16)
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
|
||||
return ~sum;
|
||||
}
|
||||
|
||||
cComponentIGMP::cComponentIGMP(void):
|
||||
cServerComponent("IGMP", "0.0.0.0", 0, SOCK_RAW, IPPROTO_IGMP),
|
||||
cThread("IGMP timeout handler"),
|
||||
m_BindIp(inet_addr(StreamdevServerSetup.IGMPBindIP)),
|
||||
m_MaxChannelNumber(0),
|
||||
m_StartupQueryCount(IGMP_STARTUP_QUERY_COUNT),
|
||||
m_Querier(true)
|
||||
{
|
||||
}
|
||||
|
||||
cComponentIGMP::~cComponentIGMP(void)
|
||||
{
|
||||
}
|
||||
|
||||
cMulticastGroup* cComponentIGMP::FindGroup(in_addr_t Group) const
|
||||
{
|
||||
cMulticastGroup *group = m_Groups.First();
|
||||
while (group && group->group != Group)
|
||||
group = m_Groups.Next(group);
|
||||
return group;
|
||||
}
|
||||
|
||||
bool cComponentIGMP::Initialize(void)
|
||||
{
|
||||
if (cServerComponent::Initialize() && IGMPMembership(IGMP_ALL_ROUTER))
|
||||
{
|
||||
for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
|
||||
{
|
||||
if (channel->GroupSep())
|
||||
continue;
|
||||
int num = channel->Number();
|
||||
if (!IGMPMembership(htonl(MULTICAST_PRIV_MIN + num)))
|
||||
break;
|
||||
m_MaxChannelNumber = num;
|
||||
}
|
||||
if (m_MaxChannelNumber == 0)
|
||||
{
|
||||
IGMPMembership(IGMP_ALL_ROUTER, false);
|
||||
esyslog("streamdev-server IGMP: no multicast group joined");
|
||||
}
|
||||
else
|
||||
{
|
||||
Start();
|
||||
}
|
||||
}
|
||||
return m_MaxChannelNumber > 0;
|
||||
}
|
||||
|
||||
void cComponentIGMP::Destruct(void)
|
||||
{
|
||||
if (m_MaxChannelNumber > 0)
|
||||
{
|
||||
Cancel(-1);
|
||||
m_CondWait.Signal();
|
||||
Cancel(2);
|
||||
for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
|
||||
{
|
||||
if (channel->GroupSep())
|
||||
continue;
|
||||
int num = channel->Number();
|
||||
if (num > m_MaxChannelNumber)
|
||||
break;
|
||||
IGMPMembership(htonl(MULTICAST_PRIV_MIN + num), false);
|
||||
}
|
||||
IGMPMembership(IGMP_ALL_ROUTER, false);
|
||||
}
|
||||
m_MaxChannelNumber = 0;
|
||||
cServerComponent::Destruct();
|
||||
}
|
||||
|
||||
cServerConnection *cComponentIGMP::NewClient(void)
|
||||
{
|
||||
return new cConnectionIGMP("IGMP", StreamdevServerSetup.IGMPClientPort, (eStreamType) StreamdevServerSetup.IGMPStreamType);
|
||||
}
|
||||
|
||||
cServerConnection* cComponentIGMP::Accept(void)
|
||||
{
|
||||
ssize_t recv_len;
|
||||
int ip_hdrlen, ip_datalen;
|
||||
struct ip *ip;
|
||||
struct igmp *igmp;
|
||||
|
||||
while ((recv_len = ::recvfrom(Socket(), m_ReadBuffer, sizeof(m_ReadBuffer), 0, NULL, NULL)) < 0 && errno == EINTR)
|
||||
errno = 0;
|
||||
|
||||
if (recv_len < 0) {
|
||||
esyslog("streamdev-server IGMP: read failed: %m");
|
||||
return NULL;
|
||||
}
|
||||
else if (recv_len < (ssize_t) sizeof(struct ip)) {
|
||||
esyslog("streamdev-server IGMP: IP packet too short");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ip = (struct ip*) m_ReadBuffer;
|
||||
|
||||
// filter out my own packets
|
||||
if (ip->ip_src.s_addr == m_BindIp)
|
||||
return NULL;
|
||||
|
||||
ip_hdrlen = ip->ip_hl << 2;
|
||||
#ifdef __FreeBSD__
|
||||
ip_datalen = ip->ip_len;
|
||||
#else
|
||||
ip_datalen = ntohs(ip->ip_len) - ip_hdrlen;
|
||||
#endif
|
||||
if (ip->ip_p != IPPROTO_IGMP) {
|
||||
esyslog("streamdev-server IGMP: Unexpected protocol %hhu", ip->ip_p);
|
||||
return NULL;
|
||||
}
|
||||
if (recv_len < ip_hdrlen + IGMP_MINLEN) {
|
||||
esyslog("streamdev-server IGMP: packet too short");
|
||||
return NULL;
|
||||
}
|
||||
igmp = (struct igmp*) (m_ReadBuffer + ip_hdrlen);
|
||||
uint16_t chksum = igmp->igmp_cksum;
|
||||
igmp->igmp_cksum = 0;
|
||||
if (chksum != inetChecksum((uint16_t *)igmp, ip_datalen))
|
||||
{
|
||||
esyslog("INVALID CHECKSUM %d %d %d %lu 0x%x 0x%x", (int) ntohs(ip->ip_len), ip_hdrlen, ip_datalen, (unsigned long int) recv_len, chksum, inetChecksum((uint16_t *)igmp, ip_datalen));
|
||||
return NULL;
|
||||
}
|
||||
logIGMP(igmp->igmp_type, ip->ip_src, ip->ip_dst, igmp->igmp_group);
|
||||
return ProcessMessage(igmp, igmp->igmp_group.s_addr, ip->ip_src.s_addr);
|
||||
}
|
||||
|
||||
cServerConnection* cComponentIGMP::ProcessMessage(struct igmp *Igmp, in_addr_t Group, in_addr_t Sender)
|
||||
{
|
||||
cServerConnection* conn = NULL;
|
||||
cMulticastGroup* group;
|
||||
LOCK_THREAD;
|
||||
switch (Igmp->igmp_type) {
|
||||
case IGMP_MEMBERSHIP_QUERY:
|
||||
if (ntohl(Sender) < ntohl(m_BindIp))
|
||||
IGMPStartOtherQuerierPresentTimer();
|
||||
break;
|
||||
case IGMP_V1_MEMBERSHIP_REPORT:
|
||||
case IGMP_V2_MEMBERSHIP_REPORT:
|
||||
group = FindGroup(Group);
|
||||
if (!group) {
|
||||
group = new cMulticastGroup(Group);
|
||||
m_Groups.Add(group);
|
||||
}
|
||||
conn = IGMPStartMulticast(group);
|
||||
IGMPStartTimer(group, Sender);
|
||||
if (Igmp->igmp_type == IGMP_V1_MEMBERSHIP_REPORT)
|
||||
IGMPStartV1HostTimer(group);
|
||||
break;
|
||||
case IGMP_V2_LEAVE_GROUP:
|
||||
group = FindGroup(Group);
|
||||
if (group && !TV_SET(group->v1timer)) {
|
||||
if (group->reporter == Sender) {
|
||||
IGMPStartTimerAfterLeave(group, m_Querier ? IGMP_LAST_MEMBER_QUERY_INTERVAL_TS : Igmp->igmp_code);
|
||||
if (m_Querier)
|
||||
IGMPSendGroupQuery(group);
|
||||
IGMPStartRetransmitTimer(group);
|
||||
}
|
||||
m_CondWait.Signal();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
void cComponentIGMP::Action()
|
||||
{
|
||||
while (Running()) {
|
||||
struct timeval now;
|
||||
struct timeval next;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
TV_CPY(next, now);
|
||||
next.tv_sec += IGMP_QUERY_INTERVAL;
|
||||
|
||||
cMulticastGroup *del = NULL;
|
||||
{
|
||||
LOCK_THREAD;
|
||||
if (TV_CMP(m_GeneralQueryTimer, <, now)) {
|
||||
dsyslog("General Query");
|
||||
IGMPSendGeneralQuery();
|
||||
IGMPStartGeneralQueryTimer();
|
||||
}
|
||||
if (TV_CMP(next, >, m_GeneralQueryTimer))
|
||||
TV_CPY(next, m_GeneralQueryTimer);
|
||||
|
||||
for (cMulticastGroup *group = m_Groups.First(); group; group = m_Groups.Next(group)) {
|
||||
if (TV_CMP(group->timeout, <, now)) {
|
||||
IGMPStopMulticast(group);
|
||||
IGMPClearRetransmitTimer(group);
|
||||
if (del)
|
||||
m_Groups.Del(del);
|
||||
del = group;
|
||||
}
|
||||
else if (m_Querier && TV_SET(group->retransmit) && TV_CMP(group->retransmit, <, now)) {
|
||||
IGMPSendGroupQuery(group);
|
||||
IGMPStartRetransmitTimer(group);
|
||||
if (TV_CMP(next, >, group->retransmit))
|
||||
TV_CPY(next, group->retransmit);
|
||||
}
|
||||
else if (TV_SET(group->v1timer) && TV_CMP(group->v1timer, <, now)) {
|
||||
TV_CLR(group->v1timer);
|
||||
}
|
||||
else {
|
||||
if (TV_CMP(next, >, group->timeout))
|
||||
TV_CPY(next, group->timeout);
|
||||
if (TV_SET(group->retransmit) && TV_CMP(next, >, group->retransmit))
|
||||
TV_CPY(next, group->retransmit);
|
||||
if (TV_SET(group->v1timer) && TV_CMP(next, >, group->v1timer))
|
||||
TV_CPY(next, group->v1timer);
|
||||
}
|
||||
}
|
||||
if (del)
|
||||
m_Groups.Del(del);
|
||||
}
|
||||
|
||||
int sleep = (next.tv_sec - now.tv_sec) * 1000;
|
||||
sleep += (next.tv_usec - now.tv_usec) / 1000;
|
||||
if (next.tv_usec < now.tv_usec)
|
||||
sleep += 1000;
|
||||
dsyslog("Sleeping %d ms", sleep);
|
||||
m_CondWait.Wait(sleep);
|
||||
}
|
||||
}
|
||||
|
||||
bool cComponentIGMP::IGMPMembership(in_addr_t Group, bool Add)
|
||||
{
|
||||
struct ip_mreqn mreq;
|
||||
mreq.imr_multiaddr.s_addr = Group;
|
||||
mreq.imr_address.s_addr = INADDR_ANY;
|
||||
mreq.imr_ifindex = 0;
|
||||
if (setsockopt(Socket(), IPPROTO_IP, Add ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
|
||||
{
|
||||
esyslog("streamdev-server IGMP: unable to %s %s: %m", Add ? "join" : "leave", inet_ntoa(mreq.imr_multiaddr));
|
||||
if (errno == ENOBUFS)
|
||||
esyslog("consider increasing sys.net.ipv4.igmp_max_memberships");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void cComponentIGMP::IGMPSendQuery(in_addr_t Group, int Timeout)
|
||||
{
|
||||
struct sockaddr_in dst;
|
||||
struct igmp query;
|
||||
|
||||
dst.sin_family = AF_INET;
|
||||
dst.sin_port = IPPROTO_IGMP;
|
||||
dst.sin_addr.s_addr = Group;
|
||||
query.igmp_type = IGMP_MEMBERSHIP_QUERY;
|
||||
query.igmp_code = Timeout * 10;
|
||||
query.igmp_cksum = 0;
|
||||
query.igmp_group.s_addr = (Group == IGMP_ALL_HOSTS) ? 0 : Group;
|
||||
query.igmp_cksum = inetChecksum((uint16_t *) &query, sizeof(query));
|
||||
|
||||
for (int i = 0; i < 5 && ::sendto(Socket(), &query, sizeof(query), 0, (sockaddr*)&dst, sizeof(dst)) == -1; i++) {
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
esyslog("streamdev-server IGMP: unable to query group %s: %m", inet_ntoa(dst.sin_addr));
|
||||
break;
|
||||
}
|
||||
cCondWait::SleepMs(10);
|
||||
}
|
||||
}
|
||||
|
||||
// Querier state actions
|
||||
void cComponentIGMP::IGMPStartGeneralQueryTimer()
|
||||
{
|
||||
m_Querier = true;
|
||||
if (m_StartupQueryCount) {
|
||||
gettimeofday(&m_GeneralQueryTimer, NULL);
|
||||
m_GeneralQueryTimer.tv_sec += IGMP_STARTUP_QUERY_INTERVAL;
|
||||
m_StartupQueryCount--;
|
||||
}
|
||||
else {
|
||||
gettimeofday(&m_GeneralQueryTimer, NULL);
|
||||
m_GeneralQueryTimer.tv_sec += IGMP_QUERY_INTERVAL;
|
||||
}
|
||||
}
|
||||
|
||||
void cComponentIGMP::IGMPStartOtherQuerierPresentTimer()
|
||||
{
|
||||
m_Querier = false;
|
||||
m_StartupQueryCount = 0;
|
||||
gettimeofday(&m_GeneralQueryTimer, NULL);
|
||||
m_GeneralQueryTimer.tv_sec += IGMP_OTHER_QUERIER_PRESENT_INTERVAL;
|
||||
}
|
||||
|
||||
void cComponentIGMP::IGMPSendGeneralQuery()
|
||||
{
|
||||
IGMPSendQuery(IGMP_ALL_HOSTS, IGMP_QUERY_RESPONSE_INTERVAL);
|
||||
}
|
||||
|
||||
// Group state actions
|
||||
void cComponentIGMP::IGMPStartTimer(cMulticastGroup* Group, in_addr_t Member)
|
||||
{
|
||||
gettimeofday(&Group->timeout, NULL);
|
||||
Group->timeout.tv_sec += IGMP_GROUP_MEMBERSHIP_INTERVAL;
|
||||
TV_CLR(Group->retransmit);
|
||||
Group->reporter = Member;
|
||||
|
||||
}
|
||||
|
||||
void cComponentIGMP::IGMPStartV1HostTimer(cMulticastGroup* Group)
|
||||
{
|
||||
gettimeofday(&Group->v1timer, NULL);
|
||||
Group->v1timer.tv_sec += IGMP_GROUP_MEMBERSHIP_INTERVAL;
|
||||
}
|
||||
|
||||
void cComponentIGMP::IGMPStartTimerAfterLeave(cMulticastGroup* Group, unsigned int MaxResponseTimeTs)
|
||||
{
|
||||
//Group->Update(time(NULL) + MaxResponseTime * IGMP_LAST_MEMBER_QUERY_COUNT / 10);
|
||||
MaxResponseTimeTs *= IGMP_LAST_MEMBER_QUERY_COUNT;
|
||||
gettimeofday(&Group->timeout, NULL);
|
||||
TV_ADD(Group->timeout, MaxResponseTimeTs);
|
||||
TV_CLR(Group->retransmit);
|
||||
Group->reporter = 0;
|
||||
}
|
||||
|
||||
void cComponentIGMP::IGMPStartRetransmitTimer(cMulticastGroup* Group)
|
||||
{
|
||||
gettimeofday(&Group->retransmit, NULL);
|
||||
TV_ADD(Group->retransmit, IGMP_LAST_MEMBER_QUERY_INTERVAL_TS);
|
||||
}
|
||||
|
||||
void cComponentIGMP::IGMPClearRetransmitTimer(cMulticastGroup* Group)
|
||||
{
|
||||
TV_CLR(Group->retransmit);
|
||||
}
|
||||
|
||||
void cComponentIGMP::IGMPSendGroupQuery(cMulticastGroup* Group)
|
||||
{
|
||||
IGMPSendQuery(Group->group, IGMP_LAST_MEMBER_QUERY_INTERVAL_TS);
|
||||
}
|
||||
|
||||
cServerConnection* cComponentIGMP::IGMPStartMulticast(cMulticastGroup* Group)
|
||||
{
|
||||
cServerConnection *conn = NULL;
|
||||
in_addr_t g = ntohl(Group->group);
|
||||
if (g > MULTICAST_PRIV_MIN && g <= MULTICAST_PRIV_MAX) {
|
||||
cThreadLock lock;
|
||||
cChannel *channel = Channels.GetByNumber(g - MULTICAST_PRIV_MIN);
|
||||
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
||||
cServerConnection *s = clients.First();
|
||||
while (s) {
|
||||
if (s->RemoteIpAddr() == Group->group)
|
||||
break;
|
||||
s = clients.Next(s);
|
||||
}
|
||||
if (!s) {
|
||||
conn = NewClient();
|
||||
if (!((cConnectionIGMP *)conn)->SetChannel(channel, Group->group)) {
|
||||
DELETENULL(conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
void cComponentIGMP::IGMPStopMulticast(cMulticastGroup* Group)
|
||||
{
|
||||
cThreadLock lock;
|
||||
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
||||
for (cServerConnection *s = clients.First(); s; s = clients.Next(s)) {
|
||||
if (s->RemoteIpAddr() == Group->group)
|
||||
s->Close();
|
||||
}
|
||||
}
|
||||
61
server/componentIGMP.h
Normal file
61
server/componentIGMP.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* $Id: componentIGMP.h,v 1.1 2009/02/13 10:39:22 schmirl Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_IGMPSERVER_H
|
||||
#define VDR_STREAMDEV_IGMPSERVER_H
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <vdr/thread.h>
|
||||
#include "server/component.h"
|
||||
|
||||
class cMulticastGroup;
|
||||
|
||||
class cComponentIGMP: public cServerComponent, public cThread {
|
||||
private:
|
||||
char m_ReadBuffer[2048];
|
||||
cList<cMulticastGroup> m_Groups;
|
||||
in_addr_t m_BindIp;
|
||||
int m_MaxChannelNumber;
|
||||
struct timeval m_GeneralQueryTimer;
|
||||
int m_StartupQueryCount;
|
||||
bool m_Querier;
|
||||
cCondWait m_CondWait;
|
||||
|
||||
cMulticastGroup* FindGroup(in_addr_t Group) const;
|
||||
|
||||
/* Add or remove local host to multicast group */
|
||||
bool IGMPMembership(in_addr_t Group, bool Add = true);
|
||||
void IGMPSendQuery(in_addr_t Group, int Timeout);
|
||||
|
||||
cServerConnection* ProcessMessage(struct igmp *Igmp, in_addr_t Group, in_addr_t Sender);
|
||||
|
||||
void IGMPStartGeneralQueryTimer();
|
||||
void IGMPStartOtherQuerierPresentTimer();
|
||||
void IGMPSendGeneralQuery();
|
||||
|
||||
void IGMPStartTimer(cMulticastGroup* Group, in_addr_t Member);
|
||||
void IGMPStartV1HostTimer(cMulticastGroup* Group);
|
||||
void IGMPStartTimerAfterLeave(cMulticastGroup* Group, unsigned int MaxResponseTime);
|
||||
void IGMPStartRetransmitTimer(cMulticastGroup* Group);
|
||||
void IGMPClearRetransmitTimer(cMulticastGroup* Group);
|
||||
void IGMPSendGroupQuery(cMulticastGroup* Group);
|
||||
cServerConnection* IGMPStartMulticast(cMulticastGroup* Group);
|
||||
void IGMPStopMulticast(cMulticastGroup* Group);
|
||||
|
||||
virtual void Action();
|
||||
|
||||
protected:
|
||||
virtual cServerConnection *NewClient(void);
|
||||
|
||||
public:
|
||||
virtual bool Initialize(void);
|
||||
virtual void Destruct(void);
|
||||
virtual cServerConnection* Accept(void);
|
||||
|
||||
cComponentIGMP(void);
|
||||
~cComponentIGMP(void);
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_IGMPSERVER_H
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: connection.c,v 1.10 2007/05/07 12:25:11 schmirl Exp $
|
||||
* $Id: connection.c,v 1.16 2010/08/03 10:51:53 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include "server/connection.h"
|
||||
@@ -8,11 +8,62 @@
|
||||
#include "common.h"
|
||||
|
||||
#include <vdr/tools.h>
|
||||
#include <vdr/thread.h>
|
||||
#include <vdr/transfer.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
|
||||
cServerConnection::cServerConnection(const char *Protocol):
|
||||
class cSwitchLive {
|
||||
private:
|
||||
cMutex mutex;
|
||||
cCondWait switched;
|
||||
cDevice *device;
|
||||
const cChannel *channel;
|
||||
public:
|
||||
cDevice* Switch(cDevice *Device, const cChannel *Channel);
|
||||
void Switch(void);
|
||||
cSwitchLive(void);
|
||||
};
|
||||
|
||||
cSwitchLive::cSwitchLive(): device(NULL), channel(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
cDevice* cSwitchLive::Switch(cDevice *Device, const cChannel *Channel)
|
||||
{
|
||||
mutex.Lock();
|
||||
device = Device;
|
||||
channel = Channel;
|
||||
mutex.Unlock();
|
||||
switched.Wait();
|
||||
return device;
|
||||
}
|
||||
|
||||
void cSwitchLive::Switch(void)
|
||||
{
|
||||
mutex.Lock();
|
||||
if (channel && device) {
|
||||
cDevice::SetAvoidDevice(device);
|
||||
if (!Channels.SwitchTo(cDevice::CurrentChannel())) {
|
||||
if (StreamdevServerSetup.SuspendMode == smAlways) {
|
||||
Channels.SwitchTo(channel->Number());
|
||||
Skins.Message(mtInfo, tr("Streaming active"));
|
||||
}
|
||||
else {
|
||||
esyslog("streamdev: Can't receive channel %d (%s) from device %d. Moving live TV to other device failed (PrimaryDevice=%d, ActualDevice=%d)", channel->Number(), channel->Name(), device->CardIndex(), cDevice::PrimaryDevice()->CardIndex(), cDevice::ActualDevice()->CardIndex());
|
||||
device = NULL;
|
||||
}
|
||||
}
|
||||
// make sure we don't come in here next time
|
||||
channel = NULL;
|
||||
switched.Signal();
|
||||
}
|
||||
mutex.Unlock();
|
||||
}
|
||||
|
||||
cServerConnection::cServerConnection(const char *Protocol, int Type):
|
||||
cTBSocket(Type),
|
||||
m_Protocol(Protocol),
|
||||
m_DeferClose(false),
|
||||
m_Pending(false),
|
||||
@@ -20,10 +71,72 @@ cServerConnection::cServerConnection(const char *Protocol):
|
||||
m_WriteBytes(0),
|
||||
m_WriteIndex(0)
|
||||
{
|
||||
m_SwitchLive = new cSwitchLive();
|
||||
}
|
||||
|
||||
cServerConnection::~cServerConnection()
|
||||
{
|
||||
delete m_SwitchLive;
|
||||
}
|
||||
|
||||
const cChannel* cServerConnection::ChannelFromString(const char *String, int *Apid, int *Dpid) {
|
||||
const cChannel *channel = NULL;
|
||||
char *string = strdup(String);
|
||||
char *ptr, *end;
|
||||
int apididx = 0;
|
||||
|
||||
if ((ptr = strrchr(string, '+')) != NULL) {
|
||||
*(ptr++) = '\0';
|
||||
apididx = strtoul(ptr, &end, 10);
|
||||
Dprintf("found apididx: %d\n", apididx);
|
||||
}
|
||||
|
||||
if (isnumber(string)) {
|
||||
int temp = strtol(String, NULL, 10);
|
||||
if (temp >= 1 && temp <= Channels.MaxNumber())
|
||||
channel = Channels.GetByNumber(temp);
|
||||
} else {
|
||||
channel = Channels.GetByChannelID(tChannelID::FromString(string));
|
||||
|
||||
if (channel == NULL) {
|
||||
int i = 1;
|
||||
while ((channel = Channels.GetByNumber(i, 1)) != NULL) {
|
||||
if (String == channel->Name())
|
||||
break;
|
||||
|
||||
i = channel->Number() + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (channel != NULL && apididx > 0) {
|
||||
int apid = 0, dpid = 0;
|
||||
int index = 1;
|
||||
|
||||
for (int i = 0; channel->Apid(i) != 0; ++i, ++index) {
|
||||
if (index == apididx) {
|
||||
apid = channel->Apid(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (apid == 0) {
|
||||
for (int i = 0; channel->Dpid(i) != 0; ++i, ++index) {
|
||||
if (index == apididx) {
|
||||
dpid = channel->Dpid(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Apid != NULL)
|
||||
*Apid = apid;
|
||||
if (Dpid != NULL)
|
||||
*Dpid = dpid;
|
||||
}
|
||||
|
||||
free(string);
|
||||
return channel;
|
||||
}
|
||||
|
||||
bool cServerConnection::Read(void)
|
||||
@@ -123,83 +236,184 @@ bool cServerConnection::Respond(const char *Message, bool Last, ...)
|
||||
m_Pending = !Last;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool cServerConnection::Close()
|
||||
{
|
||||
if (IsOpen())
|
||||
isyslog("streamdev-server: closing %s connection to %s:%d", Protocol(), RemoteIp().c_str(), RemotePort());
|
||||
return cTBSocket::Close();
|
||||
}
|
||||
|
||||
#if APIVERSNUM >= 10700
|
||||
static int GetClippedNumProvidedSystems(int AvailableBits, cDevice *Device)
|
||||
{
|
||||
int MaxNumProvidedSystems = (1 << AvailableBits) - 1;
|
||||
int NumProvidedSystems = Device->NumProvidedSystems();
|
||||
if (NumProvidedSystems > MaxNumProvidedSystems) {
|
||||
esyslog("ERROR: device %d supports %d modulation systems but cDevice::GetDevice() currently only supports %d delivery systems which should be fixed", Device->CardIndex() + 1, NumProvidedSystems, MaxNumProvidedSystems);
|
||||
NumProvidedSystems = MaxNumProvidedSystems;
|
||||
}
|
||||
else if (NumProvidedSystems <= 0) {
|
||||
esyslog("ERROR: device %d reported an invalid number (%d) of supported delivery systems - assuming 1", Device->CardIndex() + 1, NumProvidedSystems);
|
||||
NumProvidedSystems = 1;
|
||||
}
|
||||
return NumProvidedSystems;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* copy of cDevice::GetDevice(...) but without side effects (not detaching receivers)
|
||||
*/
|
||||
cDevice* cServerConnection::CheckDevice(const cChannel *Channel, int Priority, bool LiveView, const cDevice *AvoidDevice)
|
||||
{
|
||||
//cDevice *AvoidDevice = avoidDevice;
|
||||
//avoidDevice = NULL;
|
||||
// Collect the current priorities of all CAM slots that can decrypt the channel:
|
||||
int NumCamSlots = CamSlots.Count();
|
||||
int SlotPriority[NumCamSlots];
|
||||
int NumUsableSlots = 0;
|
||||
if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
|
||||
for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
|
||||
SlotPriority[CamSlot->Index()] = MAXPRIORITY + 1; // assumes it can't be used
|
||||
if (CamSlot->ModuleStatus() == msReady) {
|
||||
if (CamSlot->ProvidesCa(Channel->Caids())) {
|
||||
if (!ChannelCamRelations.CamChecked(Channel->GetChannelID(), CamSlot->SlotNumber())) {
|
||||
SlotPriority[CamSlot->Index()] = CamSlot->Priority();
|
||||
NumUsableSlots++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!NumUsableSlots)
|
||||
return NULL; // no CAM is able to decrypt this channel
|
||||
}
|
||||
|
||||
cDevice *d = NULL;
|
||||
//cCamSlot *s = NULL;
|
||||
|
||||
uint32_t Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
|
||||
for (int j = 0; j < NumCamSlots || !NumUsableSlots; j++) {
|
||||
if (NumUsableSlots && SlotPriority[j] > MAXPRIORITY)
|
||||
continue; // there is no CAM available in this slot
|
||||
for (int i = 0; i < cDevice::NumDevices(); i++) {
|
||||
cDevice *device = cDevice::GetDevice(i);
|
||||
if (device == AvoidDevice)
|
||||
continue; // we've been asked to skip this device
|
||||
if (Channel->Ca() && Channel->Ca() <= CA_DVB_MAX && Channel->Ca() != device->CardIndex() + 1)
|
||||
continue; // a specific card was requested, but not this one
|
||||
if (NumUsableSlots && !CamSlots.Get(j)->Assign(device, true))
|
||||
continue; // CAM slot can't be used with this device
|
||||
bool ndr;
|
||||
if (device->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
|
||||
if (NumUsableSlots && device->CamSlot() && device->CamSlot() != CamSlots.Get(j))
|
||||
ndr = true; // using a different CAM slot requires detaching receivers
|
||||
// Put together an integer number that reflects the "impact" using
|
||||
// this device would have on the overall system. Each condition is represented
|
||||
// by one bit in the number (or several bits, if the condition is actually
|
||||
// a numeric value). The sequence in which the conditions are listed corresponds
|
||||
// to their individual severity, where the one listed first will make the most
|
||||
// difference, because it results in the most significant bit of the result.
|
||||
uint32_t imp = 0;
|
||||
imp <<= 1; imp |= LiveView ? !device->IsPrimaryDevice() || ndr : 0; // prefer the primary device for live viewing if we don't need to detach existing receivers
|
||||
imp <<= 1; imp |= !device->Receiving() && (device != cTransferControl::ReceiverDevice() || device->IsPrimaryDevice()) || ndr; // use receiving devices if we don't need to detach existing receivers, but avoid primary device in local transfer mode
|
||||
imp <<= 1; imp |= device->Receiving(); // avoid devices that are receiving
|
||||
#if APIVERSNUM >= 10700
|
||||
imp <<= 4; imp |= GetClippedNumProvidedSystems(4, device) - 1; // avoid cards which support multiple delivery systems
|
||||
#endif
|
||||
imp <<= 1; imp |= device == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device
|
||||
imp <<= 8; imp |= min(max(device->Priority() + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
|
||||
imp <<= 8; imp |= min(max((NumUsableSlots ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
|
||||
imp <<= 1; imp |= ndr; // avoid devices if we need to detach existing receivers
|
||||
#if VDRVERSNUM < 10719
|
||||
imp <<= 1; imp |= device->IsPrimaryDevice(); // avoid the primary device
|
||||
#endif
|
||||
imp <<= 1; imp |= NumUsableSlots ? 0 : device->HasCi(); // avoid cards with Common Interface for FTA channels
|
||||
#if VDRVERSNUM < 10719
|
||||
imp <<= 1; imp |= device->HasDecoder(); // avoid full featured cards
|
||||
#else
|
||||
imp <<= 1; imp |= device->AvoidRecording(); // avoid SD full featured cards
|
||||
#endif
|
||||
imp <<= 1; imp |= NumUsableSlots ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
|
||||
#if VDRVERSNUM >= 10719
|
||||
imp <<= 1; imp |= device->IsPrimaryDevice(); // avoid the primary device
|
||||
#endif
|
||||
if (imp < Impact) {
|
||||
// This device has less impact than any previous one, so we take it.
|
||||
Impact = imp;
|
||||
d = device;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!NumUsableSlots)
|
||||
break; // no CAM necessary, so just one loop over the devices
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
bool cServerConnection::UsedByLiveTV(cDevice *device)
|
||||
{
|
||||
return device == cTransferControl::ReceiverDevice() ||
|
||||
(device->IsPrimaryDevice() && device->HasDecoder() && !device->Replaying());
|
||||
}
|
||||
|
||||
cDevice *cServerConnection::GetDevice(const cChannel *Channel, int Priority)
|
||||
{
|
||||
cDevice *device = NULL;
|
||||
// turn off the streams of this connection
|
||||
Detach();
|
||||
// This call may detach receivers of the device it returns
|
||||
cDevice *device = cDevice::GetDevice(Channel, Priority, false);
|
||||
|
||||
/*Dprintf("+ Statistics:\n");
|
||||
Dprintf("+ Current Channel: %d\n", cDevice::CurrentChannel());
|
||||
Dprintf("+ Current Device: %d\n", cDevice::ActualDevice()->CardIndex());
|
||||
Dprintf("+ Transfer Mode: %s\n", cDevice::ActualDevice()
|
||||
== cDevice::PrimaryDevice() ? "false" : "true");
|
||||
Dprintf("+ Replaying: %s\n", cDevice::PrimaryDevice()->Replaying() ? "true"
|
||||
: "false");*/
|
||||
|
||||
Dprintf(" * GetDevice(const cChannel*, int)\n");
|
||||
Dprintf(" * -------------------------------\n");
|
||||
|
||||
#if VDRVERSNUM < 10500
|
||||
device = cDevice::GetDevice(Channel, Priority);
|
||||
#else
|
||||
device = cDevice::GetDevice(Channel, Priority, false);
|
||||
#endif
|
||||
|
||||
Dprintf(" * Found following device: %p (%d)\n", device,
|
||||
device ? device->CardIndex() + 1 : 0);
|
||||
if (device == cDevice::ActualDevice())
|
||||
Dprintf(" * is actual device\n");
|
||||
if (!cSuspendCtl::IsActive() && StreamdevServerSetup.SuspendMode != smAlways)
|
||||
Dprintf(" * NOT suspended\n");
|
||||
|
||||
if (!device || (device == cDevice::ActualDevice()
|
||||
&& !cSuspendCtl::IsActive()
|
||||
&& StreamdevServerSetup.SuspendMode != smAlways)) {
|
||||
// mustn't switch actual device
|
||||
// maybe a device would be free if THIS connection did turn off its streams?
|
||||
Dprintf(" * trying again...\n");
|
||||
const cChannel *current = Channels.GetByNumber(cDevice::CurrentChannel());
|
||||
isyslog("streamdev-server: Detaching current receiver");
|
||||
Detach();
|
||||
#if VDRVERSNUM < 10500
|
||||
device = cDevice::GetDevice(Channel, Priority);
|
||||
#else
|
||||
device = cDevice::GetDevice(Channel, Priority, false);
|
||||
#endif
|
||||
Attach();
|
||||
Dprintf(" * Found following device: %p (%d)\n", device,
|
||||
device ? device->CardIndex() + 1 : 0);
|
||||
if (device == cDevice::ActualDevice())
|
||||
Dprintf(" * is actual device\n");
|
||||
if (!cSuspendCtl::IsActive()
|
||||
&& StreamdevServerSetup.SuspendMode != smAlways)
|
||||
Dprintf(" * NOT suspended\n");
|
||||
if (current && !TRANSPONDER(Channel, current))
|
||||
Dprintf(" * NOT same transponder\n");
|
||||
if (device && (device == cDevice::ActualDevice()
|
||||
&& !cSuspendCtl::IsActive()
|
||||
&& StreamdevServerSetup.SuspendMode != smAlways
|
||||
&& current != NULL
|
||||
&& !TRANSPONDER(Channel, current))) {
|
||||
// now we would have to switch away live tv...let's see if live tv
|
||||
// can be handled by another device
|
||||
cDevice *newdev = NULL;
|
||||
for (int i = 0; i < cDevice::NumDevices(); ++i) {
|
||||
cDevice *dev = cDevice::GetDevice(i);
|
||||
if (dev->ProvidesChannel(current, 0) && dev != device) {
|
||||
newdev = dev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Dprintf(" * Found device for live tv: %p (%d)\n", newdev,
|
||||
newdev ? newdev->CardIndex() + 1 : 0);
|
||||
if (newdev == NULL || newdev == device)
|
||||
// no suitable device to continue live TV, giving up...
|
||||
device = NULL;
|
||||
else
|
||||
newdev->SwitchChannel(current, true);
|
||||
}
|
||||
if (device && !device->IsTunedToTransponder(Channel)
|
||||
&& UsedByLiveTV(device)) {
|
||||
// now we would have to switch away live tv...let's see if live tv
|
||||
// can be handled by another device
|
||||
device = m_SwitchLive->Switch(device, Channel);
|
||||
}
|
||||
|
||||
if (!device) {
|
||||
// can't switch - continue the current stream
|
||||
Attach();
|
||||
dsyslog("streamdev: GetDevice failed for channel %d (%s) at priority %d (PrimaryDevice=%d, ActualDevice=%d)", Channel->Number(), Channel->Name(), Priority, cDevice::PrimaryDevice()->CardIndex(), cDevice::ActualDevice()->CardIndex());
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
bool cServerConnection::ProvidesChannel(const cChannel *Channel, int Priority)
|
||||
{
|
||||
cDevice *device = CheckDevice(Channel, Priority, false);
|
||||
if (!device || (StreamdevServerSetup.SuspendMode != smAlways
|
||||
&& !device->IsTunedToTransponder(Channel)
|
||||
&& UsedByLiveTV(device))) {
|
||||
// no device available or the device is in use for live TV and suspend mode doesn't allow us to switch it:
|
||||
// maybe a device would be free if THIS connection did turn off its streams?
|
||||
Detach();
|
||||
device = CheckDevice(Channel, Priority, false);
|
||||
Attach();
|
||||
if (device && StreamdevServerSetup.SuspendMode != smAlways
|
||||
&& !device->IsTunedToTransponder(Channel)
|
||||
&& UsedByLiveTV(device)) {
|
||||
// now we would have to switch away live tv...let's see if live tv
|
||||
// can be handled by another device
|
||||
const cChannel *current = Channels.GetByNumber(cDevice::CurrentChannel());
|
||||
cDevice *newdev = current ? CheckDevice(current, 0, true, device) : NULL;
|
||||
if (newdev) {
|
||||
dsyslog("streamdev: Providing channel %d (%s) at priority %d requires moving live TV to device %d (PrimaryDevice=%d, ActualDevice=%d)", Channel->Number(), Channel->Name(), Priority, newdev->CardIndex(), cDevice::PrimaryDevice()->CardIndex(), cDevice::ActualDevice()->CardIndex());
|
||||
}
|
||||
else {
|
||||
device = NULL;
|
||||
dsyslog("streamdev: Not providing channel %d (%s) at priority %d - live TV not suspended", Channel->Number(), Channel->Name(), Priority);
|
||||
}
|
||||
}
|
||||
else if (!device)
|
||||
dsyslog("streamdev: No device provides channel %d (%s) at priority %d", Channel->Number(), Channel->Name(), Priority);
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
void cServerConnection::MainThreadHook()
|
||||
{
|
||||
m_SwitchLive->Switch();
|
||||
}
|
||||
|
||||
cString cServerConnection::ToText() const
|
||||
{ return cString::sprintf("%s\t%s:%d", Protocol(), RemoteIp().c_str(), RemotePort()); }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: connection.h,v 1.5 2007/04/16 11:01:02 schmirl Exp $
|
||||
* $Id: connection.h,v 1.10 2010/08/03 10:46:41 schmirl Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_SERVER_CONNECTION_H
|
||||
@@ -8,8 +8,14 @@
|
||||
#include "tools/socket.h"
|
||||
#include "common.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
typedef std::map<std::string,std::string> tStrStrMap;
|
||||
typedef std::pair<std::string,std::string> tStrStr;
|
||||
|
||||
class cChannel;
|
||||
class cDevice;
|
||||
class cSwitchLive;
|
||||
|
||||
/* Basic capabilities of a straight text-based protocol, most functions
|
||||
virtual to support more complicated protocols */
|
||||
@@ -28,6 +34,19 @@ private:
|
||||
uint m_WriteBytes;
|
||||
uint m_WriteIndex;
|
||||
|
||||
cSwitchLive *m_SwitchLive;
|
||||
|
||||
tStrStrMap m_Headers;
|
||||
|
||||
/* Check if a device would be available for transfering the given
|
||||
channel. This call has no side effects except for temporarily
|
||||
detaching this connection's receivers. */
|
||||
cDevice *CheckDevice(const cChannel *Channel, int Priority, bool LiveView, const cDevice *AvoidDevice = NULL);
|
||||
|
||||
/* Test if device is in use as the transfer mode receiver device
|
||||
or a FF card, displaying live TV from internal tuner */
|
||||
static bool UsedByLiveTV(cDevice *device);
|
||||
|
||||
protected:
|
||||
/* Will be called when a command terminated by a newline has been
|
||||
received */
|
||||
@@ -41,12 +60,20 @@ protected:
|
||||
virtual bool Respond(const char *Message, bool Last = true, ...);
|
||||
//__attribute__ ((format (printf, 2, 4)));
|
||||
|
||||
/* Add a request header */
|
||||
void SetHeader(const char *Name, const char *Value, const char *Prefix = "") { m_Headers.insert(tStrStr(std::string(Prefix) + Name, Value)); }
|
||||
|
||||
static const cChannel *ChannelFromString(const char *String, int *Apid = NULL, int *Dpid = NULL);
|
||||
|
||||
public:
|
||||
/* If you derive, specify a short string such as HTTP for Protocol, which
|
||||
will be displayed in error messages */
|
||||
cServerConnection(const char *Protocol);
|
||||
cServerConnection(const char *Protocol, int Type = SOCK_STREAM);
|
||||
virtual ~cServerConnection();
|
||||
|
||||
/* If true, any client IP will be accepted */
|
||||
virtual bool CanAuthenticate(void) { return false; }
|
||||
|
||||
/* Gets called if the client has been accepted by the core */
|
||||
virtual void Welcome(void) { }
|
||||
|
||||
@@ -76,14 +103,33 @@ 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. Use the returned
|
||||
/* Close the socket */
|
||||
virtual bool Close(void);
|
||||
|
||||
/* Will retrieve an unused device for transmitting data. Receivers have
|
||||
already been attached from the device if necessary. Use the returned
|
||||
cDevice in a following call to StartTransfer */
|
||||
cDevice *GetDevice(const cChannel *Channel, int Priority);
|
||||
|
||||
/* Test if a call to GetDevice would return a usable device. */
|
||||
bool ProvidesChannel(const cChannel *Channel, int Priority);
|
||||
|
||||
/* Do things which must be done in VDR's main loop */
|
||||
void MainThreadHook();
|
||||
|
||||
virtual void Flushed(void) {}
|
||||
|
||||
virtual void Detach(void) = 0;
|
||||
virtual void Attach(void) = 0;
|
||||
|
||||
/* This connections protocol name */
|
||||
virtual const char* Protocol(void) const { return m_Protocol; }
|
||||
|
||||
/* Representation in menu */
|
||||
virtual cString ToText(void) const;
|
||||
|
||||
/* std::map with additional information */
|
||||
const tStrStrMap& Headers(void) const { return m_Headers; }
|
||||
};
|
||||
|
||||
inline bool cServerConnection::HasData(void) const
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
/*
|
||||
* $Id: connectionHTTP.c,v 1.13 2008/03/28 15:11:40 schmirl Exp $
|
||||
* $Id: connectionHTTP.c,v 1.21 2010/08/03 10:46:41 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "server/connectionHTTP.h"
|
||||
#include "server/menuHTTP.h"
|
||||
#include "server/server.h"
|
||||
#include "server/setup.h"
|
||||
|
||||
cConnectionHTTP::cConnectionHTTP(void):
|
||||
cServerConnection("HTTP"),
|
||||
m_Status(hsRequest),
|
||||
m_LiveStreamer(NULL),
|
||||
m_StreamerParameter(""),
|
||||
m_Channel(NULL),
|
||||
m_Apid(0),
|
||||
m_StreamType((eStreamType)StreamdevServerSetup.HTTPStreamType),
|
||||
m_ChannelList(NULL)
|
||||
{
|
||||
Dprintf("constructor hsRequest\n");
|
||||
m_Apid[0] = m_Apid[1] = 0;
|
||||
m_Dpid[0] = m_Dpid[1] = 0;
|
||||
}
|
||||
|
||||
cConnectionHTTP::~cConnectionHTTP()
|
||||
@@ -26,28 +27,79 @@ cConnectionHTTP::~cConnectionHTTP()
|
||||
delete m_LiveStreamer;
|
||||
}
|
||||
|
||||
bool cConnectionHTTP::CanAuthenticate(void)
|
||||
{
|
||||
return opt_auth != NULL;
|
||||
}
|
||||
|
||||
bool cConnectionHTTP::Command(char *Cmd)
|
||||
{
|
||||
Dprintf("command %s\n", Cmd);
|
||||
switch (m_Status) {
|
||||
case hsRequest:
|
||||
Dprintf("Request\n");
|
||||
m_Request = Cmd;
|
||||
m_Status = hsHeaders;
|
||||
return true;
|
||||
// parse METHOD PATH[?QUERY] VERSION
|
||||
{
|
||||
char *p, *q, *v;
|
||||
p = strchr(Cmd, ' ');
|
||||
if (p) {
|
||||
*p = 0;
|
||||
v = strchr(++p, ' ');
|
||||
if (v) {
|
||||
*v = 0;
|
||||
SetHeader("REQUEST_METHOD", Cmd);
|
||||
q = strchr(p, '?');
|
||||
if (q)
|
||||
*q = 0;
|
||||
SetHeader("QUERY_STRING", q ? ++q : "");
|
||||
SetHeader("PATH_INFO", p);
|
||||
m_Status = hsHeaders;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
case hsHeaders:
|
||||
if (*Cmd == '\0') {
|
||||
m_Status = hsBody;
|
||||
return ProcessRequest();
|
||||
}
|
||||
if (strncasecmp(Cmd, "Host:", 5) == 0) {
|
||||
Dprintf("Host-Header\n");
|
||||
m_Host = (std::string) skipspace(Cmd + 5);
|
||||
else if (isspace(*Cmd)) {
|
||||
; //TODO: multi-line header
|
||||
}
|
||||
else {
|
||||
// convert header name to CGI conventions:
|
||||
// uppercase, '-' replaced with '_', prefix "HTTP_"
|
||||
char *p;
|
||||
for (p = Cmd; *p != 0 && *p != ':'; p++) {
|
||||
if (*p == '-')
|
||||
*p = '_';
|
||||
else
|
||||
*p = toupper(*p);
|
||||
}
|
||||
if (*p == ':') {
|
||||
*p = 0;
|
||||
p = skipspace(++p);
|
||||
// don't disclose Authorization header
|
||||
if (strcmp(Cmd, "AUTHORIZATION") == 0) {
|
||||
char *q;
|
||||
for (q = p; *q != 0 && *q != ' '; q++)
|
||||
*q = toupper(*q);
|
||||
if (p != q) {
|
||||
*q = 0;
|
||||
SetHeader("AUTH_TYPE", p);
|
||||
m_Authorization = (std::string) skipspace(++q);
|
||||
}
|
||||
}
|
||||
else
|
||||
SetHeader(Cmd, p, "HTTP_");
|
||||
}
|
||||
}
|
||||
Dprintf("header\n");
|
||||
return true;
|
||||
default:
|
||||
// skip additional blank lines
|
||||
if (*Cmd == '\0')
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
return false; // ??? shouldn't happen
|
||||
@@ -55,45 +107,118 @@ bool cConnectionHTTP::Command(char *Cmd)
|
||||
|
||||
bool cConnectionHTTP::ProcessRequest(void)
|
||||
{
|
||||
Dprintf("process\n");
|
||||
if (m_Request.substr(0, 4) == "GET " && CmdGET(m_Request.substr(4))) {
|
||||
switch (m_Job) {
|
||||
case hjListing:
|
||||
if (m_ChannelList)
|
||||
return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
|
||||
break;
|
||||
// keys for Headers() hash
|
||||
const static std::string AUTH_TYPE("AUTH_TYPE");
|
||||
const static std::string REQUEST_METHOD("REQUEST_METHOD");
|
||||
const static std::string PATH_INFO("PATH_INFO");
|
||||
|
||||
case hjTransfer:
|
||||
if (m_Channel == NULL) {
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 404 not found");
|
||||
Dprintf("process\n");
|
||||
if (!StreamdevHosts.Acceptable(RemoteIpAddr())) {
|
||||
bool authOk = opt_auth && !m_Authorization.empty();
|
||||
if (authOk) {
|
||||
tStrStrMap::const_iterator it = Headers().find(AUTH_TYPE);
|
||||
|
||||
if (it == Headers().end()) {
|
||||
// no authorization header present
|
||||
authOk = false;
|
||||
}
|
||||
|
||||
m_LiveStreamer = new cStreamdevLiveStreamer(0, m_StreamerParameter);
|
||||
cDevice *device = GetDevice(m_Channel, 0);
|
||||
else if (it->second.compare("BASIC") == 0) {
|
||||
// basic auth
|
||||
authOk &= m_Authorization.compare(opt_auth) == 0;
|
||||
}
|
||||
else {
|
||||
// unsupported auth type
|
||||
authOk = false;
|
||||
}
|
||||
}
|
||||
if (!authOk) {
|
||||
isyslog("streamdev-server: HTTP authorization required");
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 401 Authorization Required")
|
||||
&& Respond("WWW-authenticate: basic Realm=\"Streamdev-Server\")")
|
||||
&& Respond("");
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
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);
|
||||
if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid)) {
|
||||
m_LiveStreamer = new cStreamdevLiveStreamer(0, this);
|
||||
if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL)) {
|
||||
m_LiveStreamer->SetDevice(device);
|
||||
if (!SetDSCP())
|
||||
LOG_ERROR_STR("unable to set DSCP sockopt");
|
||||
if (m_StreamType == stES && (m_Apid != 0 || ISRADIO(m_Channel))) {
|
||||
if (m_StreamType == stEXT) {
|
||||
return Respond("HTTP/1.0 200 OK");
|
||||
} else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) {
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: audio/mpeg")
|
||||
&& Respond("icy-name: %s", true, m_Channel->Name())
|
||||
&& Respond("");
|
||||
} else if (ISRADIO(m_Channel)) {
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: audio/mpeg")
|
||||
&& Respond("");
|
||||
} else {
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: video/mpeg")
|
||||
&& Respond("");
|
||||
}
|
||||
}
|
||||
DELETENULL(m_LiveStreamer);
|
||||
}
|
||||
DELETENULL(m_LiveStreamer);
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 409 Channel not available")
|
||||
&& Respond("");
|
||||
}
|
||||
else {
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 404 not found")
|
||||
&& Respond("");
|
||||
}
|
||||
} else if (it_method->second.compare("HEAD") == 0 && ProcessURI(it_pathinfo->second)) {
|
||||
DeferClose();
|
||||
if (m_ChannelList)
|
||||
return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
|
||||
else if (m_Channel != NULL) {
|
||||
if (ProvidesChannel(m_Channel, 0)) {
|
||||
if (m_StreamType == stEXT) {
|
||||
// TODO
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("");
|
||||
} else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) {
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: audio/mpeg")
|
||||
&& Respond("icy-name: %s", true, m_Channel->Name())
|
||||
&& Respond("");
|
||||
} else if (ISRADIO(m_Channel)) {
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: audio/mpeg")
|
||||
&& Respond("");
|
||||
} else {
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: video/mpeg")
|
||||
&& Respond("");
|
||||
}
|
||||
}
|
||||
return Respond("HTTP/1.0 409 Channel not available")
|
||||
&& Respond("");
|
||||
}
|
||||
else {
|
||||
return Respond("HTTP/1.0 404 not found")
|
||||
&& Respond("");
|
||||
}
|
||||
}
|
||||
|
||||
DeferClose();
|
||||
@@ -103,83 +228,112 @@ bool cConnectionHTTP::ProcessRequest(void)
|
||||
|
||||
void cConnectionHTTP::Flushed(void)
|
||||
{
|
||||
std::string line;
|
||||
|
||||
if (m_Status != hsBody)
|
||||
return;
|
||||
|
||||
switch (m_Job) {
|
||||
case hjListing:
|
||||
if (m_ChannelList) {
|
||||
if (m_ChannelList->HasNext()) {
|
||||
if (!Respond("%s", true, m_ChannelList->Next().c_str()))
|
||||
DeferClose();
|
||||
}
|
||||
else {
|
||||
DELETENULL(m_ChannelList);
|
||||
m_Status = hsFinished;
|
||||
if (m_ChannelList) {
|
||||
if (m_ChannelList->HasNext()) {
|
||||
if (!Respond("%s", true, m_ChannelList->Next().c_str()))
|
||||
DeferClose();
|
||||
}
|
||||
return;
|
||||
}
|
||||
// should never be reached
|
||||
esyslog("streamdev-server cConnectionHTTP::Flushed(): no channel list");
|
||||
m_Status = hsFinished;
|
||||
break;
|
||||
|
||||
case hjTransfer:
|
||||
else {
|
||||
DELETENULL(m_ChannelList);
|
||||
m_Status = hsFinished;
|
||||
DeferClose();
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (m_Channel != NULL) {
|
||||
Dprintf("streamer start\n");
|
||||
m_LiveStreamer->Start(this);
|
||||
m_Status = hsFinished;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// should never be reached
|
||||
esyslog("streamdev-server cConnectionHTTP::Flushed(): no job to do");
|
||||
m_Status = hsFinished;
|
||||
}
|
||||
}
|
||||
|
||||
bool cConnectionHTTP::CmdGET(const std::string &Opts)
|
||||
cChannelList* cConnectionHTTP::ChannelListFromString(const std::string& Path, const std::string& Filebase, const std::string& Fileext) const
|
||||
{
|
||||
const char *ptr, *sp, *pp, *fp, *xp, *qp, *ep;
|
||||
const cChannel *chan;
|
||||
int apid = 0;
|
||||
// keys for Headers() hash
|
||||
const static std::string QUERY_STRING("QUERY_STRING");
|
||||
const static std::string HOST("HTTP_HOST");
|
||||
|
||||
ptr = Opts.c_str();
|
||||
tStrStrMap::const_iterator it_query = Headers().find(QUERY_STRING);
|
||||
const std::string& query = it_query == Headers().end() ? "" : it_query->second;
|
||||
|
||||
// find begin of URL
|
||||
sp = skipspace(ptr);
|
||||
// find end of URL (\0 or first space character)
|
||||
for (ep = sp; *ep && !isspace(*ep); ep++)
|
||||
;
|
||||
// find begin of query string (first ?)
|
||||
for (qp = sp; qp < ep && *qp != '?'; qp++)
|
||||
;
|
||||
// find begin of filename (last /)
|
||||
for (fp = qp; fp > sp && *fp != '/'; --fp)
|
||||
;
|
||||
// find begin of section params (first ;)
|
||||
for (pp = sp; pp < fp && *pp != ';'; pp++)
|
||||
;
|
||||
// find filename extension (first .)
|
||||
for (xp = fp; xp < qp && *xp != '.'; xp++)
|
||||
;
|
||||
if (qp - xp > 5) // too long for a filename extension
|
||||
xp = qp;
|
||||
std::string groupTarget;
|
||||
cChannelIterator *iterator = NULL;
|
||||
|
||||
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);
|
||||
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);
|
||||
} else if (Filebase.compare("channels") == 0) {
|
||||
iterator = new cListChannels();
|
||||
} else if (Filebase.compare("all") == 0 ||
|
||||
(Filebase.empty() && Fileext.empty())) {
|
||||
iterator = new cListAll();
|
||||
}
|
||||
|
||||
if (iterator) {
|
||||
if (Filebase.empty() || Fileext.compare(".htm") == 0 || Fileext.compare(".html") == 0) {
|
||||
std::string self = Filebase + Fileext;
|
||||
if (!query.empty())
|
||||
self += '?' + query;
|
||||
return new cHtmlChannelList(iterator, m_StreamType, self.c_str(), groupTarget.c_str());
|
||||
} else if (Fileext.compare(".m3u") == 0) {
|
||||
std::string base;
|
||||
tStrStrMap::const_iterator it = Headers().find(HOST);
|
||||
if (it != Headers().end())
|
||||
base = "http://" + it->second + "/";
|
||||
else
|
||||
base = (std::string) "http://" + LocalIp() + ":" +
|
||||
(const char*) itoa(StreamdevServerSetup.HTTPServerPort) + "/";
|
||||
base += Path;
|
||||
return new cM3uChannelList(iterator, base.c_str());
|
||||
} else {
|
||||
delete iterator;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
|
||||
{
|
||||
std::string filespec, fileext;
|
||||
size_t file_pos = PathInfo.rfind('/');
|
||||
|
||||
if (file_pos != std::string::npos) {
|
||||
size_t ext_pos = PathInfo.rfind('.');
|
||||
// file basename with leading / stripped off
|
||||
filespec = PathInfo.substr(file_pos + 1, ext_pos - file_pos - 1);
|
||||
if (ext_pos != std::string::npos)
|
||||
// file extension including leading .
|
||||
fileext = PathInfo.substr(ext_pos);
|
||||
}
|
||||
if (fileext.length() > 5) {
|
||||
//probably not an extension
|
||||
filespec += fileext;
|
||||
fileext.clear();
|
||||
}
|
||||
|
||||
std::string type, filespec, fileext, query;
|
||||
// Streamtype with leading / stripped off
|
||||
if (pp > sp)
|
||||
type = Opts.substr(sp - ptr + 1, pp - sp - 1);
|
||||
// Section parameters with leading ; stripped off
|
||||
if (fp > pp)
|
||||
m_StreamerParameter = Opts.substr(pp - ptr + 1, fp - pp - 1);
|
||||
// file basename with leading / stripped off
|
||||
if (xp > fp)
|
||||
filespec = Opts.substr(fp - ptr + 1, xp - fp - 1);
|
||||
// file extension including leading .
|
||||
fileext = Opts.substr(xp - ptr, qp - xp);
|
||||
// query string including leading ?
|
||||
query = Opts.substr(qp - ptr, ep - qp);
|
||||
|
||||
Dprintf("before channelfromstring: type(%s) param(%s) filespec(%s) fileext(%s) query(%s)\n", type.c_str(), m_StreamerParameter.c_str(), filespec.c_str(), fileext.c_str(), query.c_str());
|
||||
|
||||
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;
|
||||
@@ -189,80 +343,24 @@ bool cConnectionHTTP::CmdGET(const std::string &Opts)
|
||||
m_StreamType = stTS;
|
||||
} else if (strcasecmp(pType, "ES") == 0) {
|
||||
m_StreamType = stES;
|
||||
} else if (strcasecmp(pType, "Extern") == 0) {
|
||||
m_StreamType = stExtern;
|
||||
} else if (strcasecmp(pType, "EXT") == 0) {
|
||||
m_StreamType = stEXT;
|
||||
}
|
||||
|
||||
std::string groupTarget;
|
||||
cChannelIterator *iterator = NULL;
|
||||
Dprintf("before channelfromstring: type(%s) filespec(%s) fileext(%s)\n", type.c_str(), filespec.c_str(), fileext.c_str());
|
||||
|
||||
if (filespec.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);
|
||||
groupTarget = filespec + fileext;
|
||||
} else if (filespec.compare("groups") == 0) {
|
||||
iterator = new cListGroups();
|
||||
groupTarget = (std::string) "group" + fileext;
|
||||
} else if (filespec.compare("group") == 0) {
|
||||
const cChannel* c = NULL;
|
||||
size_t groupIndex = query.find("group=");
|
||||
if (groupIndex != std::string::npos)
|
||||
c = cChannelList::GetGroup(atoi(query.c_str() + groupIndex + 6));
|
||||
iterator = new cListGroup(c);
|
||||
} else if (filespec.compare("channels") == 0) {
|
||||
iterator = new cListChannels();
|
||||
} else if (filespec.compare("all") == 0 ||
|
||||
(filespec.empty() && fileext.empty())) {
|
||||
iterator = new cListAll();
|
||||
}
|
||||
|
||||
if (iterator) {
|
||||
if (filespec.empty() || fileext.compare(".htm") == 0 || fileext.compare(".html") == 0) {
|
||||
m_ChannelList = new cHtmlChannelList(iterator, m_StreamType, (filespec + fileext + query).c_str(), groupTarget.c_str());
|
||||
m_Job = hjListing;
|
||||
} else if (fileext.compare(".m3u") == 0) {
|
||||
std::string base;
|
||||
if (*(m_Host.c_str()))
|
||||
base = "http://" + m_Host + "/";
|
||||
else
|
||||
base = (std::string) "http://" + LocalIp() + ":" +
|
||||
(const char*) itoa(StreamdevServerSetup.HTTPServerPort) + "/";
|
||||
if (type.empty())
|
||||
{
|
||||
switch (m_StreamType)
|
||||
{
|
||||
case stTS: base += "TS/"; break;
|
||||
case stPS: base += "PS/"; break;
|
||||
case stPES: base += "PES/"; break;
|
||||
case stES: base += "ES/"; break;
|
||||
case stExtern: base += "Extern/"; break;
|
||||
default: break;
|
||||
|
||||
}
|
||||
} else {
|
||||
base += type;
|
||||
if (!m_StreamerParameter.empty())
|
||||
base += ";" + m_StreamerParameter;
|
||||
base += "/";
|
||||
}
|
||||
m_ChannelList = new cM3uChannelList(iterator, base.c_str());
|
||||
m_Job = hjListing;
|
||||
} else {
|
||||
delete iterator;
|
||||
return false;
|
||||
}
|
||||
} else if ((chan = ChannelFromString(filespec.c_str(), &apid)) != NULL) {
|
||||
m_Channel = chan;
|
||||
m_Apid = apid;
|
||||
Dprintf("Apid is %d\n", apid);
|
||||
m_Job = hjTransfer;
|
||||
if ((m_ChannelList = ChannelListFromString(PathInfo.substr(1, file_pos), filespec.c_str(), fileext.c_str())) != NULL) {
|
||||
Dprintf("Channel list requested\n");
|
||||
return true;
|
||||
} else if ((m_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;
|
||||
} else
|
||||
return false;
|
||||
|
||||
Dprintf("after channelfromstring\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
cString cConnectionHTTP::ToText() const
|
||||
{
|
||||
cString str = cServerConnection::ToText();
|
||||
return m_LiveStreamer ? cString::sprintf("%s\t%s", *str, *m_LiveStreamer->ToText()) : str;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: connectionHTTP.h,v 1.5 2008/03/28 15:11:40 schmirl Exp $
|
||||
* $Id: connectionHTTP.h,v 1.7 2010/07/19 13:49:31 schmirl Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_SERVERS_CONNECTIONHTTP_H
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "connection.h"
|
||||
#include "server/livestreamer.h"
|
||||
|
||||
#include <map>
|
||||
#include <tools/select.h>
|
||||
|
||||
class cChannel;
|
||||
@@ -23,25 +24,19 @@ private:
|
||||
hsFinished,
|
||||
};
|
||||
|
||||
enum eHTTPJob {
|
||||
hjTransfer,
|
||||
hjListing,
|
||||
};
|
||||
|
||||
std::string m_Request;
|
||||
std::string m_Host;
|
||||
//std::map<std::string,std::string> m_Headers; TODO: later?
|
||||
std::string m_Authorization;
|
||||
eHTTPStatus m_Status;
|
||||
eHTTPJob m_Job;
|
||||
// job: transfer
|
||||
cStreamdevLiveStreamer *m_LiveStreamer;
|
||||
std::string m_StreamerParameter;
|
||||
const cChannel *m_Channel;
|
||||
int m_Apid;
|
||||
int m_Apid[2];
|
||||
int m_Dpid[2];
|
||||
eStreamType m_StreamType;
|
||||
// job: listing
|
||||
cChannelList *m_ChannelList;
|
||||
|
||||
cChannelList* ChannelListFromString(const std::string &PathInfo, const std::string &Filebase, const std::string &Fileext) const;
|
||||
bool ProcessURI(const std::string &PathInfo);
|
||||
protected:
|
||||
bool ProcessRequest(void);
|
||||
|
||||
@@ -52,8 +47,11 @@ public:
|
||||
virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); }
|
||||
virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); }
|
||||
|
||||
virtual cString ToText() const;
|
||||
|
||||
virtual bool CanAuthenticate(void);
|
||||
|
||||
virtual bool Command(char *Cmd);
|
||||
bool CmdGET(const std::string &Opts);
|
||||
|
||||
virtual bool Abort(void) const;
|
||||
virtual void Flushed(void);
|
||||
@@ -61,7 +59,7 @@ public:
|
||||
|
||||
inline bool cConnectionHTTP::Abort(void) const
|
||||
{
|
||||
return m_LiveStreamer && m_LiveStreamer->Abort();
|
||||
return !IsOpen() || (m_LiveStreamer && m_LiveStreamer->Abort());
|
||||
}
|
||||
|
||||
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
|
||||
|
||||
78
server/connectionIGMP.c
Normal file
78
server/connectionIGMP.c
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* $Id: connectionIGMP.c,v 1.3 2010/08/03 10:46:41 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "server/connectionIGMP.h"
|
||||
#include "server/server.h"
|
||||
#include "server/setup.h"
|
||||
#include <vdr/channels.h>
|
||||
|
||||
cConnectionIGMP::cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType) :
|
||||
cServerConnection(Name, SOCK_DGRAM),
|
||||
m_LiveStreamer(NULL),
|
||||
m_ClientPort(ClientPort),
|
||||
m_StreamType(StreamType),
|
||||
m_Channel(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
cConnectionIGMP::~cConnectionIGMP()
|
||||
{
|
||||
delete m_LiveStreamer;
|
||||
}
|
||||
|
||||
bool cConnectionIGMP::SetChannel(cChannel *Channel, in_addr_t Dst)
|
||||
{
|
||||
if (Channel) {
|
||||
m_Channel = Channel;
|
||||
struct in_addr ip;
|
||||
ip.s_addr = Dst;
|
||||
if (Connect(inet_ntoa(ip), m_ClientPort))
|
||||
return true;
|
||||
else
|
||||
esyslog("streamdev-server IGMP: Connect failed: %m");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
esyslog("streamdev-server IGMP: Channel not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
void cConnectionIGMP::Welcome()
|
||||
{
|
||||
cDevice *device = NULL;
|
||||
if (ProvidesChannel(m_Channel, 0))
|
||||
device = GetDevice(m_Channel, 0);
|
||||
if (device != NULL) {
|
||||
device->SwitchChannel(m_Channel, false);
|
||||
m_LiveStreamer = new cStreamdevLiveStreamer(0, this);
|
||||
if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType)) {
|
||||
m_LiveStreamer->SetDevice(device);
|
||||
if (!SetDSCP())
|
||||
LOG_ERROR_STR("unable to set DSCP sockopt");
|
||||
Dprintf("streamer start\n");
|
||||
m_LiveStreamer->Start(this);
|
||||
}
|
||||
else {
|
||||
esyslog("streamdev-server IGMP: SetChannel failed");
|
||||
DELETENULL(m_LiveStreamer);
|
||||
}
|
||||
}
|
||||
else
|
||||
esyslog("streamdev-server IGMP: GetDevice failed");
|
||||
}
|
||||
|
||||
bool cConnectionIGMP::Close()
|
||||
{
|
||||
if (m_LiveStreamer)
|
||||
m_LiveStreamer->Stop();
|
||||
return cServerConnection::Close();
|
||||
}
|
||||
|
||||
cString cConnectionIGMP::ToText() const
|
||||
{
|
||||
cString str = cServerConnection::ToText();
|
||||
return m_LiveStreamer ? cString::sprintf("%s\t%s", *str, *m_LiveStreamer->ToText()) : str;
|
||||
}
|
||||
48
server/connectionIGMP.h
Normal file
48
server/connectionIGMP.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* $Id: connectionIGMP.h,v 1.1 2009/02/13 10:39:22 schmirl Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H
|
||||
#define VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H
|
||||
|
||||
#include "connection.h"
|
||||
#include "server/livestreamer.h"
|
||||
|
||||
#include <tools/select.h>
|
||||
|
||||
#define MULTICAST_PRIV_MIN ((uint32_t) 0xefff0000)
|
||||
#define MULTICAST_PRIV_MAX ((uint32_t) 0xeffffeff)
|
||||
|
||||
class cStreamdevLiveStreamer;
|
||||
|
||||
class cConnectionIGMP: public cServerConnection {
|
||||
private:
|
||||
cStreamdevLiveStreamer *m_LiveStreamer;
|
||||
int m_ClientPort;
|
||||
eStreamType m_StreamType;
|
||||
cChannel *m_Channel;
|
||||
|
||||
public:
|
||||
cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType);
|
||||
virtual ~cConnectionIGMP();
|
||||
|
||||
bool SetChannel(cChannel *Channel, in_addr_t Dst);
|
||||
virtual void Welcome(void);
|
||||
virtual cString ToText() const;
|
||||
|
||||
/* Not used here */
|
||||
virtual bool Command(char *Cmd) { return false; }
|
||||
|
||||
virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); }
|
||||
virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); }
|
||||
virtual bool Close(void);
|
||||
|
||||
virtual bool Abort(void) const;
|
||||
};
|
||||
|
||||
inline bool cConnectionIGMP::Abort(void) const
|
||||
{
|
||||
return !IsOpen() || !m_LiveStreamer || m_LiveStreamer->Abort();
|
||||
}
|
||||
|
||||
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@
|
||||
#define VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
|
||||
|
||||
#include "server/connection.h"
|
||||
#include "server/recplayer.h"
|
||||
|
||||
class cTBSocket;
|
||||
class cStreamdevLiveStreamer;
|
||||
@@ -9,28 +10,37 @@ class cStreamdevFilterStreamer;
|
||||
class cLSTEHandler;
|
||||
class cLSTCHandler;
|
||||
class cLSTTHandler;
|
||||
class cLSTRHandler;
|
||||
|
||||
class cConnectionVTP: public cServerConnection {
|
||||
friend class cLSTEHandler;
|
||||
// if your compiler doesn't understand the following statement
|
||||
// (e.g. gcc 2.x), simply remove it and try again ;-)
|
||||
#if !defined __GNUC__ || __GNUC__ >= 3
|
||||
using cServerConnection::Respond;
|
||||
#endif
|
||||
|
||||
private:
|
||||
cTBSocket *m_LiveSocket;
|
||||
cStreamdevLiveStreamer *m_LiveStreamer;
|
||||
cTBSocket *m_FilterSocket;
|
||||
cStreamdevFilterStreamer *m_FilterStreamer;
|
||||
cTBSocket *m_RecSocket;
|
||||
cTBSocket *m_DataSocket;
|
||||
|
||||
char *m_LastCommand;
|
||||
eStreamType m_StreamType;
|
||||
bool m_FiltersSupport;
|
||||
RecPlayer *m_RecPlayer;
|
||||
|
||||
// Priority is only known in PROV command
|
||||
// Store in here for later use in TUNE call
|
||||
const cChannel *m_TuneChannel;
|
||||
int m_TunePriority;
|
||||
|
||||
// Members adopted for SVDRP
|
||||
cRecordings Recordings;
|
||||
cLSTEHandler *m_LSTEHandler;
|
||||
cLSTCHandler *m_LSTCHandler;
|
||||
cLSTTHandler *m_LSTTHandler;
|
||||
cLSTRHandler *m_LSTRHandler;
|
||||
|
||||
protected:
|
||||
template<class cHandler>
|
||||
@@ -43,6 +53,8 @@ public:
|
||||
virtual void Welcome(void);
|
||||
virtual void Reject(void);
|
||||
|
||||
virtual cString ToText() const;
|
||||
|
||||
virtual bool Abort(void) const;
|
||||
virtual void Detach(void);
|
||||
virtual void Attach(void);
|
||||
@@ -51,7 +63,11 @@ public:
|
||||
bool CmdCAPS(char *Opts);
|
||||
bool CmdPROV(char *Opts);
|
||||
bool CmdPORT(char *Opts);
|
||||
bool CmdREAD(char *Opts);
|
||||
bool CmdTUNE(char *Opts);
|
||||
bool CmdPLAY(char *Opts);
|
||||
bool CmdPRIO(char *Opts);
|
||||
bool CmdSGNL(char *Opts);
|
||||
bool CmdADDP(char *Opts);
|
||||
bool CmdDELP(char *Opts);
|
||||
bool CmdADDF(char *Opts);
|
||||
@@ -64,14 +80,20 @@ public:
|
||||
bool CmdLSTE(char *Opts);
|
||||
bool CmdLSTC(char *Opts);
|
||||
bool CmdLSTT(char *Opts);
|
||||
bool CmdLSTR(char *Opts);
|
||||
|
||||
// Commands adopted from SVDRP
|
||||
bool CmdSTAT(const char *Option);
|
||||
bool CmdMODT(const char *Option);
|
||||
bool CmdNEWT(const char *Option);
|
||||
bool CmdDELT(const char *Option);
|
||||
|
||||
//bool CmdLSTR(char *Opts);
|
||||
//bool CmdDELR(char *Opts);
|
||||
bool CmdNEXT(const char *Option);
|
||||
bool CmdNEWC(const char *Option);
|
||||
bool CmdMODC(const char *Option);
|
||||
bool CmdMOVC(const char *Option);
|
||||
bool CmdDELC(const char *Option);
|
||||
bool CmdDELR(const char *Option);
|
||||
bool CmdRENR(const char *Option);
|
||||
|
||||
bool Respond(int Code, const char *Message, ...)
|
||||
__attribute__ ((format (printf, 3, 4)));
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
/*
|
||||
* $Id: livefilter.c,v 1.5 2008/04/07 14:27:31 schmirl Exp $
|
||||
* $Id: livefilter.c,v 1.7 2009/02/13 13:02:40 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include "server/livefilter.h"
|
||||
#include "server/streamer.h"
|
||||
#include "common.h"
|
||||
|
||||
#ifndef TS_SIZE
|
||||
# define TS_SIZE 188
|
||||
#endif
|
||||
#ifndef TS_SYNC_BYTE
|
||||
# define TS_SYNC_BYTE 0x47
|
||||
#endif
|
||||
@@ -29,6 +26,7 @@ void cStreamdevLiveFilter::Process(u_short Pid, u_char Tid, const u_char *Data,
|
||||
buffer[1] = ((Pid >> 8) & 0x3f) | (pos==0 ? 0x40 : 0); /* bit 6: payload unit start indicator (PUSI) */
|
||||
buffer[2] = Pid & 0xff;
|
||||
buffer[3] = Tid;
|
||||
// this makes it a proprietary stream
|
||||
buffer[4] = (uchar)chunk;
|
||||
memcpy(buffer + 5, Data + pos, chunk);
|
||||
length -= chunk;
|
||||
|
||||
@@ -3,16 +3,18 @@
|
||||
#include <libsi/section.h>
|
||||
#include <libsi/descriptor.h>
|
||||
|
||||
#include "remux/ts2ps.h"
|
||||
#include "remux/ts2pes.h"
|
||||
#include "remux/ts2es.h"
|
||||
#include "remux/extern.h"
|
||||
|
||||
#include <vdr/ringbuffer.h>
|
||||
|
||||
#include "server/livestreamer.h"
|
||||
#include "server/livefilter.h"
|
||||
#include "remux/ts2ps.h"
|
||||
#include "remux/ts2es.h"
|
||||
#include "remux/extern.h"
|
||||
#include "common.h"
|
||||
|
||||
#define TSPATREPACKER
|
||||
using namespace Streamdev;
|
||||
|
||||
// --- cStreamdevLiveReceiver -------------------------------------------------
|
||||
|
||||
@@ -27,25 +29,24 @@ protected:
|
||||
virtual void Receive(uchar *Data, int Length);
|
||||
|
||||
public:
|
||||
#if VDRVERSNUM < 10500
|
||||
cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, int Ca, int Priority, const int *Pids);
|
||||
#else
|
||||
cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, tChannelID ChannelID, int Priority, const int *Pids);
|
||||
#endif
|
||||
cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, const cChannel *Channel, int Priority, const int *Pids);
|
||||
virtual ~cStreamdevLiveReceiver();
|
||||
};
|
||||
|
||||
#if VDRVERSNUM < 10500
|
||||
cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, int Ca,
|
||||
cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, const cChannel *Channel,
|
||||
int Priority, const int *Pids):
|
||||
cReceiver(Ca, Priority, 0, Pids),
|
||||
#if APIVERSNUM >= 10712
|
||||
cReceiver(Channel, Priority),
|
||||
#else
|
||||
cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, tChannelID ChannelID,
|
||||
int Priority, const int *Pids):
|
||||
cReceiver(ChannelID, Priority, 0, Pids),
|
||||
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()
|
||||
@@ -73,6 +74,8 @@ private:
|
||||
int pmtPid;
|
||||
int pmtSid;
|
||||
int pmtVersion;
|
||||
uchar tspat_buf[TS_SIZE];
|
||||
cStreamdevBuffer siBuffer;
|
||||
|
||||
const cChannel *m_Channel;
|
||||
cStreamdevLiveStreamer *m_Streamer;
|
||||
@@ -82,11 +85,13 @@ private:
|
||||
int GetPid(SI::PMT::Stream& stream);
|
||||
public:
|
||||
cStreamdevPatFilter(cStreamdevLiveStreamer *Streamer, const cChannel *Channel);
|
||||
uchar* Get(int &Count) { return siBuffer.Get(Count); }
|
||||
void Del(int Count) { return siBuffer.Del(Count); }
|
||||
};
|
||||
|
||||
cStreamdevPatFilter::cStreamdevPatFilter(cStreamdevLiveStreamer *Streamer, const cChannel *Channel)
|
||||
cStreamdevPatFilter::cStreamdevPatFilter(cStreamdevLiveStreamer *Streamer, const cChannel *Channel): siBuffer(10 * TS_SIZE, TS_SIZE)
|
||||
{
|
||||
Dprintf("cStreamdevPatFilter(\"%s\")", Channel->Name());
|
||||
Dprintf("cStreamdevPatFilter(\"%s\")\n", Channel->Name());
|
||||
assert(Streamer);
|
||||
m_Channel = Channel;
|
||||
m_Streamer = Streamer;
|
||||
@@ -94,6 +99,29 @@ cStreamdevPatFilter::cStreamdevPatFilter(cStreamdevLiveStreamer *Streamer, const
|
||||
pmtSid = 0;
|
||||
pmtVersion = -1;
|
||||
Set(0x00, 0x00); // PAT
|
||||
// initialize PAT buffer. Only some values are dynamic (see comments)
|
||||
memset(tspat_buf, 0xff, TS_SIZE);
|
||||
tspat_buf[0] = TS_SYNC_BYTE; // Transport packet header sunchronization byte (1000011 = 0x47h)
|
||||
tspat_buf[1] = 0x40; // Set payload unit start indicator bit
|
||||
tspat_buf[2] = 0x0; // PID
|
||||
tspat_buf[3] = 0x10; // Set payload flag, DYNAMIC: Continuity counter
|
||||
tspat_buf[4] = 0x0; // SI pointer field
|
||||
tspat_buf[5] = 0x0; // PAT table id
|
||||
tspat_buf[6] = 0xb0; // Section syntax indicator bit and reserved bits set
|
||||
tspat_buf[7] = 12 + 1; // Section length (12 bit): PAT_TABLE_LEN + 1
|
||||
tspat_buf[8] = 0; // DYNAMIC: Transport stream ID (bits 8-15)
|
||||
tspat_buf[9] = 0; // DYNAMIC: Transport stream ID (bits 0-7)
|
||||
tspat_buf[10] = 0xc0; // Reserved, DYNAMIC: Version number, DYNAMIC: Current next indicator
|
||||
tspat_buf[11] = 0x0; // Section number
|
||||
tspat_buf[12] = 0x0; // Last section number
|
||||
tspat_buf[13] = 0; // DYNAMIC: Program number (bits 8-15)
|
||||
tspat_buf[14] = 0; // DYNAMIC: Program number (bits 0-7)
|
||||
tspat_buf[15] = 0xe0; // Reserved, DYNAMIC: Network ID (bits 8-12)
|
||||
tspat_buf[16] = 0; // DYNAMIC: Network ID (bits 0-7)
|
||||
tspat_buf[17] = 0; // DYNAMIC: Checksum
|
||||
tspat_buf[18] = 0; // DYNAMIC: Checksum
|
||||
tspat_buf[19] = 0; // DYNAMIC: Checksum
|
||||
tspat_buf[20] = 0; // DYNAMIC: Checksum
|
||||
}
|
||||
|
||||
static const char * const psStreamTypes[] = {
|
||||
@@ -145,7 +173,7 @@ int cStreamdevPatFilter::GetPid(SI::PMT::Stream& stream)
|
||||
case 0x10: // ISO/IEC 14496-2 Visual (MPEG-4)
|
||||
case 0x11: // ISO/IEC 14496-3 Audio with LATM transport syntax
|
||||
case 0x1b: // ISO/IEC 14496-10 Video (MPEG-4 part 10/AVC, aka H.264)
|
||||
Dprintf("cStreamdevPatFilter PMT scanner adding PID %d (%s)",
|
||||
Dprintf("cStreamdevPatFilter PMT scanner adding PID %d (%s)\n",
|
||||
stream.getPid(), psStreamTypes[stream.getStreamType()]);
|
||||
return stream.getPid();
|
||||
case 0x05: // ISO/IEC 13818-1 private sections
|
||||
@@ -153,19 +181,23 @@ int cStreamdevPatFilter::GetPid(SI::PMT::Stream& stream)
|
||||
for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
|
||||
switch (d->getDescriptorTag()) {
|
||||
case SI::AC3DescriptorTag:
|
||||
Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s",
|
||||
case SI::EnhancedAC3DescriptorTag:
|
||||
Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s\n",
|
||||
stream.getPid(), psStreamTypes[stream.getStreamType()], "AC3");
|
||||
delete d;
|
||||
return stream.getPid();
|
||||
case SI::TeletextDescriptorTag:
|
||||
Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s",
|
||||
Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s\n",
|
||||
stream.getPid(), psStreamTypes[stream.getStreamType()], "Teletext");
|
||||
delete d;
|
||||
return stream.getPid();
|
||||
case SI::SubtitlingDescriptorTag:
|
||||
Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s",
|
||||
Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s\n",
|
||||
stream.getPid(), psStreamTypes[stream.getStreamType()], "DVBSUB");
|
||||
delete d;
|
||||
return stream.getPid();
|
||||
default:
|
||||
Dprintf("cStreamdevPatFilter PMT scanner: NOT adding PID %d (%s) %s",
|
||||
Dprintf("cStreamdevPatFilter PMT scanner: NOT adding PID %d (%s) %s\n",
|
||||
stream.getPid(), psStreamTypes[stream.getStreamType()], "UNKNOWN");
|
||||
break;
|
||||
}
|
||||
@@ -195,6 +227,7 @@ int cStreamdevPatFilter::GetPid(SI::PMT::Stream& stream)
|
||||
stream.getPid(), stream.getStreamType(),
|
||||
d->getLength(), rawdata[2], rawdata[3],
|
||||
rawdata[4], rawdata[5]);
|
||||
delete d;
|
||||
return stream.getPid();
|
||||
}
|
||||
}
|
||||
@@ -210,7 +243,7 @@ int cStreamdevPatFilter::GetPid(SI::PMT::Stream& stream)
|
||||
return stream.getPid();
|
||||
}
|
||||
}
|
||||
Dprintf("cStreamdevPatFilter PMT scanner: NOT adding PID %d (%s) %s",
|
||||
Dprintf("cStreamdevPatFilter PMT scanner: NOT adding PID %d (%s) %s\n",
|
||||
stream.getPid(), psStreamTypes[stream.getStreamType()<0x1c?stream.getStreamType():0], "UNKNOWN");
|
||||
break;
|
||||
}
|
||||
@@ -220,7 +253,7 @@ int cStreamdevPatFilter::GetPid(SI::PMT::Stream& stream)
|
||||
void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
|
||||
{
|
||||
if (Pid == 0x00) {
|
||||
if (Tid == 0x00 && !pmtPid) {
|
||||
if (Tid == 0x00) {
|
||||
SI::PAT pat(Data, false);
|
||||
if (!pat.CheckCRCAndParse())
|
||||
return;
|
||||
@@ -229,58 +262,46 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
|
||||
if (!assoc.isNITPid()) {
|
||||
const cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId());
|
||||
if (Channel && (Channel == m_Channel)) {
|
||||
int prevPmtPid = pmtPid;
|
||||
if (0 != (pmtPid = assoc.getPid())) {
|
||||
Dprintf("cStreamdevPatFilter: PMT pid for channel %s: %d", Channel->Name(), pmtPid);
|
||||
Dprintf("cStreamdevPatFilter: PMT pid for channel %s: %d\n", Channel->Name(), pmtPid);
|
||||
pmtSid = assoc.getServiceId();
|
||||
if (Length < TS_SIZE-5) {
|
||||
// repack PAT to TS frame and send to client
|
||||
#ifndef TSPATREPACKER
|
||||
uint8_t pat_ts[TS_SIZE] = {TS_SYNC_BYTE, 0x40 /* pusi=1 */, 0 /* pid=0 */, 0x10 /* adaption=1 */, 0 /* pointer */};
|
||||
memcpy(pat_ts + 5, Data, Length);
|
||||
m_Streamer->Put(pat_ts, TS_SIZE);
|
||||
#else
|
||||
int ts_id;
|
||||
unsigned int crc, i, len;
|
||||
uint8_t *tmp, tspat_buf[TS_SIZE];
|
||||
memset(tspat_buf, 0xff, TS_SIZE);
|
||||
memset(tspat_buf, 0x0, 4 + 12 + 5); // TS_HDR_LEN + PAT_TABLE_LEN + 5
|
||||
ts_id = Channel->Tid(); // Get transport stream id of the channel
|
||||
tspat_buf[0] = TS_SYNC_BYTE; // Transport packet header sunchronization byte (1000011 = 0x47h)
|
||||
tspat_buf[1] = 0x40; // Set payload unit start indicator bit
|
||||
tspat_buf[2] = 0x0; // PID
|
||||
tspat_buf[3] = 0x10; // Set payload flag to indicate precence of payload data
|
||||
tspat_buf[4] = 0x0; // PSI
|
||||
tspat_buf[5] = 0x0; // PAT table id
|
||||
tspat_buf[6] = 0xb0; // Section syntax indicator bit and reserved bits set
|
||||
tspat_buf[7] = 12 + 1; // Section length (12 bit): PAT_TABLE_LEN + 1
|
||||
tspat_buf[8] = (ts_id >> 8) & 0xff; // Transport stream ID (bits 8-15)
|
||||
tspat_buf[9] = (ts_id & 0xff); // Transport stream ID (bits 0-7)
|
||||
tspat_buf[10] = 0x01; // Version number 0, Current next indicator bit set
|
||||
tspat_buf[11] = 0x0; // Section number
|
||||
tspat_buf[12] = 0x0; // Last section number
|
||||
tspat_buf[13] = (pmtSid >> 8) & 0xff; // Program number (bits 8-15)
|
||||
tspat_buf[14] = (pmtSid & 0xff); // Program number (bits 0-7)
|
||||
tspat_buf[15] = (pmtPid >> 8) & 0xff; // Network ID (bits 8-12)
|
||||
tspat_buf[16] = (pmtPid & 0xff); // Network ID (bits 0-7)
|
||||
crc = 0xffffffff;
|
||||
len = 12; // PAT_TABLE_LEN
|
||||
tmp = &tspat_buf[4 + 1]; // TS_HDR_LEN + 1
|
||||
while (len--) {
|
||||
crc ^= *tmp++ << 24;
|
||||
for (i = 0; i < 8; i++)
|
||||
crc = (crc << 1) ^ ((crc & 0x80000000) ? 0x04c11db7 : 0); // CRC32POLY
|
||||
}
|
||||
tspat_buf[17] = crc >> 24 & 0xff; // Checksum
|
||||
tspat_buf[18] = crc >> 16 & 0xff; // Checksum
|
||||
tspat_buf[19] = crc >> 8 & 0xff; // Checksum
|
||||
tspat_buf[20] = crc & 0xff; // Checksum
|
||||
m_Streamer->Put(tspat_buf, TS_SIZE);
|
||||
#endif
|
||||
} else
|
||||
isyslog("cStreamdevPatFilter: PAT size %d too large to fit in one TS", Length);
|
||||
m_Streamer->SetPids(pmtPid);
|
||||
Add(pmtPid, 0x02);
|
||||
pmtVersion = -1;
|
||||
// repack PAT to TS frame and send to client
|
||||
int ts_id;
|
||||
unsigned int crc, i, len;
|
||||
uint8_t *tmp;
|
||||
static uint8_t ccounter = 0;
|
||||
ccounter = (ccounter + 1) % 16;
|
||||
ts_id = Channel->Tid(); // Get transport stream id of the channel
|
||||
tspat_buf[3] = 0x10 | ccounter; // Set payload flag, Continuity counter
|
||||
tspat_buf[8] = (ts_id >> 8); // Transport stream ID (bits 8-15)
|
||||
tspat_buf[9] = (ts_id & 0xff); // Transport stream ID (bits 0-7)
|
||||
tspat_buf[10] = 0xc0 | ((pat.getVersionNumber() << 1) & 0x3e) |
|
||||
pat.getCurrentNextIndicator();// Version number, Current next indicator
|
||||
tspat_buf[13] = (pmtSid >> 8); // Program number (bits 8-15)
|
||||
tspat_buf[14] = (pmtSid & 0xff); // Program number (bits 0-7)
|
||||
tspat_buf[15] = 0xe0 | (pmtPid >> 8); // Network ID (bits 8-12)
|
||||
tspat_buf[16] = (pmtPid & 0xff); // Network ID (bits 0-7)
|
||||
crc = 0xffffffff;
|
||||
len = 12; // PAT_TABLE_LEN
|
||||
tmp = &tspat_buf[4 + 1]; // TS_HDR_LEN + 1
|
||||
while (len--) {
|
||||
crc ^= *tmp++ << 24;
|
||||
for (i = 0; i < 8; i++)
|
||||
crc = (crc << 1) ^ ((crc & 0x80000000) ? 0x04c11db7 : 0); // CRC32POLY
|
||||
}
|
||||
tspat_buf[17] = crc >> 24 & 0xff; // Checksum
|
||||
tspat_buf[18] = crc >> 16 & 0xff; // Checksum
|
||||
tspat_buf[19] = crc >> 8 & 0xff; // Checksum
|
||||
tspat_buf[20] = crc & 0xff; // Checksum
|
||||
int written = siBuffer.PutTS(tspat_buf, TS_SIZE);
|
||||
if (written != TS_SIZE)
|
||||
siBuffer.ReportOverflow(TS_SIZE - written);
|
||||
if (pmtPid != prevPmtPid) {
|
||||
m_Streamer->SetPid(pmtPid, true);
|
||||
Add(pmtPid, 0x02);
|
||||
pmtVersion = -1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -295,8 +316,8 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
|
||||
return; // skip broken PMT records
|
||||
if (pmtVersion != -1) {
|
||||
if (pmtVersion != pmt.getVersionNumber()) {
|
||||
Dprintf("cStreamdevPatFilter: PMT version changed, detaching all pids");
|
||||
Del(pmtPid, 0x02);
|
||||
Dprintf("cStreamdevPatFilter: PMT version changed, detaching all pids\n");
|
||||
cFilter::Del(pmtPid, 0x02);
|
||||
pmtPid = 0; // this triggers PAT scan
|
||||
}
|
||||
return;
|
||||
@@ -308,9 +329,9 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
|
||||
pids[npids++] = pmtPid;
|
||||
#if 0
|
||||
pids[npids++] = 0x10; // pid 0x10, tid 0x40: NIT
|
||||
#endif
|
||||
pids[npids++] = 0x11; // pid 0x11, tid 0x42: SDT
|
||||
pids[npids++] = 0x14; // pid 0x14, tid 0x70: TDT
|
||||
#endif
|
||||
pids[npids++] = 0x12; // pid 0x12, tid 0x4E...0x6F: EIT
|
||||
for (SI::Loop::Iterator it; pmt.streamLoop.getNext(stream, it); )
|
||||
if (0 != (pids[npids] = GetPid(stream)) && npids < MAXRECEIVEPIDS)
|
||||
@@ -323,20 +344,16 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
|
||||
|
||||
// --- cStreamdevLiveStreamer -------------------------------------------------
|
||||
|
||||
cStreamdevLiveStreamer::cStreamdevLiveStreamer(int Priority, std::string Parameter):
|
||||
cStreamdevStreamer("streamdev-livestreaming"),
|
||||
cStreamdevLiveStreamer::cStreamdevLiveStreamer(int Priority, const cServerConnection *Connection):
|
||||
cStreamdevStreamer("streamdev-livestreaming", Connection),
|
||||
m_Priority(Priority),
|
||||
m_Parameter(Parameter),
|
||||
m_NumPids(0),
|
||||
m_StreamType(stTSPIDS),
|
||||
m_Channel(NULL),
|
||||
m_Device(NULL),
|
||||
m_Receiver(NULL),
|
||||
m_PatFilter(NULL),
|
||||
m_PESRemux(NULL),
|
||||
m_ESRemux(NULL),
|
||||
m_PSRemux(NULL),
|
||||
m_ExtRemux(NULL)
|
||||
m_Remux(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -349,10 +366,7 @@ cStreamdevLiveStreamer::~cStreamdevLiveStreamer()
|
||||
DELETENULL(m_PatFilter);
|
||||
}
|
||||
DELETENULL(m_Receiver);
|
||||
delete m_PESRemux;
|
||||
delete m_ESRemux;
|
||||
delete m_PSRemux;
|
||||
delete m_ExtRemux;
|
||||
delete m_Remux;
|
||||
}
|
||||
|
||||
bool cStreamdevLiveStreamer::HasPid(int Pid)
|
||||
@@ -429,148 +443,136 @@ bool cStreamdevLiveStreamer::SetPids(int Pid, const int *Pids1, const int *Pids2
|
||||
return true;
|
||||
}
|
||||
|
||||
void cStreamdevLiveStreamer::StartReceiver(void)
|
||||
void cStreamdevLiveStreamer::SetPriority(int Priority)
|
||||
{
|
||||
DELETENULL(m_Receiver);
|
||||
if (m_NumPids > 0) {
|
||||
Dprintf("Creating Receiver to respect changed pids\n");
|
||||
#if VDRVERSNUM < 10500
|
||||
m_Receiver = new cStreamdevLiveReceiver(this, m_Channel->Ca(), m_Priority, m_Pids);
|
||||
#else
|
||||
m_Receiver = new cStreamdevLiveReceiver(this, m_Channel->GetChannelID(), m_Priority, m_Pids);
|
||||
m_Priority = Priority;
|
||||
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
|
||||
if (IsRunning() && m_Device != NULL) {
|
||||
Dprintf("Attaching new receiver\n");
|
||||
Attach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType StreamType, int Apid)
|
||||
cString cStreamdevLiveStreamer::ToText() const
|
||||
{
|
||||
if (m_Device && m_Channel) {
|
||||
return cString::sprintf("DVB%-2d %3d %s", m_Device->DeviceNumber() + 1, m_Channel->Number(), m_Channel->Name());
|
||||
}
|
||||
return cString("");
|
||||
}
|
||||
|
||||
void cStreamdevLiveStreamer::StartReceiver(void)
|
||||
{
|
||||
if (m_NumPids > 0) {
|
||||
Dprintf("Creating Receiver to respect changed pids\n");
|
||||
cReceiver *current = m_Receiver;
|
||||
m_Receiver = new cStreamdevLiveReceiver(this, m_Channel, m_Priority, m_Pids);
|
||||
cThreadLock ThreadLock(m_Device);
|
||||
if (IsRunning())
|
||||
Attach();
|
||||
delete current;
|
||||
}
|
||||
else
|
||||
DELETENULL(m_Receiver);
|
||||
}
|
||||
|
||||
bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType StreamType, const int* Apid, const int *Dpid)
|
||||
{
|
||||
Dprintf("Initializing Remuxer for full channel transfer\n");
|
||||
//printf("ca pid: %d\n", Channel->Ca());
|
||||
m_Channel = Channel;
|
||||
m_StreamType = StreamType;
|
||||
|
||||
int apid[2] = { Apid, 0 };
|
||||
const int *Apids = Apid ? apid : m_Channel->Apids();
|
||||
const int *Dpids = Apid ? NULL : m_Channel->Dpids();
|
||||
const int *Apids = Apid ? Apid : m_Channel->Apids();
|
||||
const int *Dpids = Dpid ? Dpid : m_Channel->Dpids();
|
||||
|
||||
switch (m_StreamType) {
|
||||
case stES:
|
||||
{
|
||||
int pid = ISRADIO(m_Channel) ? m_Channel->Apid(0) : m_Channel->Vpid();
|
||||
if (Apid != 0)
|
||||
pid = Apid;
|
||||
m_ESRemux = new cTS2ESRemux(pid);
|
||||
if (Apid && Apid[0])
|
||||
pid = Apid[0];
|
||||
else if (Dpid && Dpid[0])
|
||||
pid = Dpid[0];
|
||||
m_Remux = new cTS2ESRemux(pid);
|
||||
return SetPids(pid);
|
||||
}
|
||||
|
||||
case stPES:
|
||||
m_PESRemux = new cRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
|
||||
m_Channel->Spids(), false);
|
||||
m_Remux = new cTS2PESRemux(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
|
||||
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
|
||||
|
||||
case stPS:
|
||||
m_PSRemux = new cTS2PSRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
|
||||
m_Channel->Spids());
|
||||
m_Remux = new cTS2PSRemux(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
|
||||
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
|
||||
|
||||
case stEXT:
|
||||
m_Remux = new cExternRemux(Connection(), m_Channel, Apids, Dpids);
|
||||
// fall through
|
||||
case stTS:
|
||||
// This should never happen, but ...
|
||||
if (m_PatFilter) {
|
||||
Detach();
|
||||
DELETENULL(m_PatFilter);
|
||||
}
|
||||
// Set pids from cChannel
|
||||
SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
|
||||
if (m_Channel->Vpid() != m_Channel->Ppid())
|
||||
SetPid(m_Channel->Ppid(), true);
|
||||
// Set pids from PMT
|
||||
m_PatFilter = new cStreamdevPatFilter(this, m_Channel);
|
||||
return true;
|
||||
|
||||
case stExtern:
|
||||
m_ExtRemux = new cExternRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
|
||||
m_Channel->Spids(), m_Parameter);
|
||||
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
|
||||
|
||||
case stTSPIDS:
|
||||
Dprintf("pid streaming mode\n");
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int cStreamdevLiveStreamer::Put(const uchar *Data, int Count)
|
||||
{
|
||||
switch (m_StreamType) {
|
||||
case stTS:
|
||||
case stTSPIDS:
|
||||
return cStreamdevStreamer::Put(Data, Count);
|
||||
|
||||
case stPES:
|
||||
return m_PESRemux->Put(Data, Count);
|
||||
|
||||
case stES:
|
||||
return m_ESRemux->Put(Data, Count);
|
||||
|
||||
case stPS:
|
||||
return m_PSRemux->Put(Data, Count);
|
||||
|
||||
case stExtern:
|
||||
return m_ExtRemux->Put(Data, Count);
|
||||
|
||||
default: // shouldn't happen???
|
||||
return 0;
|
||||
// insert si data
|
||||
if (m_PatFilter) {
|
||||
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);
|
||||
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)
|
||||
{
|
||||
switch (m_StreamType) {
|
||||
case stTS:
|
||||
case stTSPIDS:
|
||||
if (m_Remux)
|
||||
return m_Remux->Get(Count);
|
||||
else
|
||||
return cStreamdevStreamer::Get(Count);
|
||||
|
||||
case stPES:
|
||||
return m_PESRemux->Get(Count);
|
||||
|
||||
case stES:
|
||||
return m_ESRemux->Get(Count);
|
||||
|
||||
case stPS:
|
||||
return m_PSRemux->Get(Count);
|
||||
|
||||
case stExtern:
|
||||
return m_ExtRemux->Get(Count);
|
||||
|
||||
default: // shouldn't happen???
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void cStreamdevLiveStreamer::Del(int Count)
|
||||
{
|
||||
switch (m_StreamType) {
|
||||
case stTS:
|
||||
case stTSPIDS:
|
||||
if (m_Remux)
|
||||
m_Remux->Del(Count);
|
||||
else
|
||||
cStreamdevStreamer::Del(Count);
|
||||
break;
|
||||
|
||||
case stPES:
|
||||
m_PESRemux->Del(Count);
|
||||
break;
|
||||
|
||||
case stES:
|
||||
m_ESRemux->Del(Count);
|
||||
break;
|
||||
|
||||
case stPS:
|
||||
m_PSRemux->Del(Count);
|
||||
break;
|
||||
|
||||
case stExtern:
|
||||
m_ExtRemux->Del(Count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void cStreamdevLiveStreamer::Attach(void)
|
||||
@@ -665,12 +667,10 @@ void cStreamdevFilterStreamer::SetDevice(cDevice *Device)
|
||||
{
|
||||
Dprintf("cStreamdevFilterStreamer::SetDevice()\n");
|
||||
LOCK_THREAD;
|
||||
if(Device != m_Device) {
|
||||
Detach();
|
||||
m_Device = Device;
|
||||
//m_Channel = NULL;
|
||||
Attach();
|
||||
}
|
||||
Detach();
|
||||
m_Device = Device;
|
||||
//m_Channel = NULL;
|
||||
Attach();
|
||||
}
|
||||
|
||||
bool cStreamdevFilterStreamer::SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On)
|
||||
|
||||
@@ -7,10 +7,9 @@
|
||||
#include "server/streamer.h"
|
||||
#include "common.h"
|
||||
|
||||
class cTS2PSRemux;
|
||||
class cTS2ESRemux;
|
||||
class cExternRemux;
|
||||
class cRemux;
|
||||
namespace Streamdev {
|
||||
class cTSRemux;
|
||||
}
|
||||
class cStreamdevPatFilter;
|
||||
class cStreamdevLiveReceiver;
|
||||
|
||||
@@ -19,7 +18,6 @@ class cStreamdevLiveReceiver;
|
||||
class cStreamdevLiveStreamer: public cStreamdevStreamer {
|
||||
private:
|
||||
int m_Priority;
|
||||
std::string m_Parameter;
|
||||
int m_Pids[MAXRECEIVEPIDS + 1];
|
||||
int m_NumPids;
|
||||
eStreamType m_StreamType;
|
||||
@@ -27,22 +25,22 @@ private:
|
||||
cDevice *m_Device;
|
||||
cStreamdevLiveReceiver *m_Receiver;
|
||||
cStreamdevPatFilter *m_PatFilter;
|
||||
cRemux *m_PESRemux;
|
||||
cTS2ESRemux *m_ESRemux;
|
||||
cTS2PSRemux *m_PSRemux;
|
||||
cExternRemux *m_ExtRemux;
|
||||
Streamdev::cTSRemux *m_Remux;
|
||||
|
||||
void StartReceiver(void);
|
||||
bool HasPid(int Pid);
|
||||
|
||||
public:
|
||||
cStreamdevLiveStreamer(int Priority, std::string Parameter = "");
|
||||
cStreamdevLiveStreamer(int Priority, const cServerConnection *Connection);
|
||||
virtual ~cStreamdevLiveStreamer();
|
||||
|
||||
void SetDevice(cDevice *Device) { m_Device = Device; }
|
||||
bool SetPid(int Pid, bool On);
|
||||
bool SetPids(int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL);
|
||||
bool SetChannel(const cChannel *Channel, eStreamType StreamType, int Apid = 0);
|
||||
bool SetChannel(const cChannel *Channel, eStreamType StreamType, const int* Apid = NULL, const int* Dpid = NULL);
|
||||
void SetPriority(int Priority);
|
||||
void GetSignal(int *DevNum, int *Strength, int *Quality) const;
|
||||
cString ToText() const;
|
||||
|
||||
virtual int Put(const uchar *Data, int Count);
|
||||
virtual uchar *Get(int &Count);
|
||||
|
||||
68
server/menu.c
Normal file
68
server/menu.c
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* $Id: menu.c,v 1.10 2010/07/19 13:49:31 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include <vdr/menuitems.h>
|
||||
#include <vdr/thread.h>
|
||||
#include <vdr/player.h>
|
||||
|
||||
#include "server/menu.h"
|
||||
#include "server/setup.h"
|
||||
#include "server/server.h"
|
||||
#include "server/suspend.h"
|
||||
|
||||
cStreamdevServerMenu::cStreamdevServerMenu(): cOsdMenu(tr("Streamdev Connections"), 4, 20) {
|
||||
cThreadLock lock;
|
||||
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
||||
for (cServerConnection *s = clients.First(); s; s = clients.Next(s))
|
||||
Add(new cOsdItem(s->ToText()));
|
||||
SetHelpKeys();
|
||||
Display();
|
||||
}
|
||||
|
||||
cStreamdevServerMenu::~cStreamdevServerMenu() {
|
||||
}
|
||||
|
||||
void cStreamdevServerMenu::SetHelpKeys() {
|
||||
SetHelp(Count() ? tr("Disconnect") : NULL, NULL, NULL, StreamdevServerSetup.SuspendMode == smOffer ? tr("Suspend") : NULL);
|
||||
}
|
||||
|
||||
eOSState cStreamdevServerMenu::Disconnect() {
|
||||
cOsdItem *item = Get(Current());
|
||||
if (item) {
|
||||
cThreadLock lock;
|
||||
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
||||
const char *text = item->Text();
|
||||
for (cServerConnection *s = clients.First(); s; s = clients.Next(s)) {
|
||||
if (!strcmp(text, s->ToText())) {
|
||||
s->Close();
|
||||
Del(Current());
|
||||
SetHelpKeys();
|
||||
Display();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return osContinue;
|
||||
}
|
||||
|
||||
eOSState cStreamdevServerMenu::Suspend() {
|
||||
if (StreamdevServerSetup.SuspendMode == smOffer && !cSuspendCtl::IsActive()) {
|
||||
cControl::Launch(new cSuspendCtl);
|
||||
return osBack;
|
||||
}
|
||||
return osContinue;
|
||||
}
|
||||
|
||||
eOSState cStreamdevServerMenu::ProcessKey(eKeys Key) {
|
||||
eOSState state = cOsdMenu::ProcessKey(Key);
|
||||
if (state == osUnknown) {
|
||||
switch (Key) {
|
||||
case kRed: return Disconnect();
|
||||
case kBlue: return Suspend();
|
||||
case kOk: return osBack;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
24
server/menu.h
Normal file
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
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "server/menuHTTP.h"
|
||||
|
||||
//**************************** cChannelIterator **************
|
||||
cChannelIterator::cChannelIterator(cChannel *First): channel(First)
|
||||
cChannelIterator::cChannelIterator(const cChannel *First): channel(First)
|
||||
{}
|
||||
|
||||
const cChannel* cChannelIterator::Next()
|
||||
@@ -19,7 +19,7 @@ cListAll::cListAll(): cChannelIterator(Channels.First())
|
||||
const cChannel* cListAll::NextChannel(const cChannel *Channel)
|
||||
{
|
||||
if (Channel)
|
||||
Channel = Channels.Next(Channel);
|
||||
Channel = SkipFakeGroups(Channels.Next(Channel));
|
||||
return Channel;
|
||||
}
|
||||
|
||||
@@ -46,14 +46,19 @@ const cChannel* cListGroups::NextChannel(const cChannel *Channel)
|
||||
}
|
||||
//
|
||||
// ********************* cListGroup ****************
|
||||
cListGroup::cListGroup(const cChannel *Group): cChannelIterator((Group && Group->GroupSep() && Channels.Next(Group) && !Channels.Next(Group)->GroupSep()) ? Channels.Next(Group) : NULL)
|
||||
cListGroup::cListGroup(const cChannel *Group): cChannelIterator(GetNextChannelInGroup(Group))
|
||||
{}
|
||||
|
||||
const cChannel* cListGroup::GetNextChannelInGroup(const cChannel *Channel)
|
||||
{
|
||||
if (Channel)
|
||||
Channel = SkipFakeGroups(Channels.Next(Channel));
|
||||
return Channel && !Channel->GroupSep() ? Channel : NULL;
|
||||
}
|
||||
|
||||
const cChannel* cListGroup::NextChannel(const cChannel *Channel)
|
||||
{
|
||||
if (Channel)
|
||||
Channel = Channels.Next(Channel);
|
||||
return (Channel && !Channel->GroupSep()) ? Channel : NULL;
|
||||
return GetNextChannelInGroup(Channel);
|
||||
}
|
||||
//
|
||||
// ********************* cListTree ****************
|
||||
@@ -68,7 +73,7 @@ const cChannel* cListTree::NextChannel(const cChannel *Channel)
|
||||
if (currentGroup == selectedGroup)
|
||||
{
|
||||
if (Channel)
|
||||
Channel = Channels.Next(Channel);
|
||||
Channel = SkipFakeGroups(Channels.Next(Channel));
|
||||
if (Channel && Channel->GroupSep())
|
||||
currentGroup = Channel;
|
||||
}
|
||||
@@ -112,10 +117,10 @@ const cChannel* cChannelList::GetGroup(int Index)
|
||||
|
||||
// ******************** cHtmlChannelList ******************
|
||||
const char* cHtmlChannelList::menu =
|
||||
"[<a href=\"/\">Home</a> (<a href=\"all.html\">no script</a>)] "
|
||||
"[<a href=\"tree.html\">Tree View</a>] "
|
||||
"[<a href=\"groups.html\">Groups</a> (<a href=\"groups.m3u\">Playlist</a>)] "
|
||||
"[<a href=\"channels.html\">Channels</a> (<a href=\"channels.m3u\">Playlist</a>)] ";
|
||||
"[<a href=\"/\">Home</a> (<a href=\"all.html\" tvid=\"RED\">no script</a>)] "
|
||||
"[<a href=\"tree.html\" tvid=\"GREEN\">Tree View</a>] "
|
||||
"[<a href=\"groups.html\" tvid=\"YELLOW\">Groups</a> (<a href=\"groups.m3u\">Playlist</a>)] "
|
||||
"[<a href=\"channels.html\" tvid=\"BLUE\">Channels</a> (<a href=\"channels.m3u\">Playlist</a>)] ";
|
||||
|
||||
const char* cHtmlChannelList::css =
|
||||
"<style type=\"text/css\">\n"
|
||||
@@ -205,8 +210,8 @@ std::string cHtmlChannelList::StreamTypeMenu()
|
||||
(std::string) "[<a href=\"/PES/" + self + "\">PES</a>] ");
|
||||
typeMenu += (streamType == stES ? (std::string) "[ES] " :
|
||||
(std::string) "[<a href=\"/ES/" + self + "\">ES</a>] ");
|
||||
typeMenu += (streamType == stExtern ? (std::string) "[Extern] " :
|
||||
(std::string) "[<a href=\"/Extern/" + self + "\">Extern</a>] ");
|
||||
typeMenu += (streamType == stEXT ? (std::string) "[EXT] " :
|
||||
(std::string) "[<a href=\"/EXT/" + self + "\">EXT</a>] ");
|
||||
return typeMenu;
|
||||
}
|
||||
|
||||
@@ -336,9 +341,24 @@ std::string cHtmlChannelList::GroupTitle()
|
||||
std::string cHtmlChannelList::ItemText()
|
||||
{
|
||||
std::string line;
|
||||
std::string suffix;
|
||||
|
||||
switch (streamType) {
|
||||
case stTS: suffix = (std::string) ".ts"; break;
|
||||
case stPS: suffix = (std::string) ".vob"; break;
|
||||
// for Network Media Tank
|
||||
case stPES: suffix = (std::string) ".vdr"; break;
|
||||
default: suffix = "";
|
||||
}
|
||||
line += (std::string) "<li value=\"" + (const char*) itoa(current->Number()) + "\">";
|
||||
line += (std::string) "<a href=\"" + (std::string) current->GetChannelID().ToString() + "\">" +
|
||||
current->Name() + "</a>";
|
||||
line += (std::string) "<a href=\"" + (std::string) current->GetChannelID().ToString() + suffix + "\"";
|
||||
|
||||
// for Network Media Tank
|
||||
line += (std::string) " vod ";
|
||||
if (current->Number() < 1000)
|
||||
line += (std::string) " tvid=\"" + (const char*) itoa(current->Number()) + "\"";
|
||||
|
||||
line += (std::string) ">" + current->Name() + "</a>";
|
||||
|
||||
int count = 0;
|
||||
for (int i = 0; current->Apid(i) != 0; ++i, ++count)
|
||||
@@ -351,11 +371,11 @@ std::string cHtmlChannelList::ItemText()
|
||||
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) + "\" class=\"apid\">" + current->Alang(i) + "</a>";
|
||||
"+" + (const char*)itoa(index) + suffix + "\" class=\"apid\" vod>" + current->Alang(i) + "</a>";
|
||||
}
|
||||
for (int i = 0; current->Dpid(i) != 0; ++i, ++index) {
|
||||
line += (std::string) " <a href=\"" + (std::string) current->GetChannelID().ToString() +
|
||||
"+" + (const char*)itoa(index) + "\" class=\"dpid\">" + current->Dlang(i) + "</a>";
|
||||
"+" + (const char*)itoa(index) + suffix + "\" class=\"dpid\" vod>" + current->Dlang(i) + "</a>";
|
||||
}
|
||||
}
|
||||
line += "</li>";
|
||||
@@ -364,10 +384,8 @@ std::string cHtmlChannelList::ItemText()
|
||||
|
||||
// ******************** cM3uChannelList ******************
|
||||
cM3uChannelList::cM3uChannelList(cChannelIterator *Iterator, const char* Base)
|
||||
: cChannelList(Iterator)
|
||||
#if defined(APIVERSNUM) && APIVERSNUM >= 10503
|
||||
, m_IConv(cCharSetConv::SystemCharacterTable(), "UTF-8")
|
||||
#endif
|
||||
: cChannelList(Iterator),
|
||||
m_IConv(cCharSetConv::SystemCharacterTable(), "UTF-8")
|
||||
{
|
||||
base = strdup(Base);
|
||||
m3uState = msFirst;
|
||||
@@ -398,21 +416,17 @@ std::string cM3uChannelList::Next()
|
||||
return "";
|
||||
}
|
||||
|
||||
#if defined(APIVERSNUM) && APIVERSNUM >= 10503
|
||||
std::string name = (std::string) m_IConv.Convert(channel->Name());
|
||||
#else
|
||||
std::string name = channel->Name();
|
||||
#endif
|
||||
|
||||
if (channel->GroupSep())
|
||||
{
|
||||
return (std::string) "#EXTINF:0," + name + "\r\n" +
|
||||
return (std::string) "#EXTINF:-1," + name + "\r\n" +
|
||||
base + "group.m3u?group=" +
|
||||
(const char*) itoa(cChannelList::GetGroupIndex(channel));
|
||||
}
|
||||
else
|
||||
{
|
||||
return (std::string) "#EXTINF:0," +
|
||||
return (std::string) "#EXTINF:-1," +
|
||||
(const char*) itoa(channel->Number()) + " " + name + "\r\n" +
|
||||
base + (std::string) channel->GetChannelID().ToString();
|
||||
}
|
||||
|
||||
@@ -13,9 +13,10 @@ class cChannelIterator
|
||||
const cChannel *channel;
|
||||
protected:
|
||||
virtual const cChannel* NextChannel(const cChannel *Channel) = 0;
|
||||
static inline const cChannel* SkipFakeGroups(const cChannel *Channel);
|
||||
public:
|
||||
const cChannel* Next();
|
||||
cChannelIterator(cChannel *First);
|
||||
cChannelIterator(const cChannel *First);
|
||||
virtual ~cChannelIterator() {};
|
||||
};
|
||||
|
||||
@@ -48,6 +49,8 @@ class cListGroups: public cChannelIterator
|
||||
|
||||
class cListGroup: public cChannelIterator
|
||||
{
|
||||
private:
|
||||
static const cChannel* GetNextChannelInGroup(const cChannel *Channel);
|
||||
protected:
|
||||
virtual const cChannel* NextChannel(const cChannel *Channel);
|
||||
public:
|
||||
@@ -113,7 +116,12 @@ class cHtmlChannelList: public cChannelList
|
||||
std::string ItemText();
|
||||
std::string PageBottom();
|
||||
public:
|
||||
virtual std::string HttpHeader() { return cChannelList::HttpHeader() + "Content-type: text/html\r\n\r\n"; }
|
||||
virtual std::string HttpHeader() {
|
||||
return cChannelList::HttpHeader()
|
||||
+ "Content-type: text/html; charset="
|
||||
+ (cCharSetConv::SystemCharacterTable() ? cCharSetConv::SystemCharacterTable() : "UTF-8")
|
||||
+ "\r\n";
|
||||
}
|
||||
virtual bool HasNext();
|
||||
virtual std::string Next();
|
||||
cHtmlChannelList(cChannelIterator *Iterator, eStreamType StreamType, const char *Self, const char *GroupTarget);
|
||||
@@ -126,15 +134,20 @@ class cM3uChannelList: public cChannelList
|
||||
char *base;
|
||||
enum eM3uState { msFirst, msContinue, msLast };
|
||||
eM3uState m3uState;
|
||||
#if defined(APIVERSNUM) && APIVERSNUM >= 10503
|
||||
cCharSetConv m_IConv;
|
||||
#endif
|
||||
public:
|
||||
virtual std::string HttpHeader() { return cChannelList::HttpHeader() + "Content-type: audio/x-mpegurl\r\n"; };
|
||||
virtual std::string HttpHeader() { return cChannelList::HttpHeader() + "Content-type: audio/x-mpegurl; charset=UTF-8\r\n"; };
|
||||
virtual bool HasNext();
|
||||
virtual std::string Next();
|
||||
cM3uChannelList(cChannelIterator *Iterator, const char* Base);
|
||||
virtual ~cM3uChannelList();
|
||||
};
|
||||
|
||||
inline const cChannel* cChannelIterator::SkipFakeGroups(const cChannel* Group)
|
||||
{
|
||||
while (Group && Group->GroupSep() && !*Group->Name())
|
||||
Group = Channels.Next(Group);
|
||||
return Group;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
88
server/po/de_DE.po
Normal file
88
server/po/de_DE.po
Normal file
@@ -0,0 +1,88 @@
|
||||
# VDR streamdev plugin language source file.
|
||||
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
||||
# This file is distributed under the same license as the VDR streamdev package.
|
||||
# Frank Schmirler <vdrdev@schmirler.de>, 2008
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
|
||||
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
||||
"Last-Translator: Frank Schmirler <vdrdev@schmirler.de>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "VDR Streaming Server"
|
||||
|
||||
msgid "Streaming active"
|
||||
msgstr "Streamen im Gange"
|
||||
|
||||
msgid "Streamdev Connections"
|
||||
msgstr "Streamdev Verbindungen"
|
||||
|
||||
msgid "Disconnect"
|
||||
msgstr "Trennen"
|
||||
|
||||
msgid "Suspend"
|
||||
msgstr "Pausieren"
|
||||
|
||||
msgid "Offer suspend mode"
|
||||
msgstr "Pausieren anbieten"
|
||||
|
||||
msgid "Always suspended"
|
||||
msgstr "Immer pausiert"
|
||||
|
||||
msgid "Never suspended"
|
||||
msgstr "Nie pausiert"
|
||||
|
||||
msgid "Common Settings"
|
||||
msgstr "Allgemeines"
|
||||
|
||||
msgid "Maximum Number of Clients"
|
||||
msgstr "Maximalanzahl an Clients"
|
||||
|
||||
msgid "Suspend behaviour"
|
||||
msgstr "Pausierverhalten"
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Client darf pausieren"
|
||||
|
||||
msgid "VDR-to-VDR Server"
|
||||
msgstr "VDR-zu-VDR Server"
|
||||
|
||||
msgid "Start VDR-to-VDR Server"
|
||||
msgstr "VDR-zu-VDR Server starten"
|
||||
|
||||
msgid "VDR-to-VDR Server Port"
|
||||
msgstr "Port des VDR-zu-VDR Servers"
|
||||
|
||||
msgid "Bind to IP"
|
||||
msgstr "Binde an IP"
|
||||
|
||||
msgid "HTTP Server"
|
||||
msgstr "HTTP Server"
|
||||
|
||||
msgid "Start HTTP Server"
|
||||
msgstr "HTTP Server starten"
|
||||
|
||||
msgid "HTTP Server Port"
|
||||
msgstr "Port des HTTP Servers"
|
||||
|
||||
msgid "HTTP Streamtype"
|
||||
msgstr "HTTP Streamtyp"
|
||||
|
||||
msgid "Multicast Streaming Server"
|
||||
msgstr "Multicast Streaming Server"
|
||||
|
||||
msgid "Start IGMP Server"
|
||||
msgstr "IGMP Server starten"
|
||||
|
||||
msgid "Multicast Client Port"
|
||||
msgstr "Port des Multicast Clients"
|
||||
|
||||
msgid "Multicast Streamtype"
|
||||
msgstr "Multicast Streamtyp"
|
||||
88
server/po/es_ES.po
Normal file
88
server/po/es_ES.po
Normal file
@@ -0,0 +1,88 @@
|
||||
# VDR streamdev plugin language source file.
|
||||
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
||||
# This file is distributed under the same license as the VDR streamdev package.
|
||||
# Javier Bradineras <jbradi@hotmail.com>, 2011
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
|
||||
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
|
||||
"Last-Translator: Javier Bradineras <jbradi@hotmail.com>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "Servidor de transmisiones del VDR"
|
||||
|
||||
msgid "Streaming active"
|
||||
msgstr "Trasmisión activa"
|
||||
|
||||
msgid "Streamdev Connections"
|
||||
msgstr ""
|
||||
|
||||
msgid "Disconnect"
|
||||
msgstr ""
|
||||
|
||||
msgid "Suspend"
|
||||
msgstr "Suspender"
|
||||
|
||||
msgid "Offer suspend mode"
|
||||
msgstr "Ofrecer modo de suspensión"
|
||||
|
||||
msgid "Always suspended"
|
||||
msgstr "Siempre suspendido"
|
||||
|
||||
msgid "Never suspended"
|
||||
msgstr "Nunca suspendido"
|
||||
|
||||
msgid "Common Settings"
|
||||
msgstr "Configuración común"
|
||||
|
||||
msgid "Maximum Number of Clients"
|
||||
msgstr "Numero máximo de clientes"
|
||||
|
||||
msgid "Suspend behaviour"
|
||||
msgstr "Comportamiento de la suspensión"
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Permitir suspender al cliente"
|
||||
|
||||
msgid "VDR-to-VDR Server"
|
||||
msgstr "Servidor VDR-a-VDR"
|
||||
|
||||
msgid "Start VDR-to-VDR Server"
|
||||
msgstr "Iniciar Servidor VDR-a-VDR"
|
||||
|
||||
msgid "VDR-to-VDR Server Port"
|
||||
msgstr "Puerto del Servidor VDR-a-VDR"
|
||||
|
||||
msgid "Bind to IP"
|
||||
msgstr "IP asociada"
|
||||
|
||||
msgid "HTTP Server"
|
||||
msgstr "Servidor HTTP"
|
||||
|
||||
msgid "Start HTTP Server"
|
||||
msgstr "Iniciar Servidor HTTP"
|
||||
|
||||
msgid "HTTP Server Port"
|
||||
msgstr "Puerto del Servidor HTTP"
|
||||
|
||||
msgid "HTTP Streamtype"
|
||||
msgstr "Tipo de flujo HTTP"
|
||||
|
||||
msgid "Multicast Streaming Server"
|
||||
msgstr "Servidor de transmisión Multicast"
|
||||
|
||||
msgid "Start IGMP Server"
|
||||
msgstr "Iniciar Servidor IGMP"
|
||||
|
||||
msgid "Multicast Client Port"
|
||||
msgstr "Puerto del Cliente Multicast"
|
||||
|
||||
msgid "Multicast Streamtype"
|
||||
msgstr "Tipo de flujo Multicast"
|
||||
88
server/po/fi_FI.po
Normal file
88
server/po/fi_FI.po
Normal file
@@ -0,0 +1,88 @@
|
||||
# VDR streamdev plugin language source file.
|
||||
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
||||
# This file is distributed under the same license as the VDR streamdev package.
|
||||
# Rolf Ahrenberg <rahrenbe@cc.hut.fi>, 2008
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
|
||||
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
||||
"Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "VDR-suoratoistopalvelin"
|
||||
|
||||
msgid "Streaming active"
|
||||
msgstr "Suoratoistopalvelin aktiivinen"
|
||||
|
||||
msgid "Streamdev Connections"
|
||||
msgstr ""
|
||||
|
||||
msgid "Disconnect"
|
||||
msgstr ""
|
||||
|
||||
msgid "Suspend"
|
||||
msgstr "Pysäytä"
|
||||
|
||||
msgid "Offer suspend mode"
|
||||
msgstr "tyrkytä"
|
||||
|
||||
msgid "Always suspended"
|
||||
msgstr "aina"
|
||||
|
||||
msgid "Never suspended"
|
||||
msgstr "ei koskaan"
|
||||
|
||||
msgid "Common Settings"
|
||||
msgstr "Yleiset asetukset"
|
||||
|
||||
msgid "Maximum Number of Clients"
|
||||
msgstr "Suurin sallittu asiakkaiden määrä"
|
||||
|
||||
msgid "Suspend behaviour"
|
||||
msgstr "Pysäytystoiminto"
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Asiakas saa pysäyttää palvelimen"
|
||||
|
||||
msgid "VDR-to-VDR Server"
|
||||
msgstr "VDR-palvelin"
|
||||
|
||||
msgid "Start VDR-to-VDR Server"
|
||||
msgstr "Käynnistä VDR-palvelin"
|
||||
|
||||
msgid "VDR-to-VDR Server Port"
|
||||
msgstr "VDR-palvelimen portti"
|
||||
|
||||
msgid "Bind to IP"
|
||||
msgstr "Sido osoitteeseen"
|
||||
|
||||
msgid "HTTP Server"
|
||||
msgstr "HTTP-palvelin"
|
||||
|
||||
msgid "Start HTTP Server"
|
||||
msgstr "Käynnistä HTTP-palvelin"
|
||||
|
||||
msgid "HTTP Server Port"
|
||||
msgstr "HTTP-palvelimen portti"
|
||||
|
||||
msgid "HTTP Streamtype"
|
||||
msgstr "HTTP-lähetysmuoto"
|
||||
|
||||
msgid "Multicast Streaming Server"
|
||||
msgstr "Multicast-suoratoistopalvelin"
|
||||
|
||||
msgid "Start IGMP Server"
|
||||
msgstr "Käynnistä IGMP-palvelin"
|
||||
|
||||
msgid "Multicast Client Port"
|
||||
msgstr "Multicast-portti"
|
||||
|
||||
msgid "Multicast Streamtype"
|
||||
msgstr "Multicast-lähetysmuoto"
|
||||
88
server/po/fr_FR.po
Normal file
88
server/po/fr_FR.po
Normal file
@@ -0,0 +1,88 @@
|
||||
# VDR streamdev plugin language source file.
|
||||
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
||||
# This file is distributed under the same license as the VDR streamdev package.
|
||||
# Frank Schmirler <vdrdev@schmirler.de>, 2008
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
|
||||
"PO-Revision-Date: 2008-03-30 02:11+0200\n"
|
||||
"Last-Translator: micky979 <micky979@free.fr>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "Serveur de streaming VDR"
|
||||
|
||||
msgid "Streaming active"
|
||||
msgstr "Streaming actif"
|
||||
|
||||
msgid "Streamdev Connections"
|
||||
msgstr ""
|
||||
|
||||
msgid "Disconnect"
|
||||
msgstr ""
|
||||
|
||||
msgid "Suspend"
|
||||
msgstr "Suspendre"
|
||||
|
||||
msgid "Offer suspend mode"
|
||||
msgstr "Offrir le mode suspendre"
|
||||
|
||||
msgid "Always suspended"
|
||||
msgstr "Toujours suspendre"
|
||||
|
||||
msgid "Never suspended"
|
||||
msgstr "Jamais suspendre"
|
||||
|
||||
msgid "Common Settings"
|
||||
msgstr "Paramètres communs"
|
||||
|
||||
msgid "Maximum Number of Clients"
|
||||
msgstr "Nombre maximun de clients"
|
||||
|
||||
msgid "Suspend behaviour"
|
||||
msgstr "Suspendre"
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Le client peut suspendre"
|
||||
|
||||
msgid "VDR-to-VDR Server"
|
||||
msgstr "VDR-to-VDR Serveur"
|
||||
|
||||
msgid "Start VDR-to-VDR Server"
|
||||
msgstr "Démarrer le serveur VDR-to-VDR"
|
||||
|
||||
msgid "VDR-to-VDR Server Port"
|
||||
msgstr "Port du serveur VDR-to-VDR"
|
||||
|
||||
msgid "Bind to IP"
|
||||
msgstr "Attacher aux IP"
|
||||
|
||||
msgid "HTTP Server"
|
||||
msgstr "Serveur HTTP"
|
||||
|
||||
msgid "Start HTTP Server"
|
||||
msgstr "Démarrer le serveur HTTP"
|
||||
|
||||
msgid "HTTP Server Port"
|
||||
msgstr "Port du serveur HTTP"
|
||||
|
||||
msgid "HTTP Streamtype"
|
||||
msgstr "Type de Streaming HTTP"
|
||||
|
||||
msgid "Multicast Streaming Server"
|
||||
msgstr ""
|
||||
|
||||
msgid "Start IGMP Server"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multicast Client Port"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multicast Streamtype"
|
||||
msgstr ""
|
||||
90
server/po/it_IT.po
Normal file
90
server/po/it_IT.po
Normal file
@@ -0,0 +1,90 @@
|
||||
# VDR streamdev plugin language source file.
|
||||
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
||||
# This file is distributed under the same license as the VDR streamdev package.
|
||||
# Alberto Carraro <bertocar@tin.it>, 2001
|
||||
# Antonio Ospite <ospite@studenti.unina.it>, 2003
|
||||
# Sean Carlos <seanc@libero.it>, 2005
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
|
||||
"PO-Revision-Date: 2010-06-19 03:58+0100\n"
|
||||
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "Server trasmissione VDR"
|
||||
|
||||
msgid "Streaming active"
|
||||
msgstr "Trasmissione attiva"
|
||||
|
||||
msgid "Streamdev Connections"
|
||||
msgstr ""
|
||||
|
||||
msgid "Disconnect"
|
||||
msgstr ""
|
||||
|
||||
msgid "Suspend"
|
||||
msgstr "Sospendi"
|
||||
|
||||
msgid "Offer suspend mode"
|
||||
msgstr "Offri mod. sospensione"
|
||||
|
||||
msgid "Always suspended"
|
||||
msgstr "Sempre sospeso"
|
||||
|
||||
msgid "Never suspended"
|
||||
msgstr "Mai sospeso"
|
||||
|
||||
msgid "Common Settings"
|
||||
msgstr "Impostazioni comuni"
|
||||
|
||||
msgid "Maximum Number of Clients"
|
||||
msgstr "Numero massimo di Client"
|
||||
|
||||
msgid "Suspend behaviour"
|
||||
msgstr "Tipo sospensione"
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Permetti sospensione al Client"
|
||||
|
||||
msgid "VDR-to-VDR Server"
|
||||
msgstr "Server VDR-a-VDR"
|
||||
|
||||
msgid "Start VDR-to-VDR Server"
|
||||
msgstr "Avvia Server VDR-a-VDR"
|
||||
|
||||
msgid "VDR-to-VDR Server Port"
|
||||
msgstr "Porta Server VDR-a-VDR"
|
||||
|
||||
msgid "Bind to IP"
|
||||
msgstr "IP associati"
|
||||
|
||||
msgid "HTTP Server"
|
||||
msgstr "Server HTTP"
|
||||
|
||||
msgid "Start HTTP Server"
|
||||
msgstr "Avvia Server HTTP"
|
||||
|
||||
msgid "HTTP Server Port"
|
||||
msgstr "Porta Server HTTP"
|
||||
|
||||
msgid "HTTP Streamtype"
|
||||
msgstr "Tipo flusso HTTP"
|
||||
|
||||
msgid "Multicast Streaming Server"
|
||||
msgstr "Server trasmissione Multicast"
|
||||
|
||||
msgid "Start IGMP Server"
|
||||
msgstr "Avvia Server IGMP"
|
||||
|
||||
msgid "Multicast Client Port"
|
||||
msgstr "Porta Client Multicast"
|
||||
|
||||
msgid "Multicast Streamtype"
|
||||
msgstr "Tipo flusso Multicast"
|
||||
88
server/po/lt_LT.po
Normal file
88
server/po/lt_LT.po
Normal file
@@ -0,0 +1,88 @@
|
||||
# VDR streamdev plugin language source file.
|
||||
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
||||
# This file is distributed under the same license as the VDR streamdev package.
|
||||
# Frank Schmirler <vdrdev@schmirler.de>, 2008
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
|
||||
"PO-Revision-Date: 2009-11-26 21:57+0200\n"
|
||||
"Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
|
||||
"Language-Team: Lietuvių\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "VDR transliavimo serveris"
|
||||
|
||||
msgid "Streaming active"
|
||||
msgstr "Transliavimas vyksta"
|
||||
|
||||
msgid "Streamdev Connections"
|
||||
msgstr ""
|
||||
|
||||
msgid "Disconnect"
|
||||
msgstr ""
|
||||
|
||||
msgid "Suspend"
|
||||
msgstr "Pristabdyti"
|
||||
|
||||
msgid "Offer suspend mode"
|
||||
msgstr "Klausti dėl sustabdymo"
|
||||
|
||||
msgid "Always suspended"
|
||||
msgstr "Visada stabdyti"
|
||||
|
||||
msgid "Never suspended"
|
||||
msgstr "Niekada nestabdyti"
|
||||
|
||||
msgid "Common Settings"
|
||||
msgstr "Bendri nustatymai"
|
||||
|
||||
msgid "Maximum Number of Clients"
|
||||
msgstr "Maksimalus klientų skaičius"
|
||||
|
||||
msgid "Suspend behaviour"
|
||||
msgstr "Pristabdyti veikimą"
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Klientas gali pristabdyti"
|
||||
|
||||
msgid "VDR-to-VDR Server"
|
||||
msgstr "VDR-su-VDR Serveris"
|
||||
|
||||
msgid "Start VDR-to-VDR Server"
|
||||
msgstr "Paleisti VDR-su-VDR serverį"
|
||||
|
||||
msgid "VDR-to-VDR Server Port"
|
||||
msgstr "VDR-su-VDR Serverio portas"
|
||||
|
||||
msgid "Bind to IP"
|
||||
msgstr "Pririšti IP"
|
||||
|
||||
msgid "HTTP Server"
|
||||
msgstr "HTTP Serveris"
|
||||
|
||||
msgid "Start HTTP Server"
|
||||
msgstr "Paleisti HTTP serverį"
|
||||
|
||||
msgid "HTTP Server Port"
|
||||
msgstr "HTTP serverio portas"
|
||||
|
||||
msgid "HTTP Streamtype"
|
||||
msgstr "HTTP transliavimo tipas"
|
||||
|
||||
msgid "Multicast Streaming Server"
|
||||
msgstr "Multicast transliavimo serveris"
|
||||
|
||||
msgid "Start IGMP Server"
|
||||
msgstr "Paleisti IGMP serverį"
|
||||
|
||||
msgid "Multicast Client Port"
|
||||
msgstr "Multicast kliento portas"
|
||||
|
||||
msgid "Multicast Streamtype"
|
||||
msgstr "Multicast transliavimo tipas"
|
||||
88
server/po/ru_RU.po
Normal file
88
server/po/ru_RU.po
Normal file
@@ -0,0 +1,88 @@
|
||||
# VDR streamdev plugin language source file.
|
||||
# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
|
||||
# This file is distributed under the same license as the VDR streamdev package.
|
||||
# Frank Schmirler <vdrdev@schmirler.de>, 2008
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev 0.5.0\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
|
||||
"PO-Revision-Date: 2008-06-26 15:36+0100\n"
|
||||
"Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n"
|
||||
"Language-Team: <vdr@linuxtv.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-5\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "VDR Streaming áÕàÒÕà"
|
||||
|
||||
msgid "Streaming active"
|
||||
msgstr "ÁâàØÜØÝÓ ÐÚâØÒÕÝ"
|
||||
|
||||
msgid "Streamdev Connections"
|
||||
msgstr ""
|
||||
|
||||
msgid "Disconnect"
|
||||
msgstr ""
|
||||
|
||||
msgid "Suspend"
|
||||
msgstr "¾áâÐÝÞÒÚÐ"
|
||||
|
||||
msgid "Offer suspend mode"
|
||||
msgstr "¿àÕÔÛÐÓÐâì ÞáâÐÝÞÒÚã"
|
||||
|
||||
msgid "Always suspended"
|
||||
msgstr "²áÕÓÔÐ ÞáâÐÝÞÒÛÕÝ"
|
||||
|
||||
msgid "Never suspended"
|
||||
msgstr "½ØÚÞÓÔÐ ÝÕ ÞáâÐÝÞÒÛÕÝ"
|
||||
|
||||
msgid "Common Settings"
|
||||
msgstr "½ÐáâàÞÙÚØ"
|
||||
|
||||
msgid "Maximum Number of Clients"
|
||||
msgstr "¼ÐÚá. ÚÞÛØçÕáâÒÞ ÚÛØÕÝâÞÒ"
|
||||
|
||||
msgid "Suspend behaviour"
|
||||
msgstr "¿ÞÒÕÔÕÝØÕ ÞáâÐÝÞÒÚØ"
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "ºÛØÕÝâ ÜÞÖÕâ ÞáâÐÝÐÒÛØÒÐâì"
|
||||
|
||||
msgid "VDR-to-VDR Server"
|
||||
msgstr "VDR-to-VDR áÕàÒÕà"
|
||||
|
||||
msgid "Start VDR-to-VDR Server"
|
||||
msgstr "ÁâÐàâ VDR-to-VDR áÕàÒÕà"
|
||||
|
||||
msgid "VDR-to-VDR Server Port"
|
||||
msgstr "VDR-to-VDR ßÞàâ áÕàÒÕàÐ"
|
||||
|
||||
msgid "Bind to IP"
|
||||
msgstr "¿àØáÞÕÔØÝØâìáï Ú IP"
|
||||
|
||||
msgid "HTTP Server"
|
||||
msgstr "HTTP áÕàÒÕà"
|
||||
|
||||
msgid "Start HTTP Server"
|
||||
msgstr "ÁâÐàâ HTTP áÕàÒÕàÐ"
|
||||
|
||||
msgid "HTTP Server Port"
|
||||
msgstr "HTTP áÕàÒÕà ¿Þàâ"
|
||||
|
||||
msgid "HTTP Streamtype"
|
||||
msgstr "ÂØß HTTP ßÞâÞÚÐ"
|
||||
|
||||
msgid "Multicast Streaming Server"
|
||||
msgstr ""
|
||||
|
||||
msgid "Start IGMP Server"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multicast Client Port"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multicast Streamtype"
|
||||
msgstr ""
|
||||
90
server/po/sk_SK.po
Normal file
90
server/po/sk_SK.po
Normal file
@@ -0,0 +1,90 @@
|
||||
# VDR streamdev plugin language source file.
|
||||
# Copyright (C) 2009 streamdev development team. See http://streamdev.vdr-developer.org
|
||||
# This file is distributed under the same license as the VDR streamdev package.
|
||||
# Milan Hrala <hrala.milan@gmail.com>, 2009
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: streamdev_SK\n"
|
||||
"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
|
||||
"POT-Creation-Date: 2011-11-22 01:05+0100\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
|
||||
"Language-Team: Slovak <hrala.milan@gmail.com>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=iso-8859-2\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Poedit-Language: Slovak\n"
|
||||
"X-Poedit-Country: SLOVAKIA\n"
|
||||
|
||||
msgid "VDR Streaming Server"
|
||||
msgstr "VDR prúdový server"
|
||||
|
||||
msgid "Streaming active"
|
||||
msgstr "streamovanie aktivne"
|
||||
|
||||
msgid "Streamdev Connections"
|
||||
msgstr ""
|
||||
|
||||
msgid "Disconnect"
|
||||
msgstr ""
|
||||
|
||||
msgid "Suspend"
|
||||
msgstr "Pozastavenie"
|
||||
|
||||
msgid "Offer suspend mode"
|
||||
msgstr "Výber re¾ímu pozastavenia"
|
||||
|
||||
msgid "Always suspended"
|
||||
msgstr "V¾dy pozastavi»"
|
||||
|
||||
msgid "Never suspended"
|
||||
msgstr "Nikdy nepozastavi»"
|
||||
|
||||
msgid "Common Settings"
|
||||
msgstr "V¹eobecné nastavenia"
|
||||
|
||||
msgid "Maximum Number of Clients"
|
||||
msgstr "Maximály poèet klientov"
|
||||
|
||||
msgid "Suspend behaviour"
|
||||
msgstr "Správanie preru¹enia"
|
||||
|
||||
msgid "Client may suspend"
|
||||
msgstr "Klient mô¾e pozastavi»"
|
||||
|
||||
msgid "VDR-to-VDR Server"
|
||||
msgstr "VDR-do-VDR server"
|
||||
|
||||
msgid "Start VDR-to-VDR Server"
|
||||
msgstr "Spusti» VDR-do-VDR Server"
|
||||
|
||||
msgid "VDR-to-VDR Server Port"
|
||||
msgstr "Port serveru pre VDR-do-VDR"
|
||||
|
||||
msgid "Bind to IP"
|
||||
msgstr "viaza» na IP"
|
||||
|
||||
msgid "HTTP Server"
|
||||
msgstr "server HTTP"
|
||||
|
||||
msgid "Start HTTP Server"
|
||||
msgstr "Spusti» HTTP Server"
|
||||
|
||||
msgid "HTTP Server Port"
|
||||
msgstr "Port serveru HTTP"
|
||||
|
||||
msgid "HTTP Streamtype"
|
||||
msgstr "typ prúdu HTTP"
|
||||
|
||||
msgid "Multicast Streaming Server"
|
||||
msgstr "Multicast prúdový server"
|
||||
|
||||
msgid "Start IGMP Server"
|
||||
msgstr "Spusti» IGMP Server"
|
||||
|
||||
msgid "Multicast Client Port"
|
||||
msgstr "Port klienta Multicast"
|
||||
|
||||
msgid "Multicast Streamtype"
|
||||
msgstr "Multicast typ streamu"
|
||||
291
server/recplayer.c
Normal file
291
server/recplayer.c
Normal file
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
Copyright 2004-2005 Chris Tallon
|
||||
|
||||
This file is part of VOMP.
|
||||
and adopted for streamdev to play recordings
|
||||
|
||||
VOMP is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
VOMP is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with VOMP; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "recplayer.h"
|
||||
|
||||
// for TSPLAY patch detection
|
||||
#include "vdr/device.h"
|
||||
|
||||
#define _XOPEN_SOURCE 600
|
||||
#include <fcntl.h>
|
||||
|
||||
RecPlayer::RecPlayer(cRecording* rec)
|
||||
{
|
||||
file = NULL;
|
||||
fileOpen = 0;
|
||||
lastPosition = 0;
|
||||
recording = rec;
|
||||
for(int i = 1; i < 1000; i++) segments[i] = NULL;
|
||||
|
||||
// FIXME find out max file path / name lengths
|
||||
|
||||
#if VDRVERSNUM >= 10703 || defined(TSPLAY_PATCH_VERSION)
|
||||
indexFile = new cIndexFile(recording->FileName(), false, rec->IsPesRecording());
|
||||
#else
|
||||
indexFile = new cIndexFile(recording->FileName(), false);
|
||||
#endif
|
||||
if (!indexFile) esyslog("ERROR: Streamdev: Failed to create indexfile!");
|
||||
|
||||
scan();
|
||||
}
|
||||
|
||||
void RecPlayer::scan()
|
||||
{
|
||||
if (file) fclose(file);
|
||||
totalLength = 0;
|
||||
fileOpen = 0;
|
||||
totalFrames = 0;
|
||||
|
||||
int i = 1;
|
||||
while(segments[i++]) delete segments[i];
|
||||
|
||||
char fileName[2048];
|
||||
for(i = 1; i < 1000; i++)
|
||||
{
|
||||
|
||||
#if APIVERSNUM < 10703 || !defined(TSPLAY_PATCH_VERSION)
|
||||
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
|
||||
//log->log("RecPlayer", Log::DEBUG, "FILENAME: %s", fileName);
|
||||
file = fopen(fileName, "r");
|
||||
#else
|
||||
snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), i);
|
||||
file = fopen(fileName, "r");
|
||||
if (!file) {
|
||||
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
|
||||
file = fopen(fileName, "r");
|
||||
}
|
||||
#endif
|
||||
if (!file) break;
|
||||
|
||||
segments[i] = new Segment();
|
||||
segments[i]->start = totalLength;
|
||||
fseek(file, 0, SEEK_END);
|
||||
totalLength += ftell(file);
|
||||
totalFrames = indexFile->Last();
|
||||
//log->log("RecPlayer", Log::DEBUG, "File %i found, totalLength now %llu, numFrames = %lu", i, totalLength, totalFrames);
|
||||
segments[i]->end = totalLength;
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
file = NULL;
|
||||
}
|
||||
|
||||
RecPlayer::~RecPlayer()
|
||||
{
|
||||
//log->log("RecPlayer", Log::DEBUG, "destructor");
|
||||
int i = 1;
|
||||
while(segments[i++]) delete segments[i];
|
||||
if (file) fclose(file);
|
||||
}
|
||||
|
||||
int RecPlayer::openFile(int index)
|
||||
{
|
||||
if (file) fclose(file);
|
||||
|
||||
char fileName[2048];
|
||||
|
||||
#if APIVERSNUM >= 10703 || defined(TSPLAY_PATCH_VERSION)
|
||||
snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), index);
|
||||
isyslog("openFile called for index %i string:%s", index, fileName);
|
||||
|
||||
file = fopen(fileName, "r");
|
||||
if (file)
|
||||
{
|
||||
fileOpen = index;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), index);
|
||||
isyslog("openFile called for index %i string:%s", index, fileName);
|
||||
//log->log("RecPlayer", Log::DEBUG, "openFile called for index %i string:%s", index, fileName);
|
||||
|
||||
file = fopen(fileName, "r");
|
||||
if (file)
|
||||
{
|
||||
fileOpen = index;
|
||||
return 1;
|
||||
}
|
||||
|
||||
//log->log("RecPlayer", Log::DEBUG, "file failed to open");
|
||||
fileOpen = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t RecPlayer::getLengthBytes()
|
||||
{
|
||||
return totalLength;
|
||||
}
|
||||
|
||||
uint32_t RecPlayer::getLengthFrames()
|
||||
{
|
||||
return totalFrames;
|
||||
}
|
||||
|
||||
unsigned long RecPlayer::getBlock(unsigned char* buffer, uint64_t position, unsigned long amount)
|
||||
{
|
||||
if ((amount > totalLength) || (amount > 500000))
|
||||
{
|
||||
//log->log("RecPlayer", Log::DEBUG, "Amount %lu requested and rejected", amount);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (position >= totalLength)
|
||||
{
|
||||
//log->log("RecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((position + amount) > totalLength)
|
||||
{
|
||||
//log->log("RecPlayer", Log::DEBUG, "Client asked for some data past the end of recording, adjusting amount");
|
||||
amount = totalLength - position;
|
||||
}
|
||||
|
||||
// work out what block position is in
|
||||
int segmentNumber;
|
||||
for(segmentNumber = 1; segmentNumber < 1000; segmentNumber++)
|
||||
{
|
||||
if ((position >= segments[segmentNumber]->start) && (position < segments[segmentNumber]->end)) break;
|
||||
// position is in this block
|
||||
}
|
||||
|
||||
// we could be seeking around
|
||||
if (segmentNumber != fileOpen)
|
||||
{
|
||||
if (!openFile(segmentNumber)) return 0;
|
||||
}
|
||||
|
||||
uint64_t currentPosition = position;
|
||||
uint32_t yetToGet = amount;
|
||||
uint32_t got = 0;
|
||||
uint32_t getFromThisSegment = 0;
|
||||
uint32_t filePosition;
|
||||
|
||||
while(got < amount)
|
||||
{
|
||||
if (got)
|
||||
{
|
||||
// if(got) then we have already got some and we are back around
|
||||
// advance the file pointer to the next file
|
||||
if (!openFile(++segmentNumber)) return 0;
|
||||
}
|
||||
|
||||
// is the request completely in this block?
|
||||
if ((currentPosition + yetToGet) <= segments[segmentNumber]->end)
|
||||
getFromThisSegment = yetToGet;
|
||||
else
|
||||
getFromThisSegment = segments[segmentNumber]->end - currentPosition;
|
||||
|
||||
filePosition = currentPosition - segments[segmentNumber]->start;
|
||||
fseek(file, filePosition, SEEK_SET);
|
||||
if (fread(&buffer[got], getFromThisSegment, 1, file) != 1) return 0; // umm, big problem.
|
||||
|
||||
// Tell linux not to bother keeping the data in the FS cache
|
||||
posix_fadvise(file->_fileno, filePosition, getFromThisSegment, POSIX_FADV_DONTNEED);
|
||||
|
||||
got += getFromThisSegment;
|
||||
currentPosition += getFromThisSegment;
|
||||
yetToGet -= getFromThisSegment;
|
||||
}
|
||||
|
||||
lastPosition = position;
|
||||
return got;
|
||||
}
|
||||
|
||||
uint64_t RecPlayer::getLastPosition()
|
||||
{
|
||||
return lastPosition;
|
||||
}
|
||||
|
||||
cRecording* RecPlayer::getCurrentRecording()
|
||||
{
|
||||
return recording;
|
||||
}
|
||||
|
||||
uint64_t RecPlayer::positionFromFrameNumber(uint32_t frameNumber)
|
||||
{
|
||||
if (!indexFile) return 0;
|
||||
|
||||
#if VDRVERSNUM >= 10703 || defined(TSPLAY_PATCH_VERSION)
|
||||
uint16_t retFileNumber;
|
||||
off_t retFileOffset;
|
||||
#else
|
||||
uchar retFileNumber;
|
||||
int retFileOffset;
|
||||
#endif
|
||||
|
||||
if (!indexFile->Get((int)frameNumber, &retFileNumber, &retFileOffset))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// log->log("RecPlayer", Log::DEBUG, "FN: %u FO: %i", retFileNumber, retFileOffset);
|
||||
if (!segments[retFileNumber]) return 0;
|
||||
uint64_t position = segments[retFileNumber]->start + retFileOffset;
|
||||
// log->log("RecPlayer", Log::DEBUG, "Pos: %llu", position);
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
uint32_t RecPlayer::frameNumberFromPosition(uint64_t position)
|
||||
{
|
||||
if (!indexFile) return 0;
|
||||
|
||||
if (position >= totalLength)
|
||||
{
|
||||
//log->log("RecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t segmentNumber;
|
||||
for(segmentNumber = 1; segmentNumber < 255; segmentNumber++)
|
||||
{
|
||||
if ((position >= segments[segmentNumber]->start) && (position < segments[segmentNumber]->end)) break;
|
||||
// position is in this block
|
||||
}
|
||||
uint32_t askposition = position - segments[segmentNumber]->start;
|
||||
return indexFile->Get((int)segmentNumber, askposition);
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool RecPlayer::getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength)
|
||||
{
|
||||
// 0 = backwards
|
||||
// 1 = forwards
|
||||
|
||||
if (!indexFile) return false;
|
||||
|
||||
int iframeLength;
|
||||
int indexReturnFrameNumber;
|
||||
|
||||
indexReturnFrameNumber = (uint32_t)indexFile->GetNextIFrame(frameNumber, (direction==1 ? true : false), NULL, NULL, &iframeLength);
|
||||
//log->log("RecPlayer", Log::DEBUG, "GNIF input framenumber:%lu, direction=%lu, output:framenumber=%i, framelength=%i", frameNumber, direction, indexReturnFrameNumber, iframeLength);
|
||||
|
||||
if (indexReturnFrameNumber == -1) return false;
|
||||
|
||||
*rfilePosition = positionFromFrameNumber(indexReturnFrameNumber);
|
||||
*rframeNumber = (uint32_t)indexReturnFrameNumber;
|
||||
*rframeLength = (uint32_t)iframeLength;
|
||||
|
||||
return true;
|
||||
}
|
||||
63
server/recplayer.h
Normal file
63
server/recplayer.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright 2004-2005 Chris Tallon
|
||||
|
||||
This file is part of VOMP.
|
||||
|
||||
VOMP is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
VOMP is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with VOMP; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef RECPLAYER_H
|
||||
#define RECPLAYER_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <vdr/recording.h>
|
||||
|
||||
#include "server/streamer.h"
|
||||
|
||||
class Segment
|
||||
{
|
||||
public:
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
};
|
||||
|
||||
class RecPlayer
|
||||
{
|
||||
public:
|
||||
RecPlayer(cRecording* rec);
|
||||
~RecPlayer();
|
||||
uint64_t getLengthBytes();
|
||||
uint32_t getLengthFrames();
|
||||
unsigned long getBlock(unsigned char* buffer, uint64_t position, unsigned long amount);
|
||||
int openFile(int index);
|
||||
uint64_t getLastPosition();
|
||||
cRecording* getCurrentRecording();
|
||||
void scan();
|
||||
uint64_t positionFromFrameNumber(uint32_t frameNumber);
|
||||
uint32_t frameNumberFromPosition(uint64_t position);
|
||||
bool getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength);
|
||||
|
||||
private:
|
||||
cRecording* recording;
|
||||
cIndexFile* indexFile;
|
||||
FILE* file;
|
||||
int fileOpen;
|
||||
Segment* segments[1000];
|
||||
uint64_t totalLength;
|
||||
uint64_t lastPosition;
|
||||
uint32_t totalFrames;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,10 +1,11 @@
|
||||
/*
|
||||
* $Id: server.c,v 1.5 2007/04/02 10:32:34 schmirl Exp $
|
||||
* $Id: server.c,v 1.10 2009/02/13 10:39:22 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include "server/server.h"
|
||||
#include "server/componentVTP.h"
|
||||
#include "server/componentHTTP.h"
|
||||
#include "server/componentIGMP.h"
|
||||
#include "server/setup.h"
|
||||
|
||||
#include <vdr/tools.h>
|
||||
@@ -13,14 +14,15 @@
|
||||
#include <errno.h>
|
||||
|
||||
cSVDRPhosts StreamdevHosts;
|
||||
char *opt_auth = NULL;
|
||||
char *opt_remux = NULL;
|
||||
|
||||
cStreamdevServer *cStreamdevServer::m_Instance = NULL;
|
||||
cList<cServerComponent> cStreamdevServer::m_Servers;
|
||||
cList<cServerConnection> cStreamdevServer::m_Clients;
|
||||
|
||||
cStreamdevServer::cStreamdevServer(void):
|
||||
cThread("streamdev server"),
|
||||
m_Active(false)
|
||||
cThread("streamdev server")
|
||||
{
|
||||
Start();
|
||||
}
|
||||
@@ -35,6 +37,12 @@ void cStreamdevServer::Initialize(void)
|
||||
if (m_Instance == NULL) {
|
||||
if (StreamdevServerSetup.StartVTPServer) Register(new cComponentVTP);
|
||||
if (StreamdevServerSetup.StartHTTPServer) Register(new cComponentHTTP);
|
||||
if (StreamdevServerSetup.StartIGMPServer) {
|
||||
if (strcmp(StreamdevServerSetup.IGMPBindIP, "0.0.0.0") == 0)
|
||||
esyslog("streamdev-server: Not starting IGMP. IGMP must be bound to a local IP");
|
||||
else
|
||||
Register(new cComponentIGMP);
|
||||
}
|
||||
|
||||
m_Instance = new cStreamdevServer;
|
||||
}
|
||||
@@ -47,10 +55,8 @@ void cStreamdevServer::Destruct(void)
|
||||
|
||||
void cStreamdevServer::Stop(void)
|
||||
{
|
||||
if (m_Active) {
|
||||
m_Active = false;
|
||||
if (Running())
|
||||
Cancel(3);
|
||||
}
|
||||
}
|
||||
|
||||
void cStreamdevServer::Register(cServerComponent *Server)
|
||||
@@ -60,8 +66,6 @@ void cStreamdevServer::Register(cServerComponent *Server)
|
||||
|
||||
void cStreamdevServer::Action(void)
|
||||
{
|
||||
m_Active = true;
|
||||
|
||||
/* Initialize Server components, deleting those that failed */
|
||||
for (cServerComponent *c = m_Servers.First(); c;) {
|
||||
cServerComponent *next = m_Servers.Next(c);
|
||||
@@ -72,11 +76,11 @@ void cStreamdevServer::Action(void)
|
||||
|
||||
if (m_Servers.Count() == 0) {
|
||||
esyslog("ERROR: no streamdev server activated, exiting");
|
||||
m_Active = false;
|
||||
Cancel(-1);
|
||||
}
|
||||
|
||||
cTBSelect select;
|
||||
while (m_Active) {
|
||||
while (Running()) {
|
||||
select.Clear();
|
||||
|
||||
/* Ask all Server components to register to the selector */
|
||||
@@ -102,9 +106,9 @@ void cStreamdevServer::Action(void)
|
||||
sel = 0;
|
||||
}
|
||||
}
|
||||
} while (sel < 0 && errno == ETIMEDOUT && m_Active);
|
||||
} while (sel < 0 && errno == ETIMEDOUT && Running());
|
||||
|
||||
if (!m_Active)
|
||||
if (!Running())
|
||||
break;
|
||||
if (sel < 0) {
|
||||
esyslog("fatal error, server exiting: %m");
|
||||
@@ -115,13 +119,17 @@ void cStreamdevServer::Action(void)
|
||||
for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c)){
|
||||
if (sel && select.CanRead(c->Socket())) {
|
||||
cServerConnection *client = c->Accept();
|
||||
if (!client)
|
||||
continue;
|
||||
Lock();
|
||||
m_Clients.Add(client);
|
||||
Unlock();
|
||||
|
||||
if (m_Clients.Count() > StreamdevServerSetup.MaxClients) {
|
||||
esyslog("streamdev: too many clients, rejecting %s:%d",
|
||||
client->RemoteIp().c_str(), client->RemotePort());
|
||||
client->Reject();
|
||||
} else if (!StreamdevHosts.Acceptable(client->RemoteIpAddr())) {
|
||||
} else if (!client->CanAuthenticate() && !StreamdevHosts.Acceptable(client->RemoteIpAddr())) {
|
||||
esyslog("streamdev: client %s:%d not allowed to connect",
|
||||
client->RemoteIp().c_str(), client->RemotePort());
|
||||
client->Reject();
|
||||
@@ -144,26 +152,33 @@ 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();
|
||||
}
|
||||
s = next;
|
||||
}
|
||||
}
|
||||
|
||||
Lock();
|
||||
while (m_Clients.Count() > 0) {
|
||||
cServerConnection *s = m_Clients.First();
|
||||
s->Close();
|
||||
m_Clients.Del(s);
|
||||
}
|
||||
Unlock();
|
||||
|
||||
while (m_Servers.Count() > 0) {
|
||||
cServerComponent *c = m_Servers.First();
|
||||
c->Destruct();
|
||||
m_Servers.Del(c);
|
||||
}
|
||||
|
||||
m_Active = false;
|
||||
}
|
||||
|
||||
const cList<cServerConnection>& cStreamdevServer::Clients(cThreadLock& Lock)
|
||||
{
|
||||
Lock.Lock(m_Instance);
|
||||
return m_Clients;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: server.h,v 1.3 2008/04/07 14:50:33 schmirl Exp $
|
||||
* $Id: server.h,v 1.6 2008/10/22 11:59:32 schmirl Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_SERVER_H
|
||||
@@ -10,13 +10,14 @@
|
||||
#include "server/component.h"
|
||||
#include "server/connection.h"
|
||||
|
||||
#define EXTERNREMUXPATH (*AddDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N), "externremux.sh"))
|
||||
#define DEFAULT_EXTERNREMUX (*AddDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N), "externremux.sh"))
|
||||
#define STREAMDEVHOSTSPATH (*AddDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N), "streamdevhosts.conf"))
|
||||
|
||||
extern char *opt_auth;
|
||||
extern char *opt_remux;
|
||||
|
||||
class cStreamdevServer: public cThread {
|
||||
private:
|
||||
bool m_Active;
|
||||
|
||||
static cStreamdevServer *m_Instance;
|
||||
static cList<cServerComponent> m_Servers;
|
||||
static cList<cServerConnection> m_Clients;
|
||||
@@ -35,6 +36,8 @@ public:
|
||||
static void Initialize(void);
|
||||
static void Destruct(void);
|
||||
static bool Active(void);
|
||||
|
||||
static const cList<cServerConnection>& Clients(cThreadLock& Lock);
|
||||
};
|
||||
|
||||
inline bool cStreamdevServer::Active(void)
|
||||
|
||||
104
server/setup.c
104
server/setup.c
@@ -1,12 +1,11 @@
|
||||
/*
|
||||
* $Id: setup.c,v 1.3 2008/04/07 14:50:33 schmirl Exp $
|
||||
* $Id: setup.c,v 1.10 2010/07/19 13:49:31 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include <vdr/menuitems.h>
|
||||
|
||||
#include "server/setup.h"
|
||||
#include "server/server.h"
|
||||
#include "i18n.h"
|
||||
|
||||
cStreamdevServerSetup StreamdevServerSetup;
|
||||
|
||||
@@ -16,11 +15,15 @@ cStreamdevServerSetup::cStreamdevServerSetup(void) {
|
||||
VTPServerPort = 2004;
|
||||
StartHTTPServer = true;
|
||||
HTTPServerPort = 3000;
|
||||
HTTPStreamType = stPES;
|
||||
HTTPStreamType = stTS;
|
||||
StartIGMPServer = false;
|
||||
IGMPClientPort = 1234;
|
||||
IGMPStreamType = stTS;
|
||||
SuspendMode = smAlways;
|
||||
AllowSuspend = false;
|
||||
strcpy(VTPBindIP, "0.0.0.0");
|
||||
strcpy(HTTPBindIP, "0.0.0.0");
|
||||
strcpy(IGMPBindIP, "0.0.0.0");
|
||||
}
|
||||
|
||||
bool cStreamdevServerSetup::SetupParse(const char *Name, const char *Value) {
|
||||
@@ -32,37 +35,82 @@ bool cStreamdevServerSetup::SetupParse(const char *Name, const char *Value) {
|
||||
else if (strcmp(Name, "HTTPServerPort") == 0) HTTPServerPort = atoi(Value);
|
||||
else if (strcmp(Name, "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, "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;
|
||||
}
|
||||
|
||||
const char* cStreamdevServerMenuSetupPage::StreamTypes[st_Count - 1] = {
|
||||
"TS",
|
||||
"PES",
|
||||
"PS",
|
||||
"ES",
|
||||
"EXT"
|
||||
};
|
||||
|
||||
const char* cStreamdevServerMenuSetupPage::SuspendModes[sm_Count] = {
|
||||
trNOOP("Offer suspend mode"),
|
||||
trNOOP("Always suspended"),
|
||||
trNOOP("Never suspended")
|
||||
};
|
||||
|
||||
cStreamdevServerMenuSetupPage::cStreamdevServerMenuSetupPage(void) {
|
||||
m_NewSetup = StreamdevServerSetup;
|
||||
|
||||
AddCategory (tr("Common Settings"));
|
||||
AddRangeEdit(tr("Maximum Number of Clients"), m_NewSetup.MaxClients, 0, 100);
|
||||
AddSuspEdit (tr("Suspend behaviour"), m_NewSetup.SuspendMode);
|
||||
AddBoolEdit (tr("Client may suspend"), m_NewSetup.AllowSuspend);
|
||||
|
||||
AddCategory (tr("VDR-to-VDR Server"));
|
||||
AddBoolEdit (tr("Start VDR-to-VDR Server"), m_NewSetup.StartVTPServer);
|
||||
AddShortEdit(tr("VDR-to-VDR Server Port"), m_NewSetup.VTPServerPort);
|
||||
AddIpEdit (tr("Bind to IP"), m_NewSetup.VTPBindIP);
|
||||
|
||||
AddCategory (tr("HTTP Server"));
|
||||
AddBoolEdit (tr("Start HTTP Server"), m_NewSetup.StartHTTPServer);
|
||||
AddShortEdit(tr("HTTP Server Port"), m_NewSetup.HTTPServerPort);
|
||||
AddTypeEdit (tr("HTTP Streamtype"), m_NewSetup.HTTPStreamType);
|
||||
AddIpEdit (tr("Bind to IP"), m_NewSetup.HTTPBindIP);
|
||||
|
||||
SetCurrent(Get(1));
|
||||
Set();
|
||||
}
|
||||
|
||||
cStreamdevServerMenuSetupPage::~cStreamdevServerMenuSetupPage() {
|
||||
}
|
||||
|
||||
void cStreamdevServerMenuSetupPage::Set(void) {
|
||||
static const char* modes[sm_Count];
|
||||
for (int i = 0; i < sm_Count; i++)
|
||||
modes[i] = tr(SuspendModes[i]);
|
||||
|
||||
int current = Current();
|
||||
Clear();
|
||||
AddCategory (tr("Common Settings"));
|
||||
Add(new cMenuEditIntItem (tr("Maximum Number of Clients"), &m_NewSetup.MaxClients, 0, 100));
|
||||
|
||||
Add(new cMenuEditStraItem(tr("Suspend behaviour"), &m_NewSetup.SuspendMode, sm_Count, modes));
|
||||
if (m_NewSetup.SuspendMode == smOffer)
|
||||
Add(new cMenuEditBoolItem(tr("Client may suspend"), &m_NewSetup.AllowSuspend));
|
||||
|
||||
AddCategory (tr("VDR-to-VDR Server"));
|
||||
Add(new cMenuEditBoolItem(tr("Start VDR-to-VDR Server"), &m_NewSetup.StartVTPServer));
|
||||
Add(new cMenuEditIntItem (tr("VDR-to-VDR Server Port"), &m_NewSetup.VTPServerPort, 0, 65535));
|
||||
Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.VTPBindIP));
|
||||
|
||||
AddCategory (tr("HTTP Server"));
|
||||
Add(new cMenuEditBoolItem(tr("Start HTTP Server"), &m_NewSetup.StartHTTPServer));
|
||||
Add(new cMenuEditIntItem (tr("HTTP Server Port"), &m_NewSetup.HTTPServerPort, 0, 65535));
|
||||
Add(new cMenuEditStraItem(tr("HTTP Streamtype"), &m_NewSetup.HTTPStreamType, st_Count - 1, StreamTypes));
|
||||
Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.HTTPBindIP));
|
||||
AddCategory (tr("Multicast Streaming Server"));
|
||||
Add(new cMenuEditBoolItem(tr("Start IGMP Server"), &m_NewSetup.StartIGMPServer));
|
||||
Add(new cMenuEditIntItem (tr("Multicast Client Port"), &m_NewSetup.IGMPClientPort, 0, 65535));
|
||||
Add(new cMenuEditStraItem(tr("Multicast Streamtype"), &m_NewSetup.IGMPStreamType, st_Count - 1, StreamTypes));
|
||||
Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.IGMPBindIP));
|
||||
SetCurrent(Get(current));
|
||||
Display();
|
||||
}
|
||||
|
||||
void cStreamdevServerMenuSetupPage::AddCategory(const char *Title) {
|
||||
|
||||
cString str = cString::sprintf("--- %s -------------------------------------------------"
|
||||
"---------------", Title );
|
||||
|
||||
cOsdItem *item = new cOsdItem(*str);
|
||||
item->SetSelectable(false);
|
||||
Add(item);
|
||||
}
|
||||
|
||||
void cStreamdevServerMenuSetupPage::Store(void) {
|
||||
bool restart = false;
|
||||
if (m_NewSetup.StartVTPServer != StreamdevServerSetup.StartVTPServer
|
||||
@@ -70,7 +118,10 @@ void cStreamdevServerMenuSetupPage::Store(void) {
|
||||
|| strcmp(m_NewSetup.VTPBindIP, StreamdevServerSetup.VTPBindIP) != 0
|
||||
|| m_NewSetup.StartHTTPServer != StreamdevServerSetup.StartHTTPServer
|
||||
|| m_NewSetup.HTTPServerPort != StreamdevServerSetup.HTTPServerPort
|
||||
|| strcmp(m_NewSetup.HTTPBindIP, StreamdevServerSetup.HTTPBindIP) != 0) {
|
||||
|| strcmp(m_NewSetup.HTTPBindIP, StreamdevServerSetup.HTTPBindIP) != 0
|
||||
|| m_NewSetup.StartIGMPServer != StreamdevServerSetup.StartIGMPServer
|
||||
|| m_NewSetup.IGMPClientPort != StreamdevServerSetup.IGMPClientPort
|
||||
|| strcmp(m_NewSetup.IGMPBindIP, StreamdevServerSetup.IGMPBindIP) != 0) {
|
||||
restart = true;
|
||||
cStreamdevServer::Destruct();
|
||||
}
|
||||
@@ -83,6 +134,10 @@ void cStreamdevServerMenuSetupPage::Store(void) {
|
||||
SetupStore("HTTPServerPort", m_NewSetup.HTTPServerPort);
|
||||
SetupStore("HTTPStreamType", m_NewSetup.HTTPStreamType);
|
||||
SetupStore("HTTPBindIP", m_NewSetup.HTTPBindIP);
|
||||
SetupStore("StartIGMPServer", m_NewSetup.StartIGMPServer);
|
||||
SetupStore("IGMPClientPort", m_NewSetup.IGMPClientPort);
|
||||
SetupStore("IGMPStreamType", m_NewSetup.IGMPStreamType);
|
||||
SetupStore("IGMPBindIP", m_NewSetup.IGMPBindIP);
|
||||
SetupStore("SuspendMode", m_NewSetup.SuspendMode);
|
||||
SetupStore("AllowSuspend", m_NewSetup.AllowSuspend);
|
||||
|
||||
@@ -92,3 +147,10 @@ void cStreamdevServerMenuSetupPage::Store(void) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: setup.h,v 1.1 2004/12/30 22:44:21 lordjaxom Exp $
|
||||
* $Id: setup.h,v 1.4 2010/07/19 13:49:31 schmirl Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_SETUPSERVER_H
|
||||
@@ -20,18 +20,27 @@ struct cStreamdevServerSetup {
|
||||
int HTTPServerPort;
|
||||
int HTTPStreamType;
|
||||
char HTTPBindIP[20];
|
||||
int StartIGMPServer;
|
||||
int IGMPClientPort;
|
||||
int IGMPStreamType;
|
||||
char IGMPBindIP[20];
|
||||
int SuspendMode;
|
||||
int AllowSuspend;
|
||||
};
|
||||
|
||||
extern cStreamdevServerSetup StreamdevServerSetup;
|
||||
|
||||
class cStreamdevServerMenuSetupPage: public cStreamdevMenuSetupPage {
|
||||
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);
|
||||
|
||||
@@ -3,22 +3,21 @@
|
||||
*
|
||||
* See the README file for copyright information and how to reach the author.
|
||||
*
|
||||
* $Id: streamdev-server.c,v 1.7 2008/04/07 14:27:27 schmirl Exp $
|
||||
* $Id: streamdev-server.c,v 1.2 2010/07/19 13:49:32 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
#include <vdr/tools.h>
|
||||
#include "streamdev-server.h"
|
||||
#include "server/menu.h"
|
||||
#include "server/setup.h"
|
||||
#include "server/server.h"
|
||||
#include "server/suspend.h"
|
||||
#include "remux/extern.h"
|
||||
#include "i18n.h"
|
||||
|
||||
#if VDRVERSNUM < 10400
|
||||
#error "VDR-1.4.0 or greater is required"
|
||||
#if !defined(APIVERSNUM) || APIVERSNUM < 10516
|
||||
#error "VDR-1.5.16 API version or greater is required!"
|
||||
#endif
|
||||
|
||||
const char *cPluginStreamdevServer::DESCRIPTION = "VDR Streaming Server";
|
||||
const char *cPluginStreamdevServer::DESCRIPTION = trNOOP("VDR Streaming Server");
|
||||
|
||||
cPluginStreamdevServer::cPluginStreamdevServer(void)
|
||||
{
|
||||
@@ -26,6 +25,8 @@ cPluginStreamdevServer::cPluginStreamdevServer(void)
|
||||
|
||||
cPluginStreamdevServer::~cPluginStreamdevServer()
|
||||
{
|
||||
free(opt_auth);
|
||||
free(opt_remux);
|
||||
}
|
||||
|
||||
const char *cPluginStreamdevServer::Description(void)
|
||||
@@ -36,22 +37,39 @@ const char *cPluginStreamdevServer::Description(void)
|
||||
const char *cPluginStreamdevServer::CommandLineHelp(void)
|
||||
{
|
||||
// return a string that describes all known command line options.
|
||||
return " -r <CMD>, --remux=<CMD> Define an external command for remuxing.\n";
|
||||
return
|
||||
" -a <LOGIN:PASSWORD>, --auth=<LOGIN:PASSWORD> Credentials for HTTP authentication.\n"
|
||||
" -r <CMD>, --remux=<CMD> Define an external command for remuxing.\n"
|
||||
;
|
||||
}
|
||||
|
||||
bool cPluginStreamdevServer::ProcessArgs(int argc, char *argv[])
|
||||
{
|
||||
// implement command line argument processing here if applicable.
|
||||
static const struct option long_options[] = {
|
||||
{ "auth", required_argument, NULL, 'a' },
|
||||
{ "remux", required_argument, NULL, 'r' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
int c;
|
||||
while((c = getopt_long(argc, argv, "r:", long_options, NULL)) != -1) {
|
||||
while((c = getopt_long(argc, argv, "a:r:", long_options, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'a':
|
||||
{
|
||||
if (opt_auth)
|
||||
free(opt_auth);
|
||||
int l = strlen(optarg);
|
||||
cBase64Encoder Base64((uchar*) optarg, l, l * 4 / 3 + 3);
|
||||
const char *s = Base64.NextLine();
|
||||
if (s)
|
||||
opt_auth = strdup(s);
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
g_ExternRemux = optarg;
|
||||
if (opt_remux)
|
||||
free(opt_remux);
|
||||
opt_remux = strdup(optarg);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
@@ -62,9 +80,7 @@ bool cPluginStreamdevServer::ProcessArgs(int argc, char *argv[])
|
||||
|
||||
bool cPluginStreamdevServer::Start(void)
|
||||
{
|
||||
i18n_name = Name();
|
||||
RegisterI18n(Phrases);
|
||||
|
||||
I18nRegister(PLUGIN_NAME_I18N);
|
||||
if (!StreamdevHosts.Load(STREAMDEVHOSTSPATH, true, true)) {
|
||||
esyslog("streamdev-server: error while loading %s", STREAMDEVHOSTSPATH);
|
||||
fprintf(stderr, "streamdev-server: error while loading %s\n", STREAMDEVHOSTSPATH);
|
||||
@@ -77,6 +93,8 @@ bool cPluginStreamdevServer::Start(void)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!opt_remux)
|
||||
opt_remux = strdup(DEFAULT_EXTERNREMUX);
|
||||
|
||||
cStreamdevServer::Initialize();
|
||||
|
||||
@@ -101,15 +119,20 @@ cString cPluginStreamdevServer::Active(void)
|
||||
|
||||
const char *cPluginStreamdevServer::MainMenuEntry(void)
|
||||
{
|
||||
if (StreamdevServerSetup.SuspendMode == smOffer && !cSuspendCtl::IsActive())
|
||||
return tr("Suspend Live TV");
|
||||
return NULL;
|
||||
return tr("Streamdev Connections");
|
||||
}
|
||||
|
||||
cOsdObject *cPluginStreamdevServer::MainMenuAction(void)
|
||||
{
|
||||
cControl::Launch(new cSuspendCtl);
|
||||
return NULL;
|
||||
return new cStreamdevServerMenu();
|
||||
}
|
||||
|
||||
void cPluginStreamdevServer::MainThreadHook(void)
|
||||
{
|
||||
cThreadLock lock;
|
||||
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
||||
for (cServerConnection *s = clients.First(); s; s = clients.Next(s))
|
||||
s->MainThreadHook();
|
||||
}
|
||||
|
||||
cMenuSetupPage *cPluginStreamdevServer::SetupMenu(void)
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: streamdev-server.h,v 1.4 2007/02/19 12:08:16 schmirl Exp $
|
||||
* $Id: streamdev-server.h,v 1.2 2010/07/19 13:49:32 schmirl Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEVSERVER_H
|
||||
@@ -26,6 +26,7 @@ public:
|
||||
virtual cString Active(void);
|
||||
virtual const char *MainMenuEntry(void);
|
||||
virtual cOsdObject *MainMenuAction(void);
|
||||
virtual void MainThreadHook(void);
|
||||
virtual cMenuSetupPage *SetupMenu(void);
|
||||
virtual bool SetupParse(const char *Name, const char *Value);
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: streamer.c,v 1.16 2007/09/21 11:45:53 schmirl Exp $
|
||||
* $Id: streamer.c,v 1.21 2010/07/30 10:01:11 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include <vdr/ringbuffer.h>
|
||||
@@ -14,22 +14,28 @@
|
||||
#include "tools/select.h"
|
||||
#include "common.h"
|
||||
|
||||
// --- cStreamdevBuffer -------------------------------------------------------
|
||||
|
||||
cStreamdevBuffer::cStreamdevBuffer(int Size, int Margin, bool Statistics, const char *Description):
|
||||
cRingBufferLinear(Size, Margin, Statistics, Description)
|
||||
{
|
||||
}
|
||||
|
||||
// --- cStreamdevWriter -------------------------------------------------------
|
||||
|
||||
cStreamdevWriter::cStreamdevWriter(cTBSocket *Socket,
|
||||
cStreamdevStreamer *Streamer):
|
||||
cThread("streamdev-writer"),
|
||||
m_Streamer(Streamer),
|
||||
m_Socket(Socket),
|
||||
m_Active(false)
|
||||
m_Socket(Socket)
|
||||
{
|
||||
}
|
||||
|
||||
cStreamdevWriter::~cStreamdevWriter()
|
||||
{
|
||||
Dprintf("destructing writer\n");
|
||||
m_Active = false;
|
||||
Cancel(3);
|
||||
if (Running())
|
||||
Cancel(3);
|
||||
}
|
||||
|
||||
void cStreamdevWriter::Action(void)
|
||||
@@ -39,57 +45,78 @@ void cStreamdevWriter::Action(void)
|
||||
int max = 0;
|
||||
uchar *block = NULL;
|
||||
int count, offset = 0;
|
||||
m_Active = true;
|
||||
int timeout = 0;
|
||||
|
||||
#if APIVERSNUM >= 10705
|
||||
SetPriority(-3);
|
||||
#endif
|
||||
sel.Clear();
|
||||
sel.Add(*m_Socket, true);
|
||||
while (m_Active) {
|
||||
while (Running()) {
|
||||
if (block == NULL) {
|
||||
block = m_Streamer->Get(count);
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
if (block != NULL) {
|
||||
if (sel.Select(15000) == -1) {
|
||||
if (sel.Select(600) == -1) {
|
||||
if (errno == ETIMEDOUT && timeout++ < 20)
|
||||
continue; // still Running()?
|
||||
esyslog("ERROR: streamdev-server: couldn't send data: %m");
|
||||
break;
|
||||
}
|
||||
timeout = 0;
|
||||
|
||||
if (sel.CanWrite(*m_Socket)) {
|
||||
int written;
|
||||
if ((written = m_Socket->Write(block + offset, count)) == -1) {
|
||||
esyslog("ERROR: streamdev-server: couldn't send data: %m");
|
||||
int pkgsize = count;
|
||||
// SOCK_DGRAM indicates multicast
|
||||
if (m_Socket->Type() == SOCK_DGRAM) {
|
||||
// don't fragment multicast packets
|
||||
// max. payload on standard local ethernet is 1416 to 1456 bytes
|
||||
// and some STBs expect complete TS packets
|
||||
// so let's always limit to 7 * TS_SIZE = 1316
|
||||
if (pkgsize > 7 * TS_SIZE)
|
||||
pkgsize = 7 * TS_SIZE;
|
||||
else
|
||||
pkgsize -= pkgsize % TS_SIZE;
|
||||
}
|
||||
if ((written = m_Socket->Write(block + offset, pkgsize)) == -1) {
|
||||
esyslog("ERROR: streamdev-server: couldn't send %d bytes: %m", pkgsize);
|
||||
break;
|
||||
}
|
||||
|
||||
// statistics
|
||||
if (count > max)
|
||||
max = count;
|
||||
|
||||
offset += written;
|
||||
count -= written;
|
||||
if (count == 0) {
|
||||
|
||||
// less than one TS packet left:
|
||||
// delete what we've written so far and get next chunk
|
||||
if (count < TS_SIZE) {
|
||||
m_Streamer->Del(offset);
|
||||
block = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_Active = false;
|
||||
Dprintf("Max. Transmit Blocksize was: %d\n", max);
|
||||
}
|
||||
|
||||
// --- cStreamdevStreamer -----------------------------------------------------
|
||||
|
||||
cStreamdevStreamer::cStreamdevStreamer(const char *Name):
|
||||
cStreamdevStreamer::cStreamdevStreamer(const char *Name, const cServerConnection *Connection):
|
||||
cThread(Name),
|
||||
m_Active(false),
|
||||
m_Running(false),
|
||||
m_Connection(Connection),
|
||||
m_Writer(NULL),
|
||||
m_RingBuffer(new cRingBufferLinear(STREAMERBUFSIZE, TS_SIZE * 2,
|
||||
m_RingBuffer(new cStreamdevBuffer(STREAMERBUFSIZE, TS_SIZE * 2,
|
||||
true, "streamdev-streamer")),
|
||||
m_SendBuffer(new cRingBufferLinear(WRITERBUFSIZE, TS_SIZE * 2))
|
||||
m_SendBuffer(new cStreamdevBuffer(WRITERBUFSIZE, TS_SIZE * 2))
|
||||
{
|
||||
m_RingBuffer->SetTimeouts(0, 100);
|
||||
m_SendBuffer->SetTimeouts(0, 100);
|
||||
m_SendBuffer->SetTimeouts(100, 100);
|
||||
}
|
||||
|
||||
cStreamdevStreamer::~cStreamdevStreamer()
|
||||
@@ -103,13 +130,12 @@ void cStreamdevStreamer::Start(cTBSocket *Socket)
|
||||
{
|
||||
Dprintf("start streamer\n");
|
||||
m_Writer = new cStreamdevWriter(Socket, this);
|
||||
m_Running = true;
|
||||
Attach();
|
||||
}
|
||||
|
||||
void cStreamdevStreamer::Activate(bool On)
|
||||
{
|
||||
if (On && !m_Active) {
|
||||
if (On && !Active()) {
|
||||
Dprintf("activate streamer\n");
|
||||
m_Writer->Start();
|
||||
cThread::Start();
|
||||
@@ -118,22 +144,22 @@ void cStreamdevStreamer::Activate(bool On)
|
||||
|
||||
void cStreamdevStreamer::Stop(void)
|
||||
{
|
||||
if (m_Active) {
|
||||
if (Running()) {
|
||||
Dprintf("stopping streamer\n");
|
||||
m_Active = false;
|
||||
Cancel(3);
|
||||
}
|
||||
if (m_Running) {
|
||||
if (m_Writer) {
|
||||
Detach();
|
||||
m_Running = false;
|
||||
DELETENULL(m_Writer);
|
||||
}
|
||||
}
|
||||
|
||||
void cStreamdevStreamer::Action(void)
|
||||
{
|
||||
m_Active = true;
|
||||
while (m_Active) {
|
||||
#if APIVERSNUM >= 10705
|
||||
SetPriority(-3);
|
||||
#endif
|
||||
while (Running()) {
|
||||
int got;
|
||||
uchar *block = m_RingBuffer->Get(got);
|
||||
|
||||
@@ -141,8 +167,6 @@ void cStreamdevStreamer::Action(void)
|
||||
int count = Put(block, got);
|
||||
if (count)
|
||||
m_RingBuffer->Del(count);
|
||||
else
|
||||
cCondWait::SleepMs(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: streamer.h,v 1.8 2007/04/02 10:32:34 schmirl Exp $
|
||||
* $Id: streamer.h,v 1.12 2010/07/19 13:49:32 schmirl Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_STREAMER_H
|
||||
@@ -11,9 +11,40 @@
|
||||
|
||||
class cTBSocket;
|
||||
class cStreamdevStreamer;
|
||||
class cServerConnection;
|
||||
|
||||
#define STREAMERBUFSIZE MEGABYTE(4)
|
||||
#define WRITERBUFSIZE KILOBYTE(256)
|
||||
#ifndef TS_SIZE
|
||||
#define TS_SIZE 188
|
||||
#endif
|
||||
|
||||
#define STREAMERBUFSIZE (20000 * TS_SIZE)
|
||||
#define WRITERBUFSIZE (20000 * TS_SIZE)
|
||||
|
||||
// --- cStreamdevBuffer -------------------------------------------------------
|
||||
|
||||
class cStreamdevBuffer: public cRingBufferLinear {
|
||||
public:
|
||||
// make public
|
||||
void WaitForPut(void) { cRingBuffer::WaitForPut(); }
|
||||
// Always write complete TS packets
|
||||
// (assumes Count is a multiple of TS_SIZE)
|
||||
int PutTS(const uchar *Data, int Count);
|
||||
cStreamdevBuffer(int Size, int Margin = 0, bool Statistics = false, const char *Description = NULL);
|
||||
};
|
||||
|
||||
inline int cStreamdevBuffer::PutTS(const uchar *Data, int Count)
|
||||
{
|
||||
int free = Free();
|
||||
if (free < Count)
|
||||
Count = free;
|
||||
|
||||
Count -= Count % TS_SIZE;
|
||||
if (Count)
|
||||
Count = Put(Data, Count);
|
||||
else
|
||||
WaitForPut();
|
||||
return Count;
|
||||
}
|
||||
|
||||
// --- cStreamdevWriter -------------------------------------------------------
|
||||
|
||||
@@ -21,7 +52,6 @@ class cStreamdevWriter: public cThread {
|
||||
private:
|
||||
cStreamdevStreamer *m_Streamer;
|
||||
cTBSocket *m_Socket;
|
||||
bool m_Active;
|
||||
|
||||
protected:
|
||||
virtual void Action(void);
|
||||
@@ -29,38 +59,37 @@ protected:
|
||||
public:
|
||||
cStreamdevWriter(cTBSocket *Socket, cStreamdevStreamer *Streamer);
|
||||
virtual ~cStreamdevWriter();
|
||||
|
||||
bool IsActive(void) const { return m_Active; }
|
||||
};
|
||||
|
||||
// --- cStreamdevStreamer -----------------------------------------------------
|
||||
|
||||
class cStreamdevStreamer: public cThread {
|
||||
private:
|
||||
bool m_Active;
|
||||
bool m_Running;
|
||||
const cServerConnection *m_Connection;
|
||||
cStreamdevWriter *m_Writer;
|
||||
cRingBufferLinear *m_RingBuffer;
|
||||
cRingBufferLinear *m_SendBuffer;
|
||||
cStreamdevBuffer *m_RingBuffer;
|
||||
cStreamdevBuffer *m_SendBuffer;
|
||||
|
||||
protected:
|
||||
virtual void Action(void);
|
||||
|
||||
bool IsRunning(void) const { return m_Running; }
|
||||
bool IsRunning(void) const { return m_Writer; }
|
||||
|
||||
public:
|
||||
cStreamdevStreamer(const char *Name);
|
||||
cStreamdevStreamer(const char *Name, const cServerConnection *Connection = NULL);
|
||||
virtual ~cStreamdevStreamer();
|
||||
|
||||
const cServerConnection* Connection(void) const { return m_Connection; }
|
||||
|
||||
virtual void Start(cTBSocket *Socket);
|
||||
virtual void Stop(void);
|
||||
bool Abort(void) const;
|
||||
bool Abort(void);
|
||||
|
||||
void Activate(bool On);
|
||||
int Receive(uchar *Data, int Length) { return m_RingBuffer->Put(Data, Length); }
|
||||
int Receive(uchar *Data, int Length) { return m_RingBuffer->PutTS(Data, Length); }
|
||||
void ReportOverflow(int Bytes) { m_RingBuffer->ReportOverflow(Bytes); }
|
||||
|
||||
virtual int Put(const uchar *Data, int Count) { return m_SendBuffer->Put(Data, Count); }
|
||||
virtual int Put(const uchar *Data, int Count) { return m_SendBuffer->PutTS(Data, Count); }
|
||||
virtual uchar *Get(int &Count) { return m_SendBuffer->Get(Count); }
|
||||
virtual void Del(int Count) { m_SendBuffer->Del(Count); }
|
||||
|
||||
@@ -68,9 +97,9 @@ public:
|
||||
virtual void Attach(void) {}
|
||||
};
|
||||
|
||||
inline bool cStreamdevStreamer::Abort(void) const
|
||||
inline bool cStreamdevStreamer::Abort(void)
|
||||
{
|
||||
return m_Active && !m_Writer->IsActive();
|
||||
return Active() && !m_Writer->Active();
|
||||
}
|
||||
|
||||
#endif // VDR_STREAMDEV_STREAMER_H
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: suspend.c,v 1.2 2008/04/07 14:27:31 schmirl Exp $
|
||||
* $Id: suspend.c,v 1.3 2008/10/22 11:59:32 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include "server/suspend.h"
|
||||
@@ -12,6 +12,7 @@ cSuspendLive::cSuspendLive(void)
|
||||
}
|
||||
|
||||
cSuspendLive::~cSuspendLive() {
|
||||
Stop();
|
||||
Detach();
|
||||
}
|
||||
|
||||
@@ -24,17 +25,14 @@ void cSuspendLive::Activate(bool On) {
|
||||
}
|
||||
|
||||
void cSuspendLive::Stop(void) {
|
||||
if (m_Active) {
|
||||
m_Active = false;
|
||||
if (Running())
|
||||
Cancel(3);
|
||||
}
|
||||
}
|
||||
|
||||
void cSuspendLive::Action(void) {
|
||||
m_Active = true;
|
||||
while (m_Active) {
|
||||
while (Running()) {
|
||||
DeviceStillPicture(suspend_mpg, sizeof(suspend_mpg));
|
||||
usleep(100000);
|
||||
cCondWait::SleepMs(100);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +49,7 @@ cSuspendCtl::~cSuspendCtl() {
|
||||
}
|
||||
|
||||
eOSState cSuspendCtl::ProcessKey(eKeys Key) {
|
||||
if (!m_Suspend->IsActive() || Key == kBack) {
|
||||
if (!m_Suspend->Active() || Key == kBack) {
|
||||
DELETENULL(m_Suspend);
|
||||
return osEnd;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: suspend.h,v 1.1 2004/12/30 22:44:26 lordjaxom Exp $
|
||||
* $Id: suspend.h,v 1.2 2008/10/22 11:59:32 schmirl Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_SUSPEND_H
|
||||
@@ -7,10 +7,7 @@
|
||||
|
||||
#include <vdr/player.h>
|
||||
|
||||
class cSuspendLive: public cPlayer, cThread {
|
||||
private:
|
||||
bool m_Active;
|
||||
|
||||
class cSuspendLive: public cPlayer, public cThread {
|
||||
protected:
|
||||
virtual void Activate(bool On);
|
||||
virtual void Action(void);
|
||||
@@ -20,8 +17,6 @@ protected:
|
||||
public:
|
||||
cSuspendLive(void);
|
||||
virtual ~cSuspendLive();
|
||||
|
||||
bool IsActive(void) const { return m_Active; }
|
||||
};
|
||||
|
||||
class cSuspendCtl: public cControl {
|
||||
|
||||
323
streamdev-server/externremux.sh
Executable file
323
streamdev-server/externremux.sh
Executable file
@@ -0,0 +1,323 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# externremux.sh - sample remux script using mencoder for remuxing.
|
||||
#
|
||||
# Install this script as VDRCONFDIR/plugins/streamdev-server/externremux.sh
|
||||
#
|
||||
# The parameter QUALITY selects the default remux parameters. Adjust
|
||||
# to your needs and point your web browser to http://servername:3000/ext/
|
||||
# To select different remux parameters on the fly, insert a semicolon
|
||||
# followed by the name and value of the requested parameter, e.g:
|
||||
# e.g. http://servername:3000/ext;QUALITY=WLAN11;VBR=512/
|
||||
# The following parameters are recognized:
|
||||
#
|
||||
# PROG actual remux program
|
||||
# VC video codec
|
||||
# VBR video bitrate (kbit)
|
||||
# VOPTS custom video options
|
||||
# WIDTH scale video to width
|
||||
# HEIGHT scale video to height
|
||||
# FPS output frames per second
|
||||
# AC audio codec
|
||||
# ABR audio bitrate (kbit)
|
||||
# AOPTS custom audio options
|
||||
#
|
||||
|
||||
##########################################################################
|
||||
|
||||
### GENERAL CONFIG START
|
||||
###
|
||||
# Pick one of DSL1000/DSL2000/DSL3000/DSL6000/DSL16000/LAN10/WLAN11/WLAN54
|
||||
QUALITY='DSL1000'
|
||||
# Program used for logging (logging disabled if empty)
|
||||
LOGGER=logger
|
||||
# Path and name of FIFO
|
||||
FIFO=/tmp/externremux-${RANDOM:-$$}
|
||||
# Default remux program (cat/mencoder/ogg)
|
||||
PROG=mencoder
|
||||
# Use mono if $ABR is lower than this value
|
||||
ABR_MONO=64
|
||||
###
|
||||
### GENERAL CONFIG END
|
||||
|
||||
### MENCODER CONFIG START
|
||||
###
|
||||
# mencoder binary
|
||||
MENCODER=mencoder
|
||||
# verbosity from all=-1 to all=9 (-msglevel ...)
|
||||
MENCODER_MSGLEVEL=all=1
|
||||
### video part
|
||||
# Default video codec (e.g. lavc/x264/copy)
|
||||
MENCODER_VC=lavc
|
||||
# Default video options if lavc is used (-ovc lavc -lavcopts ...)
|
||||
MENCODER_LAVC_VOPTS=vcodec=mpeg4
|
||||
# Default video options if x264 is used (-ovc x264 -x264encopts ...)
|
||||
MENCODER_X264_VOPTS=threads=auto
|
||||
### audio part
|
||||
# Audio language to use if several audio PIDs are available (-alang ...)
|
||||
MENCODER_ALANG=eng
|
||||
# Default audio codec (e.g. lavc/mp3lame/faac/copy)
|
||||
MENCODER_AC=mp3lame
|
||||
# Default audio options if lavc is used (-oac lavc -lavcopts ...)
|
||||
MENCODER_LAVC_AOPTS=acodec=mp2
|
||||
# Default audio options if mp3lame is used (-oac mp3lame -lameopts ...)
|
||||
MENCODER_LAME_AOPTS=
|
||||
# Default audio options if faac is used (-oac faac -faacopts ...)
|
||||
MENCODER_FAAC_AOPTS=
|
||||
###
|
||||
### MENCODER CONFIG END
|
||||
|
||||
### OGG CONFIG START
|
||||
###
|
||||
# ffmpeg2theora binary
|
||||
OGG=ffmpeg2theora
|
||||
# speedlevel - lower value gives better quality but is slower (0..2)
|
||||
OGG_SPEED=1
|
||||
# videoquality - higher value gives better quality but is slower (0..10)
|
||||
OGG_VQUALITY=0
|
||||
# audioquality - higher value gives better quality but is slower (0..10)
|
||||
OGG_AQUALITY=0
|
||||
# aspect ratio used for scaling if only one of HEIGHT/WIDTH given (16/9 or 4/3)
|
||||
OGG_ASPECT='4 / 3'
|
||||
###
|
||||
### OGG CONFIG END
|
||||
|
||||
##########################################################################
|
||||
|
||||
function hasOpt { echo "$1" | grep -q "\b${2}\b"; }
|
||||
|
||||
# $1: concatenation of already set option=value pairs
|
||||
# $2-$n: option=value pairs to be echod if the option is not present in $1
|
||||
function addOpts
|
||||
{
|
||||
local opts="$1"
|
||||
shift
|
||||
while [ $# -gt 0 ]; do
|
||||
hasOpt "$opts" ${1%%=*}= || echo $1
|
||||
shift
|
||||
done
|
||||
}
|
||||
|
||||
function isNumeric() { echo "$@" | grep -q '^-\?[0-9]\{1,\}$'; }
|
||||
|
||||
function remux_cat
|
||||
{
|
||||
startReply
|
||||
exec 3<&0
|
||||
cat 0<&3 >"$FIFO" &
|
||||
}
|
||||
|
||||
function remux_mencoder
|
||||
{
|
||||
# lavc may be used for video and audio
|
||||
LAVCOPTS=()
|
||||
|
||||
# Assemble video options
|
||||
VC=${REMUX_PARAM_VC:-$MENCODER_VC}
|
||||
VOPTS=${REMUX_PARAM_VOPTS}
|
||||
FPS=${REMUX_PARAM_FPS:-$FPS}
|
||||
|
||||
# if only one of HEIGHT/WIDTH given:
|
||||
# have mencoder calculate other value depending on actual aspect ratio
|
||||
if [ "$HEIGHT" -a -z "$WIDTH" ]; then
|
||||
WIDTH=-3
|
||||
elif [ "$WIDTH" -a -z "$HEIGHT" ]; then
|
||||
HEIGHT=-3
|
||||
fi
|
||||
|
||||
case "$VC" in
|
||||
lavc)
|
||||
LAVCOPTS=(
|
||||
${VOPTS}
|
||||
$(IFS=$IFS:; addOpts "$VOPTS" $MENCODER_LAVC_VOPTS)
|
||||
${VBR:+vbitrate=$VBR}
|
||||
)
|
||||
[ ${#LAVCOPTS[*]} -gt 0 ] && VOPTS=$(IFS=:; echo -lavcopts "${LAVCOPTS[*]}")
|
||||
;;
|
||||
x264)
|
||||
isNumeric "$HEIGHT" && [ $HEIGHT -lt 0 -a $HEIGHT -gt -8 ] && ((HEIGHT-=8))
|
||||
isNumeric "$WIDTH" && [ $WIDTH -lt 0 -a $WIDTH -gt -8 ] && ((WIDTH-=8))
|
||||
X264OPTS=(
|
||||
${VOPTS}
|
||||
$(IFS=$IFS:; addOpts "$VOPTS" $MENCODER_X264_VOPTS)
|
||||
${VBR:+bitrate=$VBR}
|
||||
)
|
||||
[ ${#X264OPTS[*]} -gt 0 ] && VOPTS=$(IFS=:; echo -x264encopts "${X264OPTS[*]}")
|
||||
;;
|
||||
copy)
|
||||
VOPTS=
|
||||
;;
|
||||
*)
|
||||
error "Unknown video codec '$VC'"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Assemble audio options
|
||||
AC=${REMUX_PARAM_AC:-$MENCODER_AC}
|
||||
AOPTS=${REMUX_PARAM_AOPTS}
|
||||
case "$AC" in
|
||||
lavc)
|
||||
LAVCOPTS=(
|
||||
${LAVCOPTS[*]}
|
||||
${AOPTS}
|
||||
$(IFS=$IFS:; addOpts "$AOPTS" $MENCODER_LAVC_AOPTS)
|
||||
${ABR:+abitrate=$ABR}
|
||||
)
|
||||
|
||||
[ ${#LAVCOPTS[*]} -gt 0 ] && AOPTS=$(IFS=:; echo -lavcopts "${LAVCOPTS[*]}")
|
||||
# lavc used for video and audio decoding - wipe out VOPTS as video options became part of AOPTS
|
||||
[ "$VC" = lavc ] && VOPTS=
|
||||
;;
|
||||
mp3lame)
|
||||
LAMEOPTS=(
|
||||
${AOPTS}
|
||||
$(isNumeric "${ABR}" && [ "${ABR}" -lt "$ABR_MONO" ] && ! hasOpt "${AOPTS}" mode ] && echo 'mode=3')
|
||||
$(IFS=$IFS:; addOpts "$AOPTS" $MENCODER_LAME_AOPTS)
|
||||
${ABR:+preset=$ABR}
|
||||
)
|
||||
[ ${#LAMEOPTS[*]} -gt 0 ] && AOPTS=$(IFS=:; echo -lameopts "${LAMEOPTS[*]}")
|
||||
;;
|
||||
faac)
|
||||
FAACOPTS=(
|
||||
${AOPTS}
|
||||
$(IFS=$IFS:; addOpts "$AOPTS" $MENCODER_FAAC_AOPTS)
|
||||
${ABR:+br=$ABR}
|
||||
)
|
||||
[ ${#FAACOPTS[*]} -gt 0 ] && AOPTS=$(IFS=:; echo -faacopts "${FAACOPTS[*]}")
|
||||
;;
|
||||
copy)
|
||||
AOPTS=
|
||||
;;
|
||||
*)
|
||||
error "Unknown audio codec '$AC'"
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
startReply
|
||||
exec 3<&0
|
||||
echo $MENCODER \
|
||||
${MENCODER_MSGLEVEL:+-msglevel $MENCODER_MSGLEVEL} \
|
||||
-ovc $VC $VOPTS \
|
||||
-oac $AC $AOPTS \
|
||||
${MENCODER_ALANG:+-alang $MENCODER_ALANG} \
|
||||
${WIDTH:+-vf scale=$WIDTH:$HEIGHT -zoom} \
|
||||
${FPS:+-ofps $FPS} \
|
||||
-o "$FIFO" -- - >&2
|
||||
$MENCODER \
|
||||
${MENCODER_MSGLEVEL:+-msglevel $MENCODER_MSGLEVEL} \
|
||||
-ovc $VC $VOPTS \
|
||||
-oac $AC $AOPTS \
|
||||
${MENCODER_ALANG:+-alang $MENCODER_ALANG} \
|
||||
${WIDTH:+-vf scale=$WIDTH:$HEIGHT -zoom} \
|
||||
${FPS:+-ofps $FPS} \
|
||||
-o "$FIFO" -- - 0<&3 >/dev/null &
|
||||
}
|
||||
|
||||
function remux_ogg
|
||||
{
|
||||
VOPTS=${REMUX_PARAM_VOPTS//[:=]/ }
|
||||
AOPTS=${REMUX_PARAM_AOPTS//[:=]/ }
|
||||
|
||||
# if only one of HEIGHT/WIDTH given:
|
||||
# calculate other value depending on configured aspect ratio
|
||||
# trim to multiple of 8
|
||||
if [ "$HEIGHT" -a -z "$WIDTH" ]; then
|
||||
WIDTH=$((HEIGHT * $OGG_ASPECT / 8 * 8))
|
||||
elif [ "$WIDTH" -a -z "$HEIGHT" ]; then
|
||||
HEIGHT=$(($WIDTH * $( echo $OGG_ASPECT | sed 's#^\([0-9]\+\) */ *\([0-9]\+\)$#\2 / \1#') / 8 * 8))
|
||||
fi
|
||||
|
||||
OGGOPTS=(
|
||||
${VOPTS}
|
||||
${VBR:+--videobitrate $VBR}
|
||||
$(hasOpt "${VOPTS}" videoquality || echo "--videoquality $OGG_VQUALITY")
|
||||
$(hasOpt "${VOPTS}" speedlevel || echo "--speedlevel $OGG_SPEED")
|
||||
${AOPTS}
|
||||
${ABR:+--audiobitrate $ABR}
|
||||
$(isNumeric "${ABR}" && [ "${ABR}" -lt "$ABR_MONO" ] && ! hasOpt "${AOPTS}" channels ] && echo '--channels 1')
|
||||
$(hasOpt "${AOPTS}" audioquality || echo "--audioquality $OGG_AQUALITY")
|
||||
$(hasOpt "${AOPTS}" audiostream || echo '--audiostream 1')
|
||||
)
|
||||
|
||||
startReply
|
||||
exec 3<&0
|
||||
echo $OGG --format ts \
|
||||
${OGGOPTS[*]} \
|
||||
${WIDTH:+--width $WIDTH --height $HEIGHT} \
|
||||
--title "VDR Streamdev: ${REMUX_CHANNEL_NAME}" \
|
||||
--output "$FIFO" -- - 0<&3 >&2
|
||||
$OGG --format ts \
|
||||
${OGGOPTS[*]} \
|
||||
${WIDTH:+--width $WIDTH --height $HEIGHT} \
|
||||
--title "VDR Streamdev: ${REMUX_CHANNEL_NAME}" \
|
||||
--output "$FIFO" -- - 0<&3 >/dev/null &
|
||||
}
|
||||
|
||||
function error
|
||||
{
|
||||
if [ "$SERVER_PROTOCOL" = HTTP ]; then
|
||||
echo -ne "Content-type: text/plain\r\n"
|
||||
echo -ne '\r\n'
|
||||
echo "$*"
|
||||
fi
|
||||
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
function startReply
|
||||
{
|
||||
if [ "$SERVER_PROTOCOL" = HTTP ]; then
|
||||
# send content-type and custom headers
|
||||
echo -ne "Content-type: ${CONTENTTYPE}\r\n"
|
||||
for header in "${HEADER[@]}"; do echo -ne "$header\r\n"; done
|
||||
echo -ne '\r\n'
|
||||
|
||||
# abort after headers
|
||||
[ "$REQUEST_METHOD" = HEAD ] && exit 0
|
||||
fi
|
||||
|
||||
# create FIFO and read from it in the background
|
||||
mkfifo "$FIFO"
|
||||
trap "trap '' EXIT HUP INT TERM ABRT PIPE CHLD; kill -INT 0; sleep 1; fuser -k '$FIFO'; rm '$FIFO'" EXIT HUP INT TERM ABRT PIPE CHLD
|
||||
cat "$FIFO" <&- &
|
||||
}
|
||||
|
||||
HEADER=()
|
||||
|
||||
[ "$LOGGER" ] && exec 2> >($LOGGER -t "vdr: [$$] ${0##*/}" 2>&-)
|
||||
|
||||
# set default content-types
|
||||
case "$REMUX_VPID" in
|
||||
''|0|1) CONTENTTYPE='audio/mpeg';;
|
||||
*) CONTENTTYPE='video/mpeg';;
|
||||
esac
|
||||
|
||||
QUALITY=${REMUX_PARAM_QUALITY:-$QUALITY}
|
||||
case "$QUALITY" in
|
||||
DSL1000|dsl1000) VBR=96; ABR=16; WIDTH=160;;
|
||||
DSL2000|dsl2000) VBR=128; ABR=16; WIDTH=160;;
|
||||
DSL3000|dsl3000) VBR=256; ABR=16; WIDTH=320;;
|
||||
DSL6000|dsl6000) VBR=378; ABR=32; WIDTH=320;;
|
||||
DSL16000|dsl16000) VBR=512; ABR=32; WIDTH=480;;
|
||||
WLAN11|wlan11) VBR=768; ABR=64; WIDTH=640;;
|
||||
WLAN54|wlan54) VBR=2048; ABR=128; WIDTH=;;
|
||||
LAN10|lan10) VBR=4096; ABR=; WIDTH=;;
|
||||
*) error "Unknown quality '$QUALITY'";;
|
||||
esac
|
||||
ABR=${REMUX_PARAM_ABR:-$ABR}
|
||||
VBR=${REMUX_PARAM_VBR:-$VBR}
|
||||
WIDTH=${REMUX_PARAM_WIDTH:-$WIDTH}
|
||||
HEIGHT=${REMUX_PARAM_HEIGHT:-$HEIGHT}
|
||||
PROG=${REMUX_PARAM_PROG:-$PROG}
|
||||
|
||||
case "$PROG" in
|
||||
cat) remux_cat;;
|
||||
mencoder) remux_mencoder;;
|
||||
ogg) remux_ogg;;
|
||||
*) error "Unknown remuxer '$PROG'";;
|
||||
esac
|
||||
|
||||
set -o monitor
|
||||
wait
|
||||
@@ -10,4 +10,5 @@
|
||||
127.0.0.1 # always accept localhost
|
||||
#192.168.100.0/24 # any host on the local net
|
||||
#204.152.189.113 # a specific host
|
||||
#0.0.0.0/0 # any host on any net (USE THIS WITH CARE!)
|
||||
#239.255.0.0/16 # uncomment for IGMP multicast streaming
|
||||
#0.0.0.0/0 # any host on any net (DON'T DO THAT! USE AUTHENTICATION)
|
||||
@@ -1,48 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# externremux.sh - sample remux script using mencoder for remuxing.
|
||||
#
|
||||
# Install this script as VDRCONFDIR/plugins/streamdev/externremux.sh
|
||||
#
|
||||
# The parameter STREAMQUALITY selects the default remux parameters. Adjust
|
||||
# to your needs and point your web browser to http://servername:3000/extern/
|
||||
# To select different remux parameters on the fly, insert a semicolon and
|
||||
# the name of the requested quality: http://servername:3000/extern;WLAN11/
|
||||
|
||||
# CONFIG START
|
||||
STREAMQUALITY="DSL6000" # DSL{1,2,3,6}000, LAN10, WLAN{11,54}, IPAQ
|
||||
TMP=/tmp/externremux-${RANDOM:-$$}
|
||||
MENCODER=mencoder
|
||||
# CONFIG END
|
||||
|
||||
mkdir -p $TMP
|
||||
mkfifo $TMP/out.avi
|
||||
(trap "rm -rf $TMP" EXIT HUP INT TERM ABRT; cat $TMP/out.avi) &
|
||||
|
||||
case ${1:-$STREAMQUALITY} in
|
||||
DSL1000) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=100 \
|
||||
-oac mp3lame -lameopts preset=15:mode=3 -vf scale=160:104 \
|
||||
-o $TMP/out.avi -- - &>$TMP/out.log ;;
|
||||
DSL2000) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=128 \
|
||||
-oac mp3lame -lameopts preset=15:mode=3 -vf scale=160:104 \
|
||||
-o $TMP/out.avi -- - &>$TMP/out.log ;;
|
||||
DSL3000) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=250 \
|
||||
-oac mp3lame -lameopts preset=15:mode=3 -vf scale=320:208 \
|
||||
-o $TMP/out.avi -- - &>$TMP/out.log ;;
|
||||
DSL6000) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=350 \
|
||||
-oac mp3lame -lameopts preset=15:mode=3 -vf scale=320:208 \
|
||||
-o $TMP/out.avi -- - &>$TMP/out.log ;;
|
||||
LAN10) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=4096 \
|
||||
-oac mp3lame -lameopts preset=standard \
|
||||
-o $TMP/out.avi -- - &>$TMP/out.log ;;
|
||||
WLAN11) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=768 \
|
||||
-oac mp3lame -lameopts preset=standard -vf scale=640:408 \
|
||||
-o $TMP/out.avi -- - &>$TMP/out.log ;;
|
||||
WLAN54) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=2048 \
|
||||
-oac mp3lame -lameopts preset=standard \
|
||||
-o $TMP/out.avi -- - &>$TMP/out.log ;;
|
||||
IPAQ) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=350 \
|
||||
-oac mp3lame -lameopts preset=15:mode=3 -vf scale=320:208 \
|
||||
-o $TMP/out.avi -- - &>$TMP/out.log ;;
|
||||
*) touch $TMP/out.avi ;;
|
||||
esac
|
||||
34
tools/Makefile
Normal file
34
tools/Makefile
Normal file
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# Makefile for a Video Disk Recorder plugin
|
||||
#
|
||||
# $Id: Makefile,v 1.2 2010/07/19 13:49:44 schmirl Exp $
|
||||
|
||||
### The object files (add further files here):
|
||||
|
||||
OBJS = select.o socket.o source.o tools.o
|
||||
|
||||
### The main target:
|
||||
|
||||
.PHONY: clean
|
||||
sockettools.a: $(OBJS)
|
||||
ar -rcs sockettools.a $(OBJS)
|
||||
|
||||
### Implicit rules:
|
||||
|
||||
%.o: %.c
|
||||
$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
|
||||
|
||||
### Dependencies:
|
||||
|
||||
MAKEDEP = $(CXX) -MM -MG
|
||||
DEPFILE = .dependencies
|
||||
|
||||
$(DEPFILE): Makefile
|
||||
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
|
||||
|
||||
-include $(DEPFILE)
|
||||
|
||||
### Targets:
|
||||
|
||||
clean:
|
||||
@-rm -f $(OBJS) $(DEPFILE) *.a core* *~
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "tools/socket.h"
|
||||
|
||||
#include <vdr/tools.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
@@ -15,10 +16,11 @@
|
||||
// actual DSCP value used
|
||||
#define STREAMDEV_DSCP DSCP_AF41
|
||||
|
||||
cTBSocket::cTBSocket(int Type) {
|
||||
cTBSocket::cTBSocket(int Type, int Protocol) {
|
||||
memset(&m_LocalAddr, 0, sizeof(m_LocalAddr));
|
||||
memset(&m_RemoteAddr, 0, sizeof(m_RemoteAddr));
|
||||
m_Type = Type;
|
||||
m_Protocol = Protocol;
|
||||
}
|
||||
|
||||
cTBSocket::~cTBSocket() {
|
||||
@@ -31,7 +33,7 @@ bool cTBSocket::Connect(const std::string &Host, unsigned int Port) {
|
||||
|
||||
if (IsOpen()) Close();
|
||||
|
||||
if ((socket = ::socket(PF_INET, m_Type, IPPROTO_IP)) == -1)
|
||||
if ((socket = ::socket(PF_INET, m_Type, m_Protocol)) == -1)
|
||||
return false;
|
||||
|
||||
m_LocalAddr.sin_family = AF_INET;
|
||||
@@ -52,10 +54,12 @@ bool cTBSocket::Connect(const std::string &Host, unsigned int Port) {
|
||||
return false;
|
||||
}
|
||||
|
||||
len = sizeof(struct sockaddr_in);
|
||||
if (::getpeername(socket, (struct sockaddr*)&m_RemoteAddr, &len) == -1) {
|
||||
::close(socket);
|
||||
return false;
|
||||
if (m_Type == SOCK_STREAM) {
|
||||
len = sizeof(struct sockaddr_in);
|
||||
if (::getpeername(socket, (struct sockaddr*)&m_RemoteAddr, &len) == -1) {
|
||||
::close(socket);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
len = sizeof(struct sockaddr_in);
|
||||
@@ -64,7 +68,11 @@ bool cTBSocket::Connect(const std::string &Host, unsigned int Port) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return cTBSource::Open(socket);
|
||||
if (!cTBSource::Open(socket)) {
|
||||
::close(socket);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cTBSocket::Listen(const std::string &Ip, unsigned int Port, int BackLog) {
|
||||
@@ -74,7 +82,7 @@ bool cTBSocket::Listen(const std::string &Ip, unsigned int Port, int BackLog) {
|
||||
|
||||
if (IsOpen()) Close();
|
||||
|
||||
if ((socket = ::socket(PF_INET, m_Type, IPPROTO_IP)) == -1)
|
||||
if ((socket = ::socket(PF_INET, m_Type, m_Protocol)) == -1)
|
||||
return false;
|
||||
|
||||
val = 1;
|
||||
@@ -116,6 +124,10 @@ bool cTBSocket::Accept(const cTBSocket &Listener) {
|
||||
if (::getsockname(socket, (struct sockaddr*)&m_LocalAddr, &addrlen) == -1)
|
||||
return false;
|
||||
|
||||
int sol=1;
|
||||
// Ignore possible errors here, proceed as usual
|
||||
::setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &sol, sizeof(sol));
|
||||
|
||||
if (!cTBSource::Open(socket))
|
||||
return false;
|
||||
|
||||
|
||||
@@ -18,9 +18,10 @@ private:
|
||||
struct sockaddr_in m_RemoteAddr;
|
||||
|
||||
int m_Type;
|
||||
int m_Protocol;
|
||||
|
||||
public:
|
||||
cTBSocket(int Type = SOCK_STREAM);
|
||||
cTBSocket(int Type = SOCK_STREAM, int Protocol = 0);
|
||||
virtual ~cTBSocket();
|
||||
|
||||
/* See cTBSource::SysRead()
|
||||
@@ -97,15 +98,22 @@ public:
|
||||
};
|
||||
|
||||
inline ssize_t cTBSocket::SysRead(void *Buffer, size_t Length) const {
|
||||
if (m_Type == SOCK_DGRAM) {
|
||||
if (m_Type == SOCK_STREAM)
|
||||
return ::recv(*this, Buffer, Length, 0);
|
||||
else {
|
||||
socklen_t len = sizeof(m_RemoteAddr);
|
||||
return ::recvfrom(*this, Buffer, Length, 0, (sockaddr*)&m_RemoteAddr, &len);
|
||||
} else
|
||||
return ::recv(*this, Buffer, Length, 0);
|
||||
}
|
||||
}
|
||||
|
||||
inline ssize_t cTBSocket::SysWrite(const void *Buffer, size_t Length) const {
|
||||
return ::send(*this, Buffer, Length, 0);
|
||||
if (m_Type == SOCK_STREAM)
|
||||
return ::send(*this, Buffer, Length, 0);
|
||||
else {
|
||||
socklen_t len = sizeof(m_RemoteAddr);
|
||||
return ::sendto(*this, Buffer, Length, 0, (sockaddr*)&m_RemoteAddr, len);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // TOOLBOX_SOCKET_H
|
||||
|
||||
Reference in New Issue
Block a user