1
0
mirror of https://github.com/rofafor/vdr-plugin-satip.git synced 2023-10-10 11:37:42 +00:00

215 Commits

Author SHA1 Message Date
Rolf Ahrenberg
6c4c8a10b7 Set the default device count to two. 2015-04-26 12:49:40 +03:00
Rolf Ahrenberg
8b43cdc634 Added initial support for detaching and attaching SAT>IP servers. 2015-04-12 21:25:41 +03:00
Rolf Ahrenberg
fe010ab72c Added a more flexible OPER command in the SVDRP interface. 2015-04-09 21:36:13 +03:00
Rolf Ahrenberg
7bdc152f76 Prepared for a release. 2015-04-03 20:37:29 +03:00
Rolf Ahrenberg
bd6774ba28 Relaxed the server cleanup timeout. 2015-03-26 00:28:17 +02:00
Rolf Ahrenberg
fbf7977853 Added a memory guard for cSatipMemoryBuffer(). 2015-03-26 00:23:55 +02:00
Rolf Ahrenberg
19a6a4a5ee Simplified GetTransponderUrlParameters(). 2015-03-22 23:44:15 +02:00
Rolf Ahrenberg
7b683dba8d Implemented cSatipMemoryBuffer(). 2015-03-22 20:06:47 +02:00
Rolf Ahrenberg
942d3a936e Fixed CURL buffer size for discovery. 2015-03-22 17:33:08 +02:00
Rolf Ahrenberg
748ea15d1d Updated against SAT>IP protocol specification version 1.2.2. 2015-03-22 16:47:02 +02:00
Rolf Ahrenberg
1d75da403a Simplified GetTransponderUrlParameters(). 2015-03-22 16:33:48 +02:00
Rolf Ahrenberg
4d8263e8fd Improved RTSP error checking. 2015-03-18 22:59:38 +02:00
Rolf Ahrenberg
7019b719a5 Fixed a memory leak in TinyXML implementation (Thanks to Oliver Endriss). 2015-03-18 20:55:10 +02:00
Rolf Ahrenberg
39249ca2d5 Fixed generic error macros. 2015-03-08 15:53:54 +02:00
Rolf Ahrenberg
68e0d1474e Got rid of SATIP_DEBUG. 2015-03-07 17:50:48 +02:00
Rolf Ahrenberg
ad5c221e44 Fixed wrong indenting. 2015-03-04 19:43:01 +02:00
Rolf Ahrenberg
826e53e8ea Fixed the discovery tracing level. 2015-03-03 19:09:51 +02:00
Rolf Ahrenberg
f4dd02a9aa Robustify the server discovery. 2015-03-02 20:08:21 +02:00
Rolf Ahrenberg
8184a785b7 Updated README. 2015-02-20 21:15:55 +02:00
Rolf Ahrenberg
1b4094696a Updated HISTORY. 2015-02-20 20:53:20 +02:00
Rolf Ahrenberg
4139e87f4a Disable socket flushing. 2015-02-20 20:51:31 +02:00
Rolf Ahrenberg
7196c9403b Updated the APIVERSNUM requirement. 2015-02-20 20:47:58 +02:00
Rolf Ahrenberg
e1c896c1a6 Added a missing linefeed. 2015-02-17 21:16:30 +02:00
Rolf Ahrenberg
6b63ad145f Fixed the frontend assignment. 2015-02-16 19:42:18 +02:00
Rolf Ahrenberg
84dfc6701e Refactored the frontend attaching/detaching signaling. 2015-02-16 17:59:27 +02:00
Rolf Ahrenberg
653d9d659b Updated README. 2015-02-16 16:49:51 +02:00
Rolf Ahrenberg
26cd34f965 Updated version to 2.2.0. 2015-02-08 22:41:01 +02:00
Rolf Ahrenberg
df258d127f Reverted the force locking quirk for Triax TSS 400. 2015-01-27 21:31:39 +02:00
Rolf Ahrenberg
3e4b1c0383 Updated the server info message to show used quirks. 2015-01-27 17:19:06 +02:00
Rolf Ahrenberg
73ed299ed9 Fixed to nag about any malfunctioning firmware only once. 2015-01-26 00:24:07 +02:00
Rolf Ahrenberg
37e151b3e3 Added the force locking quirk for Triax TSS 400. 2015-01-25 23:47:56 +02:00
Rolf Ahrenberg
b1aad3fb80 Updated the version number. 2015-01-24 17:21:04 +02:00
Rolf Ahrenberg
a87dfc43f7 Refactored the frontend handling. 2015-01-24 17:16:09 +02:00
Rolf Ahrenberg
26be862d89 Refactored some errno handling. 2015-01-24 16:20:13 +02:00
Rolf Ahrenberg
ab2a47e3e7 Got rid of scan-build warnings. 2015-01-22 21:37:33 +02:00
Rolf Ahrenberg
3d1efe7a80 Fixed freeing some memory. 2015-01-21 22:40:24 +02:00
Rolf Ahrenberg
c9898bfbfd Added support for showing the frontend id. 2015-01-20 00:35:46 +02:00
Rolf Ahrenberg
46bfc805e6 Removed SATIP_XCI. 2015-01-18 15:08:57 +02:00
Rolf Ahrenberg
30925337cb Updated Spanish and Catalan translations (Thanks to Gabriel Bonich). 2015-01-18 00:13:29 +02:00
Rolf Ahrenberg
63e47ad24a Reworded the README. 2015-01-17 20:02:18 +02:00
Rolf Ahrenberg
14123e160f Fixed the server parsing multiple times. 2015-01-17 19:42:21 +02:00
Rolf Ahrenberg
278d0478cf Added a command-line option to disable all the SAT>IP server quirks. 2015-01-17 17:29:21 +02:00
Rolf Ahrenberg
9989c36eee Delayed the server parameter parsing. 2015-01-17 16:47:19 +02:00
Rolf Ahrenberg
d2064f0c04 Added an option to disable sources via sources.conf. 2015-01-17 01:39:32 +02:00
Rolf Ahrenberg
714d3ed902 Increased the max number of disabled sources. 2015-01-17 00:45:30 +02:00
Rolf Ahrenberg
b466d6836e Added NULL checks for cSatipServer parameters. 2015-01-17 00:00:59 +02:00
Rolf Ahrenberg
184ddf2a53 Fixed a wrong parameter type. 2015-01-16 00:08:48 +02:00
Rolf Ahrenberg
65a2158051 Fixed some whitespaces. 2015-01-15 23:38:02 +02:00
Rolf Ahrenberg
e833ca6fdd Added a separate trace mode for pids. 2015-01-15 20:03:35 +02:00
Rolf Ahrenberg
c9abfddf11 Changed SATIP_USE_SINGLE_MODEL_SERVERS_ONLY into a command-line switch. 2015-01-15 19:27:02 +02:00
Rolf Ahrenberg
fb7a7fe3a2 Fixed parsing of the setup values. 2015-01-15 00:12:55 +02:00
Rolf Ahrenberg
3b9269e9de Added a separate trace mode for server discovery. 2015-01-14 22:59:56 +02:00
Rolf Ahrenberg
09e6c272cd Refactored cSatipPid for upcoming API changes. 2015-01-14 20:51:16 +02:00
Rolf Ahrenberg
266aadb999 Modified the model detection a bit more readable. 2015-01-14 20:34:12 +02:00
Rolf Ahrenberg
aed5a7820a Fixed 'length' typos. 2015-01-14 17:58:13 +02:00
Rolf Ahrenberg
bcb11b6257 Added a compile-time option to split any mixed model server into several single ones. 2015-01-13 21:45:14 +02:00
Rolf Ahrenberg
21261f8042 Clamp signal quality and level values. 2015-01-12 23:58:30 +02:00
Rolf Ahrenberg
2f11ad7a98 Added server parameter exanples. 2015-01-12 23:51:38 +02:00
Rolf Ahrenberg
83a0233780 Updated German translation (Thanks to Frank Neumann). 2015-01-12 13:09:09 +02:00
Rolf Ahrenberg
6f3715eb8b Merge pull request #5 from CvH/patch-1
Fixed a typo in firmware version.
2015-01-12 09:22:43 +02:00
CvH
92991cd144 fixed typo at fw version 2015-01-11 22:52:40 +01:00
Rolf Ahrenberg
304addbc00 Added missing CI/CAM translations. 2015-01-11 23:49:12 +02:00
Rolf Ahrenberg
26ff10ce1e Added configurable CI slots. 2015-01-11 16:34:32 +02:00
Rolf Ahrenberg
e654b3bbd2 Updated version number. 2015-01-11 16:33:42 +02:00
Rolf Ahrenberg
acea8748a4 Added SATIP_XCI variable for enabling CI support. 2015-01-10 18:30:07 +02:00
Rolf Ahrenberg
755f1049bb Reduced tuner sleep timeout to get better performance for pid updates. 2015-01-10 14:14:30 +02:00
Rolf Ahrenberg
2f6315280f Tweaked setting the pid update cache timer. 2015-01-09 19:56:43 +02:00
Rolf Ahrenberg
695dd53bfc Added initial support for x_ci parameter. 2015-01-09 16:38:02 +02:00
Rolf Ahrenberg
5fef77518e Changed the PMT debugging into a separate trace mode. 2015-01-09 02:06:20 +02:00
Rolf Ahrenberg
791767f02b Documented CI extension parameters. 2015-01-07 17:45:48 +02:00
Rolf Ahrenberg
0d52649dbd Added the missing translation item. 2015-01-06 21:41:52 +02:00
Rolf Ahrenberg
0c03c1b8d4 Added a CI extension item into the device info menu. 2015-01-06 19:46:56 +02:00
Rolf Ahrenberg
db59aa9c29 Added support for Digital Devices CI extension. 2015-01-06 15:53:31 +02:00
Rolf Ahrenberg
0fd559ce79 Added a fallback for older glibc libraries. 2015-01-05 20:24:45 +02:00
Rolf Ahrenberg
e10ce57210 Removed the unnecessary config directory definition. 2015-01-05 20:15:04 +02:00
Rolf Ahrenberg
c7f4b265d5 Fixed the server teardown. 2015-01-05 20:12:15 +02:00
Rolf Ahrenberg
35c0f25c38 Updated the command-line help and README. 2015-01-05 20:09:45 +02:00
Rolf Ahrenberg
dadd26f980 Removed an unnecessary wait. 2014-12-22 21:19:24 +02:00
Rolf Ahrenberg
5c5f21631f Added transponder information into the device info menu. 2014-12-22 20:16:28 +02:00
Rolf Ahrenberg
29eaf994f8 Added a multicast mode for the RTSP setup. 2014-12-21 16:31:38 +02:00
Rolf Ahrenberg
f884b23f7c Renamed logging to tracing. 2014-12-21 16:11:05 +02:00
Rolf Ahrenberg
1bc2dc01fd Updated HISTORY. 2014-12-20 18:58:15 +02:00
Rolf Ahrenberg
eba49c979f Removed the special logging mode for a debug build. 2014-12-20 18:50:34 +02:00
Rolf Ahrenberg
3475c179d1 Added an info message for malfunctioning devices with quirks. 2014-12-20 18:47:02 +02:00
Rolf Ahrenberg
4cb25ce93c Fixed CURLOPT_URL. 2014-12-20 18:46:00 +02:00
Rolf Ahrenberg
c5499ce1da Prepare for a release. 2014-12-19 20:08:27 +02:00
Rolf Ahrenberg
fa226b0d2f Removed DVB-C model quirks from OctopusNet. The firmware version 1.0.40 looks correct. 2014-12-18 00:27:13 +02:00
Rolf Ahrenberg
4b714196f3 Added a SCAN command into the SVDRP interface. 2014-12-18 00:25:01 +02:00
Rolf Ahrenberg
9cace319a3 Reformatted some info messages. 2014-12-16 23:09:31 +02:00
Rolf Ahrenberg
01d2afcfc2 Fixed reconnection. 2014-12-16 23:09:14 +02:00
Rolf Ahrenberg
8ea561a021 Fixed the default logging mode in a DEBUG build. 2014-12-16 22:00:22 +02:00
Rolf Ahrenberg
d23578cea9 Fixed a race condition while updating pids. 2014-12-14 22:09:18 +02:00
Rolf Ahrenberg
629c4ff378 Renamed threads and ringbuffers. 2014-12-14 21:39:13 +02:00
Rolf Ahrenberg
2d997b3fdd Added RTCP packet logging. 2014-12-14 19:22:52 +02:00
Rolf Ahrenberg
7f9d016b52 Deduplicated pid listings. 2014-12-14 17:54:37 +02:00
Rolf Ahrenberg
778f3bd84a Preliminary documentation for used logging modes. 2014-12-14 17:45:55 +02:00
nafets227
382e1dedef Performance enhancement via recvmmsg().
Use recvmmsg() in order to read multiple packets with one system call.
This improves performance, especially in have loaded areas when catching up
a log of queued packets.

Original patch tweaked and optimized by Rolf Ahrenberg.
2014-12-14 15:39:18 +02:00
Rolf Ahrenberg
fecbd3cbd4 Added better logging for channel changes. 2014-12-14 01:56:21 +02:00
nafets227
7c1aa732b4 Improved RTP data logging.
Original patch tweaked ad optimized by Rolf Ahrenberg.
2014-12-12 21:38:11 +02:00
Rolf Ahrenberg
395390fb32 Added a device hook support for ProvidesTransponder(). 2014-12-09 21:56:32 +02:00
nafets227
913cdbef66 Logging improvements.
Logging of RTSP command summaries on debug 5
Logging for Performance/Locking on debug 6

Original patch tweaked and optimized by Rolf Ahrenberg.
2014-12-09 00:27:19 +02:00
Rolf Ahrenberg
60a2b1fecf Fixed the LOGG SVDRP command output. 2014-12-08 21:37:17 +02:00
Rolf Ahrenberg
b9d89b8c1d Added more debugging macros. 2014-12-08 21:23:23 +02:00
Rolf Ahrenberg
d0ffc3e1a5 Enabled CURL debug callbacks always. 2014-12-07 23:14:02 +02:00
Rolf Ahrenberg
46db1dea11 Reworked the RTSP debug callback to adapt dynamically any logging mode changes during SETUP. 2014-12-07 22:04:39 +02:00
Rolf Ahrenberg
dd320af7f2 Fixed the session id quirk. 2014-12-07 21:58:06 +02:00
Rolf Ahrenberg
d6ffffd932 Fixed the tuning status. 2014-12-07 18:12:08 +02:00
Rolf Ahrenberg
7f9060a4cb Reformatted debug messages. 2014-12-07 17:27:53 +02:00
Rolf Ahrenberg
097a607389 Renamed some "SAT>IP" texts into a simpler "SATIP". 2014-12-07 16:19:23 +02:00
Rolf Ahrenberg
45b3166729 Simplified the logging interface. 2014-12-07 16:10:12 +02:00
Rolf Ahrenberg
38f815d439 Added new debugging modes. 2014-12-06 17:10:02 +02:00
Rolf Ahrenberg
23dce9e205 Changed logging to to use SATIP instead of SAT>IP. 2014-12-06 00:37:55 +02:00
Rolf Ahrenberg
796a047401 Renamed logging level to the actual mask. 2014-12-06 00:06:24 +02:00
Rolf Ahrenberg
b62a25597b Reworked header dependencies. 2014-12-05 23:14:40 +02:00
Rolf Ahrenberg
81f6af3bdf Reformat debug messages. 2014-12-05 23:02:57 +02:00
Rolf Ahrenberg
9a40a8eeec Fixed compilation under g++-4.7. 2014-12-04 21:44:44 +02:00
Rolf Ahrenberg
0668fb7a15 Added a new extra() macro. 2014-12-03 22:57:04 +02:00
Rolf Ahrenberg
735e7487d3 Added a preliminary support for dynamic logging level. 2014-12-03 19:57:23 +02:00
Rolf Ahrenberg
d48fe3bced Fixed OPTIONS url and enabled retuning. 2014-12-03 01:42:33 +02:00
Rolf Ahrenberg
98437ce57e Fixed retuning. 2014-12-02 23:19:24 +02:00
Rolf Ahrenberg
f6ab251294 Removed unnecessary this declarations. 2014-12-01 21:50:02 +02:00
Rolf Ahrenberg
0fe1722dee Increased TS buffer size to one megabyte to avoid overrun errors on my RPi. 2014-12-01 20:26:19 +02:00
Rolf Ahrenberg
9b88c0d55f Decreased TS buffer timeouts to 10ms. 2014-12-01 18:12:12 +02:00
Rolf Ahrenberg
e6e185cbd7 Changed int to uint64_t. 2014-11-30 01:00:20 +02:00
nafets227
561ca26098 Reduced locking scope for a performance improvement.
Reduced the time that cSatipTunerStatistics() is locked.
This is important for poller thread as every lock in cSatipTunerStatistics()
leads to the poller thread waiting in SatipTuner::ProcessVideoData().
2014-11-30 00:54:05 +02:00
nafets227
4600a2a070 Performance improvement by avoiding locks in cSatipTuner::ProcessApplicationData().
Don't use malloc, but put variable on stack (=local char array).
This avoids a possible lock with other threads, because malloc uses global
storage and needs a lock then.

Tests eliminated the long lasting processing of RTCP packaged in poller
Thread.
2014-11-29 21:24:29 +02:00
Rolf Ahrenberg
52f54d2177 Prepare for a release. 2014-11-29 17:03:07 +02:00
Rolf Ahrenberg
8415075de9 Added preliminary tuner state queues. 2014-11-29 17:00:31 +02:00
Rolf Ahrenberg
eea0aa33bd Added cSatipDiscoverIf(). 2014-11-29 15:37:21 +02:00
Rolf Ahrenberg
cdb2e0e3b4 Added logging of maximum processing time in cPoller(). 2014-11-29 14:51:27 +02:00
nafets227
e0727516ce Bugfix: Don't block in cTunerIf()::Process(Video|Application)Data() methods.
The original patch is polished and tweaked by Rolf Ahrenberg.
2014-11-29 14:26:59 +02:00
nafets227
fde3198997 Bugfix: Reading from sockets.
In cSatipRtp::Process() und cSatipRtcp::Process() we need to read until no more data is
available, not just once.

The original patch is polished and tweaked by Rolf Ahrenberg.
2014-11-27 23:13:51 +02:00
nafets227
c966d28d13 Bugfix: Write data to tsbuffer only if DvrOpen has been called. This avoids Buffer Overflow when not recording or viewing live. 2014-11-26 23:51:49 +02:00
Rolf Ahrenberg
6d68ef3e49 Simplified poller interface. 2014-11-25 22:04:34 +02:00
Rolf Ahrenberg
1642f59980 Get rid of CheckData() as we are receiving only live streams. 2014-11-23 19:28:29 +02:00
nafets227
80abbddae7 Corrected log Macros.
If used like the code below, it would produce a compile error as
in the 'if' path there would be two semicolons:

if (anything)
   debug(...);
else
   doanything();
2014-11-23 17:59:32 +02:00
Rolf Ahrenberg
8bd4a1a67d Fixed teardown. 2014-11-23 00:13:15 +02:00
Rolf Ahrenberg
3a16e57f87 Get rid of OPTIONS while retuning. 2014-11-22 23:17:32 +02:00
Rolf Ahrenberg
1244397365 Updated debug messages. 2014-11-22 21:55:58 +02:00
Rolf Ahrenberg
1f528cf7e1 Fixed tuner shutdown. 2014-11-22 21:33:31 +02:00
Rolf Ahrenberg
5d697c36dc Fixed reading reception status. 2014-11-22 20:06:56 +02:00
Rolf Ahrenberg
005fa59dd6 Tweaked tuning. 2014-11-22 17:04:32 +02:00
Rolf Ahrenberg
6d64a8b0a7 Fixed keep-alive and retuning. 2014-11-22 15:01:37 +02:00
Rolf Ahrenberg
4e2e6d0b9b Fixed OPTIONS syntax. 2014-11-22 12:26:50 +02:00
Rolf Ahrenberg
ade52d5a22 Cleaned up debug messages. 2014-11-21 23:56:03 +02:00
Rolf Ahrenberg
524b21e042 Updated against SAT>IP protocol specification version 1.2.1. 2014-11-20 22:39:19 +02:00
Rolf Ahrenberg
0896df33e6 Merge pull request #3 from rofafor/poller
Refactored poller implementation.
2014-11-19 22:17:48 +02:00
Rolf Ahrenberg
111a1ff16d Merge branch 'master' into poller
Conflicts:
	tuner.c
	tuner.h
2014-11-19 22:10:54 +02:00
Rolf Ahrenberg
8d198178bb Updated version number. 2014-11-19 21:16:51 +02:00
Rolf Ahrenberg
ec596a02b8 Tweaked tuner state machine. 2014-11-19 21:05:18 +02:00
Rolf Ahrenberg
8ec972d4ee Factored tuner state machine a bit more robust. 2014-11-17 22:59:01 +02:00
Rolf Ahrenberg
54f8b4f0a6 Get rid of pointers. 2014-11-17 22:33:38 +02:00
Rolf Ahrenberg
f5015bcfba Refactored device discovery. 2014-11-17 22:28:11 +02:00
Rolf Ahrenberg
443dd9706a Get rid of a magic number. 2014-11-16 21:42:24 +02:00
Rolf Ahrenberg
2dcf3bbd6a Get rid of pointers. 2014-11-16 17:03:40 +02:00
Rolf Ahrenberg
12d61d37cf Renamed Action() to Process(). 2014-11-16 17:03:35 +02:00
Rolf Ahrenberg
a94d25b635 Get rid of pointers. 2014-11-16 16:39:20 +02:00
Rolf Ahrenberg
6b2090e9ad Moved tuner statistics back to cSatipTuner. 2014-11-16 16:30:45 +02:00
Rolf Ahrenberg
5c051d919b Fixed tuner reconnection issues. 2014-11-16 15:38:23 +02:00
Rolf Ahrenberg
02bf42b006 Fixed buffer handling in cSatipTuner::ParseReceptionParameters(). 2014-11-16 15:00:08 +02:00
nafets227
adddf3e4e7 Moved ReadVideo/Application() from cSatipSocket into cSatipRtp/cStatipRtcp classes and get rid of unnecessary memcpy.
The original patch is polished and tweaked by Rolf Ahrenberg.
2014-11-16 14:56:59 +02:00
Rolf Ahrenberg
3a742f1f14 Fixed cSatipTuner::Disconnect(). 2014-11-15 22:59:40 +02:00
Rolf Ahrenberg
82fe5c59c8 Fixed cSatipTuner::Disconnect(). 2014-11-15 22:58:52 +02:00
Rolf Ahrenberg
304f1d4af6 Fixed cSatipTuner::Connect(). 2014-11-15 22:16:38 +02:00
Rolf Ahrenberg
4a9a36a9c2 Tweaked error messages. 2014-11-15 22:15:47 +02:00
Rolf Ahrenberg
daa2f903c8 Fixed cSatipTuner::Connect(). 2014-11-15 22:05:46 +02:00
Rolf Ahrenberg
d36597954a Tweaked error messages. 2014-11-15 21:58:38 +02:00
Rolf Ahrenberg
0bf841555b Implemented a preliminary state machine for tuner. 2014-11-15 20:44:42 +02:00
Rolf Ahrenberg
0431806f24 Implemented a preliminary state machine for tuner. 2014-11-15 20:29:12 +02:00
Nafets227
ede0294943 Refactored polling.
The original patch is polished and tweaked by Rolf Ahrenberg.
2014-11-15 02:12:35 +02:00
Rolf Ahrenberg
e05801b464 Refactored epoll() to use only one thread. 2014-11-11 21:10:44 +02:00
Rolf Ahrenberg
e51650cd0a Updated HISTORY. 2014-11-11 00:38:03 +02:00
Rolf Ahrenberg
45d0227d2b Tweaked internals and log messages to match SVDRP interface. 2014-11-11 00:05:03 +02:00
Rolf Ahrenberg
7110cee7a9 Added STAT command into SVDRP interface. 2014-11-10 23:46:45 +02:00
Rolf Ahrenberg
a9109d750c Some minor tweaks. 2014-11-10 23:31:30 +02:00
Rolf Ahrenberg
ac4ab867d9 Switched to epoll(). 2014-11-10 23:05:26 +02:00
Rolf Ahrenberg
2f723e0f66 Added cSatipRtsp(). 2014-11-09 16:42:14 +02:00
Rolf Ahrenberg
72a5ad34fb Added cSatipPid(). 2014-11-09 12:48:45 +02:00
Rolf Ahrenberg
123fc5f96b Added support for CURL debug callback. 2014-11-09 02:25:47 +02:00
Rolf Ahrenberg
cf930286c2 Updated German translation (Thanks to Stefan Schallenberg). 2014-11-08 20:01:59 +02:00
Rolf Ahrenberg
2308fc4a65 Fixed EIT scan (Thanks to Stefan Schallenberg). 2014-11-08 20:01:47 +02:00
Rolf Ahrenberg
04e7648c01 Added support for SAT>IP frontend selection via Radio ID. 2014-11-04 23:20:10 +02:00
Rolf Ahrenberg
18ca0beaa7 Added support for defining multiple SAT>IP servers via the command-line switch. 2014-11-04 22:32:12 +02:00
Rolf Ahrenberg
aeb8f24474 Added support for Telestar Digibit R1 (Thanks to Dirk Wagner). 2014-11-04 21:02:30 +02:00
Rolf Ahrenberg
bde88d78b1 Added a command-line switch for manually defining a SAT>IP server. 2014-11-03 23:57:08 +02:00
Rolf Ahrenberg
30e8040f99 Decreased the default buffer size. 2014-11-03 21:57:16 +02:00
Rolf Ahrenberg
5795bb58af Fixed SetChannelDevice() with NULL channel and EOL in ReadApplication(). 2014-11-03 21:55:54 +02:00
Rolf Ahrenberg
d81f700194 Added a new device status menu. 2014-11-03 21:55:28 +02:00
Rolf Ahrenberg
0de0be5ae4 Added a preliminary support for Fritz!WLAN Repeater DVB-C (Thanks to Christian Wick). 2014-11-03 21:54:19 +02:00
Rolf Ahrenberg
92b2c63f0d Fixed server reuse for active transponders. 2014-11-01 16:31:44 +02:00
Rolf Ahrenberg
c4d82eac36 Added support for blacklisted sources. 2014-10-31 22:17:52 +02:00
Rolf Ahrenberg
3630a9f78a Added a quirk to avoid addpids/delpids in PLAY commands. 2014-10-31 18:18:33 +02:00
Rolf Ahrenberg
c8497e1fce Fixed the cable only device detection. 2014-10-30 22:10:09 +02:00
Rolf Ahrenberg
fb9c1c6a44 Added a session id quirk for Triax TSS 400. 2014-05-17 21:41:55 +03:00
Rolf Ahrenberg
5e2770a0e5 Added a validity check for the session member. 2014-05-14 22:14:59 +03:00
Rolf Ahrenberg
53b643139f Fixed the warning message about the libcurl version. 2014-05-11 17:11:28 +03:00
Rolf Ahrenberg
9a8c6f8c0d Fixed model detection and OctopusNet DVB-C model quirks.
Added a session id quirk for GSSBOX.
2014-05-08 22:09:35 +03:00
Rolf Ahrenberg
fbbfdfab2b Fixed the device discovery. 2014-04-24 20:04:43 +03:00
Rolf Ahrenberg
78ef582bea Tweaked the pid update mechanism. 2014-04-19 17:27:47 +03:00
Rolf Ahrenberg
6e72eba09c Increased the pid update interval to 250ms. 2014-04-15 20:04:01 +03:00
Rolf Ahrenberg
fb40846171 Bumped up the version number. 2014-04-12 23:07:27 +03:00
Rolf Ahrenberg
b19be5b5c5 Reformatted the channel frequency value. 2014-04-12 23:02:26 +03:00
Rolf Ahrenberg
1a304e9dc2 Improved debug messages. 2014-04-12 16:57:28 +03:00
Rolf Ahrenberg
5ff54d27a3 Removed unnecessary PLAY commands and header callbacks. 2014-04-12 16:45:29 +03:00
Rolf Ahrenberg
276370afbb Added Spanish translation (Thanks to Gabriel Bonich). 2014-04-11 13:22:34 +03:00
Rolf Ahrenberg
f2081c4f00 Fixed the OPTIONS URL and enchanced the user agent string. 2014-04-06 23:42:49 +03:00
Rolf Ahrenberg
388543a58d Added a recommendation for using libcurl version >= 7.36.0. 2014-04-06 00:55:16 +03:00
Rolf Ahrenberg
f46d07a95f Added Catalan translation (Thanks to Gabriel Bonich). 2014-04-05 13:50:55 +03:00
Rolf Ahrenberg
43cc15357e Optimized re-sending. 2014-04-05 13:46:07 +03:00
Rolf Ahrenberg
5b1af5ba29 Refactored the section filtering. 2014-04-04 00:56:00 +03:00
Rolf Ahrenberg
80fc28d8cf Fixed eMinKeepAliveIntervalMs typo. 2014-04-02 21:06:37 +03:00
Rolf Ahrenberg
5e8b7b7c5e Fixed keepalive heartbeat again. 2014-04-01 09:05:13 +03:00
Rolf Ahrenberg
88b2ad9244 Added missing RTP packet error message. 2014-03-30 22:58:43 +03:00
Rolf Ahrenberg
7b58d9f28f Added a check to write new sections only if there is no data in the read socket. 2014-03-30 22:20:56 +03:00
Rolf Ahrenberg
f493df0e3d Changed implementation to report about RTP packet errors on 5 minutes interval only. 2014-03-30 18:20:20 +03:00
44 changed files with 4164 additions and 1101 deletions

3
.gitignore vendored
View File

@@ -4,3 +4,6 @@
*~
po/*.pot
po/*.mo
.settings
.cproject
.project

112
HISTORY
View File

@@ -25,3 +25,115 @@ VDR Plugin 'satip' Revision History
- Added support for cDevice::Ready().
- Fixed pid leaking while disabling section filters.
- Fixed keepalive heartbeat.
2014-04-01: Version 0.2.1
- Changed implementation to report about RTP packet
errors on 5 minutes interval only.
- Added a check to write new sections only if there
is no data in the read socket.
- Fixed keepalive heartbeat again.
2014-04-05: Version 0.2.2
- Fixed the default keepalive interval.
- Refactored the section filtering.
- Added Catalan translation (Thanks to Gabriel Bonich).
2014-04-12: Version 0.2.3
- Added Spanish translation (Thanks to Gabriel Bonich).
- Fixed parameters of the OPTIONS command.
- Added a device identication into the user agent string.
- Removed unnecessary PLAY commands and header callbacks.
2014-04-20: Version 0.3.0
- Tweaked the pid update mechanism.
2014-04-27: Version 0.3.1
- Fixed the device discovery.
2014-05-10: Version 0.3.2
- Fixed model detection and OctopusNet DVB-C model quirks.
- Added a session id quirk for GSSBOX.
2014-05-18: Version 0.3.3
- Added a validity check for the session member.
- Added a session id quirk for Triax TSS 400.
2014-12-24: Version 1.0.0
- Fixed the cable only device detection.
- Added support for blacklisted sources.
- Fixed server reuse for active transponders.
- Added a preliminary support for Fritz!WLAN
Repeater DVB-C (Thanks to Christian Wick).
- Added a preliminary support for Telestar
Digibit R1 (Thanks to Dirk Wagner).
- Added a new device status menu.
- Added support for SAT>IP frontend selection via
Radio ID.
- Added command-line switches for manually defining
used SAT>IP servers and setting used tracing mode.
- Added new STAT and TRAC commands into the SVDRP
interface.
- Refactored the tuner implementation.
- Updated against SAT>IP protocol specification
version 1.2.1.
- Refactored input thread to increase performance.
- Added plenty of performance tweaks (Thanks to
Stefan Schallenberg).
- Fixed EIT scan (Thanks to Stefan Schallenberg).
2015-01-10: Version 1.0.1
- Updated the command-line help and README.
- Fixed the server teardown.
- Removed the unnecessary config directory definition.
- Added a fallback for older glibc libraries.
- Improved pid selection performance.
- Added support for Digital Devices CI extension.
2015-01-18: Version 1.0.2
- Added configurable CI slots.
- Fixed parsing of the setup values.
- Added an option to disable sources via sources.conf.
- Added a command-line option to disable all the
SAT>IP server quirks.
- Updated Spanish and Catalan translations (Thanks to
Gabriel Bonich).
- Updated German translations (Thanks to Frank Neumann).
===================================
VDR Plugin 'satip' Revision History
===================================
2015-02-19: Version 2.2.0
- Updated for vdr-2.2.0.
- Fixed memory deallocation errors.
- Cleaned up all scan-build warnings.
- Refactored the frontend handling.
2015-04-04: Version 2.2.1
- Improved RTSP error checking.
- Got rid of SATIP_DEBUG.
- Robustify the server discovery.
- Fixed a memory leak in TinyXML implementation
(Thanks to Oliver Endriss).
- Updated against SAT>IP protocol specification
version 1.2.2.
2015-04-26: Version 2.2.2
- Added a more flexible OPER command in the SVDRP
interface.
- Added new ATTA and DETA SVDRP commands.
- Set the default device count to two.

View File

@@ -2,18 +2,10 @@
# Makefile for SAT>IP plugin
#
# Debugging on/off
#SATIP_DEBUG = 1
# Use TinyXML instead of PugiXML
#SATIP_USE_TINYXML = 1
# Strip debug symbols? Set eg. to /bin/true if not
STRIP = strip
# 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.
@@ -40,6 +32,7 @@ TMPDIR ?= /tmp
export CFLAGS = $(call PKGCFG,cflags)
export CXXFLAGS = $(call PKGCFG,cxxflags)
STRIP ?= /bin/true
### The version number of VDR's plugin API:
@@ -75,10 +68,6 @@ else
LIBS += -lpugixml
endif
ifdef SATIP_DEBUG
DEFINES += -DDEBUG
endif
ifneq ($(strip $(GITTAG)),)
DEFINES += -DGITVERSION='"-GIT-$(GITTAG)"'
endif
@@ -88,8 +77,9 @@ all-redirect: all
### The object files (add further files here):
OBJS = $(PLUGIN).o common.o config.o device.o discover.o param.o \
sectionfilter.o server.o setup.o socket.o statistics.o tuner.o
OBJS = $(PLUGIN).o common.o config.o device.o discover.o msearch.o param.o \
poller.o rtp.o rtcp.o rtsp.o sectionfilter.o server.o setup.o socket.o \
statistics.o tuner.o
### The main target:
@@ -139,9 +129,7 @@ install-i18n: $(I18Nmsgs)
$(SOFILE): $(OBJS)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) $(LIBS) -o $@
ifndef SATIP_DEBUG
@$(STRIP) $@
endif
install-lib: $(SOFILE)
install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION)

64
README
View File

@@ -14,7 +14,7 @@ See the file COPYING for more information.
Requirements:
- Libcurl - the multiprotocol file transfer library with RTSP support
- Libcurl >= 7.36.0 - the multiprotocol file transfer library with RTSP support
http://curl.haxx.se/libcurl/
- PugiXML - Light-weight, simple and fast XML parser for C++
@@ -23,7 +23,8 @@ Requirements:
TinyXML - a simple, small, C++ XML parser
http://www.grinninglizard.com/tinyxml/
- VDR-2.1.4+ for scrambled channels
- Glibc >= 2.12 - the GNU C library (recvmmsg)
http://www.gnu.org/software/libc/
Description:
@@ -41,13 +42,24 @@ make -C satip-X.Y.Z install
Configuration:
The plugin accepts a "--devices" (-d) command-line parameter defaulting
to one. This parameter defines how many simultaneous transponders can
to two. This parameter defines how many simultaneous transponders can
be received, if there are available SAT>IP tuners.
The plugin accepts also a "--server" (-s) command-line parameter, that
can be used to manually configure static SAT>IP servers if autodetection
via UPnP somehow can't be used. The parameter string is a semicolon
separated list of "<ipaddress>|<model>|<description>" entries. The model
consists of a DVB system (DVBS2,DVBT2,DVBT,DVBC) and number of available
frontends separated by a hyphen:
vdr -P 'satip -s <ipaddress>|<model>|<description>;...'
vdr -P 'satip -s 192.168.0.1|DVBS2-2,DVBT2-2|Octo1'
vdr -P 'satip -s 192.168.0.1|DVBS2-4|Octo1;192.168.0.2|DVBT2-4|Octo2'
SAT>IP satellite positions (aka. signal sources) shall be defined via
sources.conf. If the source description begins with a number, it's used
as SAT>IP signal source selection parameter. Otherwise, the default
parameter is one:
as SAT>IP signal source selection parameter. A special number zero can
be used to disable the source. Otherwise, the default parameter is one:
S19.2E Astra 1KR/1L/1M/2C
=> Signal source = 1
@@ -58,6 +70,14 @@ S19.2E 2
S19.2E 3 Astra 1KR/1L/1M/2C
=> Signal source = 3
S19.2E 0 Astra 1KR/1L/1M/2C
=> Source is disabled
A channel can be assigned into a specific SAT>IP frontend by giving the
identifier number in RID field of a channels.conf entry:
FE = RID % 100
Valid range: 1 ... 99
Setup menu:
- Operating mode = off If you want exclude all SAT>IP devices
@@ -68,19 +88,30 @@ Setup menu:
option to "low". Similarly, the "high"
value prefers the SAT>IP over the local
DVB cards when selecting available devices.
- Use CI extension = no If you want to use the CI extension found
in some SAT>IP hardware (e.g. Digital
Devices OctopusNet), set this option to
"yes".
- CICAM #<slot> = <system> If you want to assign a CA system into
a specific CI slot, set this option to
a named one. Use "---" for autoselection.
- Enable EPG scanning = yes If you want exclude all SAT>IP devices
from VDR's EIT background scanning, set
this option to "no".
- Disabled sources = none If your SAT>IP servers don't have certain
satellite positions available you can
disable them via this option.
- Disabled filters = none Certain section filters might cause some
unwanted behaviour to VDR such as time
being falsely synchronized etc. This option
allows creation of blacklists of ill-behaving
allows creation of blacklists of ill-behaving
filters. If this option is set to a non-zero
value, the menu page will contain that many
"Disable filter" options which allow you
to disable the individual section filters.
Valid range: "none" = 0 ... 7
- [Red:Scan] Forces network scanning of SAT>IP hardware.
- [Yellow:Devices] Opens SAT>IP device status menu.
- [Blue:Info] Opens SAT>IP information/statistics menu.
- [Ok] Opens information menu of selected SAT>IP
device.
@@ -97,23 +128,22 @@ Notes:
- The stream id "-1" states about unsuccessful tuning. This might be a
result of invalid channel parameters or lack of free SAT>IP tuners.
- SAT>IP specification 1.2 doesn't support DVB-C/DVB-C2 receivers yet,
but DVB-C (KABEL>IP) is supported for Digital Devices Octopus Net
devices.
- If the plugin doesn't detect your SAT>IP network device, make sure
your setup doesn't have firewalled the UDP port 1900.
- Stream decryption requires a separate CAM plugin that works without
direct access to any DVB card devices. The integrated CAM slot in
Octopus Net devices isn't supported.
direct access to any DVB card devices. Also the integrated CAM slots
in Octopus Net devices are supported.
- The 100% compliance against SAT>IP specification 1.2 requires a
patched VDR providing channel configuration for pilot, T2 system id,
and SISO/MISO values.
- Tracing can be set on/off dynamically via command-line switch or
SVDRP command.
- If you're using Triax TSS400, you'll need the libcurl from 2013-03-20
or newer.
- OctopusNet firmware 1.0.40 or greater recommended.
- Inverto OEM firmware 1.17.0.120 or greater recommended.
The firmware 1.16.0.120 can be downloaded and installed
from their webpage: http://www.inverto.tv/support/
An update to a newer firmware should be offered afterwards.
Acknowledgements:

View File

@@ -57,6 +57,15 @@ char *StripTags(char *strP)
return NULL;
}
char *SkipZeroes(const char *strP)
{
if ((uchar)*strP != '0')
return (char *)strP;
while (*strP && (uchar)*strP == '0')
strP++;
return (char *)strP;
}
cString ChangeCase(const cString &strP, bool upperP)
{
cString res(strP);
@@ -70,10 +79,72 @@ cString ChangeCase(const cString &strP, bool upperP)
const section_filter_table_type section_filter_table[SECTION_FILTER_TABLE_SIZE] =
{
/* description tag pid tid mask */
// description tag pid tid mask
{trNOOP("PAT (0x00)"), "PAT", 0x00, 0x00, 0xFF},
{trNOOP("NIT (0x40)"), "NIT", 0x10, 0x40, 0xFF},
{trNOOP("SDT (0x42)"), "SDT", 0x11, 0x42, 0xFF},
{trNOOP("EIT (0x4E/0x4F/0x5X/0x6X)"), "EIT", 0x12, 0x40, 0xC0},
{trNOOP("TDT (0x70)"), "TDT", 0x14, 0x70, 0xFF},
};
const ca_systems_table_type ca_systems_table[CA_SYSTEMS_TABLE_SIZE] =
{
// http://www.dvb.org/index.php?id=174
// http://en.wikipedia.org/wiki/Conditional_access_system
// start end description
{0x0000, 0x0000, "---" }, // 0
{0x0100, 0x01FF, "SECA Mediaguard (100..1FF)"}, // 1
{0x0464, 0x0464, "EuroDec (464)" }, // 2
{0x0500, 0x05FF, "Viaccess (500..5FF)" }, // 3
{0x0600, 0x06FF, "Irdeto (600..6FF)" }, // 4
{0x0700, 0x07FF, "DigiCipher 2 (700..7FF)" }, // 5
{0x0900, 0x09FF, "NDS Videoguard (900..9FF)" }, // 6
{0x0B00, 0x0BFF, "Conax (B00..BFF)" }, // 7
{0x0D00, 0x0DFF, "CryptoWorks (D00..DFF)" }, // 8
{0x0E00, 0x0EFF, "PowerVu (E00..EFF)" }, // 9
{0x1000, 0x10FF, "RAS (1000..10FF)" }, // 10
{0x1200, 0x12FF, "NagraVision (1200..12FF)" }, // 11
{0x1700, 0x17FF, "VCAS (1700..17FF)" }, // 12
{0x1800, 0x18FF, "NagraVision (1800..18FF)" }, // 13
{0x22F0, 0x22F0, "Codicrypt (22F0)" }, // 14
{0x2600, 0x2600, "BISS (2600)" }, // 15
{0x2719, 0x2719, "VanyaCas (2719)" }, // 16
{0x4347, 0x4347, "CryptOn (4347)" }, // 17
{0x4800, 0x4800, "Accessgate (4800)" }, // 18
{0x4900, 0x4900, "China Crypt (4900)" }, // 19
{0x4A02, 0x4A02, "Tongfang (4A02)" }, // 20
{0x4A10, 0x4A10, "EasyCas (4A10)" }, // 21
{0x4A20, 0x4A20, "AlphaCrypt (4A20)" }, // 22
{0x4A60, 0x4A60, "SkyCrypt (4A60)" }, // 23
{0x4A61, 0x4A61, "Neotioncrypt (4A61)" }, // 24
{0x4A62, 0x4A62, "SkyCrypt (4A62)" }, // 25
{0x4A63, 0x4A63, "Neotion SHL (4A63)" }, // 26
{0x4A64, 0x4A6F, "SkyCrypt (4A64)" }, // 27
{0x4A70, 0x4A70, "DreamCrypt (4A70)" }, // 28
{0x4A80, 0x4A80, "ThalesCrypt (4A80)" }, // 29
{0x4AA1, 0x4AA1, "KeyFly (4AA1)" }, // 30
{0x4ABF, 0x4ABF, "CTI-CAS (4ABF)" }, // 31
{0x4AC1, 0x4AC1, "Latens (4AC1)" }, // 32
{0x4AD0, 0x4AD1, "X-Crypt (4AD0)" }, // 33
{0x4AD4, 0x4AD4, "OmniCrypt (4AD4)" }, // 34
{0x4AE0, 0x4AE1, "Z-Crypt (4AE0)" }, // 35
{0x4AE4, 0x4AE4, "CoreCrypt (4AE4)" }, // 36
{0x4AE5, 0x4AE5, "PRO-Crypt (4AE5)" }, // 37
{0x4AEA, 0x4AEA, "Cryptoguard (4AEA)" }, // 38
{0x4AEB, 0x4AEB, "Abel Quintic (4AEB)" }, // 39
{0x4AF0, 0x4AF0, "ABV (4AF0)" }, // 40
{0x5500, 0x5500, "Z-Crypt (5500)" }, // 41
{0x5501, 0x5501, "Griffin (5501)" }, // 42
{0x5581, 0x5581, "Bulcrypt (5581)" }, // 43
{0x7BE1, 0x7BE1, "DRE-Crypt (7BE1)" }, // 44
{0xA101, 0xA101, "RosCrypt-M (A101)" }, // 45
{0xEAD0, 0xEAD0, "VanyaCas (EAD0)" }, // 46
};
bool checkCASystem(unsigned int cicamP, int caidP)
{
// always skip the first row
if ((cicamP > 0) && (cicamP < ELEMENTS(ca_systems_table)))
return ((caidP >= ca_systems_table[cicamP].start) && (caidP <= ca_systems_table[cicamP].end));
return false;
}

101
common.h
View File

@@ -8,23 +8,14 @@
#ifndef __SATIP_COMMON_H
#define __SATIP_COMMON_H
#include <vdr/device.h>
#include <vdr/tools.h>
#include <vdr/config.h>
#include <vdr/i18n.h>
#ifdef DEBUG
#define debug(x...) dsyslog("SATIP: " x);
#define info(x...) isyslog("SATIP: " x);
#define error(x...) esyslog("ERROR: " x);
#else
#define debug(x...) ;
#define info(x...) isyslog("SATIP: " x);
#define error(x...) esyslog("ERROR: " x);
#endif
#define SATIP_MAX_DEVICES MAXDEVICES
#define ELEMENTS(x) (sizeof(x) / sizeof(x[0]))
#define SATIP_BUFFER_SIZE MEGABYTE(1)
#define SATIP_BUFFER_SIZE KILOBYTE(1024)
#define SATIP_DEVICE_INFO_ALL 0
#define SATIP_DEVICE_INFO_GENERAL 1
@@ -36,32 +27,36 @@
#define SATIP_STATS_ACTIVE_PIDS_COUNT 10
#define SATIP_STATS_ACTIVE_FILTERS_COUNT 10
#define MAX_DISABLED_SOURCES_COUNT 25
#define SECTION_FILTER_TABLE_SIZE 5
#define MAX_CICAM_COUNT 2
#define CA_SYSTEMS_TABLE_SIZE 47
#define SATIP_CURL_EASY_GETINFO(X, Y, Z) \
if ((res = curl_easy_getinfo((X), (Y), (Z))) != CURLE_OK) { \
error("curl_easy_getinfo(%s) [%s,%d] failed: %s (%d)", #Y, __FILE__, __LINE__, curl_easy_strerror(res), res); \
esyslog("curl_easy_getinfo(%s) [%s,%d] failed: %s (%d)", #Y, __FILE__, __LINE__, curl_easy_strerror(res), res); \
}
#define SATIP_CURL_EASY_SETOPT(X, Y, Z) \
if ((res = curl_easy_setopt((X), (Y), (Z))) != CURLE_OK) { \
error("curl_easy_setopt(%s, %s) [%s,%d] failed: %s (%d)", #Y, #Z, __FILE__, __LINE__, curl_easy_strerror(res), res); \
esyslog("curl_easy_setopt(%s, %s) [%s,%d] failed: %s (%d)", #Y, #Z, __FILE__, __LINE__, curl_easy_strerror(res), res); \
}
#define SATIP_CURL_EASY_PERFORM(X) \
if ((res = curl_easy_perform((X))) != CURLE_OK) { \
error("curl_easy_perform() [%s,%d] failed: %s (%d)", __FILE__, __LINE__, curl_easy_strerror(res), res); \
esyslog("curl_easy_perform() [%s,%d] failed: %s (%d)", __FILE__, __LINE__, curl_easy_strerror(res), res); \
}
#define ERROR_IF_FUNC(exp, errstr, func, ret) \
do { \
if (exp) { \
char tmp[64]; \
error("[%s,%d]: "errstr": %s", __FILE__, __LINE__, \
strerror_r(errno, tmp, sizeof(tmp))); \
func; \
ret; \
} \
#define ERROR_IF_FUNC(exp, errstr, func, ret) \
do { \
if (exp) { \
char tmp[64]; \
esyslog("[%s,%d]: "errstr": %s", __FILE__, __LINE__, \
strerror_r(errno, tmp, sizeof(tmp))); \
func; \
ret; \
} \
} while (0)
@@ -87,12 +82,59 @@
} \
} while (0)
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
#define ELEMENTS(x) (sizeof(x) / sizeof(x[0]))
class cSatipMemoryBuffer {
private:
enum {
eMaxDataSize = MEGABYTE(2)
};
char *dataM;
size_t sizeM;
void *AllocBuffer(void *ptrP, size_t sizeP)
{
// There might be a realloc() out there that doesn't like reallocing NULL pointers, so we take care of it here
if (ptrP)
return realloc(ptrP, sizeP);
else
return malloc(sizeP);
}
// to prevent copy constructor and assignment
cSatipMemoryBuffer(const cSatipMemoryBuffer&);
cSatipMemoryBuffer& operator=(const cSatipMemoryBuffer&);
public:
cSatipMemoryBuffer() : dataM(NULL), sizeM(0) {}
~cSatipMemoryBuffer() { Reset(); }
size_t Add(char *dataP, size_t sizeP)
{
if (sizeP > 0) {
size_t len = sizeM + sizeP + 1;
if (len < eMaxDataSize) {
dataM = (char *)AllocBuffer(dataM, len);
if (dataM) {
memcpy(&(dataM[sizeM]), dataP, sizeP);
sizeM += sizeP;
dataM[sizeM] = 0;
return sizeP;
}
else
esyslog("[%s,%d]: Failed to allocate memory", __FILE__, __LINE__);
}
else
esyslog("[%s,%d]: Buffer overflow", __FILE__, __LINE__);
}
return 0;
};
char *Data(void) { return dataM; }
size_t Size(void) { return sizeM; }
void Reset(void) { FREE_POINTER(dataM); sizeM = 0; };
};
uint16_t ts_pid(const uint8_t *bufP);
uint8_t payload(const uint8_t *bufP);
const char *id_pid(const u_short pidP);
char *StripTags(char *strP);
char *SkipZeroes(const char *strP);
cString ChangeCase(const cString &strP, bool upperP);
struct section_filter_table_type {
@@ -105,6 +147,15 @@ struct section_filter_table_type {
extern const section_filter_table_type section_filter_table[SECTION_FILTER_TABLE_SIZE];
struct ca_systems_table_type {
int start;
int end;
const char *description;
};
extern const ca_systems_table_type ca_systems_table[CA_SYSTEMS_TABLE_SIZE];
extern bool checkCASystem(unsigned int cicamP, int caidP);
extern const char VERSION[];
#endif // __SATIP_COMMON_H

View File

@@ -6,41 +6,74 @@
*/
#include "discover.h"
#include "log.h"
#include "config.h"
cSatipConfig SatipConfig;
cSatipConfig::cSatipConfig(void)
: operatingModeM(eOperatingModeLow),
traceModeM(eTraceModeNormal),
ciExtensionM(0),
eitScanM(1),
useBytesM(1)
useBytesM(1),
detachedModeM(false),
disableServerQuirksM(false),
useSingleModelServersM(false)
{
for (unsigned int i = 0; i < ARRAY_SIZE(disabledFiltersM); ++i)
for (unsigned int i = 0; i < ELEMENTS(cicamsM); ++i)
cicamsM[i] = 0;
for (unsigned int i = 0; i < ELEMENTS(disabledSourcesM); ++i)
disabledSourcesM[i] = cSource::stNone;
for (unsigned int i = 0; i < ELEMENTS(disabledFiltersM); ++i)
disabledFiltersM[i] = -1;
memset(configDirectoryM, 0, sizeof(configDirectoryM));
}
int cSatipConfig::GetCICAM(unsigned int indexP) const
{
return (indexP < ELEMENTS(cicamsM)) ? cicamsM[indexP] : -1;
}
void cSatipConfig::SetCICAM(unsigned int indexP, int cicamP)
{
if (indexP < ELEMENTS(cicamsM))
cicamsM[indexP] = cicamP;
}
unsigned int cSatipConfig::GetDisabledSourcesCount(void) const
{
unsigned int n = 0;
while ((n < ELEMENTS(disabledSourcesM) && (disabledSourcesM[n] != cSource::stNone)))
n++;
return n;
}
int cSatipConfig::GetDisabledSources(unsigned int indexP) const
{
return (indexP < ELEMENTS(disabledSourcesM)) ? disabledSourcesM[indexP] : cSource::stNone;
}
void cSatipConfig::SetDisabledSources(unsigned int indexP, int sourceP)
{
if (indexP < ELEMENTS(disabledSourcesM))
disabledSourcesM[indexP] = sourceP;
}
unsigned int cSatipConfig::GetDisabledFiltersCount(void) const
{
unsigned int n = 0;
while ((n < ARRAY_SIZE(disabledFiltersM) && (disabledFiltersM[n] != -1)))
while ((n < ELEMENTS(disabledFiltersM) && (disabledFiltersM[n] != -1)))
n++;
return n;
}
int cSatipConfig::GetDisabledFilters(unsigned int indexP) const
{
return (indexP < ARRAY_SIZE(disabledFiltersM)) ? disabledFiltersM[indexP] : -1;
return (indexP < ELEMENTS(disabledFiltersM)) ? disabledFiltersM[indexP] : -1;
}
void cSatipConfig::SetDisabledFilters(unsigned int indexP, int numberP)
{
if (indexP < ARRAY_SIZE(disabledFiltersM))
if (indexP < ELEMENTS(disabledFiltersM))
disabledFiltersM[indexP] = numberP;
}
void cSatipConfig::SetConfigDirectory(const char *directoryP)
{
debug("cSatipConfig::%s(%s)", __FUNCTION__, directoryP);
ERROR_IF(!realpath(directoryP, configDirectoryM), "Cannot canonicalize configuration directory");
}

View File

@@ -15,19 +15,45 @@ class cSatipConfig
{
private:
unsigned int operatingModeM;
unsigned int traceModeM;
unsigned int ciExtensionM;
unsigned int eitScanM;
unsigned int useBytesM;
bool detachedModeM;
bool disableServerQuirksM;
bool useSingleModelServersM;
int cicamsM[MAX_CICAM_COUNT];
int disabledSourcesM[MAX_DISABLED_SOURCES_COUNT];
int disabledFiltersM[SECTION_FILTER_TABLE_SIZE];
char configDirectoryM[PATH_MAX];
public:
enum {
enum eOperatingMode {
eOperatingModeOff = 0,
eOperatingModeLow,
eOperatingModeNormal,
eOperatingModeHigh,
eOperatingModeCount
};
enum eTraceMode {
eTraceModeNormal = 0x0000,
eTraceModeDebug1 = 0x0001,
eTraceModeDebug2 = 0x0002,
eTraceModeDebug3 = 0x0004,
eTraceModeDebug4 = 0x0008,
eTraceModeDebug5 = 0x0010,
eTraceModeDebug6 = 0x0020,
eTraceModeDebug7 = 0x0040,
eTraceModeDebug8 = 0x0080,
eTraceModeDebug9 = 0x0100,
eTraceModeDebug10 = 0x0200,
eTraceModeDebug11 = 0x0400,
eTraceModeDebug12 = 0x0800,
eTraceModeDebug13 = 0x1000,
eTraceModeDebug14 = 0x2000,
eTraceModeDebug15 = 0x4000,
eTraceModeDebug16 = 0x8000,
eTraceModeMask = 0xFFFF
};
cSatipConfig();
unsigned int GetOperatingMode(void) const { return operatingModeM; }
bool IsOperatingModeOff(void) const { return (operatingModeM == eOperatingModeOff); }
@@ -35,16 +61,30 @@ public:
bool IsOperatingModeNormal(void) const { return (operatingModeM == eOperatingModeNormal); }
bool IsOperatingModeHigh(void) const { return (operatingModeM == eOperatingModeHigh); }
void ToggleOperatingMode(void) { operatingModeM = (operatingModeM + 1) % eOperatingModeCount; }
unsigned int GetTraceMode(void) const { return traceModeM; }
bool IsTraceMode(eTraceMode modeP) const { return (traceModeM & modeP); }
unsigned int GetCIExtension(void) const { return ciExtensionM; }
int GetCICAM(unsigned int indexP) const;
unsigned int GetEITScan(void) const { return eitScanM; }
unsigned int GetUseBytes(void) const { return useBytesM; }
const char *GetConfigDirectory(void) const { return configDirectoryM; }
bool GetDetachedMode(void) const { return detachedModeM; }
bool GetDisableServerQuirks(void) const { return disableServerQuirksM; }
bool GetUseSingleModelServers(void) const { return useSingleModelServersM; }
unsigned int GetDisabledSourcesCount(void) const;
int GetDisabledSources(unsigned int indexP) const;
unsigned int GetDisabledFiltersCount(void) const;
int GetDisabledFilters(unsigned int indexP) const;
void SetOperatingMode(unsigned int operatingModeP) { operatingModeM = operatingModeP; }
void SetTraceMode(unsigned int modeP) { traceModeM = (modeP & eTraceModeMask); }
void SetCIExtension(unsigned int onOffP) { ciExtensionM = onOffP; }
void SetCICAM(unsigned int indexP, int cicamP);
void SetEITScan(unsigned int onOffP) { eitScanM = onOffP; }
void SetUseBytes(unsigned int onOffP) { useBytesM = onOffP; }
void SetConfigDirectory(const char *directoryP);
void SetDetachedMode(bool onOffP) { detachedModeM = onOffP; }
void SetDisableServerQuirks(bool onOffP) { disableServerQuirksM = onOffP; }
void SetUseSingleModelServers(bool onOffP) { useSingleModelServersM = onOffP; }
void SetDisabledSources(unsigned int indexP, int sourceP);
void SetDisabledFilters(unsigned int indexP, int numberP);
};

183
device.c
View File

@@ -5,13 +5,14 @@
*
*/
#include <vdr/menu.h> // cRecordControl
#include "config.h"
#include "discover.h"
#include "log.h"
#include "param.h"
#include "device.h"
#define SATIP_MAX_DEVICES MAXDEVICES
static cSatipDevice * SatipDevicesS[SATIP_MAX_DEVICES] = { NULL };
cSatipDevice::cSatipDevice(unsigned int indexP)
@@ -25,11 +26,11 @@ cSatipDevice::cSatipDevice(unsigned int indexP)
{
unsigned int bufsize = (unsigned int)SATIP_BUFFER_SIZE;
bufsize -= (bufsize % TS_SIZE);
isyslog("creating SAT>IP device %d (CardIndex=%d)", deviceIndexM, CardIndex());
info("Creating device CardIndex=%d DeviceNumber=%d [device %u]", CardIndex(), DeviceNumber(), deviceIndexM);
tsBufferM = new cRingBufferLinear(bufsize + 1, TS_SIZE, false,
*cString::sprintf("SAT>IP TS %d", deviceIndexM));
*cString::sprintf("SATIP#%d TS", deviceIndexM));
if (tsBufferM) {
tsBufferM->SetTimeouts(100, 100);
tsBufferM->SetTimeouts(10, 10);
tsBufferM->SetIoThrottle();
pTunerM = new cSatipTuner(*this, tsBufferM->Free());
}
@@ -40,7 +41,7 @@ cSatipDevice::cSatipDevice(unsigned int indexP)
cSatipDevice::~cSatipDevice()
{
debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
debug1("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
// Stop section handler
StopSectionHandler();
DELETE_POINTER(pSectionFilterHandlerM);
@@ -50,7 +51,7 @@ cSatipDevice::~cSatipDevice()
bool cSatipDevice::Initialize(unsigned int deviceCountP)
{
debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceCountP);
debug1("%s (%u)", __PRETTY_FUNCTION__, deviceCountP);
if (deviceCountP > SATIP_MAX_DEVICES)
deviceCountP = SATIP_MAX_DEVICES;
for (unsigned int i = 0; i < deviceCountP; ++i)
@@ -62,7 +63,7 @@ bool cSatipDevice::Initialize(unsigned int deviceCountP)
void cSatipDevice::Shutdown(void)
{
debug("cSatipDevice::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
for (int i = 0; i < SATIP_MAX_DEVICES; ++i) {
if (SatipDevicesS[i])
SatipDevicesS[i]->CloseDvr();
@@ -72,7 +73,7 @@ void cSatipDevice::Shutdown(void)
unsigned int cSatipDevice::Count(void)
{
unsigned int count = 0;
debug("cSatipDevice::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
for (unsigned int i = 0; i < SATIP_MAX_DEVICES; ++i) {
if (SatipDevicesS[i] != NULL)
count++;
@@ -82,19 +83,51 @@ unsigned int cSatipDevice::Count(void)
cSatipDevice *cSatipDevice::GetSatipDevice(int cardIndexP)
{
//debug("cSatipDevice::%s(%d)", __FUNCTION__, cardIndexP);
debug16("%s (%d)", __PRETTY_FUNCTION__, cardIndexP);
for (unsigned int i = 0; i < SATIP_MAX_DEVICES; ++i) {
if (SatipDevicesS[i] && (SatipDevicesS[i]->CardIndex() == cardIndexP)) {
//debug("cSatipDevice::%s(%d): found!", __FUNCTION__, cardIndexP);
debug16("%s (%d): Found!", __PRETTY_FUNCTION__, cardIndexP);
return SatipDevicesS[i];
}
}
return NULL;
}
cString cSatipDevice::GetSatipStatus(void)
{
cString info = "";
for (int i = 0; i < cDevice::NumDevices(); i++) {
const cDevice *device = cDevice::GetDevice(i);
if (device && strstr(device->DeviceType(), "SAT>IP")) {
int timers = 0;
bool live = (device == cDevice::ActualDevice());
bool lock = device->HasLock();
const cChannel *channel = device->GetCurrentlyTunedTransponder();
for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer)) {
if (timer->Recording()) {
cRecordControl *control = cRecordControls::GetRecordControl(timer);
if (control && control->Device() == device)
timers++;
}
}
info = cString::sprintf("%sDevice: %s\n", *info, *device->DeviceName());
if (lock)
info = cString::sprintf("%sCardIndex: %d HasLock: yes Strength: %d Quality: %d%s\n", *info, device->CardIndex(), device->SignalStrength(), device->SignalQuality(), live ? " Live: yes" : "");
else
info = cString::sprintf("%sCardIndex: %d HasLock: no\n", *info, device->CardIndex());
if (channel && channel->Number() > 0)
info = cString::sprintf("%sTransponder: %d Channel: %s\n", *info, (channel && channel->Number() > 0) ? channel->Transponder() : 0, (channel && channel->Number() > 0) ? channel->Name() : "---");
if (timers)
info = cString::sprintf("%sRecording: %d timer%s\n", *info, timers, (timers > 1) ? "s" : "");
info = cString::sprintf("%s\n", *info);
}
}
return isempty(*info) ? cString(tr("SAT>IP information not available!")) : info;
}
cString cSatipDevice::GetGeneralInformation(void)
{
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
debug16("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
return cString::sprintf("SAT>IP device: %d\nCardIndex: %d\nStream: %s\nSignal: %s\nStream bitrate: %s\n%sChannel: %s",
deviceIndexM, CardIndex(),
pTunerM ? *pTunerM->GetInformation() : "",
@@ -106,13 +139,13 @@ cString cSatipDevice::GetGeneralInformation(void)
cString cSatipDevice::GetPidsInformation(void)
{
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
debug16("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
return GetPidStatistic();
}
cString cSatipDevice::GetFiltersInformation(void)
{
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
debug16("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
return cString::sprintf("Active section filters:\n%s", pSectionFilterHandlerM ? *pSectionFilterHandlerM->GetInformation() : "");
}
@@ -148,50 +181,66 @@ cString cSatipDevice::GetInformation(unsigned int pageP)
bool cSatipDevice::Ready(void)
{
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
debug16("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
return ((cSatipDiscover::GetInstance()->GetServerCount() > 0) || (createdM.Elapsed() > eReadyTimeoutMs));
}
cString cSatipDevice::DeviceType(void) const
{
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
debug16("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
return "SAT>IP";
}
cString cSatipDevice::DeviceName(void) const
{
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
debug16("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
return deviceNameM;
}
bool cSatipDevice::AvoidRecording(void) const
{
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
debug16("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
return SatipConfig.IsOperatingModeLow();
}
int cSatipDevice::SignalStrength(void) const
{
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
debug16("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
return (pTunerM ? pTunerM->SignalStrength() : -1);
}
int cSatipDevice::SignalQuality(void) const
{
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
debug16("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
return (pTunerM ? pTunerM->SignalQuality() : -1);
}
bool cSatipDevice::ProvidesSource(int sourceP) const
{
debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
return (!SatipConfig.IsOperatingModeOff() && !!cSatipDiscover::GetInstance()->GetServer(sourceP));
cSource *s = Sources.Get(sourceP);
debug9("%s (%c) desc='%s' [device %u]", __PRETTY_FUNCTION__, cSource::ToChar(sourceP), s ? s->Description() : "", deviceIndexM);
if (SatipConfig.GetDetachedMode())
return false;
// source descriptions starting with '0' are disabled
if (s && s->Description() && (*(s->Description()) == '0'))
return false;
if (!SatipConfig.IsOperatingModeOff() && !!cSatipDiscover::GetInstance()->GetServer(sourceP)) {
int numDisabledSourcesM = SatipConfig.GetDisabledSourcesCount();
for (int i = 0; i < numDisabledSourcesM; ++i) {
if (sourceP == SatipConfig.GetDisabledSources(i))
return false;
}
return true;
}
return false;
}
bool cSatipDevice::ProvidesTransponder(const cChannel *channelP) const
{
debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
return (ProvidesSource(channelP->Source()));
debug9("%s (%d) transponder=%d source=%c [device %u]", __PRETTY_FUNCTION__, channelP ? channelP->Number() : -1, channelP ? channelP->Transponder() : -1, channelP ? cSource::ToChar(channelP->Source()) : '?', deviceIndexM);
if (!ProvidesSource(channelP->Source()))
return false;
return DeviceHooksProvidesTransponder(channelP);
}
bool cSatipDevice::ProvidesChannel(const cChannel *channelP, int priorityP, bool *needsDetachReceiversP) const
@@ -200,7 +249,7 @@ bool cSatipDevice::ProvidesChannel(const cChannel *channelP, int priorityP, bool
bool hasPriority = (priorityP == IDLEPRIORITY) || (priorityP > this->Priority());
bool needsDetachReceivers = false;
debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
debug9("%s (%d, %d, %d) [device %u]", __PRETTY_FUNCTION__, channelP ? channelP->Number() : -1, priorityP, !!needsDetachReceiversP, deviceIndexM);
if (channelP && ProvidesTransponder(channelP)) {
result = hasPriority;
@@ -272,31 +321,36 @@ bool cSatipDevice::MaySwitchTransponder(const cChannel *channelP) const
bool cSatipDevice::SetChannelDevice(const cChannel *channelP, bool liveViewP)
{
debug9("%s (%d, %d) [device %u]", __PRETTY_FUNCTION__, channelP ? channelP->Number() : -1, liveViewP, deviceIndexM);
if (channelP) {
cDvbTransponderParameters dtp(channelP->Parameters());
cString params = GetTransponderUrlParameters(channelP);
if (isempty(params)) {
error("Unrecognized SAT>IP channel parameters: %s", channelP->Parameters());
error("Unrecognized channel parameters: %s [device %u]", channelP->Parameters(), deviceIndexM);
return false;
}
cString address;
cSatipServer *server = cSatipDiscover::GetInstance()->GetServer(channelP->Source(), dtp.System());
cSatipServer *server = cSatipDiscover::GetInstance()->AssignServer(deviceIndexM, channelP->Source(), channelP->Transponder(), dtp.System());
if (!server) {
debug("cSatipDevice::%s(%u): no suitable server found", __FUNCTION__, deviceIndexM);
debug9("%s No suitable server found [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
return false;
}
if (pTunerM && pTunerM->SetSource(server, *params, deviceIndexM)) {
deviceNameM = cString::sprintf("%s %d %s", *DeviceType(), deviceIndexM, *cSatipDiscover::GetInstance()->GetServerString(server));
if (pTunerM && pTunerM->SetSource(server, channelP->Transponder(), *params, deviceIndexM)) {
channelM = *channelP;
deviceNameM = cString::sprintf("%s %d %s", *DeviceType(), deviceIndexM, *cSatipDiscover::GetInstance()->GetServerString(server));
return true;
}
}
else if (pTunerM) {
pTunerM->SetSource(NULL, 0, NULL, deviceIndexM);
return true;
}
return false;
}
bool cSatipDevice::SetPid(cPidHandle *handleP, int typeP, bool onP)
{
//debug("cSatipDevice::%s(%u): pid=%d type=%d on=%d", __FUNCTION__, deviceIndexM, handleP->pid, typeP, onP);
debug12("%s (%d, %d, %d) [device %u]", __PRETTY_FUNCTION__, handleP->pid, typeP, onP, deviceIndexM);
if (pTunerM && handleP && handleP->pid >= 0) {
if (onP)
return pTunerM->SetPid(handleP->pid, typeP, true);
@@ -308,7 +362,7 @@ bool cSatipDevice::SetPid(cPidHandle *handleP, int typeP, bool onP)
int cSatipDevice::OpenFilter(u_short pidP, u_char tidP, u_char maskP)
{
//debug("cSatipDevice::%s(%u): pid=%d tid=%d mask=%d", __FUNCTION__, deviceIndexM, pidP, tidP, maskP);
debug12("%s (%d, %02X, %02X) [device %d]", __PRETTY_FUNCTION__, pidP, tidP, maskP, deviceIndexM);
if (pSectionFilterHandlerM) {
int handle = pSectionFilterHandlerM->Open(pidP, tidP, maskP);
if (pTunerM && (handle >= 0))
@@ -320,17 +374,18 @@ int cSatipDevice::OpenFilter(u_short pidP, u_char tidP, u_char maskP)
void cSatipDevice::CloseFilter(int handleP)
{
//debug("cSatipDevice::%s(%u): handle=%d", __FUNCTION__, deviceIndexM, handleP);
if (pSectionFilterHandlerM) {
int pid = pSectionFilterHandlerM->GetPid(handleP);
debug12("%s (%d) [device %u]", __PRETTY_FUNCTION__, pid, deviceIndexM);
if (pTunerM)
pTunerM->SetPid(pSectionFilterHandlerM->GetPid(handleP), ptOther, false);
pTunerM->SetPid(pid, ptOther, false);
pSectionFilterHandlerM->Close(handleP);
}
}
bool cSatipDevice::OpenDvr(void)
{
debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
debug9("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
isPacketDeliveredM = false;
tsBufferM->Clear();
if (pTunerM)
@@ -341,7 +396,7 @@ bool cSatipDevice::OpenDvr(void)
void cSatipDevice::CloseDvr(void)
{
debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
debug9("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
if (pTunerM)
pTunerM->Close();
isOpenDvrM = false;
@@ -349,21 +404,21 @@ void cSatipDevice::CloseDvr(void)
bool cSatipDevice::HasLock(int timeoutMsP) const
{
//debug("cSatipDevice::%s(%u): timeoutMs=%d", __FUNCTION__, deviceIndexM, timeoutMsP);
debug16("%s (%d) [device %d]", __PRETTY_FUNCTION__, timeoutMsP, deviceIndexM);
return (pTunerM && pTunerM->HasLock());
}
bool cSatipDevice::HasInternalCam(void)
{
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
return false;
debug16("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
return SatipConfig.GetCIExtension();
}
void cSatipDevice::WriteData(uchar *bufferP, int lengthP)
{
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
debug16("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
// Fill up TS buffer
if (tsBufferM) {
if (isOpenDvrM && tsBufferM) {
int len = tsBufferM->Put(bufferP, lengthP);
if (len != lengthP)
tsBufferM->ReportOverflow(lengthP - len);
@@ -373,17 +428,41 @@ void cSatipDevice::WriteData(uchar *bufferP, int lengthP)
pSectionFilterHandlerM->Write(bufferP, lengthP);
}
unsigned int cSatipDevice::CheckData(void)
int cSatipDevice::GetId(void)
{
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
if (tsBufferM)
return (unsigned int)tsBufferM->Free();
return 0;
return deviceIndexM;
}
int cSatipDevice::GetPmtPid(void)
{
int pid = channelM.Ca() ? ::GetPmtPid(channelM.Source(), channelM.Transponder(), channelM.Sid()) : 0;
debug11("%s pmtpid=%d source=%c transponder=%d sid=%d name=%s [device %u]", __PRETTY_FUNCTION__, pid, cSource::ToChar(channelM.Source()), channelM.Transponder(), channelM.Sid(), channelM.Name(), deviceIndexM);
return pid;
}
int cSatipDevice::GetCISlot(void)
{
int slot = 0;
int ca = 0;
for (const int *id = channelM.Caids(); *id; ++id) {
if (checkCASystem(SatipConfig.GetCICAM(0), *id)) {
ca = *id;
slot = 1;
break;
}
else if (checkCASystem(SatipConfig.GetCICAM(1), *id)) {
ca = *id;
slot = 2;
break;
}
}
debug11("%s slot=%d ca=%X name=%s [device %u]", __PRETTY_FUNCTION__, slot, ca, channelM.Name(), deviceIndexM);
return slot;
}
uchar *cSatipDevice::GetData(int *availableP)
{
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
debug16("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
if (isOpenDvrM && tsBufferM) {
int count = 0;
if (isPacketDeliveredM)
@@ -414,7 +493,7 @@ uchar *cSatipDevice::GetData(int *availableP)
void cSatipDevice::SkipData(int countP)
{
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
debug16("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
tsBufferM->Del(countP);
isPacketDeliveredM = false;
// Update buffer statistics
@@ -423,9 +502,10 @@ void cSatipDevice::SkipData(int countP)
bool cSatipDevice::GetTSPacket(uchar *&dataP)
{
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
debug16("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
if (SatipConfig.GetDetachedMode())
return false;
if (tsBufferM) {
#if defined(APIVERSNUM) && APIVERSNUM >= 20104
if (cCamSlot *cs = CamSlot()) {
if (cs->WantsTsData()) {
int available;
@@ -437,12 +517,9 @@ bool cSatipDevice::GetTSPacket(uchar *&dataP)
return true;
}
}
#endif
dataP = GetData();
return true;
}
// Reduce cpu load by preventing busylooping
cCondWait::SleepMs(10);
dataP = NULL;
return true;
}

View File

@@ -23,6 +23,7 @@ public:
static void Shutdown(void);
static unsigned int Count(void);
static cSatipDevice *GetSatipDevice(int CardIndex);
static cString GetSatipStatus(void);
// private parts
private:
@@ -106,7 +107,9 @@ public:
// for internal device interface
public:
virtual void WriteData(u_char *bufferP, int lengthP);
virtual unsigned int CheckData(void);
virtual int GetId(void);
virtual int GetPmtPid(void);
virtual int GetCISlot(void);
};
#endif // __SATIP_DEVICE_H

View File

@@ -13,7 +13,9 @@ public:
cSatipDeviceIf() {}
virtual ~cSatipDeviceIf() {}
virtual void WriteData(u_char *bufferP, int lengthP) = 0;
virtual unsigned int CheckData(void) = 0;
virtual int GetId(void) = 0;
virtual int GetPmtPid(void) = 0;
virtual int GetCISlot(void) = 0;
private:
cSatipDeviceIf(const cSatipDeviceIf&);

View File

@@ -13,18 +13,12 @@
#endif
#include "common.h"
#include "config.h"
#include "log.h"
#include "socket.h"
#include "discover.h"
cSatipDiscover *cSatipDiscover::instanceS = NULL;
const char *cSatipDiscover::bcastAddressS = "239.255.255.250";
const char *cSatipDiscover::bcastMessageS = "M-SEARCH * HTTP/1.1\r\n" \
"HOST: 239.255.255.250:1900\r\n" \
"MAN: \"ssdp:discover\"\r\n" \
"ST: urn:ses-com:device:SatIPServer:1\r\n" \
"MX: 2\r\n\r\n";
cSatipDiscover *cSatipDiscover::GetInstance(void)
{
if (!instanceS)
@@ -32,87 +26,92 @@ cSatipDiscover *cSatipDiscover::GetInstance(void)
return instanceS;
}
bool cSatipDiscover::Initialize(void)
bool cSatipDiscover::Initialize(cSatipDiscoverServers *serversP)
{
debug("cSatipDiscover::%s()", __FUNCTION__);
if (instanceS)
instanceS->Activate();
debug1("%s", __PRETTY_FUNCTION__);
if (instanceS) {
if (serversP) {
for (cSatipDiscoverServer *s = serversP->First(); s; s = serversP->Next(s))
instanceS->AddServer(s->IpAddress(), s->Model(), s->Description());
}
else
instanceS->Activate();
}
return true;
}
void cSatipDiscover::Destroy(void)
{
debug("cSatipDiscover::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
if (instanceS)
instanceS->Deactivate();
}
size_t cSatipDiscover::WriteCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP)
size_t cSatipDiscover::DataCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP)
{
cSatipDiscover *obj = reinterpret_cast<cSatipDiscover *>(dataP);
size_t len = sizeP * nmembP;
//debug("cSatipDiscover::%s(%zu)", __FUNCTION__, len);
debug16("%s len=%zu", __PRETTY_FUNCTION__, len);
if (obj) {
CURLcode res = CURLE_OK;
const char *desc = NULL, *model = NULL, *addr = NULL;
#ifdef USE_TINYXML
TiXmlDocument doc;
char *xml = MALLOC(char, len + 1);
memcpy(xml, ptrP, len);
*(xml + len + 1) = 0;
doc.Parse((const char *)xml);
TiXmlHandle docHandle(&doc);
TiXmlElement *descElement = docHandle.FirstChild("root").FirstChild("device").FirstChild("friendlyName").ToElement();
if (descElement)
desc = descElement->GetText() ? descElement->GetText() : "MyBrokenHardware";
TiXmlElement *modelElement = docHandle.FirstChild("root").FirstChild("device").FirstChild("satip:X_SATIPCAP").ToElement();
if (modelElement)
model = modelElement->GetText() ? modelElement->GetText() : "DVBS2-1";
#else
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_buffer(ptrP, len);
if (result) {
pugi::xml_node descNode = doc.first_element_by_path("root/device/friendlyName");
if (descNode)
desc = descNode.text().as_string("MyBrokenHardware");
pugi::xml_node modelNode = doc.first_element_by_path("root/device/satip:X_SATIPCAP");
if (modelNode)
model = modelNode.text().as_string("DVBS2-1");
}
#endif
SATIP_CURL_EASY_GETINFO(obj->handleM, CURLINFO_PRIMARY_IP, &addr);
obj->AddServer(addr, desc, model);
}
if (obj && (len > 0))
obj->dataBufferM.Add(ptrP, len);
return len;
}
int cSatipDiscover::DebugCallback(CURL *handleP, curl_infotype typeP, char *dataP, size_t sizeP, void *userPtrP)
{
cSatipDiscover *obj = reinterpret_cast<cSatipDiscover *>(userPtrP);
if (obj) {
switch (typeP) {
case CURLINFO_TEXT:
debug2("%s HTTP INFO %.*s", __PRETTY_FUNCTION__, (int)sizeP, dataP);
break;
case CURLINFO_HEADER_IN:
debug2("%s HTTP HEAD <<< %.*s", __PRETTY_FUNCTION__, (int)sizeP, dataP);
break;
case CURLINFO_HEADER_OUT:
debug2("%s HTTP HEAD >>>\n%.*s", __PRETTY_FUNCTION__, (int)sizeP, dataP);
break;
case CURLINFO_DATA_IN:
debug2("%s HTTP DATA <<< %.*s", __PRETTY_FUNCTION__, (int)sizeP, dataP);
break;
case CURLINFO_DATA_OUT:
debug2("%s HTTP DATA >>>\n%.*s", __PRETTY_FUNCTION__, (int)sizeP, dataP);
break;
default:
break;
}
}
return 0;
}
cSatipDiscover::cSatipDiscover()
: cThread("SAT>IP discover"),
: cThread("SATIP discover"),
mutexM(),
dataBufferM(),
msearchM(*this),
probeUrlListM(),
handleM(curl_easy_init()),
socketM(new cSatipSocket()),
sleepM(),
probeIntervalM(0),
serversM(new cSatipServers())
serversM()
{
debug("cSatipDiscover::%s()", __FUNCTION__);
// Start the thread
Start();
debug1("%s", __PRETTY_FUNCTION__);
}
cSatipDiscover::~cSatipDiscover()
{
debug("cSatipDiscover::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
Deactivate();
cMutexLock MutexLock(&mutexM);
// Free allocated memory
DELETENULL(socketM);
DELETENULL(serversM);
if (handleM)
curl_easy_cleanup(handleM);
handleM = NULL;
probeUrlListM.Clear();
}
void cSatipDiscover::Activate(void)
@@ -123,7 +122,7 @@ void cSatipDiscover::Activate(void)
void cSatipDiscover::Deactivate(void)
{
debug("cSatipDiscover::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
cMutexLock MutexLock(&mutexM);
sleepM.Signal();
if (Running())
@@ -132,119 +131,137 @@ void cSatipDiscover::Deactivate(void)
void cSatipDiscover::Action(void)
{
debug("cSatipDiscover::%s(): entering", __FUNCTION__);
debug1("%s Entering", __PRETTY_FUNCTION__);
probeIntervalM.Set(eProbeIntervalMs);
msearchM.Probe();
// Do the thread loop
while (Running()) {
cStringList tmp;
if (probeIntervalM.TimedOut()) {
probeIntervalM.Set(eProbeIntervalMs);
Probe();
Janitor();
msearchM.Probe();
mutexM.Lock();
serversM.Cleanup(eCleanupTimeoutMs);
mutexM.Unlock();
}
mutexM.Lock();
if (probeUrlListM.Size()) {
for (int i = 0; i < probeUrlListM.Size(); ++i)
tmp.Insert(strdup(probeUrlListM.At(i)));
probeUrlListM.Clear();
}
mutexM.Unlock();
if (tmp.Size()) {
for (int i = 0; i < tmp.Size(); ++i)
Fetch(tmp.At(i));
tmp.Clear();
}
// to avoid busy loop and reduce cpu load
sleepM.Wait(10);
sleepM.Wait(eSleepTimeoutMs);
}
debug("cSatipDiscover::%s(): exiting", __FUNCTION__);
debug1("%s Exiting", __PRETTY_FUNCTION__);
}
void cSatipDiscover::Janitor(void)
void cSatipDiscover::Fetch(const char *urlP)
{
debug("cSatipDiscover::%s()", __FUNCTION__);
cMutexLock MutexLock(&mutexM);
if (serversM)
serversM->Cleanup(eProbeIntervalMs * 2);
}
debug1("%s (%s)", __PRETTY_FUNCTION__, urlP);
if (handleM && !isempty(urlP)) {
const char *addr = NULL;
long rc = 0;
CURLcode res = CURLE_OK;
void cSatipDiscover::Probe(void)
{
debug("cSatipDiscover::%s()", __FUNCTION__);
if (socketM && socketM->Open(eDiscoveryPort)) {
cTimeMs timeout(eProbeTimeoutMs);
socketM->Write(bcastAddressS, reinterpret_cast<const unsigned char *>(bcastMessageS), strlen(bcastMessageS));
while (Running() && !timeout.TimedOut()) {
Read();
// to avoid busy loop and reduce cpu load
sleepM.Wait(100);
}
socketM->Close();
// Verbose output
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_VERBOSE, 1L);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_DEBUGFUNCTION, cSatipDiscover::DebugCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_DEBUGDATA, this);
// Set callback
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, cSatipDiscover::DataCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, this);
// No progress meter and no signaling
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_NOPROGRESS, 1L);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_NOSIGNAL, 1L);
// Set timeouts
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_TIMEOUT_MS, (long)eConnectTimeoutMs);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_CONNECTTIMEOUT_MS, (long)eConnectTimeoutMs);
// Set user-agent
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_USERAGENT, *cString::sprintf("vdr-%s/%s", PLUGIN_NAME_I18N, VERSION));
// Set URL
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_URL, urlP);
// Fetch the data
SATIP_CURL_EASY_PERFORM(handleM);
SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_RESPONSE_CODE, &rc);
SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_PRIMARY_IP, &addr);
if (rc == 200) {
ParseDeviceInfo(addr);
dataBufferM.Reset();
}
else
error("Discovery detected invalid status code: %ld", rc);
}
}
void cSatipDiscover::Read(void)
void cSatipDiscover::ParseDeviceInfo(const char *addrP)
{
//debug("cSatipDiscover::%s()", __FUNCTION__);
if (socketM) {
unsigned char *buf = MALLOC(unsigned char, eProbeBufferSize + 1);
if (buf) {
memset(buf, 0, eProbeBufferSize + 1);
int len = socketM->Read(buf, eProbeBufferSize);
if (len > 0) {
//debug("cSatipDiscover::%s(): len=%d", __FUNCTION__, len);
bool status = false;
char *s, *p = reinterpret_cast<char *>(buf), *location = NULL;
char *r = strtok_r(p, "\r\n", &s);
while (r) {
//debug("cSatipDiscover::%s(): %s", __FUNCTION__, r);
// Check the status code
// HTTP/1.1 200 OK
if (!status && startswith(r, "HTTP/1.1 200 OK")) {
status = true;
}
// Check the location data
// LOCATION: http://192.168.0.115:8888/octonet.xml
if (status && startswith(r, "LOCATION:")) {
location = compactspace(r + 9);
debug("cSatipDiscover::%s(): location='%s'", __FUNCTION__, location);
break;
}
r = strtok_r(NULL, "\r\n", &s);
}
if (handleM && !isempty(location)) {
long rc = 0;
CURLcode res = CURLE_OK;
#ifdef DEBUG
// Verbose output
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_VERBOSE, 1L);
debug1("%s (%s)", __PRETTY_FUNCTION__, addrP);
const char *desc = NULL, *model = NULL;
#ifdef USE_TINYXML
TiXmlDocument doc;
doc.Parse(dataBufferM.Data());
TiXmlHandle docHandle(&doc);
TiXmlElement *descElement = docHandle.FirstChild("root").FirstChild("device").FirstChild("friendlyName").ToElement();
if (descElement)
desc = descElement->GetText() ? descElement->GetText() : "MyBrokenHardware";
TiXmlElement *modelElement = docHandle.FirstChild("root").FirstChild("device").FirstChild("satip:X_SATIPCAP").ToElement();
if (modelElement)
model = modelElement->GetText() ? modelElement->GetText() : "DVBS2-1";
#else
pugi::xml_document doc;
if (doc.load_buffer(dataBufferM.Data(), dataBufferM.Size())) {
pugi::xml_node descNode = doc.first_element_by_path("root/device/friendlyName");
if (descNode)
desc = descNode.text().as_string("MyBrokenHardware");
pugi::xml_node modelNode = doc.first_element_by_path("root/device/satip:X_SATIPCAP");
if (modelNode)
model = modelNode.text().as_string("DVBS2-1");
}
#endif
// Set callback
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, cSatipDiscover::WriteCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, this);
// No progress meter and no signaling
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_NOPROGRESS, 1L);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_NOSIGNAL, 1L);
// Set timeouts
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_TIMEOUT_MS, (long)eConnectTimeoutMs);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_CONNECTTIMEOUT_MS, (long)eConnectTimeoutMs);
// Set user-agent
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_USERAGENT, *cString::sprintf("vdr-%s/%s", PLUGIN_NAME_I18N, VERSION));
// Set URL
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_URL, location);
// Fetch the data
SATIP_CURL_EASY_PERFORM(handleM);
SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_RESPONSE_CODE, &rc);
if (rc != 200)
error("Discovery detected invalid status code: %ld", rc);
}
}
free(buf);
}
}
AddServer(addrP, model, desc);
}
void cSatipDiscover::AddServer(const char *addrP, const char *descP, const char * modelP)
void cSatipDiscover::AddServer(const char *addrP, const char *modelP, const char * descP)
{
debug("cSatipDiscover::%s(%s, %s, %s)", __FUNCTION__, addrP, descP, modelP);
debug1("%s (%s, %s, %s)", __PRETTY_FUNCTION__, addrP, modelP, descP);
cMutexLock MutexLock(&mutexM);
if (serversM) {
cSatipServer *tmp = new cSatipServer(addrP, descP, modelP);
// Validate against existing servers
if (!serversM->Update(tmp)) {
info("Adding device %s (%s %s)", tmp->Description(), tmp->Address(), tmp->Model());
serversM->Add(tmp);
if (SatipConfig.GetUseSingleModelServers() && modelP && !isempty(modelP)) {
int n = 0;
char *s, *p = strdup(modelP);
char *r = strtok_r(p, ",", &s);
while (r) {
r = skipspace(r);
cString desc = cString::sprintf("%s #%d", !isempty(descP) ? descP : "MyBrokenHardware", n++);
cSatipServer *tmp = new cSatipServer(addrP, r, desc);
if (!serversM.Update(tmp)) {
info("Adding server '%s|%s|%s' CI: %s Quirks: %s", tmp->Address(), tmp->Model(), tmp->Description(), tmp->HasCI() ? "yes" : "no", tmp->HasQuirk() ? tmp->Quirks() : "none");
serversM.Add(tmp);
}
else
DELETENULL(tmp);
r = strtok_r(NULL, ",", &s);
}
FREE_POINTER(p);
}
else {
cSatipServer *tmp = new cSatipServer(addrP, modelP, descP);
if (!serversM.Update(tmp)) {
info("Adding server '%s|%s|%s' CI: %s Quirks: %s", tmp->Address(), tmp->Model(), tmp->Description(), tmp->HasCI() ? "yes" : "no", tmp->HasQuirk() ? tmp->Quirks() : "none");
serversM.Add(tmp);
}
else
DELETENULL(tmp);
@@ -253,57 +270,100 @@ void cSatipDiscover::AddServer(const char *addrP, const char *descP, const char
int cSatipDiscover::GetServerCount(void)
{
//debug("cSatipDiscover::%s()", __FUNCTION__);
debug16("%s", __PRETTY_FUNCTION__);
cMutexLock MutexLock(&mutexM);
return serversM ? serversM->Count() : -1;
return serversM.Count();
}
cSatipServer *cSatipDiscover::GetServer(int sourceP, int systemP)
cSatipServer *cSatipDiscover::AssignServer(int deviceIdP, int sourceP, int transponderP, int systemP)
{
//debug("cSatipDiscover::%s(%d, %d)", __FUNCTION__, sourceP, systemP);
debug16("%s (%d, %d, %d, %d)", __PRETTY_FUNCTION__, deviceIdP, sourceP, transponderP, systemP);
cMutexLock MutexLock(&mutexM);
return serversM ? serversM->Find(sourceP, systemP) : NULL;
return serversM.Assign(deviceIdP, sourceP, transponderP, systemP);
}
cSatipServer *cSatipDiscover::GetServer(int sourceP)
{
debug16("%s (%d)", __PRETTY_FUNCTION__, sourceP);
cMutexLock MutexLock(&mutexM);
return serversM.Find(sourceP);
}
cSatipServer *cSatipDiscover::GetServer(cSatipServer *serverP)
{
//debug("cSatipDiscover::%s()", __FUNCTION__);
debug16("%s", __PRETTY_FUNCTION__);
cMutexLock MutexLock(&mutexM);
return serversM ? serversM->Find(serverP) : NULL;
return serversM.Find(serverP);
}
cSatipServers *cSatipDiscover::GetServers(void)
{
//debug("cSatipDiscover::%s()", __FUNCTION__);
debug16("%s", __PRETTY_FUNCTION__);
cMutexLock MutexLock(&mutexM);
return serversM;
return &serversM;
}
cString cSatipDiscover::GetServerString(cSatipServer *serverP)
{
//debug("cSatipDiscover::%s(%d)", __FUNCTION__, modelP);
debug16("%s", __PRETTY_FUNCTION__);
cMutexLock MutexLock(&mutexM);
return serversM ? serversM->GetString(serverP) : "";
return serversM.GetString(serverP);
}
cString cSatipDiscover::GetServerList(void)
{
//debug("cSatipDiscover::%s(%d)", __FUNCTION__, modelP);
debug16("%s", __PRETTY_FUNCTION__);
cMutexLock MutexLock(&mutexM);
return serversM ? serversM->List() : "";
return serversM.List();
}
void cSatipDiscover::UseServer(cSatipServer *serverP, bool onOffP)
void cSatipDiscover::AttachServer(cSatipServer *serverP, int deviceIdP, int transponderP)
{
//debug("cSatipDiscover::%s(%d)", __FUNCTION__, modelP);
debug16("%s (, %d, %d)", __PRETTY_FUNCTION__, deviceIdP, transponderP);
cMutexLock MutexLock(&mutexM);
if (serversM)
serversM->Use(serverP, onOffP);
serversM.Attach(serverP, deviceIdP, transponderP);
}
void cSatipDiscover::DetachServer(cSatipServer *serverP, int deviceIdP, int transponderP)
{
debug16("%s (, %d, %d)", __PRETTY_FUNCTION__, deviceIdP, transponderP);
cMutexLock MutexLock(&mutexM);
serversM.Detach(serverP, deviceIdP, transponderP);
}
bool cSatipDiscover::IsServerQuirk(cSatipServer *serverP, int quirkP)
{
debug16("%s (, %d)", __PRETTY_FUNCTION__, quirkP);
cMutexLock MutexLock(&mutexM);
return serversM.IsQuirk(serverP, quirkP);
}
bool cSatipDiscover::HasServerCI(cSatipServer *serverP)
{
debug16("%s", __PRETTY_FUNCTION__);
cMutexLock MutexLock(&mutexM);
return serversM.HasCI(serverP);
}
cString cSatipDiscover::GetServerAddress(cSatipServer *serverP)
{
debug16("%s", __PRETTY_FUNCTION__);
cMutexLock MutexLock(&mutexM);
return serversM.GetAddress(serverP);
}
int cSatipDiscover::NumProvidedSystems(void)
{
//debug("cSatipDiscover::%s(%d)", __FUNCTION__, modelP);
debug16("%s", __PRETTY_FUNCTION__);
cMutexLock MutexLock(&mutexM);
return serversM ? serversM->NumProvidedSystems() : 0;
return serversM.NumProvidedSystems();
}
void cSatipDiscover::SetUrl(const char *urlP)
{
debug16("%s (%s)", __PRETTY_FUNCTION__, urlP);
mutexM.Lock();
probeUrlListM.Insert(strdup(urlP));
mutexM.Unlock();
sleepM.Signal();
}

View File

@@ -13,34 +13,55 @@
#include <vdr/thread.h>
#include <vdr/tools.h>
#include "common.h"
#include "discoverif.h"
#include "msearch.h"
#include "server.h"
#include "socket.h"
class cSatipDiscover : public cThread {
class cSatipDiscoverServer : public cListObject {
private:
cString ipAddressM;
cString descriptionM;
cString modelM;
public:
cSatipDiscoverServer(const char *ipAddressP, const char *modelP, const char *descriptionP)
{
ipAddressM = ipAddressP; modelM = modelP; descriptionM = descriptionP;
}
const char *IpAddress(void) { return *ipAddressM; }
const char *Model(void) { return *modelM; }
const char *Description(void) { return *descriptionM; }
};
class cSatipDiscoverServers : public cList<cSatipDiscoverServer> {
};
class cSatipDiscover : public cThread, public cSatipDiscoverIf {
private:
enum {
eConnectTimeoutMs = 1500, // in milliseconds
eDiscoveryPort = 1900,
eProbeBufferSize = 1024, // in bytes
eProbeTimeoutMs = 2000, // in milliseconds
eProbeIntervalMs = 60000 // in milliseconds
eSleepTimeoutMs = 500, // in milliseconds
eConnectTimeoutMs = 1500, // in milliseconds
eProbeTimeoutMs = 2000, // in milliseconds
eProbeIntervalMs = 60000, // in milliseconds
eCleanupTimeoutMs = 124000 // in milliseoonds
};
static cSatipDiscover *instanceS;
static const char *bcastAddressS;
static const char *bcastMessageS;
static size_t WriteCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP);
static size_t DataCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP);
static int DebugCallback(CURL *handleP, curl_infotype typeP, char *dataP, size_t sizeP, void *userPtrP);
cMutex mutexM;
cSatipMemoryBuffer dataBufferM;
cSatipMsearch msearchM;
cStringList probeUrlListM;
CURL *handleM;
cSatipSocket *socketM;
cCondWait sleepM;
cTimeMs probeIntervalM;
cSatipServers *serversM;
cSatipServers serversM;
void Activate(void);
void Deactivate(void);
void Janitor(void);
void Probe(void);
void Read(void);
void AddServer(const char *addrP, const char *descP, const char *modelP);
void ParseDeviceInfo(const char *addrP);
void AddServer(const char *addrP, const char *modelP, const char *descP);
void Fetch(const char *urlP);
// constructor
cSatipDiscover();
// to prevent copy constructor and assignment
@@ -52,18 +73,27 @@ protected:
public:
static cSatipDiscover *GetInstance(void);
static bool Initialize(void);
static bool Initialize(cSatipDiscoverServers *serversP);
static void Destroy(void);
virtual ~cSatipDiscover();
void TriggerScan(void) { probeIntervalM.Set(0); }
int GetServerCount(void);
cSatipServer *GetServer(int sourceP, int systemP = -1);
cSatipServer *AssignServer(int deviceIdP, int sourceP, int transponderP, int systemP);
cSatipServer *GetServer(int sourceP);
cSatipServer *GetServer(cSatipServer *serverP);
cSatipServers *GetServers(void);
cString GetServerString(cSatipServer *serverP);
void UseServer(cSatipServer *serverP, bool onOffP);
void AttachServer(cSatipServer *serverP, int deviceIdP, int transponderP);
void DetachServer(cSatipServer *serverP, int deviceIdP, int transponderP);
bool IsServerQuirk(cSatipServer *serverP, int quirkP);
bool HasServerCI(cSatipServer *serverP);
cString GetServerAddress(cSatipServer *serverP);
cString GetServerList(void);
int NumProvidedSystems(void);
// for internal discover interface
public:
virtual void SetUrl(const char *urlP);
};
#endif // __SATIP_DISCOVER_H

22
discoverif.h Normal file
View File

@@ -0,0 +1,22 @@
/*
* discoverif.h: SAT>IP plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#ifndef __SATIP_DISCOVERIF_H
#define __SATIP_DISCOVERIF_H
class cSatipDiscoverIf {
public:
cSatipDiscoverIf() {}
virtual ~cSatipDiscoverIf() {}
virtual void SetUrl(const char *urlP) = 0;
private:
cSatipDiscoverIf(const cSatipDiscoverIf&);
cSatipDiscoverIf& operator=(const cSatipDiscoverIf&);
};
#endif // __SATIP_DISCOVERIF_H

48
log.h Normal file
View File

@@ -0,0 +1,48 @@
/*
* log.h: SAT>IP plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#ifndef __SATIP_LOG_H
#define __SATIP_LOG_H
#include "config.h"
#define error(x...) esyslog("SATIP-ERROR: " x)
#define info(x...) isyslog("SATIP: " x)
// 0x0001: Generic call stack
#define debug1(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug1) ? dsyslog("SATIP1: " x) : void() )
// 0x0002: CURL data flow
#define debug2(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug2) ? dsyslog("SATIP2: " x) : void() )
// 0x0004: Data parsing
#define debug3(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug3) ? dsyslog("SATIP3: " x) : void() )
// 0x0008: Tuner state machine
#define debug4(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug4) ? dsyslog("SATIP4: " x) : void() )
// 0x0010: RTSP responses
#define debug5(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug5) ? dsyslog("SATIP5: " x) : void() )
// 0x0020: RTP throughput performance
#define debug6(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug6) ? dsyslog("SATIP6: " x) : void() )
// 0x0040: RTP packet internals
#define debug7(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug7) ? dsyslog("SATIP7: " x) : void() )
// 0x0080: Section filtering
#define debug8(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug8) ? dsyslog("SATIP8: " x) : void() )
// 0x0100: Channel switching
#define debug9(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug9) ? dsyslog("SATIP9: " x) : void() )
// 0x0200: RTCP packets
#define debug10(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug10) ? dsyslog("SATIP10: " x) : void() )
// 0x0400: CI
#define debug11(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug11) ? dsyslog("SATIP11: " x) : void() )
// 0x0800: Pids
#define debug12(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug12) ? dsyslog("SATIP12: " x) : void() )
// 0x1000: Discovery
#define debug13(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug13) ? dsyslog("SATIP13: " x) : void() )
// 0x2000: TBD
#define debug14(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug14) ? dsyslog("SATIP14: " x) : void() )
// 0x4000: TBD
#define debug15(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug15) ? dsyslog("SATIP15: " x) : void() )
// 0x8000; Extra call stack
#define debug16(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug16) ? dsyslog("SATIP16: " x) : void() )
#endif // __SATIP_LOG_H

106
msearch.c Normal file
View File

@@ -0,0 +1,106 @@
/*
* msearch.c: SAT>IP plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#include "config.h"
#include "common.h"
#include "discover.h"
#include "log.h"
#include "poller.h"
#include "msearch.h"
const char *cSatipMsearch::bcastAddressS = "239.255.255.250";
const char *cSatipMsearch::bcastMessageS = "M-SEARCH * HTTP/1.1\r\n" \
"HOST: 239.255.255.250:1900\r\n" \
"MAN: \"ssdp:discover\"\r\n" \
"ST: urn:ses-com:device:SatIPServer:1\r\n" \
"MX: 2\r\n\r\n";
cSatipMsearch::cSatipMsearch(cSatipDiscoverIf &discoverP)
: discoverM(discoverP),
bufferLenM(eProbeBufferSize),
bufferM(MALLOC(unsigned char, bufferLenM)),
registeredM(false)
{
if (bufferM)
memset(bufferM, 0, bufferLenM);
else
error("Cannot create Msearch buffer!");
if (!Open(eDiscoveryPort))
error("Cannot open Msearch port!");
}
cSatipMsearch::~cSatipMsearch()
{
FREE_POINTER(bufferM);
}
void cSatipMsearch::Probe(void)
{
debug1("%s", __PRETTY_FUNCTION__);
if (!registeredM) {
cSatipPoller::GetInstance()->Register(*this);
registeredM = true;
}
// Send two queries with one second interval
Write(bcastAddressS, reinterpret_cast<const unsigned char *>(bcastMessageS), strlen(bcastMessageS));
cCondWait::SleepMs(1000);
Write(bcastAddressS, reinterpret_cast<const unsigned char *>(bcastMessageS), strlen(bcastMessageS));
}
int cSatipMsearch::GetFd(void)
{
return Fd();
}
void cSatipMsearch::Process(void)
{
debug16("%s", __PRETTY_FUNCTION__);
if (bufferM) {
int length;
while ((length = Read(bufferM, bufferLenM)) > 0) {
bufferM[min(length, int(bufferLenM - 1))] = 0;
debug13("%s len=%d buf=%s", __PRETTY_FUNCTION__, length, bufferM);
bool status = false, valid = false;
char *s, *p = reinterpret_cast<char *>(bufferM), *location = NULL;
char *r = strtok_r(p, "\r\n", &s);
while (r) {
debug13("%s r=%s", __PRETTY_FUNCTION__, r);
// Check the status code
// HTTP/1.1 200 OK
if (!status && startswith(r, "HTTP/1.1 200 OK"))
status = true;
if (status) {
// Check the location data
// LOCATION: http://192.168.0.115:8888/octonet.xml
if (startswith(r, "LOCATION:")) {
location = compactspace(r + 9);
debug1("%s location='%s'", __PRETTY_FUNCTION__, location);
}
// Check the source type
// ST: urn:ses-com:device:SatIPServer:1
else if (startswith(r, "ST:")) {
char *st = compactspace(r + 3);
if (strstr(st, "urn:ses-com:device:SatIPServer:1"))
valid = true;
debug1("%s st='%s'", __PRETTY_FUNCTION__, st);
}
// Check whether all the required data is found
if (valid && !isempty(location)) {
discoverM.SetUrl(location);
break;
}
}
r = strtok_r(NULL, "\r\n", &s);
}
}
}
}
cString cSatipMsearch::ToString(void) const
{
return "MSearch";
}

40
msearch.h Normal file
View File

@@ -0,0 +1,40 @@
/*
* msearch.h: SAT>IP plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#ifndef __SATIP_MSEARCH_H_
#define __SATIP_MSEARCH_H_
#include "discoverif.h"
#include "socket.h"
#include "pollerif.h"
class cSatipMsearch : public cSatipSocket, public cSatipPollerIf {
private:
enum {
eProbeBufferSize = 1024, // in bytes
eDiscoveryPort = 1900,
};
static const char *bcastAddressS;
static const char *bcastMessageS;
cSatipDiscoverIf &discoverM;
unsigned int bufferLenM;
unsigned char *bufferM;
bool registeredM;
public:
cSatipMsearch(cSatipDiscoverIf &discoverP);
virtual ~cSatipMsearch();
void Probe(void);
// for internal poller interface
public:
virtual int GetFd(void);
virtual void Process(void);
virtual cString ToString(void) const;
};
#endif /* __SATIP_MSEARCH_H_ */

83
param.c
View File

@@ -24,24 +24,24 @@ static const tSatipParameterMap SatipBandwidthValues[] = {
{ 8000000, "&bw=8" },
{ 10000000, "&bw=10" },
{ 1712000, "&bw=1.712" },
{ -1, NULL }
{ -1, NULL }
};
static const tSatipParameterMap SatipPilotValues[] = {
{ PILOT_OFF, "&plts=off" },
{ PILOT_ON, "&plts=on" },
{ PILOT_AUTO, "" },
{ -1, NULL }
{ PILOT_AUTO, "" },
{ -1, NULL }
};
static const tSatipParameterMap SatipSisoMisoValues[] = {
{ 0, "&sm=0" },
{ 1, "&sm=1" },
{ -1, NULL }
{ -1, NULL }
};
static const tSatipParameterMap SatipCodeRateValues[] = {
{ FEC_NONE, "" },
{ FEC_NONE, "" },
{ FEC_1_2, "&fec=12" },
{ FEC_2_3, "&fec=23" },
{ FEC_3_4, "&fec=34" },
@@ -52,8 +52,8 @@ static const tSatipParameterMap SatipCodeRateValues[] = {
{ FEC_7_8, "&fec=78" },
{ FEC_8_9, "&fec=89" },
{ FEC_9_10, "&fec=910" },
{ FEC_AUTO, "" },
{ -1, NULL }
{ FEC_AUTO, "" },
{ -1, NULL }
};
static const tSatipParameterMap SatipModulationValues[] = {
@@ -63,26 +63,26 @@ static const tSatipParameterMap SatipModulationValues[] = {
{ QAM_64, "&mtype=64qam" },
{ QAM_128, "&mtype=128qam" },
{ QAM_256, "&mtype=256qam" },
{ QAM_AUTO, "" },
{ -1, NULL }
{ QAM_AUTO, "" },
{ -1, NULL }
};
static const tSatipParameterMap SatipSystemValuesSat[] = {
{ 0, "&msys=dvbs" },
{ 1, "&msys=dvbs2" },
{ -1, NULL }
{ -1, NULL }
};
static const tSatipParameterMap SatipSystemValuesTerrestrial[] = {
{ 0, "&msys=dvbt" },
{ 1, "&msys=dvbt2" },
{ -1, NULL }
{ -1, NULL }
};
static const tSatipParameterMap SatipSystemValuesCable[] = {
{ 0, "&msys=dvbc" },
{ 1, "&msys=dvbc2" },
{ -1, NULL }
{ -1, NULL }
};
static const tSatipParameterMap SatipTransmissionValues[] = {
@@ -92,8 +92,8 @@ static const tSatipParameterMap SatipTransmissionValues[] = {
{ TRANSMISSION_MODE_8K, "&tmode=8k" },
{ TRANSMISSION_MODE_16K, "&tmode=16k" },
{ TRANSMISSION_MODE_32K, "&tmode=32k" },
{ TRANSMISSION_MODE_AUTO, "" },
{ -1, NULL }
{ TRANSMISSION_MODE_AUTO, "" },
{ -1, NULL }
};
static const tSatipParameterMap SatipGuardValues[] = {
@@ -104,16 +104,23 @@ static const tSatipParameterMap SatipGuardValues[] = {
{ GUARD_INTERVAL_1_128, "&gi=1128" },
{ GUARD_INTERVAL_19_128, "&gi=19128" },
{ GUARD_INTERVAL_19_256, "&gi=19256" },
{ GUARD_INTERVAL_AUTO, "" },
{ -1, NULL }
{ GUARD_INTERVAL_AUTO, "" },
{ -1, NULL }
};
static const tSatipParameterMap SatipRollOffValues[] = {
{ ROLLOFF_AUTO, "" },
{ ROLLOFF_AUTO, "" },
{ ROLLOFF_20, "&ro=0.20" },
{ ROLLOFF_25, "&ro=0.25" },
{ ROLLOFF_35, "&ro=0.35" },
{ -1, NULL }
{ -1, NULL }
};
static const tSatipParameterMap SatipInversionValues[] = {
{ INVERSION_AUTO, "" },
{ INVERSION_OFF, "&specinv=0" },
{ INVERSION_ON, "&specinv=1" },
{ -1, NULL }
};
static int SatipUserIndex(int valueP, const tSatipParameterMap *mapP)
@@ -138,15 +145,8 @@ cString GetTransponderUrlParameters(const cChannel *channelP)
if (channelP) {
char buffer[255];
cDvbTransponderParameters dtp(channelP->Parameters());
#if defined(APIVERSNUM) && APIVERSNUM < 20106
int Pilot = PILOT_AUTO;
int T2SystemId = 0;
int SisoMiso = 0;
#else
int Pilot = dtp.Pilot();
int T2SystemId = dtp.T2SystemId();
int SisoMiso = dtp.SisoMiso();
#endif
int DataSlice = 0;
int C2TuningFrequencyType = 0;
float freq = channelP->Frequency();
char type = cSource::ToChar(channelP->Source());
cSource *source = Sources.Get(channelP->Source());
@@ -158,24 +158,37 @@ cString GetTransponderUrlParameters(const cChannel *channelP)
freq /= 1000L;
#define ST(s) if (strchr(s, type) && (strchr(s, '0' + dtp.System() + 1) || strchr(s, '*')))
#define STBUFLEFT (sizeof(buffer) - (q - buffer))
q += snprintf(q, STBUFLEFT, "freq=%s", *dtoa(freq, "%.3f"));
ST(" S 1") { // to comply with SAT>IP protocol specification 1.2.2
dtp.SetPilot(PILOT_OFF);
dtp.SetModulation(QPSK);
dtp.SetRollOff(ROLLOFF_35);
}
q += snprintf(q, STBUFLEFT, "freq=%s", *dtoa(freq, "%lg"));
ST(" S *") q += snprintf(q, STBUFLEFT, "&src=%d", ((src > 0) && (src <= 255)) ? src : 1);
ST("CS *") q += snprintf(q, STBUFLEFT, "&sr=%d", channelP->Srate());
ST(" S *") q += snprintf(q, STBUFLEFT, "&sr=%d", channelP->Srate());
ST("C 1") q += snprintf(q, STBUFLEFT, "&sr=%d", channelP->Srate());
ST(" S *") q += snprintf(q, STBUFLEFT, "&pol=%c", tolower(dtp.Polarization()));
ST("C T2") q += snprintf(q, STBUFLEFT, "&plp=%d", dtp.StreamId());
ST(" T2") q += snprintf(q, STBUFLEFT, "&t2id=%d", T2SystemId);
ST(" T2") q += PrintUrlString(q, STBUFLEFT, SisoMiso, SatipSisoMisoValues);
ST(" T2") q += snprintf(q, STBUFLEFT, "&t2id=%d", dtp.T2SystemId());
ST("C 2") q += snprintf(q, STBUFLEFT, "&c2tft=%d", C2TuningFrequencyType);
ST("C 2") q += snprintf(q, STBUFLEFT, "&ds=%d", DataSlice);
ST("C 1") q += PrintUrlString(q, STBUFLEFT, dtp.Inversion(), SatipInversionValues);
ST(" T2") q += PrintUrlString(q, STBUFLEFT, dtp.SisoMiso(), SatipSisoMisoValues);
ST(" T*") q += PrintUrlString(q, STBUFLEFT, dtp.Bandwidth(), SatipBandwidthValues);
ST("C 2") q += PrintUrlString(q, STBUFLEFT, dtp.Bandwidth(), SatipBandwidthValues);
ST(" T*") q += PrintUrlString(q, STBUFLEFT, dtp.Guard(), SatipGuardValues);
ST("CST*") q += PrintUrlString(q, STBUFLEFT, dtp.CoderateH(), SatipCodeRateValues);
ST(" S 2") q += PrintUrlString(q, STBUFLEFT, Pilot, SatipPilotValues);
ST(" S 2") q += PrintUrlString(q, STBUFLEFT, dtp.Modulation(), SatipModulationValues);
ST("C T*") q += PrintUrlString(q, STBUFLEFT, dtp.Modulation(), SatipModulationValues);
ST(" S 2") q += PrintUrlString(q, STBUFLEFT, dtp.RollOff(), SatipRollOffValues);
ST(" S *") q += PrintUrlString(q, STBUFLEFT, dtp.Pilot(), SatipPilotValues);
ST(" S *") q += PrintUrlString(q, STBUFLEFT, dtp.Modulation(), SatipModulationValues);
ST(" T*") q += PrintUrlString(q, STBUFLEFT, dtp.Modulation(), SatipModulationValues);
ST("C 1") q += PrintUrlString(q, STBUFLEFT, dtp.Modulation(), SatipModulationValues);
ST(" S *") q += PrintUrlString(q, STBUFLEFT, dtp.RollOff(), SatipRollOffValues);
ST(" S *") q += PrintUrlString(q, STBUFLEFT, dtp.System(), SatipSystemValuesSat);
ST("C *") q += PrintUrlString(q, STBUFLEFT, dtp.System(), SatipSystemValuesCable);
ST(" T*") q += PrintUrlString(q, STBUFLEFT, dtp.System(), SatipSystemValuesTerrestrial);
ST(" T*") q += PrintUrlString(q, STBUFLEFT, dtp.Transmission(), SatipTransmissionValues);
if ((channelP->Rid() % 100) > 0)
snprintf(q, STBUFLEFT, "&fe=%d", channelP->Rid() % 100);
#undef ST
return buffer;
}

185
po/ca_ES.po Normal file
View File

@@ -0,0 +1,185 @@
# VDR plugin language source file.
# Copyright (C) 2007-2015 Rolf Ahrenberg
# This file is distributed under the same license as the satip package.
# Gabriel Bonich, 2014-2015
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-satip 2.2.2\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2015-04-26 04:26+0300\n"
"PO-Revision-Date: 2015-04-26 04:26+0300\n"
"Last-Translator: Gabriel Bonich <gbonich@gmail.com>\n"
"Language-Team: Catalan <vdr@linuxtv.org>\n"
"Language: ca\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "PAT (0x00)"
msgstr "PAT (0x00)"
msgid "NIT (0x40)"
msgstr "NIT (0x40)"
msgid "SDT (0x42)"
msgstr "SDT (0x42)"
msgid "EIT (0x4E/0x4F/0x5X/0x6X)"
msgstr "EIT (0x4E/0x4F/0x5X/0x6X)"
msgid "TDT (0x70)"
msgstr "TDT (0x70)"
msgid "SAT>IP information not available!"
msgstr "SAT>IP Informació no disponible!"
msgid "SAT>IP Devices"
msgstr "SAT>IP Dispositius"
msgid "SAT>IP Server"
msgstr "SAT>IP Server"
msgid "Address"
msgstr "Adressa"
msgid "Model"
msgstr "Model"
msgid "Description"
msgstr "Descripció"
msgid "CI extension"
msgstr "Extensió CI"
msgid "Creation date"
msgstr "Creació de data"
msgid "SAT>IP Device Status"
msgstr "SAT>IP Estat Dispositiu"
msgid "SAT>IP Information"
msgstr "SAT>IP Informació"
msgid "General"
msgstr "General"
msgid "Pids"
msgstr "Pids"
msgid "Filters"
msgstr "Filtres"
msgid "Bits/bytes"
msgstr "Bits/Bytes"
msgid "off"
msgstr "Apagat"
msgid "low"
msgstr "Baix"
msgid "normal"
msgstr "Normal"
msgid "high"
msgstr "Alt"
msgid "Button$Devices"
msgstr "Dispositius"
msgid "Operating mode"
msgstr "Mode de operació"
msgid ""
"Define the used operating mode for all SAT>IP devices:\n"
"\n"
"off - devices are disabled\n"
"low - devices are working at the lowest priority\n"
"normal - devices are working within normal parameters\n"
"high - devices are working at the highest priority"
msgstr ""
"Defineig la manera de operar els Dispositius SAT>IP:\n"
"\n"
"Apagat - Dispositius desactivats\n"
"Baix - Dispositius treballan a baixa prioritat\n"
"Normal - Dispositius treballan en parametres normals\n"
"Alta - Dispositius treballan a prioritat Alta"
msgid "Enable CI extension"
msgstr "Habilita la extenció CI"
msgid ""
"Define whether a CI extension shall be used.\n"
"\n"
"This setting enables integrated CI/CAM handling found in some SAT>IP hardware (e.g. Digital Devices OctopusNet)."
msgstr ""
"Definir si s'utilitzarà una extensió de CI.\n"
"\n"
"Aquesta configuració permet utilitzar CI/CAM integrat que es troba en alguns equips SAT>IP (ex. Digital Devices OctopusNet)."
msgid "CI/CAM"
msgstr "CI/CAM"
msgid ""
"Define a desired CAM type for the CI slot.\n"
"\n"
"The '---' option lets SAT>IP hardware do the auto-selection."
msgstr ""
"Definir quin tipus de CAM vols per a la ranura CI.\n"
"\n"
"L'opció '---' permet l'equip SAT>IP fer la selecció automàtica."
msgid "Enable EPG scanning"
msgstr "Activa Escanneig EPG"
msgid ""
"Define whether the EPG background scanning shall be used.\n"
"\n"
"This setting disables the automatic EIT scanning functionality for all SAT>IP devices."
msgstr ""
"Definir si s'utilitzarà l'anàlisi en segon pla del EPG.\n"
"\n"
"Aquesta configuració desactiva la funcionalitat d'escaneig EIT automàtica per a tots els dispositius SAT>IP."
msgid "Disabled sources"
msgstr "Desactiva entrades"
msgid "none"
msgstr "no"
msgid ""
"Define number of sources to be disabled.\n"
"\n"
"SAT>IP servers might not have all satellite positions available and such sources can be blacklisted here."
msgstr ""
"Definir nombre de entrades que es desactiven.\n"
"\n"
"SAT>IP els servidors podrien no tenir totes les posicions dels satèl·lits disponibles i aquestes entrades poden ser la llista negra."
msgid "Define a source to be blacklisted."
msgstr "Definir una entrada a la llista negra"
msgid "Disabled filters"
msgstr "Desactiva filtres"
msgid ""
"Define number of section filters to be disabled.\n"
"\n"
"Certain section filters might cause some unwanted behaviour to VDR such as time being falsely synchronized. By blacklisting the filters here, useful section data can be left intact for VDR to process."
msgstr ""
"Defineix el numero de filtres de secció que seran deshabilitats.\n"
"\n"
"Alguns filtres de secció podrien provocar un comportament no desitjat a VDR, com sincronitzar malament l'hora. Posant aquests filtres a la llista negra, aqui, la secció de dades útil pot ser deixada intacta pel seu procès en el VDR."
msgid "Filter"
msgstr "Filtra"
msgid "Define an ill-behaving filter to be blacklisted."
msgstr "Definir un filtre mal comportar a la llista negra."
msgid "Active SAT>IP servers:"
msgstr "Activa SAT>IP servers:"
msgid "Help"
msgstr "Ajuda"

View File

@@ -1,14 +1,14 @@
# VDR plugin language source file.
# Copyright (C) 2007-2014 Rolf Ahrenberg & Antti Seppala
# This file is distributed under the same license as the iptv package.
# Frank Neumann, 2014
# Copyright (C) 2007-2015 Rolf Ahrenberg
# This file is distributed under the same license as the satip package.
# Frank Neumann, 2014-2015
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-satip 0.2.0\n"
"Project-Id-Version: vdr-satip 2.2.2\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2014-03-28 03:28+0200\n"
"PO-Revision-Date: 2014-03-28 03:28+0200\n"
"POT-Creation-Date: 2015-04-26 04:26+0300\n"
"PO-Revision-Date: 2015-04-26 04:26+0300\n"
"Last-Translator: Frank Neumann <fnu@yavdr.org>\n"
"Language-Team: German <vdr@linuxtv.org>\n"
"Language: de\n"
@@ -31,11 +31,14 @@ msgstr "EIT (0x4E/0x4F/0x5X/0x6X)"
msgid "TDT (0x70)"
msgstr "TDT (0x70)"
msgid "SAT>IP information not available!"
msgstr "Keine SAT>IP Informationen verfügbar!"
msgid "SAT>IP Devices"
msgstr "SAT>IP Geräte"
msgid "SAT>IP Device"
msgstr "SAT>IP Gerät"
msgid "SAT>IP Server"
msgstr "SAT>IP Server"
msgid "Address"
msgstr "Adresse"
@@ -46,9 +49,15 @@ msgstr "Modell"
msgid "Description"
msgstr "Beschreibung"
msgid "CI extension"
msgstr "CI Erweiterung"
msgid "Creation date"
msgstr "Zeitpunkt der Erstellung"
msgid "SAT>IP Device Status"
msgstr "SAT>IP Geräte Status"
msgid "SAT>IP Information"
msgstr "SAT>IP Informationen"
@@ -64,9 +73,6 @@ msgstr "Filter"
msgid "Bits/bytes"
msgstr "Bits/Bytes"
msgid "SAT>IP information not available!"
msgstr "Keine SAT>IP Informationen verfügbar!"
msgid "off"
msgstr "aus"
@@ -79,6 +85,9 @@ msgstr "normal"
msgid "high"
msgstr "hoch"
msgid "Button$Devices"
msgstr "Geräte"
msgid "Operating mode"
msgstr "Betriebsmodus"
@@ -97,6 +106,30 @@ msgstr ""
"normal - Geräte arbeiten innerhalb der gewöhnlichen Parameter\n"
"hoch - Geräte arbeiten mit höchste Priorität"
msgid "Enable CI extension"
msgstr "Aktiviere CI Erweiterung"
msgid ""
"Define whether a CI extension shall be used.\n"
"\n"
"This setting enables integrated CI/CAM handling found in some SAT>IP hardware (e.g. Digital Devices OctopusNet)."
msgstr ""
"Legt fest ob eine CI Erweiterung genutzt werden soll.\n"
"\n"
"Diese Einstellung aktiviert die Nutzung des integrierten CI/CAM einiger SAT>IP Geräte (z.B. Digital Devices OctopusNet)."
msgid "CI/CAM"
msgstr "CI/CAM"
msgid ""
"Define a desired CAM type for the CI slot.\n"
"\n"
"The '---' option lets SAT>IP hardware do the auto-selection."
msgstr ""
"Bestimmt welcher CI Einschub für ein CAM genutzt werden soll.\n"
"\n"
"Die Option '---' überlässt der SAT>IP Hardware die automatische Auswahl."
msgid "Enable EPG scanning"
msgstr "Aktiviere EPG Aktualisierung"
@@ -105,20 +138,35 @@ msgid ""
"\n"
"This setting disables the automatic EIT scanning functionality for all SAT>IP devices."
msgstr ""
"Definiert ob EPG im Hintergrund aktualisiert werden soll oder nicht.\n"
"Legt fest ob EPG im Hintergrund aktualisiert werden soll oder nicht.\n"
"\n"
"Diese Einstellung schaltet die automatische EIT Aktualisierung für alle SAT>IP Geräte."
msgid "Disabled filters"
msgstr "Deaktivierte Filter"
msgid "Disabled sources"
msgstr "Deaktivierte Quellen"
msgid "none"
msgstr "keine"
msgid ""
"Define number of sources to be disabled.\n"
"\n"
"SAT>IP servers might not have all satellite positions available and such sources can be blacklisted here."
msgstr ""
"Legt die Anzahl der deaktivierten Quellen fest.\n"
"\n"
"Für einige SAT>IP server sind nicht alle Satellitenpositionen verfügbar, nicht verfügbare Quellen können hier ausgeblendet werden"
msgid "Define a source to be blacklisted."
msgstr "Bestimme eine Quelle, die ausgeblendet wird"
msgid "Disabled filters"
msgstr "Deaktivierte Filter"
msgid ""
"Define number of section filters to be disabled.\n"
"\n"
"Certain section filters might cause some unwanted behaviour to VDR such as time being falsely synchronized. By black-listing the filters here useful section data can be left intact for VDR to process."
"Certain section filters might cause some unwanted behaviour to VDR such as time being falsely synchronized. By blacklisting the filters here, useful section data can be left intact for VDR to process."
msgstr ""
"Bestimme die Anzahl der Abschnittsfilter die deaktiviert werden sollen.\n"
"\n"
@@ -130,8 +178,8 @@ msgstr "Filter"
msgid "Define an ill-behaving filter to be blacklisted."
msgstr "Bestimme einen fehlerhaften Filter der ausgeblendet werden soll."
msgid "Active SAT>IP devices:"
msgstr "Aktive SAT>IP Geräte:"
msgid "Active SAT>IP servers:"
msgstr "Aktive SAT>IP Server:"
msgid "Help"
msgstr "Hilfe"

185
po/es_ES.po Normal file
View File

@@ -0,0 +1,185 @@
# VDR plugin language source file.
# Copyright (C) 2007-2015 Rolf Ahrenberg
# This file is distributed under the same license as the satip package.
# Gabriel Bonich, 2014-2015
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-satip 2.2.2\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2015-04-26 04:26+0300\n"
"PO-Revision-Date: 2015-04-26 04:26+0300\n"
"Last-Translator: Gabriel Bonich <gbonich@gmail.com>\n"
"Language-Team: Spanish <vdr@linuxtv.org>\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "PAT (0x00)"
msgstr "PAT (0x00)"
msgid "NIT (0x40)"
msgstr "NIT (0x40)"
msgid "SDT (0x42)"
msgstr "SDT (0x42)"
msgid "EIT (0x4E/0x4F/0x5X/0x6X)"
msgstr "EIT (0x4E/0x4F/0x5X/0x6X)"
msgid "TDT (0x70)"
msgstr "TDT (0x70)"
msgid "SAT>IP information not available!"
msgstr "SAT>IP Información no disponible!"
msgid "SAT>IP Devices"
msgstr "SAT>IP Dispositivos"
msgid "SAT>IP Server"
msgstr "SAT>IP Server"
msgid "Address"
msgstr "Dirección"
msgid "Model"
msgstr "Modelo"
msgid "Description"
msgstr "Descripción"
msgid "CI extension"
msgstr "Extensión CI"
msgid "Creation date"
msgstr "Fecha creación"
msgid "SAT>IP Device Status"
msgstr "SAT>IP Estado del Dispositivo"
msgid "SAT>IP Information"
msgstr "SAT>IP Información"
msgid "General"
msgstr "General"
msgid "Pids"
msgstr "Pids"
msgid "Filters"
msgstr "Filtros"
msgid "Bits/bytes"
msgstr "Bits/Bytes"
msgid "off"
msgstr "Apagado"
msgid "low"
msgstr "Bajo"
msgid "normal"
msgstr "Normal"
msgid "high"
msgstr "Alto"
msgid "Button$Devices"
msgstr "Dispositivos"
msgid "Operating mode"
msgstr "Modo de operación"
msgid ""
"Define the used operating mode for all SAT>IP devices:\n"
"\n"
"off - devices are disabled\n"
"low - devices are working at the lowest priority\n"
"normal - devices are working within normal parameters\n"
"high - devices are working at the highest priority"
msgstr ""
"Define la manera que trabajan los Dispositivos SAT>IP:\n"
"\n"
"Apagat - Dispositivos desactivados\n"
"Baix - Dispositivos trabajando con prioridad Baja\n"
"Normal - Dispositivos trabajando con prioridad Normal\n"
"Alta - Dispositivos trabajando con prioridad Alta"
msgid "Enable CI extension"
msgstr "Habilitar extensión CI"
msgid ""
"Define whether a CI extension shall be used.\n"
"\n"
"This setting enables integrated CI/CAM handling found in some SAT>IP hardware (e.g. Digital Devices OctopusNet)."
msgstr ""
"Definir si se utilizará una extensión de CI.\n"
"\n"
"Esto permite la configuración CI/CAM integrado que se encuentra en algunos equipos SAT>IP (ej. Digital Devices OctopusNet)."
msgid "CI/CAM"
msgstr "CI/CAM"
msgid ""
"Define a desired CAM type for the CI slot.\n"
"\n"
"The '---' option lets SAT>IP hardware do the auto-selection."
msgstr ""
"Definir el tipo de CAM para la ranura CI.\n"
"\n"
"La opción '---' permite al equipo SAT>IP hacer la selección automática."
msgid "Enable EPG scanning"
msgstr "Activa Escaneo EPG"
msgid ""
"Define whether the EPG background scanning shall be used.\n"
"\n"
"This setting disables the automatic EIT scanning functionality for all SAT>IP devices."
msgstr ""
"Definir si se utilitzará el analisí en segundo plano del EPG.\n"
"\n"
"Esta configuración desactiva la funcionalidad del escaneo EIT automática para todos los Dispositivos SAT>IP."
msgid "Disabled sources"
msgstr "Desactiva fuentes"
msgid "none"
msgstr "no"
msgid ""
"Define number of sources to be disabled.\n"
"\n"
"SAT>IP servers might not have all satellite positions available and such sources can be blacklisted here."
msgstr ""
"Definir número de fuentes desactivadas.\n"
"\n"
"SAT>IP servidores que no tenga todas las posiciones de los satélites disponibles y estas se ponen en la lista negra."
msgid "Define a source to be blacklisted."
msgstr "Define fuentes de la lista negra"
msgid "Disabled filters"
msgstr "Desactiva filtros"
msgid ""
"Define number of section filters to be disabled.\n"
"\n"
"Certain section filters might cause some unwanted behaviour to VDR such as time being falsely synchronized. By blacklisting the filters here, useful section data can be left intact for VDR to process."
msgstr ""
"Define el numero de filtros de sección que seran deshabilitados.\n"
"\n"
"Algunos filtros de sección podrian causar un comportamiento no deseado a VDR, como sincronizar mal la hora. Poniendo estos filtros en la lista negra, aqui, la sección de datos útiles se puede dejar intacta para su proceso con el VDR."
msgid "Filter"
msgstr "Filtra"
msgid "Define an ill-behaving filter to be blacklisted."
msgstr "Define un filtro para poner en la lista negra."
msgid "Active SAT>IP servers:"
msgstr "Activa SAT>IP servers:"
msgid "Help"
msgstr "Ayuda"

View File

@@ -1,14 +1,14 @@
# VDR plugin language source file.
# Copyright (C) 2007-2014 Rolf Ahrenberg & Antti Seppala
# This file is distributed under the same license as the iptv package.
# Rolf Ahrenberg, 2014
# Copyright (C) 2007-2015 Rolf Ahrenberg
# This file is distributed under the same license as the satip package.
# Rolf Ahrenberg, 2015
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-satip 0.2.0\n"
"Project-Id-Version: vdr-satip 2.2.2\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2014-03-28 03:28+0200\n"
"PO-Revision-Date: 2014-03-28 03:28+0200\n"
"POT-Creation-Date: 2015-04-26 04:26+0300\n"
"PO-Revision-Date: 2015-04-26 04:26+0300\n"
"Last-Translator: Rolf Ahrenberg\n"
"Language-Team: Finnish <vdr@linuxtv.org>\n"
"Language: fi\n"
@@ -31,11 +31,14 @@ msgstr "EIT (0x4E/0x4F/0x5X/0x6X)"
msgid "TDT (0x70)"
msgstr "TDT (0x70)"
msgid "SAT>IP information not available!"
msgstr "SAT>IP-tietoja ei saatavilla!"
msgid "SAT>IP Devices"
msgstr "SAT>IP-laitteet"
msgid "SAT>IP Device"
msgstr "SAT>IP-laite"
msgid "SAT>IP Server"
msgstr "SAT>IP-palvelin"
msgid "Address"
msgstr "Osoite"
@@ -46,9 +49,15 @@ msgstr "Malli"
msgid "Description"
msgstr "Kuvaus"
msgid "CI extension"
msgstr "CI-laajennos"
msgid "Creation date"
msgstr "Luontiajankohta"
msgid "SAT>IP Device Status"
msgstr "SAT>IP-laitteiden tiedot"
msgid "SAT>IP Information"
msgstr "SAT>IP-tiedot"
@@ -64,9 +73,6 @@ msgstr "Suodattimet"
msgid "Bits/bytes"
msgstr "Bitit/tavut"
msgid "SAT>IP information not available!"
msgstr "SAT>IP-tietoja ei saatavilla!"
msgid "off"
msgstr "ei käytössä"
@@ -79,6 +85,9 @@ msgstr "normaali"
msgid "high"
msgstr "korkea"
msgid "Button$Devices"
msgstr "Laitteet"
msgid "Operating mode"
msgstr "Laitteiden toimintatapa"
@@ -96,6 +105,30 @@ msgstr ""
"normaali - laitteet toimivat normaalilla prioriteetilla\n"
"korkea - laitteet toimivat korkealla prioriteetilla"
msgid "Enable CI extension"
msgstr "Käytä CI-laajennosta"
msgid ""
"Define whether a CI extension shall be used.\n"
"\n"
"This setting enables integrated CI/CAM handling found in some SAT>IP hardware (e.g. Digital Devices OctopusNet)."
msgstr ""
"Määrittele CI-laajennoksen käyttöönotto\n"
"\n"
"Tällä asetuksella saadaan otettua käyttöön SAT>IP-laitteiden sisäinen CI-paikka (esim. Digital Devices OctopusNet)."
msgid "CI/CAM"
msgstr "CI/CAM"
msgid ""
"Define a desired CAM type for the CI slot.\n"
"\n"
"The '---' option lets SAT>IP hardware do the auto-selection."
msgstr ""
"Määrittele haluttu CAM-tyyppi CI-paikalle.\n"
"\n"
"Vaihtoehto '---' antaa SAT>IP-laitteen valita automaattisesti käytetyn CI-paikan."
msgid "Enable EPG scanning"
msgstr "Käytä ohjelmaoppaan taustapäivitystä"
@@ -108,16 +141,31 @@ msgstr ""
"\n"
"Tällä asetuksella saadaan otettua automaattinen EIT-datan päivitys pois päältä kaikilta SAT>IP-laitteilta."
msgid "Disabled filters"
msgstr "Käytöstä poistetut suodattimet"
msgid "Disabled sources"
msgstr "Käytöstä poistetut lähteet"
msgid "none"
msgstr "tyhjä"
msgid ""
"Define number of sources to be disabled.\n"
"\n"
"SAT>IP servers might not have all satellite positions available and such sources can be blacklisted here."
msgstr ""
"Määrittele käytöstä poistettavien lähteiden lukumäärä.\n"
"\n"
"SAT>IP-palvelimilla ei välttämättä ole kaikkia ohjelmalähteitä tarjolla."
msgid "Define a source to be blacklisted."
msgstr "Määrittele käytöstä"
msgid "Disabled filters"
msgstr "Käytöstä poistetut suodattimet"
msgid ""
"Define number of section filters to be disabled.\n"
"\n"
"Certain section filters might cause some unwanted behaviour to VDR such as time being falsely synchronized. By black-listing the filters here useful section data can be left intact for VDR to process."
"Certain section filters might cause some unwanted behaviour to VDR such as time being falsely synchronized. By blacklisting the filters here, useful section data can be left intact for VDR to process."
msgstr ""
"Määrittele käytöstä poistettavien suodattimien lukumäärä sektioille.\n"
"\n"
@@ -129,8 +177,8 @@ msgstr "Suodatin"
msgid "Define an ill-behaving filter to be blacklisted."
msgstr "Määrittele käytöstä poistettava suodatin, joka lisätään mustalle listalle."
msgid "Active SAT>IP devices:"
msgstr "Aktiiviset SAT>IP-laitteet:"
msgid "Active SAT>IP servers:"
msgstr "Aktiiviset SAT>IP-palvelimet:"
msgid "Help"
msgstr "Opaste"

122
poller.c Normal file
View File

@@ -0,0 +1,122 @@
/*
* poller.c: SAT>IP plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#define __STDC_FORMAT_MACROS // Required for format specifiers
#include <inttypes.h>
#include <sys/epoll.h>
#include "config.h"
#include "common.h"
#include "log.h"
#include "poller.h"
cSatipPoller *cSatipPoller::instanceS = NULL;
cSatipPoller *cSatipPoller::GetInstance(void)
{
if (!instanceS)
instanceS = new cSatipPoller();
return instanceS;
}
bool cSatipPoller::Initialize(void)
{
debug1("%s", __PRETTY_FUNCTION__);
if (instanceS)
instanceS->Activate();
return true;
}
void cSatipPoller::Destroy(void)
{
debug1("%s", __PRETTY_FUNCTION__);
if (instanceS)
instanceS->Deactivate();
}
cSatipPoller::cSatipPoller()
: cThread("SATIP poller"),
mutexM(),
fdM(epoll_create(eMaxFileDescriptors))
{
debug1("%s", __PRETTY_FUNCTION__);
}
cSatipPoller::~cSatipPoller()
{
debug1("%s", __PRETTY_FUNCTION__);
Deactivate();
cMutexLock MutexLock(&mutexM);
close(fdM);
// Free allocated memory
}
void cSatipPoller::Activate(void)
{
// Start the thread
Start();
}
void cSatipPoller::Deactivate(void)
{
debug1("%s", __PRETTY_FUNCTION__);
cMutexLock MutexLock(&mutexM);
if (Running())
Cancel(3);
}
void cSatipPoller::Action(void)
{
debug1("%s Entering", __PRETTY_FUNCTION__);
struct epoll_event events[eMaxFileDescriptors];
uint64_t maxElapsed = 0;
// Increase priority
SetPriority(-1);
// Do the thread loop
while (Running()) {
int nfds = epoll_wait(fdM, events, eMaxFileDescriptors, -1);
ERROR_IF_FUNC((nfds == -1), "epoll_wait() failed", break, ;);
for (int i = 0; i < nfds; ++i) {
cSatipPollerIf* poll = reinterpret_cast<cSatipPollerIf *>(events[i].data.ptr);
if (poll) {
uint64_t elapsed;
cTimeMs processing(0);
poll->Process();
elapsed = processing.Elapsed();
if (elapsed > maxElapsed) {
maxElapsed = elapsed;
debug1("%s Processing %s took %" PRIu64 " ms", __PRETTY_FUNCTION__, *(poll->ToString()), maxElapsed);
}
}
}
}
debug1("%s Exiting", __PRETTY_FUNCTION__);
}
bool cSatipPoller::Register(cSatipPollerIf &pollerP)
{
debug1("%s fd=%d", __PRETTY_FUNCTION__, pollerP.GetFd());
cMutexLock MutexLock(&mutexM);
struct epoll_event ev;
ev.events = EPOLLIN | EPOLLET;
ev.data.ptr = &pollerP;
ERROR_IF_RET(epoll_ctl(fdM, EPOLL_CTL_ADD, pollerP.GetFd(), &ev) == -1, "epoll_ctl(EPOLL_CTL_ADD) failed", return false);
debug1("%s Added interface fd=%d", __PRETTY_FUNCTION__, pollerP.GetFd());
return true;
}
bool cSatipPoller::Unregister(cSatipPollerIf &pollerP)
{
debug1("%s fd=%d", __PRETTY_FUNCTION__, pollerP.GetFd());
cMutexLock MutexLock(&mutexM);
ERROR_IF_RET((epoll_ctl(fdM, EPOLL_CTL_DEL, pollerP.GetFd(), NULL) == -1), "epoll_ctl(EPOLL_CTL_DEL) failed", return false);
debug1("%s Removed interface fd=%d", __PRETTY_FUNCTION__, pollerP.GetFd());
return true;
}

44
poller.h Normal file
View File

@@ -0,0 +1,44 @@
/*
* poller.h: SAT>IP plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#ifndef __SATIP_POLLER_H
#define __SATIP_POLLER_H
#include <vdr/thread.h>
#include <vdr/tools.h>
#include "pollerif.h"
class cSatipPoller : public cThread {
private:
enum {
eMaxFileDescriptors = SATIP_MAX_DEVICES * 2, // Data + Application
};
static cSatipPoller *instanceS;
cMutex mutexM;
int fdM;
void Activate(void);
void Deactivate(void);
// constructor
cSatipPoller();
// to prevent copy constructor and assignment
cSatipPoller(const cSatipPoller&);
cSatipPoller& operator=(const cSatipPoller&);
protected:
virtual void Action(void);
public:
static cSatipPoller *GetInstance(void);
static bool Initialize(void);
static void Destroy(void);
virtual ~cSatipPoller();
bool Register(cSatipPollerIf &pollerP);
bool Unregister(cSatipPollerIf &pollerP);
};
#endif // __SATIP_POLLER_H

24
pollerif.h Normal file
View File

@@ -0,0 +1,24 @@
/*
* pollerif.h: SAT>IP plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#ifndef __SATIP_POLLERIF_H
#define __SATIP_POLLERIF_H
class cSatipPollerIf {
public:
cSatipPollerIf() {}
virtual ~cSatipPollerIf() {}
virtual int GetFd(void) = 0;
virtual void Process(void) = 0;
virtual cString ToString(void) const = 0;
private:
cSatipPollerIf(const cSatipPollerIf&);
cSatipPollerIf& operator=(const cSatipPollerIf&);
};
#endif // __SATIP_POLLERIF_H

98
rtcp.c Normal file
View File

@@ -0,0 +1,98 @@
/*
* rtcp.c: SAT>IP plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#include "config.h"
#include "common.h"
#include "log.h"
#include "rtcp.h"
cSatipRtcp::cSatipRtcp(cSatipTunerIf &tunerP)
: tunerM(tunerP),
bufferLenM(eApplicationMaxSizeB),
bufferM(MALLOC(unsigned char, bufferLenM))
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
if (bufferM)
memset(bufferM, 0, bufferLenM);
else
error("Cannot create RTCP buffer! [device %d]", tunerM.GetId());
}
cSatipRtcp::~cSatipRtcp()
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
FREE_POINTER(bufferM);
}
int cSatipRtcp::GetFd(void)
{
debug16("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
return Fd();
}
int cSatipRtcp::GetApplicationOffset(int *lengthP)
{
debug16("%s (%d) [device %d]", __PRETTY_FUNCTION__, *lengthP, tunerM.GetId());
if (!lengthP)
return -1;
int offset = 0;
int total = *lengthP;
while (total > 0) {
// Version
unsigned int v = (bufferM[offset] >> 6) & 0x03;
// Padding
//unsigned int p = (bufferM[offset] >> 5) & 0x01;
// Subtype
//unsigned int st = bufferM[offset] & 0x1F;
// Payload type
unsigned int pt = bufferM[offset + 1] & 0xFF;
// Length
unsigned int length = ((bufferM[offset + 2] & 0xFF) << 8) | (bufferM[offset + 3] & 0xFF);
// Convert it to bytes
length = (length + 1) * 4;
// V=2, APP = 204
if ((v == 2) && (pt == 204)) {
// SSCR/CSCR
//unsigned int ssrc = ((bufferM[offset + 4] & 0xFF) << 24) | ((bufferM[offset + 5] & 0xFF) << 16) |
// ((bufferM[offset + 6] & 0xFF) << 8) | (bufferM[offset + 7] & 0xFF);
// Name
if ((bufferM[offset + 8] == 'S') && (bufferM[offset + 9] == 'E') &&
(bufferM[offset + 10] == 'S') && (bufferM[offset + 11] == '1')) {
// Identifier
//unsigned int id = ((bufferM[offset + 12] & 0xFF) << 8) | (bufferM[offset + 13] & 0xFF);
// String length
int string_length = ((bufferM[offset + 14] & 0xFF) << 8) | (bufferM[offset + 15] & 0xFF);
if (string_length > 0) {
*lengthP = string_length;
return (offset + 16);
}
}
}
offset += length;
total -= length;
}
*lengthP = 0;
return -1;
}
void cSatipRtcp::Process(void)
{
debug16("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
if (bufferM) {
int length;
while ((length = Read(bufferM, bufferLenM)) > 0) {
int offset = GetApplicationOffset(&length);
if (offset >= 0)
tunerM.ProcessApplicationData(bufferM + offset, length);
}
}
}
cString cSatipRtcp::ToString(void) const
{
return cString::sprintf("RTCP [device %d]", tunerM.GetId());
}

36
rtcp.h Normal file
View File

@@ -0,0 +1,36 @@
/*
* rtcp.h: SAT>IP plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#ifndef __SATIP_RTCP_H_
#define __SATIP_RTCP_H_
#include "socket.h"
#include "tunerif.h"
#include "pollerif.h"
class cSatipRtcp : public cSatipSocket, public cSatipPollerIf {
private:
enum {
eApplicationMaxSizeB = 1500,
};
cSatipTunerIf &tunerM;
unsigned int bufferLenM;
unsigned char *bufferM;
int GetApplicationOffset(int *lengthP);
public:
cSatipRtcp(cSatipTunerIf &tunerP);
virtual ~cSatipRtcp();
// for internal poller interface
public:
virtual int GetFd(void);
virtual void Process(void);
virtual cString ToString(void) const;
};
#endif /* __SATIP_RTCP_H_ */

148
rtp.c Normal file
View File

@@ -0,0 +1,148 @@
/*
* rtp.c: SAT>IP plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#define __STDC_FORMAT_MACROS // Required for format specifiers
#include <inttypes.h>
#include "config.h"
#include "common.h"
#include "log.h"
#include "rtp.h"
cSatipRtp::cSatipRtp(cSatipTunerIf &tunerP)
: tunerM(tunerP),
bufferLenM(eRtpPacketReadCount * eMaxUdpPacketSizeB),
bufferM(MALLOC(unsigned char, bufferLenM)),
lastErrorReportM(0),
packetErrorsM(0),
sequenceNumberM(-1)
{
debug1("%s () [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
if (!bufferM)
error("Cannot create RTP buffer! [device %d]", tunerM.GetId());
}
cSatipRtp::~cSatipRtp()
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
FREE_POINTER(bufferM);
}
int cSatipRtp::GetFd(void)
{
return Fd();
}
void cSatipRtp::Close(void)
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
cSatipSocket::Close();
sequenceNumberM = -1;
if (packetErrorsM) {
info("Detected %d RTP packet errors [device %d]", packetErrorsM, tunerM.GetId());
packetErrorsM = 0;
lastErrorReportM = time(NULL);
}
}
int cSatipRtp::GetHeaderLength(unsigned char *bufferP, unsigned int lengthP)
{
debug16("%s (, %d) [device %d]", __PRETTY_FUNCTION__, lengthP, tunerM.GetId());
unsigned int headerlen = 0;
if (lengthP > 0) {
if (bufferP[0] == TS_SYNC_BYTE)
return headerlen;
else if (lengthP > 3) {
// http://tools.ietf.org/html/rfc3550
// http://tools.ietf.org/html/rfc2250
// Version
unsigned int v = (bufferP[0] >> 6) & 0x03;
// Extension bit
unsigned int x = (bufferP[0] >> 4) & 0x01;
// CSCR count
unsigned int cc = bufferP[0] & 0x0F;
// Payload type: MPEG2 TS = 33
unsigned int pt = bufferP[1] & 0x7F;
if (pt != 33)
debug7("%s (%d) Received invalid RTP payload type %d - v=%d [device %d]",
__PRETTY_FUNCTION__, lengthP, pt, v, tunerM.GetId());
// Sequence number
int seq = ((bufferP[2] & 0xFF) << 8) | (bufferP[3] & 0xFF);
if ((((sequenceNumberM + 1) % 0xFFFF) == 0) && (seq == 0xFFFF))
sequenceNumberM = -1;
else if ((sequenceNumberM >= 0) && (((sequenceNumberM + 1) % 0xFFFF) != seq)) {
packetErrorsM++;
if (time(NULL) - lastErrorReportM > eReportIntervalS) {
info("Detected %d RTP packet errors [device %d]", packetErrorsM, tunerM.GetId());
packetErrorsM = 0;
lastErrorReportM = time(NULL);
}
sequenceNumberM = seq;
}
else
sequenceNumberM = seq;
// Header length
headerlen = (3 + cc) * (unsigned int)sizeof(uint32_t);
// Check if extension
if (x) {
// Extension header length
unsigned int ehl = (((bufferP[headerlen + 2] & 0xFF) << 8) | (bufferP[headerlen + 3] & 0xFF));
// Update header length
headerlen += (ehl + 1) * (unsigned int)sizeof(uint32_t);
}
// Check for empty payload
if (lengthP == headerlen) {
debug7("%s (%d) Received empty RTP packet #%d [device %d]", __PRETTY_FUNCTION__, lengthP, seq, tunerM.GetId());
headerlen = -1;
}
// Check that rtp is version 2 and payload contains multiple of TS packet data
else if ((v != 2) || (((lengthP - headerlen) % TS_SIZE) != 0) || (bufferP[headerlen] != TS_SYNC_BYTE)) {
debug7("%s (%d) Received incorrect RTP packet #%d v=%d len=%d sync=0x%02X [device %d]", __PRETTY_FUNCTION__,
lengthP, seq, v, headerlen, bufferP[headerlen], tunerM.GetId());
headerlen = -1;
}
else
debug7("%s (%d) Received RTP packet #%d v=%d len=%d sync=0x%02X [device %d]", __PRETTY_FUNCTION__,
lengthP, seq, v, headerlen, bufferP[headerlen], tunerM.GetId());
}
}
return headerlen;
}
void cSatipRtp::Process(void)
{
debug16("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
if (bufferM) {
unsigned int lenMsg[eRtpPacketReadCount];
uint64_t elapsed;
int count = 0;
cTimeMs processing(0);
do {
count = ReadMulti(bufferM, lenMsg, eRtpPacketReadCount, eMaxUdpPacketSizeB);
for (int i = 0; i < count; ++i) {
unsigned char *p = &bufferM[i * eMaxUdpPacketSizeB];
int headerlen = GetHeaderLength(p, lenMsg[i]);
if ((headerlen >= 0) && (headerlen < (int)lenMsg[i]))
tunerM.ProcessVideoData(p + headerlen, lenMsg[i] - headerlen);
}
} while (count >= eRtpPacketReadCount);
elapsed = processing.Elapsed();
if (elapsed > 1)
debug6("%s %d read(s) took %" PRIu64 " ms [device %d]", __PRETTY_FUNCTION__, count, elapsed, tunerM.GetId());
}
}
cString cSatipRtp::ToString(void) const
{
return cString::sprintf("RTP [device %d]", tunerM.GetId());
}

42
rtp.h Normal file
View File

@@ -0,0 +1,42 @@
/*
* rtp.h: SAT>IP plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#ifndef __SATIP_RTP_H_
#define __SATIP_RTP_H_
#include "socket.h"
#include "tunerif.h"
#include "pollerif.h"
class cSatipRtp : public cSatipSocket, public cSatipPollerIf {
private:
enum {
eRtpPacketReadCount = 50,
eMaxUdpPacketSizeB = TS_SIZE * 7 + 12,
eReportIntervalS = 300 // in seconds
};
cSatipTunerIf &tunerM;
unsigned int bufferLenM;
unsigned char *bufferM;
time_t lastErrorReportM;
int packetErrorsM;
int sequenceNumberM;
int GetHeaderLength(unsigned char *bufferP, unsigned int lengthP);
public:
cSatipRtp(cSatipTunerIf &tunerP);
virtual ~cSatipRtp();
virtual void Close(void);
// for internal poller interface
public:
virtual int GetFd(void);
virtual void Process(void);
virtual cString ToString(void) const;
};
#endif /* __SATIP_RTP_H_ */

450
rtsp.c Normal file
View File

@@ -0,0 +1,450 @@
/*
* rtsp.c: SAT>IP plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#define __STDC_FORMAT_MACROS // Required for format specifiers
#include <inttypes.h>
#include "config.h"
#include "common.h"
#include "log.h"
#include "rtsp.h"
cSatipRtsp::cSatipRtsp(cSatipTunerIf &tunerP)
: tunerM(tunerP),
headerBufferM(),
dataBufferM(),
modeM(cmUnicast),
handleM(NULL),
headerListM(NULL),
errorNoMoreM(""),
errorOutOfRangeM(""),
errorCheckSyntaxM("")
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
Create();
}
cSatipRtsp::~cSatipRtsp()
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
Destroy();
}
size_t cSatipRtsp::HeaderCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP)
{
cSatipRtsp *obj = reinterpret_cast<cSatipRtsp *>(dataP);
size_t len = sizeP * nmembP;
debug16("%s len=%zu", __PRETTY_FUNCTION__, len);
if (obj && (len > 0))
obj->headerBufferM.Add(ptrP, len);
return len;
}
size_t cSatipRtsp::DataCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP)
{
cSatipRtsp *obj = reinterpret_cast<cSatipRtsp *>(dataP);
size_t len = sizeP * nmembP;
debug16("%s len=%zu", __PRETTY_FUNCTION__, len);
if (obj)
obj->dataBufferM.Add(ptrP, len);
return len;
}
int cSatipRtsp::DebugCallback(CURL *handleP, curl_infotype typeP, char *dataP, size_t sizeP, void *userPtrP)
{
cSatipRtsp *obj = reinterpret_cast<cSatipRtsp *>(userPtrP);
if (obj) {
switch (typeP) {
case CURLINFO_TEXT:
debug2("%s [device %d] RTSP INFO %.*s", __PRETTY_FUNCTION__, obj->tunerM.GetId(), (int)sizeP, dataP);
break;
case CURLINFO_HEADER_IN:
debug2("%s [device %d] RTSP HEAD <<< %.*s", __PRETTY_FUNCTION__, obj->tunerM.GetId(), (int)sizeP, dataP);
break;
case CURLINFO_HEADER_OUT:
debug2("%s [device %d] RTSP HEAD >>>\n%.*s", __PRETTY_FUNCTION__, obj->tunerM.GetId(), (int)sizeP, dataP);
break;
case CURLINFO_DATA_IN:
debug2("%s [device %d] RTSP DATA <<< %.*s", __PRETTY_FUNCTION__, obj->tunerM.GetId(), (int)sizeP, dataP);
break;
case CURLINFO_DATA_OUT:
debug2("%s [device %d] RTSP DATA >>>\n%.*s", __PRETTY_FUNCTION__, obj->tunerM.GetId(), (int)sizeP, dataP);
break;
default:
break;
}
}
return 0;
}
cString cSatipRtsp::RtspUnescapeString(const char *strP)
{
debug1("%s (%s) [device %d]", __PRETTY_FUNCTION__, strP, tunerM.GetId());
if (handleM) {
char *p = curl_easy_unescape(handleM, strP, 0, NULL);
cString s = p;
curl_free(p);
return s;
}
return cString(strP);
}
void cSatipRtsp::Create(void)
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
if (!handleM)
handleM = curl_easy_init();
if (handleM) {
CURLcode res = CURLE_OK;
// Verbose output
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_VERBOSE, 1L);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_DEBUGFUNCTION, cSatipRtsp::DebugCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_DEBUGDATA, this);
// No progress meter and no signaling
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_NOPROGRESS, 1L);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_NOSIGNAL, 1L);
// Set timeouts
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_TIMEOUT_MS, (long)eConnectTimeoutMs);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_CONNECTTIMEOUT_MS, (long)eConnectTimeoutMs);
// Set user-agent
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_USERAGENT, *cString::sprintf("vdr-%s/%s (device %d)", PLUGIN_NAME_I18N, VERSION, tunerM.GetId()));
}
}
void cSatipRtsp::Destroy(void)
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
if (handleM) {
// Cleanup curl stuff
if (headerListM) {
curl_slist_free_all(headerListM);
headerListM = NULL;
}
curl_easy_cleanup(handleM);
handleM = NULL;
}
}
void cSatipRtsp::Reset(void)
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
Destroy();
Create();
}
bool cSatipRtsp::Options(const char *uriP)
{
debug1("%s (%s) [device %d]", __PRETTY_FUNCTION__, uriP, tunerM.GetId());
bool result = false;
if (handleM && !isempty(uriP)) {
long rc = 0;
cTimeMs processing(0);
CURLcode res = CURLE_OK;
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_URL, uriP);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, uriP);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_OPTIONS);
SATIP_CURL_EASY_PERFORM(handleM);
result = ValidateLatestResponse(&rc);
debug5("%s (%s) Response %ld in %" PRIu64 " ms [device %d]", __PRETTY_FUNCTION__, uriP, rc, processing.Elapsed(), tunerM.GetId());
}
return result;
}
bool cSatipRtsp::Setup(const char *uriP, int rtpPortP, int rtcpPortP)
{
debug1("%s (%s, %d, %d) [device %d]", __PRETTY_FUNCTION__, uriP, rtpPortP, rtcpPortP, tunerM.GetId());
bool result = false;
if (handleM && !isempty(uriP)) {
cString transport;
long rc = 0;
cTimeMs processing(0);
CURLcode res = CURLE_OK;
switch (modeM) {
case cmMulticast:
// RTP/AVP;multicast;destination=<IP multicast address>;port=<RTP port>-<RTCP port>;ttl=<ttl>
transport = cString::sprintf("RTP/AVP;multicast;port=%d-%d", rtpPortP, rtcpPortP);
break;
default:
case cmUnicast:
// RTP/AVP;unicast;client_port=<client RTP port>-<client RTCP port>
transport = cString::sprintf("RTP/AVP;unicast;client_port=%d-%d", rtpPortP, rtcpPortP);
break;
}
// Setup media stream
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, uriP);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_TRANSPORT, *transport);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_SETUP);
// Set header callback for catching the session and timeout
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_HEADERFUNCTION, cSatipRtsp::HeaderCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEHEADER, this);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, cSatipRtsp::DataCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, this);
SATIP_CURL_EASY_PERFORM(handleM);
// Session id is now known - disable header parsing
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_HEADERFUNCTION, NULL);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEHEADER, NULL);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, NULL);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, NULL);
if (headerBufferM.Size() > 0) {
ParseHeader();
headerBufferM.Reset();
}
if (dataBufferM.Size() > 0) {
ParseData();
dataBufferM.Reset();
}
result = ValidateLatestResponse(&rc);
debug5("%s (%s, %d, %d) Response %ld in %" PRIu64 " ms [device %d]", __PRETTY_FUNCTION__, uriP, rtpPortP, rtcpPortP, rc, processing.Elapsed(), tunerM.GetId());
}
return result;
}
bool cSatipRtsp::SetSession(const char *sessionP)
{
debug1("%s (%s) [device %d]", __PRETTY_FUNCTION__, sessionP, tunerM.GetId());
if (handleM) {
CURLcode res = CURLE_OK;
debug1("%s: session id quirk enabled [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_SESSION_ID, sessionP);
}
return true;
}
bool cSatipRtsp::Describe(const char *uriP)
{
debug1("%s (%s) [device %d]", __PRETTY_FUNCTION__, uriP, tunerM.GetId());
bool result = false;
if (handleM && !isempty(uriP)) {
long rc = 0;
cTimeMs processing(0);
CURLcode res = CURLE_OK;
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, uriP);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_DESCRIBE);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, cSatipRtsp::DataCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, this);
SATIP_CURL_EASY_PERFORM(handleM);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, NULL);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, NULL);
if (dataBufferM.Size() > 0) {
tunerM.ProcessApplicationData((u_char *)dataBufferM.Data(), dataBufferM.Size());
dataBufferM.Reset();
}
result = ValidateLatestResponse(&rc);
debug5("%s (%s) Response %ld in %" PRIu64 " ms [device %d]", __PRETTY_FUNCTION__, uriP, rc, processing.Elapsed(), tunerM.GetId());
}
return result;
}
bool cSatipRtsp::Play(const char *uriP)
{
debug1("%s (%s) [device %d]", __PRETTY_FUNCTION__, uriP, tunerM.GetId());
bool result = false;
if (handleM && !isempty(uriP)) {
long rc = 0;
cTimeMs processing(0);
CURLcode res = CURLE_OK;
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, uriP);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PLAY);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, cSatipRtsp::DataCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, this);
SATIP_CURL_EASY_PERFORM(handleM);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, NULL);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, NULL);
if (dataBufferM.Size() > 0) {
ParseData();
dataBufferM.Reset();
}
result = ValidateLatestResponse(&rc);
debug5("%s (%s) Response %ld in %" PRIu64 " ms [device %d]", __PRETTY_FUNCTION__, uriP, rc, processing.Elapsed(), tunerM.GetId());
}
return result;
}
bool cSatipRtsp::Teardown(const char *uriP)
{
debug1("%s (%s) [device %d]", __PRETTY_FUNCTION__, uriP, tunerM.GetId());
bool result = false;
if (handleM && !isempty(uriP)) {
long rc = 0;
cTimeMs processing(0);
CURLcode res = CURLE_OK;
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, uriP);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_TEARDOWN);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, cSatipRtsp::DataCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, this);
SATIP_CURL_EASY_PERFORM(handleM);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, NULL);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, NULL);
if (dataBufferM.Size() > 0) {
ParseData();
dataBufferM.Reset();
}
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_CLIENT_CSEQ, 1L);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_SESSION_ID, NULL);
result = ValidateLatestResponse(&rc);
debug5("%s (%s) Response %ld in %" PRIu64 " ms [device %d]", __PRETTY_FUNCTION__, uriP, rc, processing.Elapsed(), tunerM.GetId());
}
return result;
}
void cSatipRtsp::ParseHeader(void)
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
char *s, *p = headerBufferM.Data();
char *r = strtok_r(p, "\r\n", &s);
while (r) {
debug16("%s (%zu): %s", __PRETTY_FUNCTION__, headerBufferM.Size(), r);
r = skipspace(r);
if (strstr(r, "com.ses.streamID")) {
int streamid = -1;
if (sscanf(r, "com.ses.streamID:%11d", &streamid) == 1)
tunerM.SetStreamId(streamid);
}
else if (strstr(r, "Session:")) {
int timeout = -1;
char *session = NULL;
if (sscanf(r, "Session:%m[^;];timeout=%11d", &session, &timeout) == 2)
tunerM.SetSessionTimeout(skipspace(session), timeout * 1000);
else if (sscanf(r, "Session:%m[^;]", &session) == 1)
tunerM.SetSessionTimeout(skipspace(session), -1);
FREE_POINTER(session);
}
r = strtok_r(NULL, "\r\n", &s);
}
}
void cSatipRtsp::ParseData(void)
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
char *s, *p = dataBufferM.Data();
char *r = strtok_r(p, "\r\n", &s);
while (r) {
debug16("%s (%zu): %s", __PRETTY_FUNCTION__, dataBufferM.Size(), r);
r = skipspace(r);
if (strstr(r, "No-More:")) {
char *tmp = NULL;
if (sscanf(r, "No-More:%m[^;]", &tmp) == 1) {
errorNoMoreM = skipspace(tmp);
debug3("%s No-More: %s [device %d]", __PRETTY_FUNCTION__, *errorNoMoreM, tunerM.GetId());
}
FREE_POINTER(tmp);
}
else if (strstr(r, "Out-of-Range:")) {
char *tmp = NULL;
if (sscanf(r, "Out-of-Range:%m[^;]", &tmp) == 1) {
errorOutOfRangeM = skipspace(tmp);
debug3("%s Out-of-Range: %s [device %d]", __PRETTY_FUNCTION__, *errorOutOfRangeM, tunerM.GetId());
}
FREE_POINTER(tmp);
}
else if (strstr(r, "Check-Syntax:")) {
char *tmp = NULL;
if (sscanf(r, "Check-Syntax:%m[^;]", &tmp) == 1) {
errorCheckSyntaxM = skipspace(tmp);
debug3("%s Check-Syntax: %s [device %d]", __PRETTY_FUNCTION__, *errorCheckSyntaxM, tunerM.GetId());
}
FREE_POINTER(tmp);
}
r = strtok_r(NULL, "\r\n", &s);
}
}
bool cSatipRtsp::ValidateLatestResponse(long *rcP)
{
bool result = false;
if (handleM) {
char *url = NULL;
long rc = 0;
CURLcode res = CURLE_OK;
SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_RESPONSE_CODE, &rc);
switch (rc) {
case 200:
result = true;
break;
case 400:
// SETUP PLAY TEARDOWN
// The message body of the response may contain the "Check-Syntax:" parameter followed
// by the malformed syntax
if (!isempty(*errorCheckSyntaxM)) {
SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_EFFECTIVE_URL, &url);
error("Check syntax: %s (error code %ld: %s) [device %d]", *errorCheckSyntaxM, rc, url, tunerM.GetId());
break;
}
case 403:
// SETUP PLAY TEARDOWN
// The message body of the response may contain the "Out-of-Range:" parameter followed
// by a space-separated list of the attribute names that are not understood:
// "src" "fe" "freq" "pol" "msys" "mtype" "plts" "ro" "sr" "fec" "pids" "addpids" "delpids" "mcast
if (!isempty(*errorOutOfRangeM)) {
SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_EFFECTIVE_URL, &url);
error("Out of range: %s (error code %ld: %s) [device %d]", *errorOutOfRangeM, rc, url, tunerM.GetId());
break;
}
case 503:
// SETUP PLAY
// The message body of the response may contain the "No-More:" parameter followed
// by a space-separated list of the missing ressources: “sessions” "frontends" "pids
if (!isempty(*errorNoMoreM)) {
SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_EFFECTIVE_URL, &url);
error("No more: %s (error code %ld: %s) [device %d]", *errorNoMoreM, rc, url, tunerM.GetId());
break;
}
default:
SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_EFFECTIVE_URL, &url);
error("Detected invalid status code %ld: %s [device %d]", rc, url, tunerM.GetId());
break;
}
if (rcP)
*rcP = rc;
}
errorNoMoreM = "";
errorOutOfRangeM = "";
errorCheckSyntaxM = "";
debug1("%s result=%s [device %d]", __PRETTY_FUNCTION__, result ? "ok" : "failed", tunerM.GetId());
return result;
}

66
rtsp.h Normal file
View File

@@ -0,0 +1,66 @@
/*
* rtsp.h: SAT>IP plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#ifndef __SATIP_RTSP_H
#define __SATIP_RTSP_H
#include <curl/curl.h>
#include <curl/easy.h>
#ifndef CURLOPT_RTSPHEADER
#error "libcurl is missing required RTSP support"
#endif
#include "common.h"
#include "tunerif.h"
class cSatipRtsp {
private:
static size_t HeaderCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP);
static size_t DataCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP);
static int DebugCallback(CURL *handleP, curl_infotype typeP, char *dataP, size_t sizeP, void *userPtrP);
enum {
eConnectTimeoutMs = 1500, // in milliseconds
};
enum eCommunicationMode { cmUnicast, cmMulticast };
cSatipTunerIf &tunerM;
cSatipMemoryBuffer headerBufferM;
cSatipMemoryBuffer dataBufferM;
eCommunicationMode modeM;
CURL *handleM;
struct curl_slist *headerListM;
cString errorNoMoreM;
cString errorOutOfRangeM;
cString errorCheckSyntaxM;
void Create(void);
void Destroy(void);
void ParseHeader(void);
void ParseData(void);
bool ValidateLatestResponse(long *rcP);
// to prevent copy constructor and assignment
cSatipRtsp(const cSatipRtsp&);
cSatipRtsp& operator=(const cSatipRtsp&);
public:
cSatipRtsp(cSatipTunerIf &tunerP);
virtual ~cSatipRtsp();
cString RtspUnescapeString(const char *strP);
void Reset(void);
bool Options(const char *uriP);
bool Setup(const char *uriP, int rtpPortP, int rtcpPortP);
bool SetSession(const char *sessionP);
bool Describe(const char *uriP);
bool Play(const char *uriP);
bool Teardown(const char *uriP);
};
#endif // __SATIP_RTSP_H

263
satip.c
View File

@@ -11,23 +11,32 @@
#include "config.h"
#include "device.h"
#include "discover.h"
#include "log.h"
#include "poller.h"
#include "setup.h"
#if defined(APIVERSNUM) && APIVERSNUM < 20000
#error "VDR-2.0.0 API version or greater is required!"
#if defined(LIBCURL_VERSION_NUM) && LIBCURL_VERSION_NUM < 0x072400
#warning "CURL version >= 7.36.0 is recommended"
#endif
#if defined(APIVERSNUM) && APIVERSNUM < 20200
#error "VDR-2.2.0 API version or greater is required!"
#endif
#ifndef GITVERSION
#define GITVERSION ""
#endif
const char VERSION[] = "0.2.0" GITVERSION;
const char VERSION[] = "2.2.2" GITVERSION;
static const char DESCRIPTION[] = trNOOP("SAT>IP Devices");
class cPluginSatip : public cPlugin {
private:
unsigned int deviceCountM;
cSatipDiscover *discoverM;
cSatipDiscoverServers *serversM;
void ParseServer(const char *paramP);
int ParseCicams(const char *valueP, int *cicamsP);
int ParseSources(const char *valueP, int *sourcesP);
int ParseFilters(const char *valueP, int *filtersP);
public:
cPluginSatip(void);
@@ -53,10 +62,10 @@ public:
};
cPluginSatip::cPluginSatip(void)
: deviceCountM(1),
discoverM(NULL)
: deviceCountM(2),
serversM(NULL)
{
//debug("cPluginSatip::%s()", __FUNCTION__);
debug16("%s", __PRETTY_FUNCTION__);
// Initialize any member variables here.
// DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
// VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
@@ -64,53 +73,83 @@ cPluginSatip::cPluginSatip(void)
cPluginSatip::~cPluginSatip()
{
//debug("cPluginSatip::%s()", __FUNCTION__);
debug16("%s", __PRETTY_FUNCTION__);
// Clean up after yourself!
}
const char *cPluginSatip::CommandLineHelp(void)
{
debug("cPluginSatip::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
// Return a string that describes all known command line options.
return " -d <num>, --devices=<number> number of devices to be created\n";
return " -d <num>, --devices=<number> set number of devices to be created\n"
" -t <mode>, --trace=<mode> set the tracing mode\n"
" -s <ipaddr>|<model>|<desc>, --server=<ipaddr1>|<model1>|<desc1>;<ipaddr2>|<model2>|<desc2>\n"
" define hard-coded SAT>IP server(s)\n"
" -D, --detach set the detached mode on\n"
" -S, --single set the single model server mode on\n"
" -n, --noquirks disable all the server quirks\n";
}
bool cPluginSatip::ProcessArgs(int argc, char *argv[])
{
debug("cPluginSatip::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
// Implement command line argument processing here if applicable.
static const struct option long_options[] = {
{ "devices", required_argument, NULL, 'd' },
{ NULL, no_argument, NULL, 0 }
{ "devices", required_argument, NULL, 'd' },
{ "trace", required_argument, NULL, 't' },
{ "server", required_argument, NULL, 's' },
{ "detach", no_argument, NULL, 'D' },
{ "single", no_argument, NULL, 'S' },
{ "noquirks", no_argument, NULL, 'n' },
{ NULL, no_argument, NULL, 0 }
};
cString server;
int c;
while ((c = getopt_long(argc, argv, "d:", long_options, NULL)) != -1) {
while ((c = getopt_long(argc, argv, "d:t:s:DSn", long_options, NULL)) != -1) {
switch (c) {
case 'd':
deviceCountM = atoi(optarg);
deviceCountM = strtol(optarg, NULL, 0);
break;
case 't':
SatipConfig.SetTraceMode(strtol(optarg, NULL, 0));
break;
case 's':
server = optarg;
break;
case 'D':
SatipConfig.SetDetachedMode(true);
break;
case 'S':
SatipConfig.SetUseSingleModelServers(true);
break;
case 'n':
SatipConfig.SetDisableServerQuirks(true);
break;
default:
return false;
}
}
// this must be done after all parameters are parsed
if (!isempty(*server))
ParseServer(*server);
return true;
}
bool cPluginSatip::Initialize(void)
{
debug("cPluginSatip::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
// Initialize any background activities the plugin shall perform.
if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
error("Unable to initialize CURL");
SatipConfig.SetConfigDirectory(cPlugin::ResourceDirectory(PLUGIN_NAME_I18N));
cSatipDiscover::GetInstance()->Initialize();
cSatipPoller::GetInstance()->Initialize();
cSatipDiscover::GetInstance()->Initialize(serversM);
return cSatipDevice::Initialize(deviceCountM);
}
bool cPluginSatip::Start(void)
{
debug("cPluginSatip::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
// Start any background activities the plugin shall perform.
curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
cString info = cString::sprintf("Using CURL %s", data->version);
@@ -125,63 +164,142 @@ bool cPluginSatip::Start(void)
void cPluginSatip::Stop(void)
{
debug("cPluginSatip::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
// Stop any background activities the plugin is performing.
cSatipDevice::Shutdown();
cSatipDiscover::GetInstance()->Destroy();
cSatipPoller::GetInstance()->Destroy();
curl_global_cleanup();
}
void cPluginSatip::Housekeeping(void)
{
//debug("cPluginSatip::%s()", __FUNCTION__);
debug16("%s", __PRETTY_FUNCTION__);
// Perform any cleanup or other regular tasks.
}
void cPluginSatip::MainThreadHook(void)
{
//debug("cPluginSatip::%s()", __FUNCTION__);
debug16("%s", __PRETTY_FUNCTION__);
// Perform actions in the context of the main program thread.
// WARNING: Use with great care - see PLUGINS.html!
}
cString cPluginSatip::Active(void)
{
//debug("cPluginSatip::%s()", __FUNCTION__);
debug16("%s", __PRETTY_FUNCTION__);
// Return a message string if shutdown should be postponed
return NULL;
}
time_t cPluginSatip::WakeupTime(void)
{
//debug("cPluginSatip::%s()", __FUNCTION__);
debug16("%s", __PRETTY_FUNCTION__);
// Return custom wakeup time for shutdown script
return 0;
}
cOsdObject *cPluginSatip::MainMenuAction(void)
{
//debug("cPluginSatip::%s()", __FUNCTION__);
debug16("%s", __PRETTY_FUNCTION__);
// Perform the action when selected from the main VDR menu.
return NULL;
}
cMenuSetupPage *cPluginSatip::SetupMenu(void)
{
debug("cPluginSatip::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
// Return a setup menu in case the plugin supports one.
return new cSatipPluginSetup();
}
void cPluginSatip::ParseServer(const char *paramP)
{
debug1("%s (%s)", __PRETTY_FUNCTION__, paramP);
int n = 0;
char *s, *p = strdup(paramP);
char *r = strtok_r(p, ";", &s);
while (r) {
r = skipspace(r);
debug3("%s server[%d]=%s", __PRETTY_FUNCTION__, n, r);
cString serverAddr, serverModel, serverDescription;
int n2 = 0;
char *s2, *p2 = r;
char *r2 = strtok_r(p2, "|", &s2);
while (r2) {
debug3("%s param[%d]=%s", __PRETTY_FUNCTION__, n2, r2);
switch (n2++) {
case 0:
serverAddr = r2;
break;
case 1:
serverModel = r2;
break;
case 2:
serverDescription = r2;
break;
default:
break;
}
r2 = strtok_r(NULL, "|", &s2);
}
if (*serverAddr && *serverModel && *serverDescription) {
debug1("%s ipaddr=%s model=%s desc=%s", __PRETTY_FUNCTION__, *serverAddr, *serverModel, *serverDescription);
if (!serversM)
serversM = new cSatipDiscoverServers();
serversM->Add(new cSatipDiscoverServer(*serverAddr, *serverModel, *serverDescription));
}
++n;
r = strtok_r(NULL, ";", &s);
}
FREE_POINTER(p);
}
int cPluginSatip::ParseCicams(const char *valueP, int *cicamsP)
{
debug1("%s (%s,)", __PRETTY_FUNCTION__, valueP);
int n = 0;
char *s, *p = strdup(valueP);
char *r = strtok_r(p, " ", &s);
while (r) {
r = skipspace(r);
debug3("%s cicams[%d]=%s", __PRETTY_FUNCTION__, n, r);
if (n < MAX_CICAM_COUNT) {
cicamsP[n++] = atoi(r);
}
r = strtok_r(NULL, " ", &s);
}
FREE_POINTER(p);
return n;
}
int cPluginSatip::ParseSources(const char *valueP, int *sourcesP)
{
debug1("%s (%s,)", __PRETTY_FUNCTION__, valueP);
int n = 0;
char *s, *p = strdup(valueP);
char *r = strtok_r(p, " ", &s);
while (r) {
r = skipspace(r);
debug3("%s sources[%d]=%s", __PRETTY_FUNCTION__, n, r);
if (n < MAX_DISABLED_SOURCES_COUNT) {
sourcesP[n++] = cSource::FromString(r);
}
r = strtok_r(NULL, " ", &s);
}
FREE_POINTER(p);
return n;
}
int cPluginSatip::ParseFilters(const char *valueP, int *filtersP)
{
debug("cPluginSatip::%s(%s)", __FUNCTION__, valueP);
debug1("%s (%s,)", __PRETTY_FUNCTION__, valueP);
char buffer[256];
int n = 0;
while (valueP && *valueP && (n < SECTION_FILTER_TABLE_SIZE)) {
strn0cpy(buffer, valueP, sizeof(buffer));
int i = atoi(buffer);
//debug("cPluginSatip::%s(): filters[%d]=%d", __FUNCTION__, n, i);
debug3("%s filters[%d]=%d", __PRETTY_FUNCTION__, n, i);
if (i >= 0)
filtersP[n++] = i;
if ((valueP = strchr(valueP, ' ')) != NULL)
@@ -192,15 +310,33 @@ int cPluginSatip::ParseFilters(const char *valueP, int *filtersP)
bool cPluginSatip::SetupParse(const char *nameP, const char *valueP)
{
debug("cPluginSatip::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
// Parse your own setup parameters and store their values.
if (!strcasecmp(nameP, "OperatingMode"))
SatipConfig.SetOperatingMode(atoi(valueP));
else if (!strcasecmp(nameP, "EnableCIExtension"))
SatipConfig.SetCIExtension(atoi(valueP));
else if (!strcasecmp(nameP, "CICAM")) {
int Cicams[MAX_CICAM_COUNT];
for (unsigned int i = 0; i < ELEMENTS(Cicams); ++i)
Cicams[i] = 0;
unsigned int CicamsCount = ParseCicams(valueP, Cicams);
for (unsigned int i = 0; i < CicamsCount; ++i)
SatipConfig.SetCICAM(i, Cicams[i]);
}
else if (!strcasecmp(nameP, "EnableEITScan"))
SatipConfig.SetEITScan(atoi(valueP));
else if (!strcasecmp(nameP, "DisabledSources")) {
int DisabledSources[MAX_DISABLED_SOURCES_COUNT];
for (unsigned int i = 0; i < ELEMENTS(DisabledSources); ++i)
DisabledSources[i] = cSource::stNone;
unsigned int DisabledSourcesCount = ParseSources(valueP, DisabledSources);
for (unsigned int i = 0; i < DisabledSourcesCount; ++i)
SatipConfig.SetDisabledSources(i, DisabledSources[i]);
}
else if (!strcasecmp(nameP, "DisabledFilters")) {
int DisabledFilters[SECTION_FILTER_TABLE_SIZE];
for (unsigned int i = 0; i < ARRAY_SIZE(DisabledFilters); ++i)
for (unsigned int i = 0; i < ELEMENTS(DisabledFilters); ++i)
DisabledFilters[i] = -1;
unsigned int DisabledFiltersCount = ParseFilters(valueP, DisabledFilters);
for (unsigned int i = 0; i < DisabledFiltersCount; ++i)
@@ -213,13 +349,13 @@ bool cPluginSatip::SetupParse(const char *nameP, const char *valueP)
bool cPluginSatip::Service(const char *idP, void *dataP)
{
debug("cPluginSatip::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
return false;
}
const char **cPluginSatip::SVDRPHelpPages(void)
{
debug("cPluginSatip::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
static const char *HelpPages[] = {
"INFO [ <page> ] [ <card index> ]\n"
" Prints SAT>IP device information and statistics.\n"
@@ -229,10 +365,20 @@ const char **cPluginSatip::SVDRPHelpPages(void)
" Toggles between bit or byte information mode.\n",
"LIST\n"
" Lists active SAT>IP servers.\n",
"SCAN\n"
" Scans active SAT>IP servers.\n",
"STAT\n"
" Lists status information of SAT>IP devices.\n",
"CONT\n"
" Shows SAT>IP device count.\n",
"OPER\n"
" Toggles operating mode of SAT>IP devices.\n",
"OPER [ off | low | normal | high ]\n"
" Gets and(or sets operating mode of SAT>IP devices.\n",
"ATTA\n"
" Attach active SAT>IP servers.\n",
"DETA\n"
" Detachs active SAT>IP servers.\n",
"TRAC [ <mode> ]\n"
" Gets and/or sets used tracing mode.\n",
NULL
};
return HelpPages;
@@ -240,7 +386,7 @@ const char **cPluginSatip::SVDRPHelpPages(void)
cString cPluginSatip::SVDRPCommand(const char *commandP, const char *optionP, int &replyCodeP)
{
debug("cPluginSatip::%s(%s, %s)", __FUNCTION__, commandP, optionP);
debug1("%s (%s, %s,)", __PRETTY_FUNCTION__, commandP, optionP);
if (strcasecmp(commandP, "INFO") == 0) {
int index = cDevice::ActualDevice()->CardIndex();
int page = SATIP_DEVICE_INFO_ALL;
@@ -267,13 +413,13 @@ cString cPluginSatip::SVDRPCommand(const char *commandP, const char *optionP, in
}
else {
replyCodeP = 550; // Requested action not taken
return cString("SAT>IP information not available!");
return cString("SATIP information not available!");
}
}
else if (strcasecmp(commandP, "MODE") == 0) {
unsigned int mode = !SatipConfig.GetUseBytes();
SatipConfig.SetUseBytes(mode);
return cString::sprintf("SAT>IP information mode: %s\n", mode ? "bytes" : "bits");
return cString::sprintf("SATIP information mode: %s\n", mode ? "bytes" : "bits");
}
else if (strcasecmp(commandP, "LIST") == 0) {
cString list = cSatipDiscover::GetInstance()->GetServerList();
@@ -282,16 +428,34 @@ cString cPluginSatip::SVDRPCommand(const char *commandP, const char *optionP, in
}
else {
replyCodeP = 550; // Requested action not taken
return cString("No SAT>IP devices detected!");
return cString("No SATIP servers detected!");
}
}
else if (strcasecmp(commandP, "SCAN") == 0) {
cSatipDiscover::GetInstance()->TriggerScan();
return cString("SATIP server scan requested");
}
else if (strcasecmp(commandP, "STAT") == 0) {
return cSatipDevice::GetSatipStatus();
}
else if (strcasecmp(commandP, "CONT") == 0) {
return cString::sprintf("SAT>IP device count: %u", cSatipDevice::Count());
return cString::sprintf("SATIP device count: %u", cSatipDevice::Count());
}
else if (strcasecmp(commandP, "OPER") == 0) {
cString mode;
SatipConfig.ToggleOperatingMode();
switch (SatipConfig.GetOperatingMode()) {
unsigned int oper = SatipConfig.GetOperatingMode();
if (optionP && *optionP) {
if (strcasecmp(optionP, "off") == 0)
oper = cSatipConfig::eOperatingModeOff;
else if (strcasecmp(optionP, "low") == 0)
oper = cSatipConfig::eOperatingModeLow;
else if (strcasecmp(optionP, "normal") == 0)
oper = cSatipConfig::eOperatingModeNormal;
else if (strcasecmp(optionP, "high") == 0)
oper = cSatipConfig::eOperatingModeHigh;
SatipConfig.SetOperatingMode(oper);
}
switch (oper) {
case cSatipConfig::eOperatingModeOff:
mode = "off";
break;
@@ -308,7 +472,22 @@ cString cPluginSatip::SVDRPCommand(const char *commandP, const char *optionP, in
mode = "unknown";
break;
}
return cString::sprintf("SAT>IP operating mode: %s\n", *mode);
return cString::sprintf("SATIP operating mode: %s\n", *mode);
}
else if (strcasecmp(commandP, "ATTA") == 0) {
SatipConfig.SetDetachedMode(false);
info("SATIP servers attached");
return cString("SATIP servers attached");
}
else if (strcasecmp(commandP, "DETA") == 0) {
SatipConfig.SetDetachedMode(true);
info("SATIP servers detached");
return cString("SATIP servers detached");
}
else if (strcasecmp(commandP, "TRAC") == 0) {
if (optionP && *optionP)
SatipConfig.SetTraceMode(strtol(optionP, NULL, 0));
return cString::sprintf("SATIP tracing mode: 0x%04X\n", SatipConfig.GetTraceMode());
}
return NULL;

View File

@@ -6,6 +6,7 @@
*/
#include "config.h"
#include "log.h"
#include "sectionfilter.h"
cSatipSectionFilter::cSatipSectionFilter(int deviceIndexP, uint16_t pidP, uint8_t tidP, uint8_t maskP)
@@ -17,9 +18,10 @@ cSatipSectionFilter::cSatipSectionFilter(int deviceIndexP, uint16_t pidP, uint8_
secLenM(0),
tsFeedpM(0),
pidM(pidP),
ringBufferM(new cRingBufferFrame(eDmxMaxSectionCount * eDmxMaxSectionSize)),
deviceIndexM(deviceIndexP)
{
//debug("cSatipSectionFilter::%s(%d, %d)", __FUNCTION__, deviceIndexM, pidM);
debug16("%s (%d, %d, %d, %d) [device %d]", __PRETTY_FUNCTION__, deviceIndexM, pidM, tidP, maskP, deviceIndexM);
int i;
memset(secBufBaseM, 0, sizeof(secBufBaseM));
@@ -36,10 +38,10 @@ cSatipSectionFilter::cSatipSectionFilter(int deviceIndexP, uint16_t pidP, uint8_
for (i = 0; i < eDmxMaxFilterSize; ++i)
filterValueM[i] ^= 0xFF;
uint8_t mask, mode, doneq = 0;
uint8_t doneq = 0;
for (i = 0; i < eDmxMaxFilterSize; ++i) {
mode = filterModeM[i];
mask = filterMaskM[i];
uint8_t mode = filterModeM[i];
uint8_t mask = filterMaskM[i];
maskAndModeM[i] = (uint8_t)(mask & mode);
maskAndNotModeM[i] = (uint8_t)(mask & ~mode);
doneq |= maskAndNotModeM[i];
@@ -48,7 +50,7 @@ cSatipSectionFilter::cSatipSectionFilter(int deviceIndexP, uint16_t pidP, uint8_
// Create sockets
socketM[0] = socketM[1] = -1;
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socketM) != 0) {
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, socketM) != 0) {
char tmp[64];
error("Opening section filter sockets failed (device=%d pid=%d): %s", deviceIndexM, pidM, strerror_r(errno, tmp, sizeof(tmp)));
}
@@ -60,7 +62,7 @@ cSatipSectionFilter::cSatipSectionFilter(int deviceIndexP, uint16_t pidP, uint8_
cSatipSectionFilter::~cSatipSectionFilter()
{
//debug("cSatipSectionFilter::%s(%d, %d)", __FUNCTION__, deviceIndexM, pidM);
debug16("%s pid=%d [device %d]", __PRETTY_FUNCTION__, pidM, deviceIndexM);
int tmp = socketM[1];
socketM[1] = -1;
if (tmp >= 0)
@@ -70,6 +72,7 @@ cSatipSectionFilter::~cSatipSectionFilter()
if (tmp >= 0)
close(tmp);
secBufM = NULL;
DELETENULL(ringBufferM);
}
inline uint16_t cSatipSectionFilter::GetLength(const uint8_t *dataP)
@@ -99,13 +102,8 @@ int cSatipSectionFilter::Filter(void)
if (doneqM && !neq)
return 0;
// There is no data in the read socket, more can be written
if ((socketM[0] >= 0) && (socketM[1] >= 0) /*&& !select_single_desc(socketM[0], 0, false)*/) {
ssize_t len = write(socketM[1], secBufM, secLenM);
ERROR_IF(len < 0, "write()");
// Update statistics
AddSectionStatistic(len, 1);
}
if (ringBufferM && (secLenM > 0))
ringBufferM->Put(new cFrame(secBufM, secLenM));
}
return 0;
}
@@ -120,7 +118,7 @@ inline int cSatipSectionFilter::Feed(void)
int cSatipSectionFilter::CopyDump(const uint8_t *bufP, uint8_t lenP)
{
uint16_t limit, seclen, n;
uint16_t limit, n;
if (tsFeedpM >= eDmxMaxSectionFeedSize)
return 0;
@@ -142,7 +140,7 @@ int cSatipSectionFilter::CopyDump(const uint8_t *bufP, uint8_t lenP)
secBufM = secBufBaseM + secBufpM;
for (n = 0; secBufpM + 2 < limit; ++n) {
seclen = GetLength(secBufM);
uint16_t seclen = GetLength(secBufM);
if ((seclen <= 0) || (seclen > eDmxMaxSectionSize) || ((seclen + secBufpM) > limit))
return 0;
secLenM = seclen;
@@ -211,15 +209,35 @@ void cSatipSectionFilter::Process(const uint8_t* dataP)
}
}
bool cSatipSectionFilter::Send(void)
{
bool result = false;
cFrame *section = ringBufferM->Get();
if (section) {
uchar *data = section->Data();
int count = section->Count();
if (data && (count > 0) && (socketM[1] >= 0) && (socketM[0] >= 0)) {
ssize_t len = send(socketM[1], data, count, MSG_EOR);
ERROR_IF(len < 0 && errno != EAGAIN, "send()");
if (len > 0) {
ringBufferM->Drop(section);
result = !!ringBufferM->Available();
// Update statistics
AddSectionStatistic(len, 1);
}
}
}
return result;
}
cSatipSectionFilterHandler::cSatipSectionFilterHandler(int deviceIndexP, unsigned int bufferLenP)
: cThread("SAT>IP section handler", true),
: cThread(cString::sprintf("SATIP#%d section handler", deviceIndexP)),
ringBufferM(new cRingBufferLinear(bufferLenP, TS_SIZE, false, *cString::sprintf("SATIP %d section handler", deviceIndexP))),
mutexM(),
deviceIndexM(deviceIndexP),
processedM(false),
ringBufferM(new cRingBufferLinear(bufferLenP, TS_SIZE, false, *cString::sprintf("SAT>IP SECTION HANDLER %d", deviceIndexP)))
deviceIndexM(deviceIndexP)
{
debug("cSatipSectionFilterHandler::%s(%d)", __FUNCTION__, deviceIndexM);
debug1("%s (%d, %d) [device %d]", __PRETTY_FUNCTION__, deviceIndexM, bufferLenP, deviceIndexM);
// Initialize filter pointers
memset(filtersM, 0, sizeof(filtersM));
@@ -230,16 +248,17 @@ cSatipSectionFilterHandler::cSatipSectionFilterHandler(int deviceIndexP, unsigne
ringBufferM->SetIoThrottle();
}
else
error("Failed to allocate buffer for section filter handler (device=%d): ", deviceIndexM);
error("Failed to allocate buffer for section filter handler [device=%d]", deviceIndexM);
Start();
}
cSatipSectionFilterHandler::~cSatipSectionFilterHandler()
{
debug("cSatipSectionFilterHandler::%s(%d)", __FUNCTION__, deviceIndexM);
Stop();
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
// Stop thread
if (Running())
Cancel(3);
DELETE_POINTER(ringBufferM);
// Destroy all filters
@@ -248,26 +267,28 @@ cSatipSectionFilterHandler::~cSatipSectionFilterHandler()
Delete(i);
}
bool cSatipSectionFilterHandler::Stop(void)
{
debug("cSatipSectionFilterHandler::%s(%d): entering", __FUNCTION__, deviceIndexM);
// Stop thread
if (Running())
Cancel(3);
return true;
}
void cSatipSectionFilterHandler::Action(void)
{
debug("cSatipSectionFilterHandler::%s(%d): entering", __FUNCTION__, deviceIndexM);
debug1("%s Entering [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
bool processed = false;
// Do the thread loop
while (Running()) {
// Send demuxed section packets through all filters
bool retry = false;
mutexM.Lock();
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
if (filtersM[i] && filtersM[i]->Send())
retry = true;
}
mutexM.Unlock();
if (retry)
continue;
// Read one TS packet
if (ringBufferM) {
int len = 0;
if (processedM) {
if (processed) {
ringBufferM->Del(TS_SIZE);
processedM = false;
processed = false;
}
uchar *p = ringBufferM->Get(len);
if (p && (len >= TS_SIZE)) {
@@ -279,7 +300,7 @@ void cSatipSectionFilterHandler::Action(void)
}
}
ringBufferM->Del(len);
debug("cSatipSectionFilterHandler::%s(%d): Skipped %d bytes to sync on TS packet", __FUNCTION__, deviceIndexM, len);
debug1("%s Skipped %d bytes to sync on TS packet [device %d]", __PRETTY_FUNCTION__, len, deviceIndexM);
continue;
}
// Process TS packet through all filters
@@ -289,18 +310,18 @@ void cSatipSectionFilterHandler::Action(void)
filtersM[i]->Process(p);
}
mutexM.Unlock();
processedM = true;
processed = true;
continue;
}
}
cCondWait::SleepMs(10); // to avoid busy loop and reduce cpu load
}
debug("cSatipSectionFilterHandler::%s(%d): exiting", __FUNCTION__, deviceIndexM);
debug1("%s Exiting [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
}
cString cSatipSectionFilterHandler::GetInformation(void)
{
//debug("cSatipSectionFilterHandler::%s(%d)", __FUNCTION__, deviceIndexM);
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
// loop through active section filters
cMutexLock MutexLock(&mutexM);
cString s = "";
@@ -319,9 +340,9 @@ cString cSatipSectionFilterHandler::GetInformation(void)
bool cSatipSectionFilterHandler::Delete(unsigned int indexP)
{
//debug("cSatipSectionFilterHandler::%s(%d): index=%d", __FUNCTION__, deviceIndexM, indexP);
debug16("%s (%d) [device %d]", __PRETTY_FUNCTION__, indexP, deviceIndexM);
if ((indexP < eMaxSecFilterCount) && filtersM[indexP]) {
//debug("cSatipSectionFilterHandler::%s(%d): found %d", __FUNCTION__, deviceIndexM, indexP);
debug8("%s (%d) Found [device %d]", __PRETTY_FUNCTION__, indexP, deviceIndexM);
cSatipSectionFilter *tmp = filtersM[indexP];
filtersM[indexP] = NULL;
delete tmp;
@@ -332,7 +353,7 @@ bool cSatipSectionFilterHandler::Delete(unsigned int indexP)
bool cSatipSectionFilterHandler::IsBlackListed(u_short pidP, u_char tidP, u_char maskP) const
{
//debug("cSatipSectionFilterHandler::%s(%d): pid=%d tid=%02X mask=%02X", __FUNCTION__, deviceIndexM, pidP, tidP, maskP);
debug16("%s (%d, %02X, %02X) [device %d]", __PRETTY_FUNCTION__, pidP, tidP, maskP, deviceIndexM);
// loop through section filter table
for (int i = 0; i < SECTION_FILTER_TABLE_SIZE; ++i) {
int index = SatipConfig.GetDisabledFilters(i);
@@ -340,7 +361,7 @@ bool cSatipSectionFilterHandler::IsBlackListed(u_short pidP, u_char tidP, u_char
if ((index >= 0) && (index < SECTION_FILTER_TABLE_SIZE) &&
(section_filter_table[index].pid == pidP) && (section_filter_table[index].tid == tidP) &&
(section_filter_table[index].mask == maskP)) {
//debug("cSatipSectionFilterHandler::%s(%d): found %s", __FUNCTION__, deviceIndexM, section_filter_table[index].description);
debug8("%s Found %s [device %d]", __PRETTY_FUNCTION__, section_filter_table[index].description, deviceIndexM);
return true;
}
}
@@ -349,7 +370,6 @@ bool cSatipSectionFilterHandler::IsBlackListed(u_short pidP, u_char tidP, u_char
int cSatipSectionFilterHandler::Open(u_short pidP, u_char tidP, u_char maskP)
{
// Lock
cMutexLock MutexLock(&mutexM);
// Blacklist check, refuse certain filters
if (IsBlackListed(pidP, tidP, maskP))
@@ -358,7 +378,7 @@ int cSatipSectionFilterHandler::Open(u_short pidP, u_char tidP, u_char maskP)
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
if (!filtersM[i]) {
filtersM[i] = new cSatipSectionFilter(deviceIndexM, pidP, tidP, maskP);
//debug("cSatipSectionFilterHandler::%s(%d): pid=%d tid=%02X mask=%02X handle=%d index=%u", __FUNCTION__, deviceIndexM, pidP, tidP, maskP, filtersM[i]->GetFd(), i);
debug16("%s (%d, %02X, %02X) handle=%d index=%u [device %d]", __PRETTY_FUNCTION__, pidP, tidP, maskP, filtersM[i]->GetFd(), i, deviceIndexM);
return filtersM[i]->GetFd();
}
}
@@ -368,12 +388,11 @@ int cSatipSectionFilterHandler::Open(u_short pidP, u_char tidP, u_char maskP)
void cSatipSectionFilterHandler::Close(int handleP)
{
// Lock
cMutexLock MutexLock(&mutexM);
// Search the filter for deletion
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
if (filtersM[i] && (handleP == filtersM[i]->GetFd())) {
//debug("cSatipSectionFilterHandler::%s(%d): pid=%d handle=%d index=%u", __FUNCTION__, deviceIndexM, filtersM[i]->GetPid(), filtersM[i]->GetFd(), i);
debug8("%s (%d) pid=%d index=%u [device %d]", __PRETTY_FUNCTION__, handleP, filtersM[i]->GetPid(), i, deviceIndexM);
Delete(i);
break;
}
@@ -382,12 +401,11 @@ void cSatipSectionFilterHandler::Close(int handleP)
int cSatipSectionFilterHandler::GetPid(int handleP)
{
// Lock
cMutexLock MutexLock(&mutexM);
// Search the filter for data
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
if (filtersM[i] && (handleP == filtersM[i]->GetFd())) {
//debug("cSatipSectionFilterHandler::%s(%d): pid=%d handle=%d index=%u", __FUNCTION__, deviceIndexM, filtersM[i]->GetPid(), filtersM[i]->GetFd(), i);
debug8("%s (%d) pid=%d index=%u [device %d]", __PRETTY_FUNCTION__, handleP, filtersM[i]->GetPid(), i, deviceIndexM);
return filtersM[i]->GetPid();
}
}
@@ -396,7 +414,7 @@ int cSatipSectionFilterHandler::GetPid(int handleP)
void cSatipSectionFilterHandler::Write(uchar *bufferP, int lengthP)
{
//debug("cSatipSectionFilterHandler::%s(%d): length=%d", __FUNCTION__, deviceIndexM, lengthP);
debug16("%s (, %d) [device %d]", __PRETTY_FUNCTION__, lengthP, deviceIndexM);
// Fill up the buffer
if (ringBufferM) {
int len = ringBufferM->Put(bufferP, lengthP);

View File

@@ -8,9 +8,6 @@
#ifndef __SATIP_SECTIONFILTER_H
#define __SATIP_SECTIONFILTER_H
#ifdef __FreeBSD__
#include <sys/socket.h>
#endif // __FreeBSD__
#include <vdr/device.h>
#include "common.h"
@@ -18,9 +15,10 @@
class cSatipSectionFilter : public cSatipSectionStatistics {
private:
enum dmx_limits {
eDmxMaxFilterSize = 18,
eDmxMaxSectionSize = 4096,
enum {
eDmxMaxFilterSize = 18,
eDmxMaxSectionCount = 64,
eDmxMaxSectionSize = 4096,
eDmxMaxSectionFeedSize = (eDmxMaxSectionSize + TS_SIZE)
};
@@ -35,6 +33,7 @@ private:
uint16_t tsFeedpM;
uint16_t pidM;
cRingBufferFrame *ringBufferM;
int deviceIndexM;
int socketM[2];
@@ -56,6 +55,7 @@ public:
cSatipSectionFilter(int deviceIndexP, uint16_t pidP, uint8_t tidP, uint8_t maskP);
virtual ~cSatipSectionFilter();
void Process(const uint8_t* dataP);
bool Send(void);
int GetFd(void) { return socketM[0]; }
uint16_t GetPid(void) const { return pidM; }
};
@@ -65,10 +65,9 @@ private:
enum {
eMaxSecFilterCount = 32
};
cRingBufferLinear *ringBufferM;
cMutex mutexM;
int deviceIndexM;
bool processedM;
cRingBufferLinear *ringBufferM;
cSatipSectionFilter *filtersM[eMaxSecFilterCount];
bool Delete(unsigned int indexP);
@@ -80,7 +79,6 @@ protected:
public:
cSatipSectionFilterHandler(int deviceIndexP, unsigned int bufferLenP);
virtual ~cSatipSectionFilterHandler();
bool Stop(void);
cString GetInformation(void);
int Open(u_short pidP, u_char tidP, u_char maskP);
void Close(int handleP);

366
server.c
View File

@@ -7,52 +7,145 @@
#include <vdr/sources.h>
#include "config.h"
#include "common.h"
#include "log.h"
#include "server.h"
// --- cSatipFrontend ---------------------------------------------------------
cSatipFrontend::cSatipFrontend(const int indexP, const char *descriptionP)
: indexM(indexP),
transponderM(0),
deviceIdM(-1),
descriptionM(descriptionP)
{
}
cSatipFrontend::~cSatipFrontend()
{
}
// --- cSatipFrontends --------------------------------------------------------
bool cSatipFrontends::Matches(int deviceIdP, int transponderP)
{
for (cSatipFrontend *f = First(); f; f = Next(f)) {
if (f->Attached() && (f->DeviceId() == deviceIdP) && (f->Transponder() == transponderP))
return true;
}
return false;
}
bool cSatipFrontends::Assign(int deviceIdP, int transponderP)
{
cSatipFrontend *tmp = NULL;
// Prefer any unused one
for (cSatipFrontend *f = First(); f; f = Next(f)) {
if (!f->Attached() || (f->DeviceId() == deviceIdP)) {
tmp = f;
break;
}
}
if (tmp) {
tmp->SetTransponder(transponderP);
return true;
}
return false;
}
bool cSatipFrontends::Attach(int deviceIdP, int transponderP)
{
for (cSatipFrontend *f = First(); f; f = Next(f)) {
if (f->Transponder() == transponderP) {
f->Attach(deviceIdP);
debug9("%s (%d, %d) %s/#%d", __PRETTY_FUNCTION__, deviceIdP, transponderP, *f->Description(), f->Index());
return true;
}
}
return false;
}
bool cSatipFrontends::Detach(int deviceIdP, int transponderP)
{
for (cSatipFrontend *f = First(); f; f = Next(f)) {
if (f->Transponder() == transponderP) {
f->Detach(deviceIdP);
debug9("%s (%d, %d) %s/#%d", __PRETTY_FUNCTION__, deviceIdP, transponderP, *f->Description(), f->Index());
return true;
}
}
return false;
}
// --- cSatipServer -----------------------------------------------------------
cSatipServer::cSatipServer(const char *addressP, const char *descriptionP, const char *modelP)
: addressM(addressP),
descriptionM(descriptionP),
modelM(modelP),
modelTypeM(eSatipModelTypeMask),
useCountM(0),
cSatipServer::cSatipServer(const char *addressP, const char *modelP, const char *descriptionP)
: addressM((addressP && *addressP) ? addressP : "0.0.0.0"),
modelM((modelP && *modelP) ? modelP : "DVBS-1"),
descriptionM(!isempty(descriptionP) ? descriptionP : "MyBrokenHardware"),
quirksM(""),
quirkM(eSatipQuirkNone),
hasCiM(false),
createdM(time(NULL)),
lastSeenM(0)
{
memset(modelCountM, 0, sizeof(modelCountM));
if (isempty(*modelM))
modelM = "DVBS-1";
if (!SatipConfig.GetDisableServerQuirks()) {
// These devices contain a session id bug:
// Inverto Airscreen Server IDL 400 ?
// Elgato EyeTV Netstream 4Sat ?
if (strstr(*descriptionM, "GSSBOX") || // Grundig Sat Systems GSS.box DSI 400
strstr(*descriptionM, "DIGIBIT") || // Telestar Digibit R1
strstr(*descriptionM, "Triax SatIP Converter") // Triax TSS 400
) {
quirkM |= eSatipQuirkSessionId;
quirksM = cString::sprintf("%s%sSessionId", *quirksM, isempty(*quirksM) ? "" : ",");
}
// These devices contain a play (add/delpids) parameter bug:
if (strstr(*descriptionM, "fritzdvbc") // Fritz!WLAN Repeater DVB-C
) {
quirkM |= eSatipQuirkPlayPids;
quirksM = cString::sprintf("%s%sPlayPids", *quirksM, isempty(*quirksM) ? "" : ",");
}
// These devices contain a frontend locking bug:
if (strstr(*descriptionM, "fritzdvbc") // Fritz!WLAN Repeater DVB-C
) {
quirkM |= eSatipQuirkForceLock;
quirksM = cString::sprintf("%s%sForceLock", *quirksM, isempty(*quirksM) ? "" : ",");
}
debug3("%s description=%s quirks=%s", __PRETTY_FUNCTION__, *descriptionM, *quirksM);
}
// These devices support the X_PMT protocol extension
if (strstr(*descriptionM, "OctopusNet")) // Digital Devices OctopusNet
hasCiM = true;
char *s, *p = strdup(*modelM);
char *r = strtok_r(p, ",", &s);
while (r) {
if (strstr(r, "DVBS2")) {
modelTypeM |= cSatipServer::eSatipModelTypeDVBS2;
if (char *c = strstr(r, "-"))
modelCountM[eSatipModuleDVBS2] = atoi(++c);
else
modelCountM[eSatipModuleDVBS2] = 1;
char *c;
if (c = strstr(r, "DVBS2-")) {
int count = atoi(c + 6);
for (int i = 1; i <= count; ++i)
frontendsM[eSatipFrontendDVBS2].Add(new cSatipFrontend(i, "DVB-S2"));
}
if (strstr(r, "DVBT2")) {
modelTypeM |= cSatipServer::eSatipModelTypeDVBT | cSatipServer::eSatipModelTypeDVBT2;
if (char *c = strstr(r, "-"))
modelCountM[eSatipModuleDVBT2] = atoi(++c);
else
modelCountM[eSatipModuleDVBT2] = 1;
// Add model quirks here
if (strstr(*addressM, "OctopusNet"))
modelTypeM |= cSatipServer::eSatipModelTypeDVBC;
else if (c = strstr(r, "DVBT-")) {
int count = atoi(c + 5);
for (int i = 1; i <= count; ++i)
frontendsM[eSatipFrontendDVBT].Add(new cSatipFrontend(i, "DVB-T"));
}
if (strstr(r, "DVBT")) {
modelTypeM |= cSatipServer::eSatipModelTypeDVBT;
if (char *c = strstr(r, "-"))
modelCountM[eSatipModuleDVBT] = atoi(++c);
else
modelCountM[eSatipModuleDVBT] = 1;
// Add model quirks here
if (strstr(*addressM, "OctopusNet"))
modelTypeM |= cSatipServer::eSatipModelTypeDVBC;
else if (c = strstr(r, "DVBT2-")) {
int count = atoi(c + 6);
for (int i = 1; i <= count; ++i)
frontendsM[eSatipFrontendDVBT2].Add(new cSatipFrontend(i, "DVB-T2"));
}
else if (c = strstr(r, "DVBC-")) {
int count = atoi(c + 5);
for (int i = 1; i <= count; ++i)
frontendsM[eSatipFrontendDVBC].Add(new cSatipFrontend(i, "DVB-C"));
}
else if (c = strstr(r, "DVBC2-")) {
int count = atoi(c + 6);
for (int i = 1; i <= count; ++i)
frontendsM[eSatipFrontendDVBC2].Add(new cSatipFrontend(i, "DVB-C2"));
}
r = strtok_r(NULL, ",", &s);
}
@@ -66,15 +159,105 @@ cSatipServer::~cSatipServer()
int cSatipServer::Compare(const cListObject &listObjectP) const
{
const cSatipServer *s = (const cSatipServer *)&listObjectP;
return strcasecmp(*addressM, *s->addressM);
int result = strcasecmp(*addressM, *s->addressM);
if (!result) {
result = strcasecmp(*modelM, *s->modelM);
if (!result)
result = strcasecmp(*descriptionM, *s->descriptionM);
}
return result;
}
void cSatipServer::Use(bool onOffP)
bool cSatipServer::Assign(int deviceIdP, int sourceP, int systemP, int transponderP)
{
if (onOffP)
++useCountM;
else
--useCountM;
bool result = false;
if (cSource::IsType(sourceP, 'S'))
result = frontendsM[eSatipFrontendDVBS2].Assign(deviceIdP, transponderP);
else if (cSource::IsType(sourceP, 'T')) {
if (systemP)
result = frontendsM[eSatipFrontendDVBT2].Assign(deviceIdP, transponderP);
else
result = frontendsM[eSatipFrontendDVBT].Assign(deviceIdP, transponderP) || frontendsM[eSatipFrontendDVBT2].Assign(deviceIdP, transponderP);
}
else if (cSource::IsType(sourceP, 'C')) {
if (systemP)
result = frontendsM[eSatipFrontendDVBC2].Assign(deviceIdP, transponderP);
else
result = frontendsM[eSatipFrontendDVBC].Assign(deviceIdP, transponderP) || frontendsM[eSatipFrontendDVBC2].Assign(deviceIdP, transponderP);
}
return result;
}
bool cSatipServer::Matches(int sourceP)
{
if (cSource::IsType(sourceP, 'S'))
return GetModulesDVBS2();
else if (cSource::IsType(sourceP, 'T'))
return GetModulesDVBT() || GetModulesDVBT2();
else if (cSource::IsType(sourceP, 'C'))
return GetModulesDVBC() || GetModulesDVBC2();
return false;
}
bool cSatipServer::Matches(int deviceIdP, int sourceP, int systemP, int transponderP)
{
bool result = false;
if (cSource::IsType(sourceP, 'S'))
result = frontendsM[eSatipFrontendDVBS2].Matches(deviceIdP, transponderP);
else if (cSource::IsType(sourceP, 'T')) {
if (systemP)
result = frontendsM[eSatipFrontendDVBT2].Matches(deviceIdP, transponderP);
else
result = frontendsM[eSatipFrontendDVBT].Matches(deviceIdP, transponderP) || frontendsM[eSatipFrontendDVBT2].Matches(deviceIdP, transponderP);
}
else if (cSource::IsType(sourceP, 'C')) {
if (systemP)
result = frontendsM[eSatipFrontendDVBC2].Matches(deviceIdP, transponderP);
else
result = frontendsM[eSatipFrontendDVBC].Matches(deviceIdP, transponderP) || frontendsM[eSatipFrontendDVBC2].Matches(deviceIdP, transponderP);
}
return result;
}
void cSatipServer::Attach(int deviceIdP, int transponderP)
{
for (int i = 0; i < eSatipFrontendCount; ++i) {
if (frontendsM[i].Attach(deviceIdP, transponderP))
return;
}
}
void cSatipServer::Detach(int deviceIdP, int transponderP)
{
for (int i = 0; i < eSatipFrontendCount; ++i) {
if (frontendsM[i].Detach(deviceIdP, transponderP))
return;
}
}
int cSatipServer::GetModulesDVBS2(void)
{
return frontendsM[eSatipFrontendDVBS2].Count();
}
int cSatipServer::GetModulesDVBT(void)
{
return frontendsM[eSatipFrontendDVBT].Count();
}
int cSatipServer::GetModulesDVBT2(void)
{
return frontendsM[eSatipFrontendDVBT2].Count();
}
int cSatipServer::GetModulesDVBC(void)
{
return frontendsM[eSatipFrontendDVBC].Count();
}
int cSatipServer::GetModulesDVBC2(void)
{
return frontendsM[eSatipFrontendDVBC2].Count();
}
// --- cSatipServers ----------------------------------------------------------
@@ -82,35 +265,32 @@ void cSatipServer::Use(bool onOffP)
cSatipServer *cSatipServers::Find(cSatipServer *serverP)
{
for (cSatipServer *s = First(); s; s = Next(s)) {
if (s == serverP)
if (s->Compare(*serverP) == 0)
return s;
}
return NULL;
}
cSatipServer *cSatipServers::Find(int sourceP, int systemP)
cSatipServer *cSatipServers::Find(int sourceP)
{
cSatipServer *result = NULL;
int model = 0;
if (cSource::IsType(sourceP, 'S'))
model |= cSatipServer::eSatipModelTypeDVBS2;
else if (cSource::IsType(sourceP, 'T')) {
if (systemP < 0)
model |= cSatipServer::eSatipModelTypeDVBT2 | cSatipServer::eSatipModelTypeDVBT;
else
model |= systemP ? cSatipServer::eSatipModelTypeDVBT2 : cSatipServer::eSatipModelTypeDVBT;
}
else if (cSource::IsType(sourceP, 'C'))
model |= cSatipServer::eSatipModelTypeDVBC;
for (cSatipServer *s = First(); s; s = Next(s)) {
if (s->Match(model)) {
result = s;
if (!s->Used()) {
break;
}
}
if (s->Matches(sourceP))
return s;
}
return result;
return NULL;
}
cSatipServer *cSatipServers::Assign(int deviceIdP, int sourceP, int transponderP, int systemP)
{
for (cSatipServer *s = First(); s; s = Next(s)) {
if (s->Matches(deviceIdP, sourceP, systemP, transponderP))
return s;
}
for (cSatipServer *s = First(); s; s = Next(s)) {
if (s->Assign(deviceIdP, sourceP, systemP, transponderP))
return s;
}
return NULL;
}
cSatipServer *cSatipServers::Update(cSatipServer *serverP)
@@ -124,26 +304,72 @@ cSatipServer *cSatipServers::Update(cSatipServer *serverP)
return NULL;
}
void cSatipServers::Use(cSatipServer *serverP, bool onOffP)
void cSatipServers::Attach(cSatipServer *serverP, int deviceIdP, int transponderP)
{
for (cSatipServer *s = First(); s; s = Next(s)) {
if (s == serverP) {
s->Use(onOffP);
s->Attach(deviceIdP, transponderP);
break;
}
}
}
void cSatipServers::Detach(cSatipServer *serverP, int deviceIdP, int transponderP)
{
for (cSatipServer *s = First(); s; s = Next(s)) {
if (s == serverP) {
s->Detach(deviceIdP, transponderP);
break;
}
}
}
bool cSatipServers::IsQuirk(cSatipServer *serverP, int quirkP)
{
bool result = false;
for (cSatipServer *s = First(); s; s = Next(s)) {
if (s == serverP) {
result = s->Quirk(quirkP);
break;
}
}
return result;
}
bool cSatipServers::HasCI(cSatipServer *serverP)
{
bool result = false;
for (cSatipServer *s = First(); s; s = Next(s)) {
if (s == serverP) {
result = s->HasCI();
break;
}
}
return result;
}
void cSatipServers::Cleanup(uint64_t intervalMsP)
{
for (cSatipServer *s = First(); s; s = Next(s)) {
if (!intervalMsP || (s->LastSeen() > intervalMsP)) {
info("Removing device %s (%s %s)", s->Description(), s->Address(), s->Model());
info("Removing server %s (%s %s)", s->Description(), s->Address(), s->Model());
Del(s);
}
}
}
cString cSatipServers::GetAddress(cSatipServer *serverP)
{
cString address = "";
for (cSatipServer *s = First(); s; s = Next(s)) {
if (s == serverP) {
address = s->Address();
break;
}
}
return address;
}
cString cSatipServers::GetString(cSatipServer *serverP)
{
cString list = "";
@@ -168,10 +394,16 @@ int cSatipServers::NumProvidedSystems(void)
{
int count = 0;
for (cSatipServer *s = First(); s; s = Next(s)) {
// DVB-S*: qpsk, 8psk
count += s->Satellite() * 4;
// DVB-T*: qpsk, qam16, qam64, qam256
count += (s->Terrestrial2() ? s->Terrestrial2() : s->Terrestrial()) * 4;
// DVB-S2: qpsk, 8psk, 16apsk, 32apsk
count += s->GetModulesDVBS2() * 4;
// DVB-T: qpsk, qam16, qam64
count += s->GetModulesDVBT() * 3;
// DVB-T2: qpsk, qam16, qam64, qam256
count += s->GetModulesDVBT2() * 4;
// DVB-C: qam64, qam128, qam256
count += s->GetModulesDVBC() * 3;
// DVB-C2: qam16, qam32, qam64, qam128, qam256
count += s->GetModulesDVBC2() * 5;
}
return count;
}

112
server.h
View File

@@ -8,50 +8,93 @@
#ifndef __SATIP_SERVER_H
#define __SATIP_SERVER_H
class cSatipServer;
// --- cSatipFrontend ---------------------------------------------------------
class cSatipFrontend : public cListObject {
private:
int indexM;
int transponderM;
int deviceIdM;
cString descriptionM;
public:
cSatipFrontend(const int indexP, const char *descriptionP);
virtual ~cSatipFrontend();
void Attach(int deviceIdP) { deviceIdM = deviceIdP; }
void Detach(int deviceIdP) { if (deviceIdP == deviceIdM) deviceIdM = -1; }
cString Description(void) { return descriptionM; }
bool Attached(void) { return (deviceIdM >= 0); }
int Index(void) { return indexM; }
int Transponder(void) { return transponderM; }
int DeviceId(void) { return deviceIdM; }
void SetTransponder(int transponderP) { transponderM = transponderP; }
};
// --- cSatipFrontends --------------------------------------------------------
class cSatipFrontends : public cList<cSatipFrontend> {
public:
bool Matches(int deviceIdP, int transponderP);
bool Assign(int deviceIdP, int transponderP);
bool Attach(int deviceIdP, int transponderP);
bool Detach(int deviceIdP, int transponderP);
};
// --- cSatipServer -----------------------------------------------------------
class cSatipServer : public cListObject {
private:
enum eSatipModule {
eSatipModuleDVBS2 = 0,
eSatipModuleDVBT,
eSatipModuleDVBT2,
eSatipModuleCount
enum eSatipFrontend {
eSatipFrontendDVBS2 = 0,
eSatipFrontendDVBT,
eSatipFrontendDVBT2,
eSatipFrontendDVBC,
eSatipFrontendDVBC2,
eSatipFrontendCount
};
cString addressM;
cString descriptionM;
cString modelM;
int modelCountM[eSatipModuleCount];
int modelTypeM;
int useCountM;
cString descriptionM;
cString quirksM;
cSatipFrontends frontendsM[eSatipFrontendCount];
int quirkM;
bool hasCiM;
time_t createdM;
cTimeMs lastSeenM;
public:
enum eSatipModelType {
eSatipModelTypeDVBS2 = 0x01,
eSatipModelTypeDVBT = 0x02,
eSatipModelTypeDVBT2 = 0x04,
eSatipModelTypeDVBC = 0x08,
eSatipModelTypeMask = 0x0F
enum eSatipQuirk {
eSatipQuirkNone = 0x00,
eSatipQuirkSessionId = 0x01,
eSatipQuirkPlayPids = 0x02,
eSatipQuirkForceLock = 0x04,
eSatipQuirkMask = 0x0F
};
cSatipServer(const char *addressP, const char *descriptionP, const char *modelP);
cSatipServer(const char *addressP, const char *modelP, const char *descriptionP);
virtual ~cSatipServer();
virtual int Compare(const cListObject &listObjectP) const;
void Use(bool onOffP);
bool Used(void) { return !!useCountM; }
const char *Description() { return *descriptionM; }
const char *Address() { return *addressM; }
const char *Model(void) { return modelM; }
int ModelType(void) { return modelTypeM; }
bool Match(int modelP) { return ((modelP & eSatipModelTypeMask) & modelTypeM); }
int Cable() { return Match(eSatipModelTypeDVBC) ? (Match(eSatipModelTypeDVBT2) ? modelCountM[eSatipModuleDVBT2] : modelCountM[eSatipModuleDVBT]) : 0; } // an ugly hack
int Satellite() { return Match(eSatipModelTypeDVBS2) ? modelCountM[eSatipModuleDVBS2] : 0; }
int Terrestrial() { return Match(eSatipModelTypeDVBT) ? modelCountM[eSatipModuleDVBT] : 0; }
int Terrestrial2() { return Match(eSatipModelTypeDVBT2) ? modelCountM[eSatipModuleDVBT2] : 0; }
void Update(void) { lastSeenM.Set(); }
uint64_t LastSeen(void) { return lastSeenM.Elapsed(); }
time_t Created(void) { return createdM; }
bool Assign(int deviceIdP, int sourceP, int systemP, int transponderP);
bool Matches(int sourceP);
bool Matches(int deviceIdP, int sourceP, int systemP, int transponderP);
void Attach(int deviceIdP, int transponderP);
void Detach(int deviceIdP, int transponderP);
int GetModulesDVBS2(void);
int GetModulesDVBT(void);
int GetModulesDVBT2(void);
int GetModulesDVBC(void);
int GetModulesDVBC2(void);
const char *Address(void) { return *addressM; }
const char *Model(void) { return *modelM; }
const char *Description(void) { return *descriptionM; }
const char *Quirks(void) { return *quirksM; }
bool Quirk(int quirkP) { return ((quirkP & eSatipQuirkMask) & quirkM); }
bool HasQuirk(void) { return (quirkM != eSatipQuirkNone); }
bool HasCI(void) { return hasCiM; }
void Update(void) { lastSeenM.Set(); }
uint64_t LastSeen(void) { return lastSeenM.Elapsed(); }
time_t Created(void) { return createdM; }
};
// --- cSatipServers ----------------------------------------------------------
@@ -59,10 +102,15 @@ public:
class cSatipServers : public cList<cSatipServer> {
public:
cSatipServer *Find(cSatipServer *serverP);
cSatipServer *Find(int sourceP, int systemP);
cSatipServer *Find(int sourceP);
cSatipServer *Assign(int deviceIdP, int sourceP, int transponderP, int systemP);
cSatipServer *Update(cSatipServer *serverP);
void Use(cSatipServer *serverP, bool onOffP);
void Attach(cSatipServer *serverP, int deviceIdP, int transponderP);
void Detach(cSatipServer *serverP, int deviceIdP, int transponderP);
bool IsQuirk(cSatipServer *serverP, int quirkP);
bool HasCI(cSatipServer *serverP);
void Cleanup(uint64_t intervalMsP = 0);
cString GetAddress(cSatipServer *serverP);
cString GetString(cSatipServer *serverP);
cString List(void);
int NumProvidedSystems(void);

280
setup.c
View File

@@ -12,8 +12,75 @@
#include "config.h"
#include "device.h"
#include "discover.h"
#include "log.h"
#include "setup.h"
// --- cSatipEditSrcItem ------------------------------------------------------
// This class is a 99% copy of cMenuEditSrcItem() taken from VDR's menu.c
class cSatipEditSrcItem : public cMenuEditIntItem {
private:
const cSource *source;
protected:
virtual void Set(void);
public:
cSatipEditSrcItem(const char *Name, int *Value);
eOSState ProcessKey(eKeys Key);
};
cSatipEditSrcItem::cSatipEditSrcItem(const char *Name, int *Value)
:cMenuEditIntItem(Name, Value, 0)
{
source = Sources.Get(*Value);
if (!source)
source = Sources.First();
Set();
}
void cSatipEditSrcItem::Set(void)
{
if (source)
SetValue(cString::sprintf("%s - %s", *cSource::ToString(source->Code()), source->Description()));
else
cMenuEditIntItem::Set();
}
eOSState cSatipEditSrcItem::ProcessKey(eKeys Key)
{
eOSState state = cMenuEditItem::ProcessKey(Key);
if (state == osUnknown) {
bool IsRepeat = Key & k_Repeat;
Key = NORMALKEY(Key);
if (Key == kLeft) { // TODO might want to increase the delta if repeated quickly?
if (source) {
if (source->Prev())
source = (cSource *)source->Prev();
else if (!IsRepeat)
source = Sources.Last();
*value = source->Code();
}
}
else if (Key == kRight) {
if (source) {
if (source->Next())
source = (cSource *)source->Next();
else if (!IsRepeat)
source = Sources.First();
}
else
source = Sources.First();
if (source)
*value = source->Code();
}
else
return state; // we don't call cMenuEditIntItem::ProcessKey(Key) here since we don't accept numerical input
Set();
state = osContinue;
}
return state;
}
// --- cSatipServerInfo -------------------------------------------------------
class cSatipServerInfo : public cOsdMenu
@@ -22,6 +89,7 @@ private:
cString addressM;
cString modelM;
cString descriptionM;
cString ciExtensionM;
uint64_t createdM;
void Setup(void);
@@ -32,10 +100,11 @@ public:
};
cSatipServerInfo::cSatipServerInfo(cSatipServer *serverP)
: cOsdMenu(tr("SAT>IP Device"), 20),
: cOsdMenu(tr("SAT>IP Server"), 20),
addressM(serverP ? serverP->Address() : "---"),
modelM(serverP ? serverP->Model() : "---"),
descriptionM(serverP ? serverP->Description() : "---"),
ciExtensionM(serverP && serverP->HasCI() ? trVDR("yes") : trVDR("no")),
createdM(serverP ? serverP->Created() : 0)
{
SetMenuCategory(mcSetupPlugins);
@@ -52,6 +121,7 @@ void cSatipServerInfo::Setup(void)
Add(new cOsdItem(cString::sprintf("%s:\t%s", tr("Address"), *addressM), osUnknown, false));
Add(new cOsdItem(cString::sprintf("%s:\t%s", tr("Model"), *modelM), osUnknown, false));
Add(new cOsdItem(cString::sprintf("%s:\t%s", tr("Description"), *descriptionM), osUnknown, false));
Add(new cOsdItem(cString::sprintf("%s:\t%s", tr("CI extension"), *ciExtensionM), osUnknown, false));
Add(new cOsdItem(cString::sprintf("%s:\t%s", tr("Creation date"), *DayDateTime(createdM)), osUnknown, false));
}
@@ -94,6 +164,71 @@ void cSatipServerItem::SetMenuItem(cSkinDisplayMenu *displayMenuP, int indexP, b
displayMenuP->SetItem(Text(), indexP, currentP, selectableP);
}
// --- cSatipMenuDeviceStatus -------------------------------------------------
class cSatipMenuDeviceStatus : public cOsdMenu
{
private:
enum {
eInfoTimeoutMs = 15000
};
cString textM;
cTimeMs timeoutM;
void UpdateInfo();
public:
cSatipMenuDeviceStatus();
virtual ~cSatipMenuDeviceStatus();
virtual void Display(void);
virtual eOSState ProcessKey(eKeys keyP);
};
cSatipMenuDeviceStatus::cSatipMenuDeviceStatus()
: cOsdMenu(tr("SAT>IP Device Status")),
textM(""),
timeoutM()
{
SetMenuCategory(mcText);
timeoutM.Set(eInfoTimeoutMs);
UpdateInfo();
SetHelp(NULL, NULL, NULL, NULL);
}
cSatipMenuDeviceStatus::~cSatipMenuDeviceStatus()
{
}
void cSatipMenuDeviceStatus::UpdateInfo()
{
textM = cSatipDevice::GetSatipStatus();
Display();
timeoutM.Set(eInfoTimeoutMs);
}
void cSatipMenuDeviceStatus::Display(void)
{
cOsdMenu::Display();
DisplayMenu()->SetText(textM, true);
if (*textM)
cStatus::MsgOsdTextItem(textM);
}
eOSState cSatipMenuDeviceStatus::ProcessKey(eKeys keyP)
{
eOSState state = cOsdMenu::ProcessKey(keyP);
if (state == osUnknown) {
switch (keyP) {
case kOk: state = osBack; break;
default: if (timeoutM.TimedOut())
UpdateInfo();
state = osContinue;
break;
}
}
return state;
}
// --- cSatipMenuInfo ---------------------------------------------------------
class cSatipMenuInfo : public cOsdMenu
@@ -195,16 +330,27 @@ eOSState cSatipMenuInfo::ProcessKey(eKeys keyP)
// --- cSatipPluginSetup ------------------------------------------------------
cSatipPluginSetup::cSatipPluginSetup()
: deviceCountM(0),
: detachedModeM(SatipConfig.GetDetachedMode()),
deviceCountM(0),
operatingModeM(SatipConfig.GetOperatingMode()),
ciExtensionM(SatipConfig.GetCIExtension()),
eitScanM(SatipConfig.GetEITScan()),
numDisabledSourcesM(SatipConfig.GetDisabledSourcesCount()),
numDisabledFiltersM(SatipConfig.GetDisabledFiltersCount())
{
debug("cSatipPluginSetup::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
operatingModeTextsM[cSatipConfig::eOperatingModeOff] = tr("off");
operatingModeTextsM[cSatipConfig::eOperatingModeLow] = tr("low");
operatingModeTextsM[cSatipConfig::eOperatingModeNormal] = tr("normal");
operatingModeTextsM[cSatipConfig::eOperatingModeHigh] = tr("high");
for (unsigned int i = 0; i < ELEMENTS(cicamsM); ++i)
cicamsM[i] = SatipConfig.GetCICAM(i);
for (unsigned int i = 0; i < ELEMENTS(ca_systems_table); ++i)
cicamTextsM[i] = ca_systems_table[i].description;
if (numDisabledSourcesM > MAX_DISABLED_SOURCES_COUNT)
numDisabledSourcesM = MAX_DISABLED_SOURCES_COUNT;
for (int i = 0; i < MAX_DISABLED_SOURCES_COUNT; ++i)
disabledSourcesM[i] = SatipConfig.GetDisabledSources(i);
if (numDisabledFiltersM > SECTION_FILTER_TABLE_SIZE)
numDisabledFiltersM = SECTION_FILTER_TABLE_SIZE;
for (int i = 0; i < SECTION_FILTER_TABLE_SIZE; ++i) {
@@ -213,7 +359,7 @@ cSatipPluginSetup::cSatipPluginSetup()
}
SetMenuCategory(mcSetupPlugins);
Setup();
SetHelp(trVDR("Button$Scan"), NULL, NULL, trVDR("Button$Info"));
SetHelp(trVDR("Button$Scan"), NULL, tr("Button$Devices"), trVDR("Button$Info"));
}
void cSatipPluginSetup::Setup(void)
@@ -227,26 +373,45 @@ void cSatipPluginSetup::Setup(void)
helpM.Append(tr("Define the used operating mode for all SAT>IP devices:\n\noff - devices are disabled\nlow - devices are working at the lowest priority\nnormal - devices are working within normal parameters\nhigh - devices are working at the highest priority"));
if (operatingModeM) {
Add(new cMenuEditBoolItem(tr("Enable CI extension"), &ciExtensionM));
helpM.Append(tr("Define whether a CI extension shall be used.\n\nThis setting enables integrated CI/CAM handling found in some SAT>IP hardware (e.g. Digital Devices OctopusNet)."));
for (unsigned int i = 0; ciExtensionM && i < ELEMENTS(cicamsM); ++i) {
Add(new cMenuEditStraItem(*cString::sprintf(" %s #%d", tr("CI/CAM"), i + 1), &cicamsM[i], ELEMENTS(cicamTextsM), cicamTextsM));
helpM.Append(tr("Define a desired CAM type for the CI slot.\n\nThe '---' option lets SAT>IP hardware do the auto-selection."));
}
Add(new cMenuEditBoolItem(tr("Enable EPG scanning"), &eitScanM));
helpM.Append(tr("Define whether the EPG background scanning shall be used.\n\nThis setting disables the automatic EIT scanning functionality for all SAT>IP devices."));
Add(new cMenuEditIntItem(tr("Disabled sources"), &numDisabledSourcesM, 0, MAX_DISABLED_SOURCES_COUNT, tr("none")));
helpM.Append(tr("Define number of sources to be disabled.\n\nSAT>IP servers might not have all satellite positions available and such sources can be blacklisted here."));
for (int i = 0; i < numDisabledSourcesM; ++i) {
Add(new cSatipEditSrcItem(*cString::sprintf(" %s %d", trVDR("Source"), i + 1), &disabledSourcesM[i]));
helpM.Append(tr("Define a source to be blacklisted."));
}
Add(new cMenuEditIntItem(tr("Disabled filters"), &numDisabledFiltersM, 0, SECTION_FILTER_TABLE_SIZE, tr("none")));
helpM.Append(tr("Define number of section filters to be disabled.\n\nCertain section filters might cause some unwanted behaviour to VDR such as time being falsely synchronized. By black-listing the filters here useful section data can be left intact for VDR to process."));
helpM.Append(tr("Define number of section filters to be disabled.\n\nCertain section filters might cause some unwanted behaviour to VDR such as time being falsely synchronized. By blacklisting the filters here, useful section data can be left intact for VDR to process."));
for (int i = 0; i < numDisabledFiltersM; ++i) {
Add(new cMenuEditStraItem(*cString::sprintf(" %s %d", tr("Filter"), i + 1), &disabledFilterIndexesM[i], SECTION_FILTER_TABLE_SIZE, disabledFilterNamesM));
helpM.Append(tr("Define an ill-behaving filter to be blacklisted."));
}
}
Add(new cOsdItem(tr("Active SAT>IP devices:"), osUnknown, false));
Add(new cOsdItem(tr("Active SAT>IP servers:"), osUnknown, false));
helpM.Append("");
cSatipServers *servers = cSatipDiscover::GetInstance()->GetServers();
deviceCountM = servers->Count();
for (cSatipServer *s = servers->First(); s; s = servers->Next(s)) {
Add(new cSatipServerItem(s));
helpM.Append("");
}
detachedModeM = SatipConfig.GetDetachedMode();
if (!detachedModeM) {
cSatipServers *servers = cSatipDiscover::GetInstance()->GetServers();
deviceCountM = servers->Count();
for (cSatipServer *s = servers->First(); s; s = servers->Next(s)) {
Add(new cSatipServerItem(s));
helpM.Append("");
}
}
SetCurrent(Get(current));
Display();
@@ -254,7 +419,7 @@ void cSatipPluginSetup::Setup(void)
eOSState cSatipPluginSetup::DeviceScan(void)
{
debug("cSatipPluginSetup::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
cSatipDiscover::GetInstance()->TriggerScan();
return osContinue;
@@ -262,7 +427,7 @@ eOSState cSatipPluginSetup::DeviceScan(void)
eOSState cSatipPluginSetup::DeviceInfo(void)
{
debug("cSatipPluginSetup::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
if (HasSubMenu() || Count() == 0)
return osContinue;
@@ -273,9 +438,18 @@ eOSState cSatipPluginSetup::DeviceInfo(void)
return osContinue;
}
eOSState cSatipPluginSetup::ShowDeviceStatus(void)
{
debug1("%s", __PRETTY_FUNCTION__);
if (HasSubMenu() || Count() == 0)
return osContinue;
return AddSubMenu(new cSatipMenuDeviceStatus());
}
eOSState cSatipPluginSetup::ShowInfo(void)
{
debug("cSatipPluginSetup::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
if (HasSubMenu() || Count() == 0)
return osContinue;
@@ -286,6 +460,8 @@ eOSState cSatipPluginSetup::ProcessKey(eKeys keyP)
{
bool hadSubMenu = HasSubMenu();
int oldOperatingMode = operatingModeM;
int oldCiExtension = ciExtensionM;
int oldNumDisabledSources = numDisabledSourcesM;
int oldNumDisabledFilters = numDisabledFiltersM;
eOSState state = cMenuSetupPage::ProcessKey(keyP);
@@ -296,18 +472,21 @@ eOSState cSatipPluginSetup::ProcessKey(eKeys keyP)
if (state == osUnknown) {
switch (keyP) {
case kRed: return DeviceScan();
case kBlue: return ShowInfo();
case kInfo: if (Current() < helpM.Size())
return AddSubMenu(new cMenuText(cString::sprintf("%s - %s '%s'", tr("Help"), trVDR("Plugin"), PLUGIN_NAME_I18N), helpM[Current()]));
default: state = osContinue; break;
case kRed: return DeviceScan();
case kYellow: return ShowDeviceStatus();
case kBlue: return ShowInfo();
case kInfo: if (Current() < helpM.Size())
return AddSubMenu(new cMenuText(cString::sprintf("%s - %s '%s'", tr("Help"), trVDR("Plugin"), PLUGIN_NAME_I18N), helpM[Current()]));
default: state = osContinue; break;
}
}
if ((keyP == kNone) && (cSatipDiscover::GetInstance()->GetServers()->Count() != deviceCountM))
Setup();
if ((keyP != kNone) && ((numDisabledFiltersM != oldNumDisabledFilters) || (operatingModeM != oldOperatingMode))) {
if ((keyP != kNone) && ((numDisabledSourcesM != oldNumDisabledSources) || (numDisabledFiltersM != oldNumDisabledFilters) || (operatingModeM != oldOperatingMode) || (ciExtensionM != oldCiExtension) || (detachedModeM != SatipConfig.GetDetachedMode()))) {
while ((numDisabledSourcesM < oldNumDisabledSources) && (oldNumDisabledSources > 0))
disabledSourcesM[--oldNumDisabledSources] = cSource::stNone;
while ((numDisabledFiltersM < oldNumDisabledFilters) && (oldNumDisabledFilters > 0))
disabledFilterIndexesM[--oldNumDisabledFilters] = -1;
Setup();
@@ -316,34 +495,71 @@ eOSState cSatipPluginSetup::ProcessKey(eKeys keyP)
return state;
}
void cSatipPluginSetup::StoreCicams(const char *nameP, int *cicamsP)
{
cString buffer = "";
int n = 0;
for (int i = 0; i < MAX_CICAM_COUNT; ++i) {
if (cicamsP[i] < 0)
break;
if (n++ > 0)
buffer = cString::sprintf("%s %d", *buffer, cicamsP[i]);
else
buffer = cString::sprintf("%d", cicamsP[i]);
}
debug3("%s (%s, %s)", __PRETTY_FUNCTION__, nameP, *buffer);
SetupStore(nameP, *buffer);
}
void cSatipPluginSetup::StoreSources(const char *nameP, int *sourcesP)
{
cString buffer = "";
int n = 0;
for (int i = 0; i < MAX_DISABLED_SOURCES_COUNT; ++i) {
if (sourcesP[i] == cSource::stNone)
break;
if (n++ > 0)
buffer = cString::sprintf("%s %s", *buffer, *cSource::ToString(sourcesP[i]));
else
buffer = cString::sprintf("%s", *cSource::ToString(sourcesP[i]));
}
debug3("%s (%s, %s)", __PRETTY_FUNCTION__, nameP, *buffer);
SetupStore(nameP, *buffer);
}
void cSatipPluginSetup::StoreFilters(const char *nameP, int *valuesP)
{
char buffer[SECTION_FILTER_TABLE_SIZE * 4];
char *q = buffer;
cString buffer = "";
int n = 0;
for (int i = 0; i < SECTION_FILTER_TABLE_SIZE; ++i) {
char s[3];
if (valuesP[i] < 0)
break;
if (q > buffer)
*q++ = ' ';
snprintf(s, sizeof(s), "%d", valuesP[i]);
strncpy(q, s, strlen(s));
q += strlen(s);
if (n++ > 0)
buffer = cString::sprintf("%s %d", *buffer, valuesP[i]);
else
buffer = cString::sprintf("%d", valuesP[i]);
}
*q = 0;
debug("cSatipPluginSetup::%s(%s, %s)", __FUNCTION__, nameP, buffer);
SetupStore(nameP, buffer);
debug3("%s (%s, %s)", __PRETTY_FUNCTION__, nameP, *buffer);
SetupStore(nameP, *buffer);
}
void cSatipPluginSetup::Store(void)
{
// Store values into setup.conf
SetupStore("OperatingMode", operatingModeM);
SetupStore("EnableCIExtension", ciExtensionM);
SetupStore("EnableEITScan", eitScanM);
StoreCicams("CICAM", cicamsM);
StoreSources("DisabledSources", disabledSourcesM);
StoreFilters("DisabledFilters", disabledFilterIndexesM);
// Update global config
SatipConfig.SetOperatingMode(operatingModeM);
SatipConfig.SetCIExtension(ciExtensionM);
SatipConfig.SetEITScan(eitScanM);
for (int i = 0; i < MAX_CICAM_COUNT; ++i)
SatipConfig.SetCICAM(i, cicamsM[i]);
for (int i = 0; i < MAX_DISABLED_SOURCES_COUNT; ++i)
SatipConfig.SetDisabledSources(i, disabledSourcesM[i]);
for (int i = 0; i < SECTION_FILTER_TABLE_SIZE; ++i)
SatipConfig.SetDisabledFilters(i, disabledFilterIndexesM[i]);
}

View File

@@ -15,10 +15,16 @@
class cSatipPluginSetup : public cMenuSetupPage
{
private:
bool detachedModeM;
int deviceCountM;
int operatingModeM;
const char *operatingModeTextsM[cSatipConfig::eOperatingModeCount];
int ciExtensionM;
int cicamsM[MAX_CICAM_COUNT];
const char *cicamTextsM[CA_SYSTEMS_TABLE_SIZE];
int eitScanM;
int numDisabledSourcesM;
int disabledSourcesM[MAX_DISABLED_SOURCES_COUNT];
int numDisabledFiltersM;
int disabledFilterIndexesM[SECTION_FILTER_TABLE_SIZE];
const char *disabledFilterNamesM[SECTION_FILTER_TABLE_SIZE];
@@ -26,8 +32,11 @@ private:
eOSState DeviceScan(void);
eOSState DeviceInfo(void);
eOSState ShowDeviceStatus(void);
eOSState ShowInfo(void);
void Setup(void);
void StoreCicams(const char *nameP, int *cicamsP);
void StoreSources(const char *nameP, int *sourcesP);
void StoreFilters(const char *nameP, int *valuesP);
protected:

157
socket.c
View File

@@ -16,27 +16,26 @@
#include "common.h"
#include "config.h"
#include "log.h"
#include "socket.h"
cSatipSocket::cSatipSocket()
: socketPortM(0),
socketDescM(-1),
sequenceNumberM(-1)
socketDescM(-1)
{
debug("cSatipSocket::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
memset(&sockAddrM, 0, sizeof(sockAddrM));
}
cSatipSocket::~cSatipSocket()
{
debug("cSatipSocket::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
// Close the socket
Close();
}
bool cSatipSocket::Open(const int portP)
{
debug("cSatipSocket::%s(%d)", __FUNCTION__, portP);
// Bind to the socket if it is not active already
if (socketDescM < 0) {
socklen_t len = sizeof(sockAddrM);
@@ -62,25 +61,25 @@ bool cSatipSocket::Open(const int portP)
"getsockname()", Close(), return false);
socketPortM = ntohs(sockAddrM.sin_port);
}
debug1("%s (%d) socketPort=%d", __PRETTY_FUNCTION__, portP, socketPortM);
return true;
}
void cSatipSocket::Close(void)
{
debug("cSatipSocket::%s()", __FUNCTION__);
debug1("%s sockerPort=%d", __PRETTY_FUNCTION__, socketPortM);
// Check if socket exists
if (socketDescM >= 0) {
close(socketDescM);
socketDescM = -1;
socketPortM = 0;
sequenceNumberM = -1;
memset(&sockAddrM, 0, sizeof(sockAddrM));
}
}
bool cSatipSocket::Flush(void)
{
debug("cSatipSocket::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
if (socketDescM < 0) {
const unsigned int len = 65535;
unsigned char *buf = MALLOC(unsigned char, len);
@@ -99,10 +98,10 @@ bool cSatipSocket::Flush(void)
int cSatipSocket::Read(unsigned char *bufferAddrP, unsigned int bufferLenP)
{
//debug("cSatipSocket::%s()", __FUNCTION__);
debug16("%s (, %d)", __PRETTY_FUNCTION__, bufferLenP);
// Error out if socket not initialized
if (socketDescM <= 0) {
error("Invalid socket in cSatipUdpSocket::%s()", __FUNCTION__);
error("%s Invalid socket", __PRETTY_FUNCTION__);
return -1;
}
int len = 0;
@@ -121,7 +120,7 @@ int cSatipSocket::Read(unsigned char *bufferAddrP, unsigned int bufferLenP)
msgh.msg_controllen = sizeof(cbuf);
msgh.msg_name = &sockAddrM;
msgh.msg_namelen = addrlen;
msgh.msg_iov = &iov;
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
msgh.msg_flags = 0;
@@ -130,109 +129,63 @@ int cSatipSocket::Read(unsigned char *bufferAddrP, unsigned int bufferLenP)
if (len > 0)
return len;
} while (len > 0);
ERROR_IF_RET(len < 0 && errno != EAGAIN, "recvmsg()", return -1);
ERROR_IF_RET(len < 0 && errno != EAGAIN && errno != EWOULDBLOCK, "recvmsg()", return -1);
return 0;
}
int cSatipSocket::ReadVideo(unsigned char *bufferAddrP, unsigned int bufferLenP)
int cSatipSocket::ReadMulti(unsigned char *bufferAddrP, unsigned int *elementRecvSizeP, unsigned int elementCountP, unsigned int elementBufferSizeP)
{
//debug("cSatipSocket::%s()", __FUNCTION__);
int len = Read(bufferAddrP, bufferLenP);
if (len > 0) {
if (bufferAddrP[0] == TS_SYNC_BYTE)
return len;
else if (len > 3) {
// http://tools.ietf.org/html/rfc3550
// http://tools.ietf.org/html/rfc2250
// Version
unsigned int v = (bufferAddrP[0] >> 6) & 0x03;
// Extension bit
unsigned int x = (bufferAddrP[0] >> 4) & 0x01;
// CSCR count
unsigned int cc = bufferAddrP[0] & 0x0F;
// Payload type: MPEG2 TS = 33
//unsigned int pt = bufferAddrP[1] & 0x7F;
// Sequence number
int seq = ((bufferAddrP[2] & 0xFF) << 8) | (bufferAddrP[3] & 0xFF);
if ((((sequenceNumberM + 1) % 0xFFFF) == 0) && (seq == 0xFFFF))
sequenceNumberM = -1;
else if ((sequenceNumberM >= 0) && (((sequenceNumberM + 1) % 0xFFFF) != seq)) {
error("missed %d RTP packets", seq - sequenceNumberM - 1);
sequenceNumberM = seq;
}
else
sequenceNumberM = seq;
// Header lenght
unsigned int headerlen = (3 + cc) * (unsigned int)sizeof(uint32_t);
// Check if extension
if (x) {
// Extension header length
unsigned int ehl = (((bufferAddrP[headerlen + 2] & 0xFF) << 8) |
(bufferAddrP[headerlen + 3] & 0xFF));
// Update header length
headerlen += (ehl + 1) * (unsigned int)sizeof(uint32_t);
}
// Check that rtp is version 2 and payload contains multiple of TS packet data
if ((v == 2) && (((len - headerlen) % TS_SIZE) == 0) &&
(bufferAddrP[headerlen] == TS_SYNC_BYTE)) {
// Set argument point to payload in read buffer
memmove(bufferAddrP, &bufferAddrP[headerlen], (len - headerlen));
return (len - headerlen);
}
}
debug16("%s (, , %d, %d)", __PRETTY_FUNCTION__, elementCountP, elementBufferSizeP);
int count = -1;
// Error out if socket not initialized
if (socketDescM <= 0) {
error("%s Invalid socket", __PRETTY_FUNCTION__);
return -1;
}
return 0;
if (!bufferAddrP || !elementRecvSizeP || !elementCountP || !elementBufferSizeP) {
error("%s Invalid parameter(s)", __PRETTY_FUNCTION__);
return -1;
}
#if defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2,12)
// Initialize iov and msgh structures
struct mmsghdr mmsgh[elementCountP];
struct iovec iov[elementCountP];
memset(mmsgh, 0, sizeof(mmsgh[0]) * elementCountP);
for (unsigned int i = 0; i < elementCountP; ++i) {
iov[i].iov_base = bufferAddrP + i * elementBufferSizeP;
iov[i].iov_len = elementBufferSizeP;
mmsgh[i].msg_hdr.msg_iov = &iov[i];
mmsgh[i].msg_hdr.msg_iovlen = 1;
}
// Read data from socket as a set
count = (int)recvmmsg(socketDescM, mmsgh, elementCountP, MSG_DONTWAIT, NULL);
ERROR_IF_RET(count < 0 && errno != EAGAIN && errno != EWOULDBLOCK, "recvmmsg()", return -1);
for (int i = 0; i < count; ++i)
elementRecvSizeP[i] = mmsgh[i].msg_len;
#else
count = 0;
while (count < (int)elementCountP) {
int len = Read(bufferAddrP + count * elementBufferSizeP, elementBufferSizeP);
if (len < 0)
return -1;
else if (len == 0)
break;
elementRecvSizeP[count++] = len;
}
#endif
debug16("%s Received %d packets size[0]=%d", __PRETTY_FUNCTION__, count, elementRecvSizeP[0]);
return count;
}
int cSatipSocket::ReadApplication(unsigned char *bufferAddrP, unsigned int bufferLenP)
{
//debug("cSatipSocket::%s()", __FUNCTION__);
int len = Read(bufferAddrP, bufferLenP);
int offset = 0;
while (len > 0) {
// Version
unsigned int v = (bufferAddrP[offset] >> 6) & 0x03;
// Padding
//unsigned int p = (bufferAddrP[offset] >> 5) & 0x01;
// Subtype
//unsigned int st = bufferAddrP[offset] & 0x1F;
// Payload type
unsigned int pt = bufferAddrP[offset + 1] & 0xFF;
// Lenght
unsigned int length = ((bufferAddrP[offset + 2] & 0xFF) << 8) | (bufferAddrP[offset + 3] & 0xFF);
// Convert it to bytes
length = (length + 1) * 4;
// V=2, APP = 204
if ((v == 2) && (pt == 204)) {
// SSCR/CSCR
//unsigned int ssrc = ((bufferAddrP[offset + 4] & 0xFF) << 24) | ((bufferAddrP[offset + 5] & 0xFF) << 16) |
// ((bufferAddrP[offset + 6] & 0xFF) << 8) | (bufferAddrP[offset + 7] & 0xFF);
// Name
if ((bufferAddrP[offset + 8] == 'S') && (bufferAddrP[offset + 9] == 'E') &&
(bufferAddrP[offset + 10] == 'S') && (bufferAddrP[offset + 11] == '1')) {
// Identifier
//unsigned int id = ((bufferAddrP[offset + 12] & 0xFF) << 8) | (bufferAddrP[offset + 13] & 0xFF);
// String length
int string_length = ((bufferAddrP[offset + 14] & 0xFF) << 8) | (bufferAddrP[offset + 15] & 0xFF);
if (string_length > 0) {
// Set argument point to payload in read buffer
memmove(bufferAddrP, &bufferAddrP[offset + 16], string_length);
return string_length;
}
}
}
offset += length;
len -= length;
}
return 0;
}
bool cSatipSocket::Write(const char *addrP, const unsigned char *bufferAddrP, unsigned int bufferLenP)
{
debug("cSatipSocket::%s(%s)", __FUNCTION__, addrP);
debug1("%s (%s, , %d)", __PRETTY_FUNCTION__, addrP, bufferLenP);
// Error out if socket not initialized
if (socketDescM <= 0) {
error("cSatipSocket::%s(): Invalid socket", __FUNCTION__);
error("%s Invalid socket", __PRETTY_FUNCTION__);
return false;
}
struct sockaddr_in sockAddr;

View File

@@ -15,19 +15,18 @@ private:
int socketPortM;
int socketDescM;
struct sockaddr_in sockAddrM;
int sequenceNumberM;
public:
cSatipSocket();
~cSatipSocket();
virtual ~cSatipSocket();
bool Open(const int portP = 0);
void Close(void);
virtual void Close(void);
int Fd(void) { return socketDescM; }
int Port(void) { return socketPortM; }
bool IsOpen(void) { return (socketDescM >= 0); }
bool Flush(void);
int Read(unsigned char *bufferAddrP, unsigned int bufferLenP);
int ReadVideo(unsigned char *bufferAddrP, unsigned int bufferLenP);
int ReadApplication(unsigned char *bufferAddrP, unsigned int bufferLenP);
int ReadMulti(unsigned char *bufferAddrP, unsigned int *elementRecvSizeP, unsigned int elementCountP, unsigned int elementBufferSizeP);
bool Write(const char *addrP, const unsigned char *bufferAddrP, unsigned int bufferLenP);
};

View File

@@ -9,6 +9,7 @@
#include "common.h"
#include "statistics.h"
#include "log.h"
#include "config.h"
// Section statistics class
@@ -18,17 +19,17 @@ cSatipSectionStatistics::cSatipSectionStatistics()
timerM(),
mutexM()
{
//debug("cSatipSectionStatistics::%s()", __FUNCTION__);
debug16("%s", __PRETTY_FUNCTION__);
}
cSatipSectionStatistics::~cSatipSectionStatistics()
{
//debug("cSatipSectionStatistics::%s()", __FUNCTION__);
debug16("%s", __PRETTY_FUNCTION__);
}
cString cSatipSectionStatistics::GetSectionStatistic()
{
//debug("cSatipSectionStatistics::%s()", __FUNCTION__);
debug16("%s", __PRETTY_FUNCTION__);
cMutexLock MutexLock(&mutexM);
uint64_t elapsed = timerM.Elapsed(); /* in milliseconds */
timerM.Set();
@@ -44,7 +45,7 @@ cString cSatipSectionStatistics::GetSectionStatistic()
void cSatipSectionStatistics::AddSectionStatistic(long bytesP, long callsP)
{
//debug("cSatipSectionStatistics::%s(%ld, %ld)", __FUNCTION__, bytesP, callsP);
debug16("%s (%ld, %ld)", __PRETTY_FUNCTION__, bytesP, callsP);
cMutexLock MutexLock(&mutexM);
filteredDataM += bytesP;
numberOfCallsM += callsP;
@@ -57,7 +58,7 @@ cSatipPidStatistics::cSatipPidStatistics()
: timerM(),
mutexM()
{
debug("cSatipPidStatistics::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
const int numberOfElements = sizeof(mostActivePidsM) / sizeof(pidStruct);
for (int i = 0; i < numberOfElements; ++i) {
mostActivePidsM[i].pid = -1;
@@ -67,12 +68,12 @@ cSatipPidStatistics::cSatipPidStatistics()
cSatipPidStatistics::~cSatipPidStatistics()
{
debug("cSatipPidStatistics::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
}
cString cSatipPidStatistics::GetPidStatistic()
{
//debug("cSatipPidStatistics::%s()", __FUNCTION__);
debug16("%s", __PRETTY_FUNCTION__);
cMutexLock MutexLock(&mutexM);
const int numberOfElements = sizeof(mostActivePidsM) / sizeof(pidStruct);
uint64_t elapsed = timerM.Elapsed(); /* in milliseconds */
@@ -97,7 +98,7 @@ cString cSatipPidStatistics::GetPidStatistic()
int cSatipPidStatistics::SortPids(const void* data1P, const void* data2P)
{
//debug("cSatipPidStatistics::%s()", __FUNCTION__);
debug16("%s", __PRETTY_FUNCTION__);
const pidStruct *comp1 = reinterpret_cast<const pidStruct*>(data1P);
const pidStruct *comp2 = reinterpret_cast<const pidStruct*>(data2P);
if (comp1->dataAmount > comp2->dataAmount)
@@ -109,7 +110,7 @@ int cSatipPidStatistics::SortPids(const void* data1P, const void* data2P)
void cSatipPidStatistics::AddPidStatistic(int pidP, long payloadP)
{
//debug("cSatipPidStatistics::%s(%ld, %ld)", __FUNCTION__, pidP, payloadP);
debug16("%s (%d, %ld)", __PRETTY_FUNCTION__, pidP, payloadP);
cMutexLock MutexLock(&mutexM);
const int numberOfElements = sizeof(mostActivePidsM) / sizeof(pidStruct);
// If our statistic already is in the array, update it and quit
@@ -139,31 +140,33 @@ cSatipTunerStatistics::cSatipTunerStatistics()
timerM(),
mutexM()
{
debug("cSatipTunerStatistics::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
}
cSatipTunerStatistics::~cSatipTunerStatistics()
{
debug("cSatipTunerStatistics::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
}
cString cSatipTunerStatistics::GetTunerStatistic()
{
//debug("cSatipTunerStatistics::%s()", __FUNCTION__);
cMutexLock MutexLock(&mutexM);
debug16("%s", __PRETTY_FUNCTION__);
mutexM.Lock();
uint64_t elapsed = timerM.Elapsed(); /* in milliseconds */
timerM.Set();
long bitrate = elapsed ? (long)(1000.0L * dataBytesM / KILOBYTE(1) / elapsed) : 0L;
dataBytesM = 0;
mutexM.Unlock();
if (!SatipConfig.GetUseBytes())
bitrate *= 8;
cString s = cString::sprintf("%ld k%s/s", bitrate, SatipConfig.GetUseBytes() ? "B" : "bit");
dataBytesM = 0;
return s;
}
void cSatipTunerStatistics::AddTunerStatistic(long bytesP)
{
//debug("cSatipTunerStatistics::%s(%ld)", __FUNCTION__, bytesP);
debug16("%s (%ld)", __PRETTY_FUNCTION__, bytesP);
cMutexLock MutexLock(&mutexM);
dataBytesM += bytesP;
}
@@ -177,17 +180,17 @@ cSatipBufferStatistics::cSatipBufferStatistics()
timerM(),
mutexM()
{
debug("cSatipBufferStatistics::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
}
cSatipBufferStatistics::~cSatipBufferStatistics()
{
debug("cSatipBufferStatistics::%s()", __FUNCTION__);
debug1("%s", __PRETTY_FUNCTION__);
}
cString cSatipBufferStatistics::GetBufferStatistic()
{
//debug("cSatipBufferStatistics::%s()", __FUNCTION__);
debug16("%s", __PRETTY_FUNCTION__);
cMutexLock MutexLock(&mutexM);
uint64_t elapsed = timerM.Elapsed(); /* in milliseconds */
timerM.Set();
@@ -211,7 +214,7 @@ cString cSatipBufferStatistics::GetBufferStatistic()
void cSatipBufferStatistics::AddBufferStatistic(long bytesP, long usedP)
{
//debug("cSatipBufferStatistics::%s(%ld, %ld)", __FUNCTION__, bytesP, usedP);
debug16("%s (%ld, %ld)", __PRETTY_FUNCTION__, bytesP, usedP);
cMutexLock MutexLock(&mutexM);
dataBytesM += bytesP;
if (usedP > usedSpaceM)

733
tuner.c
View File

@@ -5,261 +5,214 @@
*
*/
#define __STDC_FORMAT_MACROS // Required for format specifiers
#include <inttypes.h>
#include "common.h"
#include "config.h"
#include "discover.h"
#include "log.h"
#include "poller.h"
#include "tuner.h"
cSatipTuner::cSatipTuner(cSatipDeviceIf &deviceP, unsigned int packetLenP)
: cThread("SAT>IP tuner"),
: cThread(cString::sprintf("SATIP#%d tuner", deviceP.GetId())),
sleepM(),
deviceM(&deviceP),
packetBufferLenM(packetLenP),
rtpSocketM(new cSatipSocket()),
rtcpSocketM(new cSatipSocket()),
deviceIdM(deviceP.GetId()),
rtspM(*this),
rtpM(*this),
rtcpM(*this),
streamAddrM(""),
streamParamM(""),
currentServerM(NULL),
nextServerM(NULL),
currentServerM(NULL, deviceP.GetId(), 0),
nextServerM(NULL, deviceP.GetId(), 0),
mutexM(),
handleM(NULL),
headerListM(NULL),
reConnectM(),
keepAliveM(),
statusUpdateM(),
pidUpdateCacheM(),
sessionM(),
timeoutM(eDefKeepAliveIntervalMs),
openedM(false),
tunedM(false),
sessionM(""),
currentStateM(tsIdle),
internalStateM(),
externalStateM(),
timeoutM(eMinKeepAliveIntervalMs),
hasLockM(false),
signalStrengthM(-1),
signalQualityM(-1),
frontendIdM(-1),
streamIdM(-1),
pidUpdatedM(false),
pmtPidM(-1),
addPidsM(),
delPidsM(),
pidsM()
{
debug("cSatipTuner::%s(%d)", __FUNCTION__, packetBufferLenM);
// Allocate packet buffer
packetBufferM = MALLOC(unsigned char, packetBufferLenM);
if (packetBufferM)
memset(packetBufferM, 0, packetBufferLenM);
else
error("MALLOC() failed for packet buffer");
debug1("%s (, %d) [device %d]", __PRETTY_FUNCTION__, packetLenP, deviceIdM);
// Open sockets
int i = 100;
while (i-- > 0) {
if (rtpM.Open(0) && rtcpM.Open(rtpM.Port() + 1))
break;
rtpM.Close();
rtcpM.Close();
}
if ((rtpM.Port() <= 0) || (rtcpM.Port() <= 0)) {
error("Cannot open required RTP/RTCP ports [device %d]", deviceIdM);
}
// Must be done after socket initialization!
cSatipPoller::GetInstance()->Register(rtpM);
cSatipPoller::GetInstance()->Register(rtcpM);
// Start thread
Start();
}
cSatipTuner::~cSatipTuner()
{
debug("cSatipTuner::%s()", __FUNCTION__);
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIdM);
// Stop thread
sleepM.Signal();
if (Running())
Cancel(3);
Close();
// Free allocated memory
free(packetBufferM);
DELETENULL(rtcpSocketM);
DELETENULL(rtpSocketM);
}
currentStateM = tsIdle;
internalStateM.Clear();
externalStateM.Clear();
size_t cSatipTuner::HeaderCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP)
{
cSatipTuner *obj = reinterpret_cast<cSatipTuner *>(dataP);
size_t len = sizeP * nmembP;
//debug("cSatipTuner::%s(%zu)", __FUNCTION__, len);
char *s, *p = (char *)ptrP;
char *r = strtok_r(p, "\r\n", &s);
while (obj && r) {
//debug("cSatipTuner::%s(%zu): %s", __FUNCTION__, len, r);
r = skipspace(r);
if (strstr(r, "com.ses.streamID")) {
int streamid = -1;
if (sscanf(r, "com.ses.streamID:%11d", &streamid) == 1)
obj->SetStreamId(streamid);
}
else if (strstr(r, "Session:")) {
int timeout = -1;
char *session = NULL;
if (sscanf(r, "Session:%m[^;];timeout=%11d", &session, &timeout) == 2)
obj->SetSessionTimeout(skipspace(session), timeout);
else if (sscanf(r, "Session:%m[^;]", &session) == 1)
obj->SetSessionTimeout(skipspace(session), -1);
FREE_POINTER(session);
}
r = strtok_r(NULL, "\r\n", &s);
}
return len;
// Close the listening sockets
cSatipPoller::GetInstance()->Unregister(rtcpM);
cSatipPoller::GetInstance()->Unregister(rtpM);
rtcpM.Close();
rtpM.Close();
}
void cSatipTuner::Action(void)
{
debug("cSatipTuner::%s(): entering", __FUNCTION__);
cTimeMs timeout(eReConnectTimeoutMs);
// Increase priority
SetPriority(-1);
debug1("%s Entering [device %d]", __PRETTY_FUNCTION__, deviceIdM);
reConnectM.Set(eConnectTimeoutMs);
// Do the thread loop
while (packetBufferM && Running()) {
int length = -1;
unsigned int size = min(deviceM->CheckData(), packetBufferLenM);
if (tunedM && (size > 0)) {
// Update pids
UpdatePids();
// Remember the heart beat
KeepAlive();
// Read reception statistics
if (rtcpSocketM && rtcpSocketM->IsOpen()) {
unsigned char buf[1450];
memset(buf, 0, sizeof(buf));
if (rtcpSocketM->ReadApplication(buf, sizeof(buf)) > 0) {
ParseReceptionParameters((const char *)buf);
timeout.Set(eReConnectTimeoutMs);
}
}
// Read data
if (rtpSocketM && rtpSocketM->IsOpen())
length = rtpSocketM->ReadVideo(packetBufferM, size);
}
if (length > 0) {
AddTunerStatistic(length);
deviceM->WriteData(packetBufferM, length);
timeout.Set(eReConnectTimeoutMs);
}
else {
// Reconnect if necessary
if (openedM && timeout.TimedOut()) {
Disconnect();
Connect();
timeout.Set(eReConnectTimeoutMs);
}
sleepM.Wait(10); // to avoid busy loop and reduce cpu load
}
while (Running()) {
UpdateCurrentState();
switch (currentStateM) {
case tsIdle:
debug4("%s: tsIdle [device %d]", __PRETTY_FUNCTION__, deviceIdM);
break;
case tsRelease:
debug4("%s: tsRelease [device %d]", __PRETTY_FUNCTION__, deviceIdM);
Disconnect();
RequestState(tsIdle, smInternal);
break;
case tsSet:
debug4("%s: tsSet [device %d]", __PRETTY_FUNCTION__, deviceIdM);
if (Connect()) {
RequestState(tsTuned, smInternal);
UpdatePids(true);
}
else
Disconnect();
break;
case tsTuned:
debug4("%s: tsTuned [device %d]", __PRETTY_FUNCTION__, deviceIdM);
reConnectM.Set(eConnectTimeoutMs);
// Read reception statistics via DESCRIBE and RTCP
if (hasLockM || ReadReceptionStatus()) {
// Quirk for devices without valid reception data
if (currentServerM.IsQuirk(cSatipServer::eSatipQuirkForceLock)) {
hasLockM = true;
signalStrengthM = eDefaultSignalStrength;
signalQualityM = eDefaultSignalQuality;
}
if (hasLockM)
RequestState(tsLocked, smInternal);
}
break;
case tsLocked:
debug4("%s: tsLocked [device %d]", __PRETTY_FUNCTION__, deviceIdM);
if (!UpdatePids()) {
error("Pid update failed - retuning [device %d]", deviceIdM);
RequestState(tsSet, smInternal);
break;
}
if (!KeepAlive()) {
error("Keep-alive failed - retuning [device %d]", deviceIdM);
RequestState(tsSet, smInternal);
break;
}
if (reConnectM.TimedOut()) {
error("Connection timeout - retuning [device %d]", deviceIdM);
RequestState(tsSet, smInternal);
break;
}
break;
default:
error("Unknown tuner status %d [device %d]", currentStateM, deviceIdM);
break;
}
if (!StateRequested())
sleepM.Wait(eSleepTimeoutMs); // to avoid busy loop and reduce cpu load
}
debug("cSatipTuner::%s(): exiting", __FUNCTION__);
debug1("%s Exiting [device %d]", __PRETTY_FUNCTION__, deviceIdM);
}
bool cSatipTuner::Open(void)
{
debug("cSatipTuner::%s()", __FUNCTION__);
if (Connect()) {
openedM = true;
return true;
}
return false;
cMutexLock MutexLock(&mutexM);
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIdM);
RequestState(tsSet, smExternal);
// return always true
return true;
}
bool cSatipTuner::Close(void)
{
debug("cSatipTuner::%s()", __FUNCTION__);
openedM = false;
Disconnect();
cMutexLock MutexLock(&mutexM);
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIdM);
RequestState(tsRelease, smExternal);
// return always true
return true;
}
bool cSatipTuner::Connect(void)
{
cMutexLock MutexLock(&mutexM);
debug("cSatipTuner::%s()", __FUNCTION__);
// Initialize the curl session
if (!handleM)
handleM = curl_easy_init();
if (handleM && !isempty(*streamAddrM)) {
cString uri, control, transport, range;
CURLcode res = CURLE_OK;
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIdM);
if (!isempty(*streamAddrM)) {
cString connectionUri = cString::sprintf("rtsp://%s/", *streamAddrM);
// Just retune
if (tunedM && (streamIdM >= 0)) {
debug("cSatipTuner::%s(): retune", __FUNCTION__);
uri = cString::sprintf("rtsp://%s/stream=%d", *streamAddrM, streamIdM);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PLAY);
SATIP_CURL_EASY_PERFORM(handleM);
if (!ValidateLatestResponse())
return false;
// Flush any old content
if (rtpSocketM)
rtpSocketM->Flush();
return true;
}
#ifdef DEBUG
// Verbose output
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_VERBOSE, 1L);
#endif
// Set callback
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_HEADERFUNCTION, cSatipTuner::HeaderCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEHEADER, this);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, this);
// No progress meter and no signaling
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_NOPROGRESS, 1L);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_NOSIGNAL, 1L);
// Set timeouts
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_TIMEOUT_MS, (long)eConnectTimeoutMs);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_CONNECTTIMEOUT_MS, (long)eConnectTimeoutMs);
// Set user-agent
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_USERAGENT, *cString::sprintf("vdr-%s/%s", PLUGIN_NAME_I18N, VERSION));
// Set URL
char *p = curl_easy_unescape(handleM, *streamAddrM, 0, NULL);
streamAddrM = p;
curl_free(p);
uri = cString::sprintf("rtsp://%s/", *streamAddrM);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_URL, *uri);
// Open sockets
int i = 100;
while (i-- > 0) {
if (rtpSocketM->Open() && rtcpSocketM->Open(rtpSocketM->Port() + 1))
break;
rtpSocketM->Close();
rtcpSocketM->Close();
if (streamIdM >= 0) {
cString uri = cString::sprintf("%sstream=%d?%s", *connectionUri, streamIdM, *streamParamM);
debug1("%s Retuning [device %d]", __PRETTY_FUNCTION__, deviceIdM);
if (rtspM.Play(*uri)) {
keepAliveM.Set(timeoutM);
return true;
}
if ((rtpSocketM->Port() <= 0) || (rtcpSocketM->Port() <= 0)) {
error("Cannot open required ports!");
return false;
}
// Request server options: "&pids=all" for the whole mux
keepAliveM.Set(timeoutM);
uri = cString::sprintf("rtsp://%s/?%s&pids=0", *streamAddrM, *streamParamM);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_OPTIONS);
SATIP_CURL_EASY_PERFORM(handleM);
if (!ValidateLatestResponse())
return false;
// Setup media stream
transport = cString::sprintf("RTP/AVP;unicast;client_port=%d-%d", rtpSocketM->Port(), rtcpSocketM->Port());
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_TRANSPORT, *transport);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_SETUP);
SATIP_CURL_EASY_PERFORM(handleM);
if (!ValidateLatestResponse())
return false;
// Start playing
uri = cString::sprintf("rtsp://%s/stream=%d", *streamAddrM, streamIdM);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
//SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_SESSION_ID, *sessionM);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PLAY);
SATIP_CURL_EASY_PERFORM(handleM);
if (!ValidateLatestResponse())
return false;
tunedM = true;
if (nextServerM) {
cSatipDiscover::GetInstance()->UseServer(nextServerM, true);
currentServerM = nextServerM;
nextServerM = NULL;
else if (rtspM.Options(*connectionUri)) {
cString uri = cString::sprintf("%s?%s", *connectionUri, *streamParamM);
// Flush any old content
//rtpM.Flush();
//rtcpM.Flush();
if (rtspM.Setup(*uri, rtpM.Port(), rtcpM.Port())) {
keepAliveM.Set(timeoutM);
if (nextServerM.IsValid()) {
currentServerM = nextServerM;
nextServerM.Reset();
}
currentServerM.Attach();
return true;
}
}
return true;
else
rtspM.Reset();
streamIdM = -1;
error("Connect failed [device %d]", deviceIdM);
}
return false;
@@ -268,76 +221,74 @@ bool cSatipTuner::Connect(void)
bool cSatipTuner::Disconnect(void)
{
cMutexLock MutexLock(&mutexM);
debug("cSatipTuner::%s()", __FUNCTION__);
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIdM);
// Terminate curl session
if (handleM) {
// Teardown rtsp session
if (!isempty(*streamAddrM) && streamIdM >= 0) {
CURLcode res = CURLE_OK;
cString uri = cString::sprintf("rtsp://%s/stream=%d", *streamAddrM, streamIdM);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_TEARDOWN);
SATIP_CURL_EASY_PERFORM(handleM);
ValidateLatestResponse();
}
// Cleanup curl stuff
if (headerListM) {
curl_slist_free_all(headerListM);
headerListM = NULL;
}
curl_easy_cleanup(handleM);
handleM = NULL;
if (!isempty(*streamAddrM) && (streamIdM >= 0)) {
cString uri = cString::sprintf("rtsp://%s/stream=%d", *streamAddrM, streamIdM);
rtspM.Teardown(*uri);
// some devices requires a teardown for TCP connection also
rtspM.Reset();
streamIdM = -1;
}
// Close the listening sockets
rtpSocketM->Close();
rtcpSocketM->Close();
// Reset signal parameters
hasLockM = false;
signalStrengthM = -1;
signalQualityM = -1;
frontendIdM = -1;
if (currentServerM)
cSatipDiscover::GetInstance()->UseServer(currentServerM, false);
tunedM = false;
timeoutM = eDefKeepAliveIntervalMs;
currentServerM.Detach();
statusUpdateM.Set(0);
timeoutM = eMinKeepAliveIntervalMs;
pmtPidM = -1;
addPidsM.Clear();
delPidsM.Clear();
// return always true
return true;
}
bool cSatipTuner::ValidateLatestResponse(void)
void cSatipTuner::ProcessVideoData(u_char *bufferP, int lengthP)
{
//debug("cSatipTuner::%s()", __FUNCTION__);
if (handleM) {
long rc = 0;
CURLcode res = CURLE_OK;
SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_RESPONSE_CODE, &rc);
if (rc == 200)
return true;
else if (rc != 0)
error("Tuner detected invalid status code: %ld", rc);
}
debug16("%s (, %d) [device %d]", __PRETTY_FUNCTION__, lengthP, deviceIdM);
if (lengthP > 0) {
uint64_t elapsed;
cTimeMs processing(0);
return false;
AddTunerStatistic(lengthP);
elapsed = processing.Elapsed();
if (elapsed > 1)
debug6("%s AddTunerStatistic() took %" PRIu64 " ms [device %d]", __PRETTY_FUNCTION__, elapsed, deviceIdM);
processing.Set(0);
deviceM->WriteData(bufferP, lengthP);
elapsed = processing.Elapsed();
if (elapsed > 1)
debug6("%s WriteData() took %" PRIu64 " ms [device %d]", __FUNCTION__, elapsed, deviceIdM);
}
reConnectM.Set(eConnectTimeoutMs);
}
void cSatipTuner::ParseReceptionParameters(const char *paramP)
void cSatipTuner::ProcessApplicationData(u_char *bufferP, int lengthP)
{
//debug("cSatipTuner::%s(%s)", __FUNCTION__, paramP);
debug16("%s (%d) [device %d]", __PRETTY_FUNCTION__, lengthP, deviceIdM);
// DVB-S2:
// ver=<major>.<minor>;src=<srcID>;tuner=<feID>,<level>,<lock>,<quality>,<frequency>,<polarisation>,<system>,<type>,<pilots>,<roll_off>,<symbol_rate>,<fec_inner>;pids=<pid0>,...,<pidn>
// DVB-T2:
// ver=1.1;tuner=<feID>,<level>,<lock>,<quality>,<freq>,<bw>,<msys>,<tmode>,<mtype>,<gi>,<fec>,<plp>,<t2id>,<sm>;pids=<pid0>,...,<pidn>
if (!isempty(paramP)) {
char *s = strdup(paramP);
// DVB-C2:
// ver=1.2;tuner=<feID>,<level>,<lock>,<quality>,<freq>,<bw>,<msys>,<mtype>,<sr>,<c2tft>,<ds>,<plp>,<specinv>;pids=<pid0>,...,<pidn>
if (lengthP > 0) {
char s[lengthP];
memcpy(s, (char *)bufferP, lengthP);
debug10("%s (%s) [device %d]", __PRETTY_FUNCTION__, s, deviceIdM);
char *c = strstr(s, ";tuner=");
if (c) {
int value;
// feID:
frontendIdM = atoi(c + 7);
// level:
// Numerical value between 0 and 255
// An incoming L-band satellite signal of
@@ -345,7 +296,7 @@ void cSatipTuner::ParseReceptionParameters(const char *paramP)
// -65dBm corresponds to 32
// No signal corresponds to 0
c = strstr(c, ",");
value = atoi(++c);
value = min(atoi(++c), 255);
// Scale value to 0-100
signalStrengthM = (value >= 0) ? (value * 100 / 255) : -1;
@@ -354,7 +305,7 @@ void cSatipTuner::ParseReceptionParameters(const char *paramP)
// "0" the frontend is not locked
// "1" the frontend is locked
c = strstr(c, ",");
hasLockM = atoi(++c);
hasLockM = !!atoi(++c);
// quality:
// Numerical value between 0 and 15
@@ -363,146 +314,286 @@ void cSatipTuner::ParseReceptionParameters(const char *paramP)
// -a BER lower than 2x10-4 after Viterbi for DVB-S
// -a PER lower than 10-7 for DVB-S2
c = strstr(c, ",");
value = atoi(++c);
value = min(atoi(++c), 15);
// Scale value to 0-100
signalQualityM = (hasLockM && (value >= 0)) ? (value * 100 / 15) : 0;
}
free(s);
}
reConnectM.Set(eConnectTimeoutMs);
}
void cSatipTuner::SetStreamId(int streamIdP)
{
cMutexLock MutexLock(&mutexM);
debug("cSatipTuner::%s(%d)", __FUNCTION__, streamIdP);
debug1("%s (%d) [device %d]", __PRETTY_FUNCTION__, streamIdP, deviceIdM);
streamIdM = streamIdP;
}
void cSatipTuner::SetSessionTimeout(const char *sessionP, int timeoutP)
{
cMutexLock MutexLock(&mutexM);
debug("cSatipTuner::%s(%s, %d)", __FUNCTION__, sessionP, timeoutP);
debug1("%s (%s, %d) [device %d]", __PRETTY_FUNCTION__, sessionP, timeoutP, deviceIdM);
sessionM = sessionP;
if (timeoutP > 30)
timeoutM = timeoutP * 1000L;
else if (timeoutP > 0)
timeoutM = eMinKeepAliveIntervalMs;
else
timeoutM = eDefKeepAliveIntervalMs;
if (nextServerM.IsQuirk(cSatipServer::eSatipQuirkSessionId) && !isempty(*sessionM) && startswith(*sessionM, "0"))
rtspM.SetSession(SkipZeroes(*sessionM));
timeoutM = (timeoutP > eMinKeepAliveIntervalMs) ? timeoutP : eMinKeepAliveIntervalMs;
}
bool cSatipTuner::SetSource(cSatipServer *serverP, const char *parameterP, const int indexP)
int cSatipTuner::GetId(void)
{
debug("cSatipTuner::%s(%s, %d)", __FUNCTION__, parameterP, indexP);
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIdM);
return deviceIdM;
}
bool cSatipTuner::SetSource(cSatipServer *serverP, const int transponderP, const char *parameterP, const int indexP)
{
debug1("%s (%d, %s, %d) [device %d]", __PRETTY_FUNCTION__, transponderP, parameterP, indexP, deviceIdM);
cMutexLock MutexLock(&mutexM);
nextServerM = cSatipDiscover::GetInstance()->GetServer(serverP);
if (nextServerM && !isempty(nextServerM->Address()) && !isempty(parameterP)) {
// Update stream address and parameter
streamAddrM = nextServerM->Address();
streamParamM = parameterP;
// Reconnect
Connect();
if (serverP) {
nextServerM.Set(serverP, transponderP);
if (!isempty(*nextServerM.GetAddress()) && !isempty(parameterP)) {
// Update stream address and parameter
streamAddrM = rtspM.RtspUnescapeString(*nextServerM.GetAddress());
streamParamM = rtspM.RtspUnescapeString(parameterP);
// Reconnect
RequestState(tsSet, smExternal);
}
}
else {
streamAddrM = "";
streamParamM = "";
}
return true;
}
bool cSatipTuner::SetPid(int pidP, int typeP, bool onP)
{
//debug("cSatipTuner::%s(%d, %d, %d)", __FUNCTION__, pidP, typeP, onP);
debug16("%s (%d, %d, %d) [device %d]", __PRETTY_FUNCTION__, pidP, typeP, onP, deviceIdM);
cMutexLock MutexLock(&mutexM);
bool found = false;
for (int i = 0; i < pidsM.Size(); ++i) {
if (pidsM[i] == pidP) {
found = true;
if (!onP) {
pidsM.Remove(i);
pidUpdatedM = true;
}
break;
}
}
if (onP && !found) {
pidsM.Append(pidP);
pidUpdatedM = true;
}
pidUpdateCacheM.Set(ePidUpdateIntervalMs);
if (onP) {
pidsM.AddPid(pidP);
addPidsM.AddPid(pidP);
delPidsM.RemovePid(pidP);
}
else {
pidsM.RemovePid(pidP);
delPidsM.AddPid(pidP);
addPidsM.RemovePid(pidP);
}
debug12("%s (%d, %d, %d) pids=%s [device %d]", __PRETTY_FUNCTION__, pidP, typeP, onP, *pidsM.ListPids(), deviceIdM);
sleepM.Signal();
return true;
}
bool cSatipTuner::UpdatePids(void)
bool cSatipTuner::UpdatePids(bool forceP)
{
debug16("%s (%d) tunerState=%s [device %d]", __PRETTY_FUNCTION__, forceP, TunerStateString(currentStateM), deviceIdM);
cMutexLock MutexLock(&mutexM);
if (pidUpdateCacheM.TimedOut() && pidUpdatedM && pidsM.Size() && tunedM && handleM && !isempty(*streamAddrM) && (streamIdM > 0)) {
//debug("cSatipTuner::%s()", __FUNCTION__);
CURLcode res = CURLE_OK;
//cString uri = cString::sprintf("rtsp://%s/stream=%d?%spids=%d", *streamAddrM, streamIdM, onP ? "add" : "del", pidP);
cString uri = cString::sprintf("rtsp://%s/stream=%d?pids=", *streamAddrM, streamIdM);
if (((forceP && pidsM.Size()) || (pidUpdateCacheM.TimedOut() && (addPidsM.Size() || delPidsM.Size()))) &&
!isempty(*streamAddrM) && (streamIdM > 0)) {
cString uri = cString::sprintf("rtsp://%s/stream=%d", *streamAddrM, streamIdM);
bool useci = (SatipConfig.GetCIExtension() && currentServerM.HasCI());
bool usedummy = currentServerM.IsQuirk(cSatipServer::eSatipQuirkPlayPids);
if (forceP || usedummy) {
if (pidsM.Size())
uri = cString::sprintf("%s?pids=%s", *uri, *pidsM.ListPids());
if (usedummy && (pidsM.Size() == 1) && (pidsM[0] < 0x20))
uri = cString::sprintf("%s,%d", *uri, eDummyPid);
}
else {
if (addPidsM.Size())
uri = cString::sprintf("%s?addpids=%s", *uri, *addPidsM.ListPids());
if (delPidsM.Size())
uri = cString::sprintf("%s%sdelpids=%s", *uri, addPidsM.Size() ? "&" : "?", *delPidsM.ListPids());
}
if (useci) {
// CI extension parameters:
// - x_pmt : specifies the PMT of the service you want the CI to decode
// - x_ci : specfies which CI slot (1..n) to use
// value 0 releases the CI slot
// CI slot released automatically if the stream is released,
// but not when used retuning to another channel
int pid = deviceM->GetPmtPid();
if ((pid > 0) && (pid != pmtPidM)) {
int slot = deviceM->GetCISlot();
uri = cString::sprintf("%s&x_pmt=%d", *uri, pid);
if (slot > 0)
uri = cString::sprintf("%s&x_ci=%d", *uri, slot);
}
pmtPidM = pid;
}
pidUpdateCacheM.Set(ePidUpdateIntervalMs);
if (!rtspM.Play(*uri))
return false;
addPidsM.Clear();
delPidsM.Clear();
}
for (int i = 0; i < pidsM.Size(); ++i)
uri = cString::sprintf("%s%d%s", *uri, pidsM[i], (i == (pidsM.Size() - 1)) ? "" : ",");
return true;
}
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PLAY);
SATIP_CURL_EASY_PERFORM(handleM);
if (ValidateLatestResponse())
pidUpdatedM = false;
else
Disconnect();
bool cSatipTuner::KeepAlive(bool forceP)
{
debug16("%s (%d) tunerState=%s [device %d]", __PRETTY_FUNCTION__, forceP, TunerStateString(currentStateM), deviceIdM);
cMutexLock MutexLock(&mutexM);
if (keepAliveM.TimedOut()) {
keepAliveM.Set(timeoutM);
forceP = true;
}
if (forceP && !isempty(*streamAddrM)) {
cString uri = cString::sprintf("rtsp://%s/", *streamAddrM);
if (!rtspM.Options(*uri))
return false;
}
return true;
return true;
}
bool cSatipTuner::ReadReceptionStatus(bool forceP)
{
debug16("%s (%d) tunerState=%s [device %d]", __PRETTY_FUNCTION__, forceP, TunerStateString(currentStateM), deviceIdM);
cMutexLock MutexLock(&mutexM);
if (statusUpdateM.TimedOut()) {
statusUpdateM.Set(eStatusUpdateTimeoutMs);
forceP = true;
}
if (forceP && !isempty(*streamAddrM) && (streamIdM > 0)) {
cString uri = cString::sprintf("rtsp://%s/stream=%d", *streamAddrM, streamIdM);
if (rtspM.Describe(*uri))
return true;
}
return false;
}
bool cSatipTuner::KeepAlive(void)
void cSatipTuner::UpdateCurrentState(void)
{
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIdM);
cMutexLock MutexLock(&mutexM);
if (tunedM && handleM && keepAliveM.TimedOut()) {
debug("cSatipTuner::%s()", __FUNCTION__);
CURLcode res = CURLE_OK;
cString uri = cString::sprintf("rtsp://%s/stream=%d", *streamAddrM, streamIdM);
eTunerState state = currentStateM;
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_OPTIONS);
SATIP_CURL_EASY_PERFORM(handleM);
if (ValidateLatestResponse())
keepAliveM.Set(timeoutM);
else
Disconnect();
return true;
if (internalStateM.Size()) {
state = internalStateM.At(0);
internalStateM.Remove(0);
}
else if (externalStateM.Size()) {
state = externalStateM.At(0);
externalStateM.Remove(0);
}
return false;
if (currentStateM != state) {
debug1("%s: Switching from %s to %s [device %d]", __PRETTY_FUNCTION__, TunerStateString(currentStateM), TunerStateString(state), deviceIdM);
currentStateM = state;
}
}
bool cSatipTuner::StateRequested(void)
{
cMutexLock MutexLock(&mutexM);
debug16("%s current=%s internal=%d external=%d [device %d]", __PRETTY_FUNCTION__, TunerStateString(currentStateM), internalStateM.Size(), externalStateM.Size(), deviceIdM);
return (internalStateM.Size() || externalStateM.Size());
}
bool cSatipTuner::RequestState(eTunerState stateP, eStateMode modeP)
{
cMutexLock MutexLock(&mutexM);
debug1("%s (%s, %s) current=%s internal=%d external=%d [device %d]", __PRETTY_FUNCTION__, TunerStateString(stateP), StateModeString(modeP), TunerStateString(currentStateM), internalStateM.Size(), externalStateM.Size(), deviceIdM);
if (modeP == smExternal)
externalStateM.Append(stateP);
else if (modeP == smInternal) {
eTunerState state = internalStateM.Size() ? internalStateM.At(internalStateM.Size() - 1) : currentStateM;
// validate legal state changes
switch (state) {
case tsIdle:
if (stateP == tsRelease)
return false;
case tsRelease:
case tsSet:
case tsLocked:
case tsTuned:
default:
break;
}
internalStateM.Append(stateP);
}
else
return false;
return true;
}
const char *cSatipTuner::StateModeString(eStateMode modeP)
{
switch (modeP) {
case smInternal:
return "smInternal";
case smExternal:
return "smExternal";
default:
break;
}
return "---";
}
const char *cSatipTuner::TunerStateString(eTunerState stateP)
{
switch (stateP) {
case tsIdle:
return "tsIdle";
case tsRelease:
return "tsRelease";
case tsSet:
return "tsSet";
case tsLocked:
return "tsLocked";
case tsTuned:
return "tsTuned";
default:
break;
}
return "---";
}
int cSatipTuner::FrontendId(void)
{
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIdM);
return frontendIdM;
}
int cSatipTuner::SignalStrength(void)
{
//debug("cSatipTuner::%s()", __FUNCTION__);
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIdM);
return signalStrengthM;
}
int cSatipTuner::SignalQuality(void)
{
//debug("cSatipTuner::%s()", __FUNCTION__);
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIdM);
return signalQualityM;
}
bool cSatipTuner::HasLock(void)
{
//debug("cSatipTuner::%s()", __FUNCTION__);
return tunedM && hasLockM;
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIdM);
return (currentStateM >= tsTuned) && hasLockM;
}
cString cSatipTuner::GetSignalStatus(void)
{
//debug("cSatipTuner::%s()", __FUNCTION__);
return cString::sprintf("lock=%d strength=%d quality=%d", HasLock(), SignalStrength(), SignalQuality());
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIdM);
return cString::sprintf("lock=%d strength=%d quality=%d frontend=%d", HasLock(), SignalStrength(), SignalQuality(), FrontendId());
}
cString cSatipTuner::GetInformation(void)
{
//debug("cSatipTuner::%s()", __FUNCTION__);
return tunedM ? cString::sprintf("rtsp://%s/?%s [stream=%d]", *streamAddrM, *streamParamM, streamIdM) : "connection failed";
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIdM);
return (currentStateM >= tsTuned) ? cString::sprintf("rtsp://%s/?%s [stream=%d]", *streamAddrM, *streamParamM, streamIdM) : "connection failed";
}

142
tuner.h
View File

@@ -8,69 +8,126 @@
#ifndef __SATIP_TUNER_H
#define __SATIP_TUNER_H
#include <curl/curl.h>
#include <curl/easy.h>
#ifndef CURLOPT_RTSPHEADER
#error "libcurl is missing required RTSP support"
#endif
#include <vdr/thread.h>
#include <vdr/tools.h>
#include "deviceif.h"
#include "discover.h"
#include "rtp.h"
#include "rtcp.h"
#include "rtsp.h"
#include "server.h"
#include "statistics.h"
#include "socket.h"
class cSatipTuner : public cThread, public cSatipTunerStatistics {
class cSatipPid : public cVector<int> {
private:
static int PidCompare(const void *aPidP, const void *bPidP)
{
return (*(int*)aPidP - *(int*)bPidP);
}
public:
void RemovePid(const int &pidP)
{
if (RemoveElement(pidP))
Sort(PidCompare);
}
void AddPid(int pidP)
{
if (AppendUnique(pidP))
Sort(PidCompare);
}
cString ListPids(void)
{
cString list = "";
if (Size()) {
for (int i = 0; i < Size(); ++i)
list = cString::sprintf("%s%d,", *list, At(i));
list = list.Truncate(-1);
}
return list;
}
};
class cSatipTunerServer
{
private:
cSatipServer *serverM;
int deviceIdM;
int transponderM;
public:
cSatipTunerServer(cSatipServer *serverP, const int deviceIdP, const int transponderP) : serverM(serverP), deviceIdM(deviceIdP), transponderM(transponderP) {}
~cSatipTunerServer() {}
cSatipTunerServer(const cSatipTunerServer &objP) { serverM = NULL; deviceIdM = -1; transponderM = 0; }
cSatipTunerServer& operator= (const cSatipTunerServer &objP) { serverM = objP.serverM; deviceIdM = objP.deviceIdM; transponderM = objP.transponderM; return *this; }
bool IsValid(void) { return !!serverM; }
bool IsQuirk(int quirkP) { return (serverM && cSatipDiscover::GetInstance()->IsServerQuirk(serverM, quirkP)); }
bool HasCI(void) { return (serverM && cSatipDiscover::GetInstance()->HasServerCI(serverM)); }
void Attach(void) { if (serverM) cSatipDiscover::GetInstance()->AttachServer(serverM, deviceIdM, transponderM); }
void Detach(void) { if (serverM) cSatipDiscover::GetInstance()->DetachServer(serverM, deviceIdM, transponderM); }
void Set(cSatipServer *serverP, const int transponderP) { serverM = serverP; transponderM = transponderP; }
void Reset(void) { serverM = NULL; transponderM = 0; }
cString GetAddress(void) { return serverM ? cSatipDiscover::GetInstance()->GetServerAddress(serverM) : ""; }
cString GetInfo(void) { return cString::sprintf("server=%s deviceid=%d transponder=%d", serverM ? "assigned" : "null", deviceIdM, transponderM); }
};
class cSatipTuner : public cThread, public cSatipTunerStatistics, public cSatipTunerIf
{
private:
enum {
eConnectTimeoutMs = 1500, // in milliseconds
ePidUpdateIntervalMs = 100, // in milliseconds
eReConnectTimeoutMs = 5000, // in milliseconds
eMinKeepAliveIntervalMs = 300000, // in milliseconds
eDefKeepAliveIntervalMs = 600000 // in milliseconds
eDummyPid = 100,
eDefaultSignalStrength = 15,
eDefaultSignalQuality = 224,
eSleepTimeoutMs = 250, // in milliseconds
eStatusUpdateTimeoutMs = 1000, // in milliseconds
ePidUpdateIntervalMs = 250, // in milliseconds
eConnectTimeoutMs = 5000, // in milliseconds
eMinKeepAliveIntervalMs = 30000 // in milliseconds
};
static size_t HeaderCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP);
enum eTunerState { tsIdle, tsRelease, tsSet, tsTuned, tsLocked };
enum eStateMode { smInternal, smExternal };
cCondWait sleepM;
cSatipDeviceIf* deviceM;
unsigned char* packetBufferM;
unsigned int packetBufferLenM;
cSatipSocket *rtpSocketM;
cSatipSocket *rtcpSocketM;
int deviceIdM;
cSatipRtsp rtspM;
cSatipRtp rtpM;
cSatipRtcp rtcpM;
cString streamAddrM;
cString streamParamM;
cSatipServer *currentServerM;
cSatipServer *nextServerM;
cSatipTunerServer currentServerM;
cSatipTunerServer nextServerM;
cMutex mutexM;
CURL *handleM;
struct curl_slist *headerListM;
cTimeMs reConnectM;
cTimeMs keepAliveM;
cTimeMs signalInfoCacheM;
cTimeMs statusUpdateM;
cTimeMs pidUpdateCacheM;
cString sessionM;
eTunerState currentStateM;
cVector<eTunerState> internalStateM;
cVector<eTunerState> externalStateM;
int timeoutM;
bool openedM;
bool tunedM;
bool hasLockM;
int signalStrengthM;
int signalQualityM;
int frontendIdM;
int streamIdM;
bool pidUpdatedM;
cVector<int> pidsM;
int pmtPidM;
cSatipPid addPidsM;
cSatipPid delPidsM;
cSatipPid pidsM;
bool Connect(void);
bool Disconnect(void);
bool ValidateLatestResponse(void);
void ParseReceptionParameters(const char *paramP);
void SetStreamId(int streamIdP);
void SetSessionTimeout(const char *sessionP, int timeoutP);
bool KeepAlive(void);
bool UpdateSignalInfoCache(void);
bool UpdatePids(void);
bool KeepAlive(bool forceP = false);
bool ReadReceptionStatus(bool forceP = false);
bool UpdatePids(bool forceP = false);
void UpdateCurrentState(void);
bool StateRequested(void);
bool RequestState(eTunerState stateP, eStateMode modeP);
const char *StateModeString(eStateMode modeP);
const char *TunerStateString(eTunerState stateP);
protected:
virtual void Action(void);
@@ -78,16 +135,25 @@ protected:
public:
cSatipTuner(cSatipDeviceIf &deviceP, unsigned int packetLenP);
virtual ~cSatipTuner();
bool IsTuned(void) const { return tunedM; }
bool SetSource(cSatipServer *serverP, const char *parameterP, const int indexP);
bool IsTuned(void) const { return (currentStateM >= tsTuned); }
bool SetSource(cSatipServer *serverP, const int transponderP, const char *parameterP, const int indexP);
bool SetPid(int pidP, int typeP, bool onP);
bool Open(void);
bool Close(void);
int FrontendId(void);
int SignalStrength(void);
int SignalQuality(void);
bool HasLock(void);
cString GetSignalStatus(void);
cString GetInformation(void);
// for internal tuner interface
public:
virtual void ProcessVideoData(u_char *bufferP, int lengthP);
virtual void ProcessApplicationData(u_char *bufferP, int lengthP);
virtual void SetStreamId(int streamIdP);
virtual void SetSessionTimeout(const char *sessionP, int timeoutP);
virtual int GetId(void);
};
#endif // __SATIP_TUNER_H

26
tunerif.h Normal file
View File

@@ -0,0 +1,26 @@
/*
* tunerif.h: SAT>IP plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#ifndef __SATIP_TUNERIF_H
#define __SATIP_TUNERIF_H
class cSatipTunerIf {
public:
cSatipTunerIf() {}
virtual ~cSatipTunerIf() {}
virtual void ProcessVideoData(u_char *bufferP, int lengthP) = 0;
virtual void ProcessApplicationData(u_char *bufferP, int lengthP) = 0;
virtual void SetStreamId(int streamIdP) = 0;
virtual void SetSessionTimeout(const char *sessionP, int timeoutP) = 0;
virtual int GetId(void) = 0;
private:
cSatipTunerIf(const cSatipTunerIf&);
cSatipTunerIf& operator=(const cSatipTunerIf&);
};
#endif // __SATIP_TUNERIF_H