Compare commits

..

692 commits

Author SHA1 Message Date
b82ae7239a http: add interpretation of "X-Forwarded-For" header
This allows to use the builtin ACL when serving TVH behind a SSL reverse
proxy.
2015-05-04 00:32:49 +02:00
Jaroslav Kysela
b6095136b3 config: fix the global config variable initialization on error, fixes #2816 2015-05-03 20:01:06 +02:00
Jaroslav Kysela
fff2c36063 config: add more null path checks 2015-05-03 19:57:51 +02:00
Nita Vesa
130f413ed8 More elegant solution by perexg 2015-05-02 20:02:39 +02:00
Nita Vesa
eb8aa7f00d Add workarounds for AVM's FRITZ\!-devices. 2015-05-02 20:02:39 +02:00
Damjan Marion
70bd3350b1 linuxdvb_ca: add gui and configurable options 2015-05-02 18:39:43 +02:00
Jaroslav Kysela
92736802f6 deferred_unlink: add (fix) the directory tree removal, fixes #2814 2015-05-02 18:38:42 +02:00
Jaroslav Kysela
8d8374b430 subscriptions: added NULL check to subscription_unsubscribe() 2015-05-02 12:01:20 +02:00
Jaroslav Kysela
41bb5cdd99 deferred_unlink() - fix wrong allocation, fixes #2814 2015-05-02 11:56:11 +02:00
Jaroslav Kysela
8dbc38df4a htsp: add subtitle to the epg event, fixes #2785 2015-05-01 22:06:30 +02:00
Jaroslav Kysela
fd16a1e3ef linuxdvb & satip: fix pid subscription locking 2015-05-01 22:03:53 +02:00
Jaroslav Kysela
a00a3da84d tsfix: do not use wrong DTS as the reference clock, fixes #2731, fixes #2754 2015-05-01 18:49:31 +02:00
Jaroslav Kysela
5971f7b84b DVR: Remove unused 'Episode Duplicate Detect' option in DVR config, fixes #2715 2015-05-01 18:23:24 +02:00
Jaroslav Kysela
e0630fa458 main: add -S or --nosyslog option to disable completely syslog logging, fixes #2787 2015-05-01 18:14:03 +02:00
Jaroslav Kysela
1c10cd6fe9 IPTV: add rtsp:// and rtsps:// support 2015-05-01 16:58:39 +02:00
Jaroslav Kysela
4d7d61e39a doc: updates for XBMC->Kodi, Showtime->Movian changes 2015-04-30 17:01:41 +02:00
Jaroslav Kysela
02948826e9 time update - make it work again using TDT/TOT tables, fixes #2776 2015-04-30 11:37:26 +02:00
Jaroslav Kysela
86b3650e42 subscriptions: fix possible NULL dereference 2015-04-30 10:48:09 +02:00
Jaroslav Kysela
01406e0e5f htsp: add dataSize field to the dvrentry msg 2015-04-30 09:34:45 +02:00
Jaroslav Kysela
cb6e8aed90 dvr: de_last_error cleanpus 2015-04-30 09:25:24 +02:00
Nick Burrett
2bcf09f2ab Fix crash when trying to configure adapters that are already in use.
If another application has an adapter open, then la==NULL.  Skip the
adapter so that another can be configured.
2015-04-29 17:35:37 +02:00
Jaroslav Kysela
09265f0205 profile: add 'continue on access error', fix 'force priority' 2015-04-29 16:08:22 +02:00
Jaroslav Kysela
ed3a02ec14 descrambler: mark ECM late msg as error 2015-04-29 15:27:39 +02:00
Damjan Marion
8ae5b7fce4 linuxdvb_ca: check for CAIDs on the ES level, fixes #2794 2015-04-29 08:40:14 +02:00
Jaroslav Kysela
aaaa7a1cfd SAT>IP Client: fix possible NULL dereference 2015-04-28 22:29:57 +02:00
Jaroslav Kysela
bd219954c1 linuxdvb: fix for the previous patch (fixed bad macro), fixes #2807 2015-04-28 15:08:29 +02:00
Jaroslav Kysela
97229aa881 linuxdvb: use DTV_LNA only when set, fixes #2807 2015-04-28 14:49:52 +02:00
Jaroslav Kysela
5ba809b6bb linuxdvb: rewrite PID subscription (limit used PID at once) 2015-04-28 14:48:32 +02:00
Jaroslav Kysela
c79a41dbb2 SAT>IP Client: fix close_pid callback 2015-04-28 09:07:29 +02:00
Damjan Marion
cb98d6a94d linuxdvb_ca: add basic mmi suport and some code cleanup
Currently it just provides log of messages received by CAM and
exits menu.
2015-04-27 20:55:07 +02:00
Jaroslav Kysela
08c4c75364 SAT>IP Client: add configurable grace timeout 2015-04-27 20:54:33 +02:00
Jaroslav Kysela
2623cc52d9 mpegts: implemented weighted PID subscriptions (sequential PMT scan) 2015-04-27 19:57:14 +02:00
Jaroslav Kysela
4fce256468 mpegts table: added MT_ONESHOT, change pmt scan weight 2015-04-27 19:57:13 +02:00
Jaroslav Kysela
db3101400d mpegts: add weight to the pid subscription 2015-04-27 19:56:58 +02:00
Jaroslav Kysela
3049486768 linuxdvb: Added LNA settings to DVB-T for DVBAPI v5.9+, fixes #2784 2015-04-24 22:05:27 +02:00
Jaroslav Kysela
d1e6a61a29 subscriptions: reschedule - add warning when a service instance goes to the bad state 2015-04-24 21:41:56 +02:00
Jaroslav Kysela
2f2d7cef42 mpegts_mux_unsubscribe_by_name: fixed nasty bug - used another link ptr 2015-04-24 21:40:06 +02:00
Jaroslav Kysela
3f4c630ecc mpegts input: move active list of services from input to mux, fixes #2801
In last changes, the mux filter was removed from the main TS packet
decoder loop. Also, it is not good to have this filter in this
"exponed" function. The solution is quite easy - move the active list
of services from the input object to the mux object. It seems that
this change reduces the code (mux filters) all around.
2015-04-24 20:27:00 +02:00
Jaroslav Kysela
0e4a41168f main: init dispatch_clock early 2015-04-24 19:41:38 +02:00
Jaroslav Kysela
dc311fd3f8 gtimer check fix - wrong fcn redirects 2015-04-24 19:41:27 +02:00
Jaroslav Kysela
209d02282b gtimer check: little cleanup (show the previous caller id) 2015-04-24 17:12:16 +02:00
Jaroslav Kysela
03f044a72b subscriptions: fix the weight init issue caused by last commits 2015-04-24 16:48:20 +02:00
Jaroslav Kysela
4112e4b30b added gtimer check framework 2015-04-24 16:46:53 +02:00
Jaroslav Kysela
79e5ff8d4b remove unused ffmpeg_lock 2015-04-24 15:40:57 +02:00
Jaroslav Kysela
99920b83e3 epgdb: deferred write 2015-04-24 15:38:17 +02:00
Jaroslav Kysela
d4f1c108fe spawn: another reap fix (zero return code), fixes #2800 2015-04-24 14:41:24 +02:00
Jaroslav Kysela
536736daaa tcp: a little optimization in tcp_server_done() and more debug msgs 2015-04-24 14:34:24 +02:00
Jaroslav Kysela
2b00b88784 implemented deferred_unlink() for DVR 2015-04-24 14:11:01 +02:00
Jaroslav Kysela
c4d8b7a3fe added tasklets 2015-04-24 13:29:25 +02:00
Jaroslav Kysela
c649ef2bdf profile: add priority settings, fixes #2783 2015-04-24 12:57:04 +02:00
Jaroslav Kysela
84ee61634d muxer: change mime types for mpegts stream to audio|video/mp2t, fixes #2775
see: http://www.w3.org/2013/12/byte-stream-format-registry/
2015-04-23 21:16:42 +02:00
Jaroslav Kysela
3e56c175e3 mpegts: fix linked inputs (abort), fixes #2791 2015-04-23 20:47:40 +02:00
Jaroslav Kysela
706beee703 SAT>IP Client: do not save config in the load procedure 2015-04-23 16:53:31 +02:00
Jaroslav Kysela
8cb63d919b config: do not take ECHILD as error from spawn_reap() 2015-04-23 16:40:51 +02:00
Jaroslav Kysela
98464be592 spawn: fix spawn_reaper - handle errors 2015-04-23 14:47:16 +02:00
Jaroslav Kysela
926e04eacc spawn_reap: wait for specified pid, check correctly return codes from waitpid(), fixes #2766 2015-04-23 09:03:59 +02:00
Jaroslav Kysela
9d042c2418 opentv: process all channels 2015-04-22 21:30:22 +02:00
Jaroslav Kysela
9270518314 EIT: process all channels, fixes #2764, fixes #2743 2015-04-22 21:19:29 +02:00
Jaroslav Kysela
d4160380a2 iptv: rewrite iptv_input_is_free() - use per IPTV network limits, fixes #2780 2015-04-22 17:26:36 +02:00
Jaroslav Kysela
6a110d871b mpegts: add mpegts_mux_t arg to mi_get_weight() callback 2015-04-22 17:22:10 +02:00
Jaroslav Kysela
89bb07fe82 iptv_input_get_weight: remove unused code 2015-04-22 17:18:21 +02:00
Jaroslav Kysela
f29a293e37 mpegts: remove mi_is_free() callback, it is no longer used 2015-04-22 16:56:35 +02:00
Jaroslav Kysela
a1844b88af subscriptions: fix source_info leak in subscription_show_info() 2015-04-22 16:13:30 +02:00
Jaroslav Kysela
91ad9832ff another satpos fix for source_info_t 2015-04-22 15:37:24 +02:00
Jaroslav Kysela
ef7946f5ee Fix memory leak for struct source_info (si_satpos) 2015-04-22 14:45:20 +02:00
Jaroslav Kysela
bfec4e7f9c EIT: Fix possible wrong memory access in _eit_process_event() 2015-04-22 14:42:23 +02:00
Glenn-1990
7749976fed bump version to 20 2015-04-22 10:21:21 +02:00
Glenn-1990
60338204e1 duplicate detect for autorecs 2015-04-22 10:21:21 +02:00
Jaroslav Kysela
1b2e2c8482 SAT>IP Client: fix the slave tuners (wrong satellite position check) 2015-04-22 09:59:23 +02:00
Mariusz Bialonczyk
a7cb06cd6b another typo fix in dvb_mux_conf_str_dvbs() 2015-04-21 14:19:38 +02:00
Jaroslav Kysela
0af90088e3 dvb_mux_create0 - fix initial orbital position handling 2015-04-19 21:10:32 +02:00
Jaroslav Kysela
1ca7ed5e89 fix typo in dvb_mux_conf_str_dvbs() 2015-04-19 20:08:33 +02:00
Jaroslav Kysela
6b472cd999 linuxdvb_ca: coding style update, don't call pthread_join when thread is off 2015-04-17 16:53:59 +02:00
Jaroslav Kysela
caa6f8ed26 mpegts network: show the mux parameter changes more nicely 2015-04-17 11:15:26 +02:00
Jaroslav Kysela
bda5d8ce25 stream status: add possibility to clear statistics, introduce tvh_input_instance_t 2015-04-17 11:11:00 +02:00
Jaroslav Kysela
abb14095cb libdvben50221 - fix detection 2015-04-16 08:49:47 +02:00
Damjan Marion
f3582868d9 Introduce DVB CA support 2015-04-16 08:29:15 +02:00
Jaroslav Kysela
b1b9b42685 mpegts service: fix compilation when \!ENABLE_MPEGTS_DVB 2015-04-16 08:26:11 +02:00
Jaroslav Kysela
4e1e909946 Makefile.ffmpeg: replace make with \$(MAKE) 2015-04-15 21:45:30 +02:00
Kai Sommerfeld
ce3a9a1347 Fix dvr_autorec_create_htsp to actually use "fulltext" parameter and not to always set fulltext to "1". 2015-04-15 18:23:05 +02:00
Jaroslav Kysela
e78b3f4e27 Makefile.ffmpeg: upgrade to ffmpeg 2.6.2 2015-04-15 18:22:51 +02:00
stbenz
01a095ceb5 transcoding: set AVFrame format, width and height, fixes #2763
ffmpeg 2.6 checks, if format, width and height is set in avcodec_encode_video2 and generates a warning, if they aren't set.
2015-04-15 18:17:39 +02:00
Ullrich Kossow
f2af011618 Corrected some unsigned int comparisons. 2015-04-15 18:16:27 +02:00
Ullrich Kossow
e3debcd435 Use llabs instead of abs on 64bit integers. 2015-04-15 18:16:27 +02:00
Ullrich Kossow
310a77ce93 Fixed include guard of src/satip/server.h 2015-04-15 18:16:27 +02:00
Adrian Strilchuk
203b48ed94 hdhomerun: Upgrade libhdhomerun to 20150406 2015-04-15 18:15:40 +02:00
Jaroslav Kysela
903ca33ed2 revert previous change 2015-04-15 18:15:07 +02:00
Glenn-1990
2221286368 add satpos and subsription status 2015-04-15 18:14:16 +02:00
Jaroslav Kysela
04e5a9d26f mpegts: warn when the mux changed parameters 2015-04-15 18:12:43 +02:00
Jaroslav Kysela
c8ae8a9cb0 SAT>IP Server: parse fec also for DVB-C 2015-04-14 22:07:34 +02:00
Jaroslav Kysela
daaebf916a SAT>IP client: add RTCP workaround for broken servers (minisatip) 2015-04-13 20:11:19 +02:00
Jaroslav Kysela
6751ade06e linuxdvb: notify user that tuners with same types cannot be used simultaneously when they're assigned to one adapter 2015-04-12 22:29:05 +02:00
Sam Stenvall
14f03b8efb avoid leaving dangling children by killing spawned processes recursively 2015-04-09 15:14:06 +02:00
Jaroslav Kysela
02427fe00c initialize PKG_CONFIG varible if empty, fixes previous commit 2015-04-09 12:05:39 +02:00
Niels Ole Salscheider
098213f545 Use $PKG_CONFIG instead of pkg-config
This allows to use prefixed versions of pkg-config for cross-compilation.
2015-04-09 12:04:09 +02:00
Dave Jaggar
d000264e28 Genre map for Sky New Zealand 2015-04-09 12:03:14 +02:00
Dave Jaggar
fdbcbcf015 Huffman dictionary for Sky New Zealand 2015-04-09 12:03:14 +02:00
Dave Jaggar
ecdf96828d Provider file for Sky New Zealand 2015-04-09 12:03:14 +02:00
Dave Jaggar
ad5d5690d1 Remove extra characters in title of skynz provider and set skynz lang to eng 2015-04-09 12:03:14 +02:00
Jaroslav Kysela
79ec2df4dd epggrab: set minimal epggrab_epgdb_periodicsave value to 3600, fixes #2765 2015-04-09 12:02:33 +02:00
Jaroslav Kysela
2a792c4e4c mpegts: show pids for complete/incomplete tables, move scan needs more time msg to trace 2015-04-09 11:55:56 +02:00
Jaroslav Kysela
e138492f5e dvb_psi_lib: remove the wrong pmt hack - causes scan FAIL issues in this form 2015-04-09 11:55:26 +02:00
Jaroslav Kysela
995aaf895b linuxdvb: allow to force using old linuxdvb ioctls, fixes#2759 2015-04-07 09:44:53 +02:00
Jaroslav Kysela
d58afc0415 mpegts: improve streaming latency for low-bandwidth subscriptions 2015-04-05 20:42:31 +02:00
Jaroslav Kysela
c7a87457cf SAT>IP Server: rtsp/options - handle also '*' url 2015-04-05 20:16:14 +02:00
Jaroslav Kysela
2518d4f821 globalheaders: when an elementary stream is disabled, notify via debug log 2015-04-05 20:15:52 +02:00
Jaroslav Kysela
e4f034aed3 SAT>IP Server: export active connection for webui 2015-04-02 12:28:42 +02:00
Jaroslav Kysela
1c885f100d SAT>IP server: subscribe at least one PID (PAT) 2015-04-02 12:12:39 +02:00
Jaroslav Kysela
d63c36a36e http: add protocol version and command to dump_request() 2015-04-02 12:02:13 +02:00
Bob Lightfoot
ffe92dfed1 changelog added to rpm spec file 2015-04-02 08:59:19 +02:00
Jaroslav Kysela
b988813057 opentv: add boundary check to _opentv_parse_event() 2015-04-02 08:58:53 +02:00
Jaroslav Kysela
08c4af7e6a mpegts mux+service: export uuid for parent network and mux 2015-04-01 12:57:57 +02:00
Jaroslav Kysela
fabd01191d htsp server: fix epg query (missing title filter), add fulltext flag 2015-04-01 10:48:00 +02:00
Jaroslav Kysela
ad4dd4fa50 simpleui: fix search, fixes #2753 2015-04-01 10:37:26 +02:00
Jaroslav Kysela
9a8242a082 cosmetic: remove old tvh_strlcatf() and rename tvh_strlcatf2() as tvh_strlcatf() 2015-04-01 10:27:15 +02:00
Jaroslav Kysela
4eeecb2a0f subscription: cleanup the subscription messages 2015-04-01 10:22:32 +02:00
Jaroslav Kysela
3b9253679b subscription: do not call subscription_schedule recursively() - mux subscriptions, fixes #2750 2015-03-31 21:16:38 +02:00
Jaroslav Kysela
f6d9813ff5 SAT>IP server: change default fec to auto for dvb-c 2015-03-31 20:21:26 +02:00
Jaroslav Kysela
3e4c8e7314 pid-count.py: improve sync 2015-03-30 17:03:56 +02:00
Jaroslav Kysela
e4cdd3ca0f descrambler: fix another thinko (and crash) 2015-03-27 18:40:30 +01:00
Jaroslav Kysela
722c9250ac webui: about page: 2015 year 2015-03-27 17:30:08 +01:00
Jaroslav Kysela
169b71bb72 lang codes: add syn 2015-03-27 12:53:15 +01:00
Jaroslav Kysela
1f79d9dd5f mpegts.h - undefine mpegts_psi_section_t (duplicate with dvb.h) 2015-03-26 21:03:36 +01:00
Bob Lightfoot
b427d7ecba Patching rpm spec file so the arm architecture builds properly modified: rpm/tvheadend.spec.in 2015-03-26 14:19:52 +01:00
Jaroslav Kysela
6bc1a6fe74 SAT>IP Server: clean/extend the doc 2015-03-26 11:48:57 +01:00
Jaroslav Kysela
0d4f44385c doc: more words to the channel / Auto EPG checkbox 2015-03-25 15:17:39 +01:00
Ben Fennema
1ccdcd33a2 tvhdhomerun: lock tuners when in use 2015-03-25 15:16:36 +01:00
Ben Fennema
266b28cfd1 epg: allow epg source to be set with auto epg channel unchecked 2015-03-25 15:16:32 +01:00
Jaroslav Kysela
baab7707a2 SAT>IP Client: do not show 12345678 error 2015-03-25 14:37:29 +01:00
Jaroslav Kysela
3fab60a1b6 mpegts pass muxer: recode the PAT rewritter to use psi library functions 2015-03-25 13:56:52 +01:00
Jaroslav Kysela
04f0ef966d mpegts pass muxer: optimize the parsing loop using mpegts_word_count() 2015-03-25 12:51:37 +01:00
Jaroslav Kysela
41431f71d8 configure: fix ffmpeg check 2015-03-25 12:35:12 +01:00
Jaroslav Kysela
9ee27b671b mpegts pass muxer: add possibility to rewrite/filter SDT and EIT tables, fixes #2043 2015-03-25 12:35:12 +01:00
Jaroslav Kysela
d5c5f67bfe psi parser: more code reorganization, add parser/remux helpers for other layers 2015-03-25 12:35:12 +01:00
Jaroslav Kysela
75cad931c4 check all snprintf() callers and modify code to work correctly with the return value using tvh_strlcatf2() macro, fixes #2734 2015-03-24 23:16:22 +01:00
Jaroslav Kysela
cc58797fb6 profile: fix transcode subscription flags, fixes #2735 2015-03-24 22:11:34 +01:00
Jaroslav Kysela
4f2b94f281 http: use stack for the session variables 2015-03-24 12:47:38 +01:00
Jaroslav Kysela
a1fd6adaaf SAT>IP Server: use ths_raw_service instead ths_service 2015-03-24 11:21:46 +01:00
Jaroslav Kysela
b941aedf93 configure: add --disable-libffmpeg_static_x264 to satisfy i386 debian builds 2015-03-24 11:02:05 +01:00
Jaroslav Kysela
27382c275f SAT>IP Server: fix new session id assignment, fix possible null dereference 2015-03-24 08:55:35 +01:00
Jaroslav Kysela
3332f8614b mpegts: fix the mpegts_input_close_pids() - fullmux/tables subscriptions 2015-03-23 21:06:38 +01:00
Jaroslav Kysela
8974fac450 config.h - include unistd to resolve git_t dependency 2015-03-23 20:10:06 +01:00
Jaroslav Kysela
55281a87ac SAT>IP server: parse correctly more auto values 2015-03-23 17:48:09 +01:00
Jaroslav Kysela
6cd5f1b379 Remove MPEGPS/V4L support 2015-03-23 16:39:35 +01:00
Jaroslav Kysela
dddc4249ef SAT>IP Client: skip universal LNB check for the TVHeadend server 2015-03-23 16:21:05 +01:00
Jaroslav Kysela
3f4002d984 http: optimize dump_request(), call it also for RTSP 2015-03-23 15:52:17 +01:00
Jaroslav Kysela
5518397784 http: optimize tcp_get_ip_str() calls for the peer ip addr 2015-03-23 15:47:42 +01:00
Jaroslav Kysela
fc68ad0a74 http: a little cleanup 2015-03-23 15:28:04 +01:00
Jaroslav Kysela
b520cbeb34 psi tables: moved base parsers outside mpegts.h 2015-03-23 15:28:04 +01:00
Jaroslav Kysela
6547bbe2e0 dvb psi reorganize - create parsing library 2015-03-23 15:28:04 +01:00
Jaroslav Kysela
30f5c44d2c SAT>IP Server: fix the pid subscriptions (stack overflow, duplicate pids) 2015-03-23 12:50:05 +01:00
Jaroslav Kysela
1063fbf472 spawn: fix stack overflow 2015-03-23 12:32:09 +01:00
Jaroslav Kysela
a139c2545c SAT>IP server: fix url= for RTSP port != 554 2015-03-23 10:54:10 +01:00
Jaroslav Kysela
47f249fe09 SAT>IP Client: increase pids limit for TVHeadend SAT>IP server 2015-03-23 10:36:50 +01:00
Jaroslav Kysela
02a9b02e1c descrambler: fix the wrong cut on the buffered data 2015-03-23 10:03:20 +01:00
Jaroslav Kysela
c54f9b0ca7 capmt: extend log for subscription 2015-03-23 09:35:57 +01:00
Ben Fennema
0c6cce080a service_mapper: move SUBSCRIPTION_PACKET from weight to flags field of subscription_create_from_service 2015-03-23 00:16:35 +01:00
Jaroslav Kysela
db5cf2adff another coverity fix for the cc checking code, fixes #2724 2015-03-22 21:38:23 +01:00
Jaroslav Kysela
c308e1705e opentv: another coverity scan fix 2015-03-22 21:24:01 +01:00
Jaroslav Kysela
d8e7bafc3b mpegts input: fix the continuity counter check, fixes #2724 2015-03-22 21:23:32 +01:00
Jaroslav Kysela
4db85f0396 revert one mutex code (from coverity fixes) 2015-03-22 21:09:10 +01:00
Jaroslav Kysela
34ed9da0e6 main: fix build using --disable-satip_server 2015-03-22 21:02:19 +01:00
Jaroslav Kysela
3ed12146c9 coverity fixes... 2015-03-22 21:01:10 +01:00
Ben Fennema
e5dc86b3dd epggrab: match off channel major/minor in addition to name 2015-03-22 18:18:23 +01:00
Jaroslav Kysela
0b548808a7 fix uninitialized error in webui.c, fixes #2725 2015-03-22 18:01:09 +01:00
Jaroslav Kysela
023563f602 mpegts input: mini-optimization in ts_sync_count() 2015-03-22 17:54:47 +01:00
Jaroslav Kysela
a7445b1f2d docs: access - add network separator; channels - add streaming URLs, fixes #2697 2015-03-22 17:18:48 +01:00
Jaroslav Kysela
e247500a08 picon: fix the hash calculation for invalid onid/tsid using Enigma2 code, fixes #2700 2015-03-22 17:05:18 +01:00
Jaroslav Kysela
93f3f6fbdd linuxdvb: improve diseqc switch handling, cleanups 2015-03-22 16:40:58 +01:00
Bob Lightfoot
c61be4ed90 On branch Bug2720Fix Changes to be committed: modified: support/version lines 20 and 21 to correct location of rpm/version files <boblfoot@gmail.com> 20150319 2015-03-22 15:52:27 +01:00
Ben Fennema
5e7efab324 epggrab: allow updating channel name/number/icon Allow epg grabber to update channel name, number, and/or icon if "Update Channel name", "Update channel number", and/or "Update channel icon" are set in "EPG Grabber" -> "General Config". 2015-03-22 15:51:25 +01:00
Mariusz Bialonczyk
ac3599bc79 capmt: fix endless loop
When tvh receives less then 4 bytes from the socket for some reason,
it stuck in the endless loop in handle_single() calling:
capmt_msg_size()
capmt_analyze_cmd()
The capmt thread eats 100% CPU and cannot be normally terminated
because the capmt_msg_size() was constantly returning 0.

The commit fixes the problem.
2015-03-22 15:50:05 +01:00
Glenn-1990
7414ebafe4 set modulation 2015-03-22 15:47:39 +01:00
Glenn-1990
72835054ce add HD pids for astra1 2015-03-22 15:47:39 +01:00
Jaroslav Kysela
bb5fe1145d SAT>IP Server: DVBC - fix stream id initialization 2015-03-19 23:01:28 +01:00
Jaroslav Kysela
db52de8151 SAT>IP Client: XML - make manufacturerURL tag optional 2015-03-19 21:29:54 +01:00
Jaroslav Kysela
fbd41d806b linuxdvb: fix build for headers < 5.5 2015-03-19 19:54:20 +01:00
Jaroslav Kysela
35029f1e51 mpegts input: Fix ommited code change for changed MIN_TS_SYN 2015-03-19 16:00:58 +01:00
Jaroslav Kysela
9c8dde32f5 main: finish tcp connections (thus subscriptions) before mpegts done call 2015-03-19 15:42:14 +01:00
Jaroslav Kysela
f72c67b629 linuxdvb: fix detection for multiple frontends (dtv_property was overwritten) 2015-03-19 13:13:56 +01:00
Jaroslav Kysela
953dfa0f3f SAT>IP Server: fix bandwidth parsing 2015-03-19 12:50:07 +01:00
Jaroslav Kysela
196462292e http: show protocol/command in http_error() 2015-03-19 12:20:36 +01:00
Jaroslav Kysela
819fc6766b SAT>IP Server: reorganize code to support VLC RTSP 2015-03-19 12:14:27 +01:00
Jaroslav Kysela
fb7ffa7a2c config: handle chown error 2015-03-18 16:34:09 +01:00
Jaroslav Kysela
a08311dff9 SAT>IP Server: Fix describe (count of tuners) 2015-03-18 13:19:47 +01:00
Jaroslav Kysela
fe8e0b0c16 mpegts input: return mutex lock order in mpegts_input_table_dispatch() 2015-03-18 12:29:15 +01:00
Jaroslav Kysela
8480557f76 SAT>IP Server: remove debug line 2015-03-18 12:00:46 +01:00
Jaroslav Kysela
5c5950b698 mpegts input: fix the assert() crash when PID is out-of-range 2015-03-18 11:48:26 +01:00
Jaroslav Kysela
3dfc1eba78 SAT>IP Server/Client: Add more tuner types (atsc, dvbcb, dvbs, dvbc2) 2015-03-18 11:45:04 +01:00
Jaroslav Kysela
28e8db1dc2 Fix the unsigned issue for gid/uid handling 2015-03-18 09:48:46 +01:00
Jaroslav Kysela
de0d8d379f config: fix the load order for the early config access 2015-03-18 00:06:41 +01:00
Jaroslav Kysela
ecf91c3866 SAT>IP Client: simplify the PID handling using mpegts_apid fcns 2015-03-17 23:43:58 +01:00
Jaroslav Kysela
e1a14dff0e SAT>IP server: add mux handling configuration option 2015-03-17 18:58:02 +01:00
Jaroslav Kysela
e424493113 Add more inputs and outputs to README.md 2015-03-17 16:44:43 +01:00
Jaroslav Kysela
2665e13507 mpegts open service: do not add pid multiple times, handle filtered PIDs correctly 2015-03-17 16:44:27 +01:00
Jaroslav Kysela
fd24aa9339 SAT>IP server: Don't enable SAT>IP server by default, fix some locking issues 2015-03-17 15:51:46 +01:00
Jaroslav Kysela
e16a5ca3d1 subscriptions: Fix crash when subscription is in the remove queue list, but unsubscribed before 2015-03-17 14:08:26 +01:00
Jaroslav Kysela
74adbe8f7d SAT>IP Client: maintain session also in the idle mode 2015-03-17 14:02:53 +01:00
Jaroslav Kysela
e1b2da8e74 descrambler: fixed thinko 2015-03-17 10:46:09 +01:00
Jaroslav Kysela
6601ca39f9 mpegts input: change ts_sync_count() return value 2015-03-17 09:53:22 +01:00
Jaroslav Kysela
a698e088e8 mpegts: optimize the data patch (join MPEG-TS packets for processing) 2015-03-17 09:52:53 +01:00
Jaroslav Kysela
c4adde611f SAT>IP Server: fixed issues for the Elgato SAT>IP Android app 2015-03-13 23:59:31 +01:00
Jaroslav Kysela
ce01edeb8c SAT>IP Server: cosmetic change 2015-03-13 19:45:10 +01:00
Jaroslav Kysela
0138a37aee SAT>IP Server: Finish service descrambling 2015-03-13 19:26:07 +01:00
Jaroslav Kysela
385c4167c0 subscription: fix the assert from the last cleanup 2015-03-13 15:55:30 +01:00
Jaroslav Kysela
2d703fbe42 udp: fix typo 2015-03-13 10:38:37 +01:00
Jaroslav Kysela
19e1ae1451 subscription: cleanup for the subscription type selection 2015-03-13 10:36:31 +01:00
Jaroslav Kysela
0eb8587510 SAT>IP Server: more work on the descrambling 2015-03-13 10:18:52 +01:00
Jaroslav Kysela
c66213c745 SAT>IP server: implement DESCRIBE RTSP command 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
be3749f45c subscriptions: manage ths_mux_link early to avoid wrong memory accesses 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
7cafa5ef70 mpegts: fix mpegts_mux_unsubscribe_by_name() - use right list 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
8b7c67abec SAT>IP Server: fixes, fixes... 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
cd99597a5c subscription: add TABLES subscription type handling, fix some memory leaks 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
ffa0639149 profile: improve prch_sq destroy 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
9dbaa441f3 udp: fix memory leak (peer_host) 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
3c889e15a9 http: fix memory leak in http_arg_get_remove(), optimize http_serve_requests() 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
38b541e420 subscription: add ONESHOT subscription type for mux subs 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
20d521975e mpegts mux: remove the unused mm_start code 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
6753203c65 SAT>IP server: fix the tuner configuration 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
a4c2cc2a0f linuxdvb: fix the signal strenght / dB(m) calculations 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
514adb06dd SAT>IP server: fix the signal strength calculation 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
af7e0a385d mpegts pid: add mpegts_pid_dump(), fix mpegts_pid_init() 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
2dfc2710b7 service/mpegts: add raw service type and raw PID handling 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
2b6dd0f73d mpegts elementary stream: remove pcr decoder - not used 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
1ef7dae510 service: remove unused s_flags field from struct service 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
29308193bd mpegts input: optimize the PID decoding 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
787ba1f1d6 dvbpsi: fix compilation without ENABLE_MPEGTS_DVB 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
9a6d0961e4 SAT>IP Server: improve PID handling (using new fcn set) 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
c522d71ed0 SAT>IP Client: add RTSP port to log messages 2015-03-11 21:41:13 +01:00
Jaroslav Kysela
afede2267f SAT>IP: use HTTP headers instead UPC XML text to pass RTSP port and Sources 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
fd505efe5a SAT>IP Server: do not put version to friendly name (XML) 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
7bb903a397 SAT>IP server: many fixes and additions, first "somewhat working" version
Tested only with another tvheadend as client with the DVB-T adapter.
2015-03-11 21:41:12 +01:00
Jaroslav Kysela
ca0a703779 http server: RTSP Session header cleanups, add more RTSP status codes 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
d90bc3ebd3 tcp: fix tcp_server_bound() function, null addr check 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
cfbe927b43 UDP: rearrange udp_connect/udp_sendinit 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
cbf1b15d00 RTSP client: handle also different RTSP port than 554 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
1c9b527011 SAT>IP: handle SRCS pass (for DVB-S) in server and client 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
a87aae0aaf SAT>IP Client: Add RTSP port detection based on the UPC string (TVheadend specific server) 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
da6eb2c4b3 SAT>IP server: handle streams correctly, add more RTSP headers 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
78e4ddf86e SAT>IP server: more work, add subscriptions and add RTP and RTCP threads 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
83f94f6f0c udp: add tx buffer size setup for udp_bind(_double) 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
56ac461d06 dvb: export dvb_network_find_mux() for the SAT>IP server 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
be1cc5c97c dvb: create mux - rearrange parameters for SAT>IP server 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
d563dc9127 udp: add multisend code (sendmmsg) 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
fd1bc1c5f7 SAT>IP server: initial RTSP code 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
1c3f608194 http: export http_serve_requests, add hc_process callback to connection 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
b306c7c9d5 http: allow to specify different paths 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
3c95a84d81 http: reorganize http_serve 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
ea60066a3f SAT>IP server: initial code 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
3e33da55c9 upnp: add debug fcn to dump output packets 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
53984a38a4 tcp: add IP_PORT_SET() macro 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
af698f0c19 config: add universal fcns to access to global variables 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
2904a96ea1 UPnP: add delay handling to write queue 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
97f52f6d20 http: export http_server variable 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
c19217e6e3 tcp: add helpers to determine default and used IP address for SAT>IP 2015-03-11 21:41:12 +01:00
Jaroslav Kysela
434fc5de6a main: move the http server registration to the end of the initialization phase 2015-03-11 21:41:12 +01:00
stbenz
96898a66e9 transcoding: fix compile error when using libav, fixes #2708
AVCodecContext.framerate and av_make_q() are only available since ffmpeg 2.5 and not available in libav
2015-03-11 21:40:12 +01:00
dero
bd06d3c79a - duplicate handling fixes according to pull comments 2015-03-09 16:18:54 +01:00
dero
ef799f9c24 - better duplicate handling (ui fix) 2015-03-09 16:18:35 +01:00
dero
2b22bc75a1 - better duplicate handling - better subtitle handling 2015-03-09 16:18:35 +01:00
dero
ce78f14570 - set subtitle from DVB OTA EPG 2015-03-09 16:18:35 +01:00
Kai Sommerfeld
cd4da6aa36 No calls to stat() in dvr_inotify_add(). This may significantly slowdown tvh startup. 2015-03-09 16:18:14 +01:00
Glenn-1990
81ae6bf0cd add streaming error for active recordings 2015-03-09 16:17:23 +01:00
stbenz
a69ee153d0 transcoding: fix encoder time_base again 2015-03-09 16:16:37 +01:00
Glenn-1990
e797e46129 update with HD pids 2015-03-09 16:15:37 +01:00
Glenn-1990
6acd4b60a3 auto create mux 2015-03-09 16:15:37 +01:00
Glenn-1990
e91074c812 set mux satpos from network satpos if set 2015-03-09 16:15:37 +01:00
Jaroslav Kysela
50016a15f2 dvb_psi: handle private data also without ENABLE_MPEGTS_DVB 2015-02-24 15:28:36 +01:00
Jaroslav Kysela
1d8b5411df cwc: mutex order cleanups, improve ECM_RESET 2015-02-21 23:08:02 +01:00
Jaroslav Kysela
19422a4d29 cwc: small optimization - prefcapid handling in cwc_table_input 2015-02-21 21:46:23 +01:00
Jaroslav Kysela
ab258685f4 cwc: reset the preferred CA PID when key is late... 2015-02-21 21:32:29 +01:00
Jaroslav Kysela
a2491ffcd7 descrambler: prefcapid_lock - add constants to increase source readability 2015-02-21 21:16:36 +01:00
Jaroslav Kysela
a50f74c9aa HTSP server: add stream and data errors to DVR entry, fixes #2515 2015-02-18 19:55:17 +01:00
Jaroslav Kysela
ba7ec12f91 HTSP server: add errors field to packet statistics 2015-02-18 19:50:27 +01:00
Jaroslav Kysela
2ba84e8a82 picons: do not use abort for not-critical check 2015-02-18 19:23:18 +01:00
Jaroslav Kysela
1bd1564a8c picons: fix filaname calculation for West positions, fixes #2685 2015-02-18 13:09:10 +01:00
stbenz
051412d7e0 transcoding: use input time base for output 2015-02-17 21:27:25 +01:00
Christian Lohmaier
2f830922f5 use iso format for date in default timerec title
as local date format often conflicts with sorting/playback order
also update documentation with pointers to escape-code documentation
2015-02-17 21:27:25 +01:00
Jaroslav Kysela
6327ca671e dvb support: fix dvb_mux_conf_str_dvbs - satpos 2015-02-17 21:15:15 +01:00
Jaroslav Kysela
f2cc2bc866 fastscan: add services to all muxes/networks, fixes #2674 2015-02-17 21:00:38 +01:00
Jaroslav Kysela
b826a7785e dvb: fix ENABLE_MPEGTS_DVB for dvb/orbitalpos/list, fixes #2683 2015-02-17 11:24:32 +01:00
Jaroslav Kysela
496e4b1f00 dvb_psi: improve check for dead muxes, fixes #2682 2015-02-16 23:59:32 +01:00
Jaroslav Kysela
51e9aec9a0 mpegts network: mux discovery - add check to avoid NULL dereference, fixes #2678, fixes #2674 2015-02-16 20:26:47 +01:00
Jaroslav Kysela
379672a6fb mpegts: linked tuner - do not crash when the linked tuner cannot be started, fixes #2678 2015-02-15 20:19:36 +01:00
Jaroslav Kysela
54533b30fc dvb mux: fix the frequency display name, add set helpers 2015-02-13 23:03:44 +01:00
Jaroslav Kysela
d386ed020a udp: fix compilation for kernels/libc without recvmmsg 2015-02-12 09:05:33 +01:00
Jaroslav Kysela
fafec1e053 parsers: AAC - fix the thinko in the previous patch 2015-02-11 21:05:00 +01:00
Jaroslav Kysela
538e7acb56 parsers: AAC - better handle SBR extension 2015-02-11 20:59:34 +01:00
Jaroslav Kysela
f6537ab39f cwc: little optimization in cwc_table_input 2015-02-11 15:03:43 +01:00
Jaroslav Kysela
89a5ca4914 dvb: mux discovery - add forced muxes also to another DVB-S networks, fixes #2674 2015-02-11 14:25:44 +01:00
Jaroslav Kysela
cc1fbb0202 parsers: improve AAC/LATM parser 2015-02-11 13:41:24 +01:00
Jaroslav Kysela
cd1f081909 transcode: fix wrong memory access introduced by previous patch 2015-02-10 20:43:52 +01:00
Jaroslav Kysela
c475b2531b transcode: handle packets with the NULL payload 2015-02-10 18:24:08 +01:00
Jaroslav Kysela
4cfbb2e024 WEBUI: dvr - add DVR Configuration to finished and failed recordings, too 2015-02-10 10:52:52 +01:00
Jaroslav Kysela
77d947fe57 channel: icons - do not generate auto icon URL with {name-not-set}, fixes #2661 2015-02-10 09:41:28 +01:00
Jaroslav Kysela
4a9dfec70f linuxdvb: fix compilation for dvb < 5.0 2015-02-09 22:17:15 +01:00
Jaroslav Kysela
f578c96949 scanfile: add some loading stats and warning when predefined muxes are empty 2015-02-09 22:05:23 +01:00
Jaroslav Kysela
a6ac407dbf mpegts dvb: fix the DVB-S mux initialization - wrong position value 2015-02-08 20:32:03 +01:00
Jaroslav Kysela
3396fe7a4b fix typo in comment 2015-02-08 20:22:20 +01:00
Jaroslav Kysela
2ebbd5f42a channel: handle better ch_enable in EPG and DVR, fixes #2668 2015-02-08 15:17:50 +01:00
Jaroslav Kysela
9fc758f946 DVR: improve autorec doc for the directory field, fixes #2667 2015-02-08 15:01:26 +01:00
Jaroslav Kysela
9ce2fa7885 add data/conf/satellites file 2015-02-08 11:29:20 +01:00
Jaroslav Kysela
6bb5e0f7f4 bouquet: save bouquet on name change 2015-02-05 17:56:42 +01:00
Glenn-1990
ac34185e67 dvb_psi: fastscan fix - force to add fastscan muxes from NIT 2015-02-05 17:52:46 +01:00
Jaroslav Kysela
36283c6598 dvb: remove orbital_dir, use negative orbital_pos value to distinguish East and West 2015-02-05 17:33:56 +01:00
Jaroslav Kysela
53f8b5068e mpegts dvb: add satellite position field to network, add proper interaction with predefined muxes 2015-02-05 16:57:50 +01:00
Jaroslav Kysela
31cd397f3c timeshift: reader - fix the possible wrong ctrl variable usage 2015-02-05 12:44:57 +01:00
Jaroslav Kysela
7ab46e3d5d dvb mux: change field names for multiple stream config parameters 2015-02-04 17:57:02 +01:00
Ben Kibbey
d09e0c9fa8 ACL: add "All DVR (rw)" to delete a DVR entry.
This is to let a user remove a DVR entry created by another user rather
than only being able to access it.
2015-02-04 17:53:09 +01:00
Glenn-1990
2726332724 update fastscan conf with astra3 2015-02-04 17:53:09 +01:00
Jaroslav Kysela
399aa99e89 dvb: add dvb_mux_conf_init() helper to move mux_conf initialization to one place 2015-02-04 17:41:37 +01:00
Jaroslav Kysela
aac2d0774f SAT>IP: Do extra shutdown only once 2015-02-04 12:45:19 +01:00
Mariusz Bialonczyk
2796c69750 Fix file permissions 2015-02-03 19:26:57 +01:00
Mariusz Bialonczyk
4050f98ef1 capmt: net proto: send stop descrambling command instead of closing socket
This should also improve zap times a little bit.
2015-02-03 19:20:06 +01:00
Jaroslav Kysela
83d05d49d4 fastscan: Fix typo for TV Vlaanderen 2015-02-03 15:31:11 +01:00
Jaroslav Kysela
1fca590172 fastscan: add discovered muxes at first unconditionally - it should be _FAST_ 2015-02-03 15:30:34 +01:00
Jaroslav Kysela
7a2560e642 matroska muxer: update total duration using only audio/video tracks 2015-02-02 21:26:07 +01:00
Jaroslav Kysela
78b5fb6dce descrambler: cleanups for EMM handling 2015-02-02 20:21:14 +01:00
Jaroslav Kysela
9a265d503d SAT>IP: add pls parameter for the DVB-T2 setup message 2015-02-02 15:14:12 +01:00
Jaroslav Kysela
18c9d1dcc2 dvb: add DVB_NO_STREAM_ID_FILTER define 2015-02-02 15:03:20 +01:00
CrazyCat
e92996132b Fixed DVB-S2 multistream tuning. 2015-02-02 14:53:37 +01:00
CrazyCat
412df6d098 DVB-S2/T2 multistream support. 2015-02-02 14:53:37 +01:00
Glenn-1990
0bc1987d1e timerec entry not saved with htsp 2015-02-02 14:38:45 +01:00
Jaroslav Kysela
d2acb64025 htsp server: do not queue packets with NULL payload 2015-02-02 11:50:24 +01:00
Jaroslav Kysela
facee6f9e5 DVR WEBUI: add and update filesize in upcoming/current recordings 2015-02-01 17:45:07 +01:00
Jaroslav Kysela
4b05735b87 dvr: implement file size change notifications 2015-02-01 17:36:26 +01:00
Jaroslav Kysela
a650b2ea79 cwc: add CAID to reader nice name 2015-02-01 12:07:10 +01:00
Jaroslav Kysela
3a92f1d2f8 descrambler: reset ecm states for all readers when key is late 2015-02-01 12:03:19 +01:00
Jaroslav Kysela
4835c6c53e tsfix: postpone the reference time decision until packets from all streams are collected 2015-01-31 20:14:44 +01:00
Jaroslav Kysela
a39bb434f4 mpegts parsers: another error forwarding fixes 2015-01-31 18:24:35 +01:00
Jaroslav Kysela
ce2fc637b6 muxers: ignore the NULL payload 2015-01-31 17:39:16 +01:00
Jaroslav Kysela
362fb68002 mpegts parsers: cleanup the error forwarding 2015-01-31 17:35:35 +01:00
Jaroslav Kysela
bdfc4d779f tsdemux: move the error handling to the parsers.c 2015-01-31 16:55:40 +01:00
Jaroslav Kysela
e2f9483dbe dvr: use direct error flag handling 2015-01-31 16:53:07 +01:00
Jaroslav Kysela
94880c5b3e opentv: fix bug introduced with recent changes - missing EPG for some channels 2015-01-31 16:22:23 +01:00
Jaroslav Kysela
30d6fa5300 DVR: Add data errors field 2015-01-30 22:53:34 +01:00
Jaroslav Kysela
adcd748fc0 tsdemux: pass TS continuity errors to subscriptions 2015-01-30 21:49:37 +01:00
beralt
5eca4d7be8 tvhdhomerun: add modulation and symbolrate to channel tuning 2015-01-28 21:00:11 +01:00
Jaroslav Kysela
e6d124d110 mpegts mux: fix the copy-and-paste type (default value for AC-3 Detection), fixes #2647 2015-01-28 20:59:38 +01:00
Jaroslav Kysela
927ebf2f0f dvb psi: add another AC-3 detection hack, fixes #2647 2015-01-28 14:56:24 +01:00
Jaroslav Kysela
ebdd85f5ba parser: add support for ADTS data to SCT_AAC parser, fixes #2645 2015-01-27 21:06:21 +01:00
Jaroslav Kysela
e95e23ab2f doc: tvadapters - little fix in the Full DiseqC description 2015-01-27 19:58:11 +01:00
Jaroslav Kysela
2f13317fa0 tvhcsa: fix compilation when TVHCSA is not enabled, fixes #2642 2015-01-27 19:55:37 +01:00
Jaroslav Kysela
5a063fe71d linuxdvb: make Full DiseqC as default 2015-01-27 19:50:13 +01:00
Jaroslav Kysela
98783a5c9e SAT>IP: add parsing for RTCP status string ver=1.2 (DVB-C), fixes #2646 2015-01-27 16:06:30 +01:00
Jaroslav Kysela
c664af6f60 mpegts mux dvb: fix the load sequence and possible memory leak 2015-01-27 12:22:42 +01:00
Jaroslav Kysela
3c0a279825 xml parser: skip UTF-8 BOM header, fixes #2644 2015-01-27 11:48:03 +01:00
Jaroslav Kysela
b98e688f57 mpegts mux subscription: fix the live check (data timeout) 2015-01-26 20:58:39 +01:00
Jaroslav Kysela
0515caa92f tvhcsa: fix the read after allocated memory valgrind error 2015-01-26 20:13:01 +01:00
Jaroslav Kysela
62e2971020 opentv: fix complation with --disable-trace 2015-01-26 19:56:16 +01:00
Jaroslav Kysela
ee66809d18 mpegts input: linked inputs - optimization, trace fixes 2015-01-26 19:55:45 +01:00
Jaroslav Kysela
879b842ea1 esfilter: rename ONCE to ONE_TIME, improve doc 2015-01-26 15:00:04 +01:00
Jaroslav Kysela
f39ddd1f05 otamux: do not queue not enabled muxes 2015-01-25 20:32:35 +01:00
Jaroslav Kysela
22554c723d mpegts input: move linked input to advanced in WEB UI 2015-01-25 19:12:43 +01:00
Jaroslav Kysela
cde875ecb2 SAT>IP: corner case fixes, fixes #2638
- fixed wait before next tune (flush the incoming rtsp replies)
- fixed mutex global_lock deadlock (satip_frontend_tuning_error)
2015-01-25 18:25:30 +01:00
Jaroslav Kysela
c3ec4b114d Makefile: add -ldl for static ffmpeg build to fix arm dependencies 2015-01-24 22:16:34 +01:00
Jaroslav Kysela
2b8ad616a4 profile: add matroska/mpegts muxers from libav 2015-01-24 21:55:01 +01:00
Jaroslav Kysela
1a9319571b filebundle: try to fix build error for older zlib.. #3 2015-01-24 20:58:57 +01:00
Jaroslav Kysela
a7d26442b1 filebundle: try to fix build error for older zlib.. #2 2015-01-24 20:47:37 +01:00
Jaroslav Kysela
078dff5b98 filebundle: try to fix build error for older zlib.. 2015-01-24 20:30:28 +01:00
Jaroslav Kysela
d83aefe0fe descrambler: fix mm_descrambler_lock deadlock in the CAT callback, fixes #2636 2015-01-24 20:22:29 +01:00
Jaroslav Kysela
44a1df1fac mpegts network scan: remove from queue disabled muxes 2015-01-24 20:07:21 +01:00
Jaroslav Kysela
28337dcbbe SAT>IP: Add frequency filter for DVB-S/S2 2015-01-24 20:06:51 +01:00
Mariusz Bialonczyk
6a27c4ccab Fix 519ec96 (capmt: network mode: fix a crash when oscam is restarted)
When there was a further subscribing requests, the queued greeting could
be queued not as the first position in the queue, leading to problems
when subscribing the channel.
Now requesting to flush the queue in capmt_notify_server() to be sure
that the greeting go out to OSCam as a first command.
2015-01-24 14:56:34 +01:00
Jaroslav Kysela
3feccf34b5 filebundle: do not use extra input buffer 2015-01-23 21:30:23 +01:00
Lauri Myllari
9508a68aa1 hdhomerun: autodetect ATSC network type 2015-01-23 18:19:19 +01:00
Jaroslav Kysela
b1883ed51e opentv: add bouquet filter to avoid heavy LCN switches 2015-01-23 18:15:02 +01:00
Jaroslav Kysela
bca6214866 WEBUI: distinguish the disabled channels/tags by {}, fixes #2635 2015-01-23 14:30:28 +01:00
Adam Sutton
dcdef424fc linuxdvb: appears some tuners are not supporting FE_GET_EVENT
This results in a terminal lockup as the ioctl() will loop indefinitely,
testing for != -1 was always a bad idea!
2015-01-22 15:12:25 +00:00
Adam Sutton
f88f2f33ff linuxdvb: fix possible bug with multi-frontend adapters 2015-01-22 15:12:09 +00:00
Mariusz Bialonczyk
519ec9677e capmt: network mode: fix a crash when oscam is restarted
Use capmt_queue_msg() after connecting instead of direct write to socket.
Fixes a crash when the channel is played and oscam is restarted.
2015-01-22 11:22:06 +01:00
Jaroslav Kysela
3077ffa4d3 epggrab: ota - add trigger button to webui 2015-01-21 20:56:22 +01:00
Jaroslav Kysela
1377fad8a0 linuxdvb: add Tune Before DiseqC option, fixes #2629 2015-01-21 17:21:29 +01:00
Jaroslav Kysela
8174eb953d mux stop: pass the stop reason for OTA epggrab to avoid re-tuning on muxes with no data 2015-01-21 12:02:50 +01:00
Jaroslav Kysela
abea248bab otamux: do not requeue muxes with failed scan status 2015-01-20 20:40:11 +01:00
Jaroslav Kysela
8018b54d6b doc: some text for satellite config 2015-01-20 17:35:51 +01:00
Jaroslav Kysela
b28f37bf8a WEBUI: Fix the LovCombo regex handling - fixes #2598 2015-01-20 15:21:14 +01:00
Jaroslav Kysela
e565e7cbf0 quick fix for the previous patch 2015-01-20 14:50:18 +01:00
Jaroslav Kysela
3ddf768edf WEBUI DVR: Fix the additional rows in the column header menus, fixes #2571 2015-01-20 14:40:12 +01:00
Jaroslav Kysela
fc7d3d8484 WEBUI DVR upcoming: use channel field instead channelname, fixes #2609 2015-01-20 13:38:33 +01:00
Jaroslav Kysela
a8a175136d channels: do not allow empty string channel names, fixes #2628 2015-01-20 11:33:12 +01:00
Jaroslav Kysela
aca5c6b976 timeshift: add 'use only RAM' option 2015-01-19 21:31:18 +01:00
Jaroslav Kysela
bc9874cc26 timeshift: implement timeshift to RAM, fixes #2626 2015-01-19 21:08:15 +01:00
Jaroslav Kysela
ca0021e3b6 htsp server: cleanup the conditions in update fcns 2015-01-19 19:07:47 +01:00
Jaroslav Kysela
b3db352ecf linuxdvb: fix the retune call 2015-01-19 18:20:10 +01:00
Jaroslav Kysela
9801ef1a1a mpegts input: fix the linked input unset action 2015-01-18 22:01:44 +01:00
Jaroslav Kysela
a2c01ed520 doc: add fulltext checkbox documentation for epg and autorec 2015-01-18 21:46:04 +01:00
Jaroslav Kysela
ba7d8fa23a service: add per-service priority modificator, fixes #2249 2015-01-18 21:35:00 +01:00
Jaroslav Kysela
c92ccca440 DVR autorec: implement fulltext (title, subtitle, summary, description) filter, fixes #2170 2015-01-18 21:19:26 +01:00
Jaroslav Kysela
941dbcbd74 fix condition for previous patch 2015-01-18 20:23:48 +01:00
Jaroslav Kysela
f8b3bf40ec fix typo for previous patch 2015-01-18 20:22:15 +01:00
Jaroslav Kysela
ac92591ba1 mpegts service: Add 'Service IDs as Channel Numbers' network option 2015-01-18 20:21:33 +01:00
Jaroslav Kysela
7006b9fd88 DVR: fix the verify functions for anonymous users, fixes #2623 2015-01-18 11:25:26 +01:00
Jaroslav Kysela
60f959ba59 EPG: implement fulltext search (title, subtitle, summary, description) 2015-01-17 22:43:14 +01:00
Jaroslav Kysela
3dba7bf3ac config: backup - handle correctly spawnv return code 2015-01-17 22:26:51 +01:00
Jaroslav Kysela
dafea1a693 linuxdvb: make the status period (read times) configurable 2015-01-17 19:20:49 +01:00
Jaroslav Kysela
f157e1818d linuxdvb: allow to modify the input buffer size 2015-01-17 19:20:43 +01:00
Jaroslav Kysela
4df20c2b6a linuxdvb: add nodata and signal retune code
Sometimes, the linux drivers requires "re-tune" requests to stabilize
or re-trigger reception which may hang. The two checks were added:

nodata - when no data are available for a little time period, issue re-tune
signal - when signal is lost for a little time period, issue re-tune
2015-01-17 18:55:08 +01:00
Jaroslav Kysela
93aac31870 doc: improve tvadapters text (add missing linuxdvb rows, re-organize) 2015-01-17 18:35:46 +01:00
Jaroslav Kysela
273835ff46 mpegts: add linked tuner feature
The DvbSky S952 drivers have numerous tuning bugs and one of it is
that if two tuners are not used together, streaming from one tuner
can "hang".

This change implements a workaround which makes the second (linked)
tuner alive (tuning is joined with these two linked tuners).
2015-01-17 18:12:18 +01:00
Jaroslav Kysela
37eb9d2cfd remove debug line in api_mpegts.c 2015-01-16 18:53:46 +01:00
Jaroslav Kysela
ed513c0d6f DVR: Add per-user filters for all DVR entries (including timerec and autorec), fixes #2533 2015-01-16 17:46:43 +01:00
Jaroslav Kysela
a65f6179e5 dvr autorec: handle filter rules also for series autorecs 2015-01-16 10:36:08 +01:00
J. Dierkse
fc57f52aeb Update timeshift_filemgr.c
It seems that in currently the timeshift_unlimited_period boolean is completely ignored when it comes to taking action on removing files or reporting a full buffer. This small patch should fix that, so that when the timeshift_unlimited_period boolean is set to true, tvheadend no longer evaluates the period of the current timeshift.
2015-01-15 14:18:03 +01:00
Jaroslav Kysela
2820a7cf41 doc: add more text to Skip initial Scan - networks 2015-01-15 14:14:52 +01:00
Jaroslav Kysela
65fd96395a prop: skip completely the PT_NONE variables, fixes #2601 2015-01-14 20:58:23 +01:00
Jaroslav Kysela
f48e390437 DVB network grid: add 'Force Scan' functionality 2015-01-14 15:49:53 +01:00
Jaroslav Kysela
d9f4230881 dvb psi: NIT/SDT/VCT callback add/handle services only for active muxes 2015-01-14 13:44:58 +01:00
Jaroslav Kysela
30cc0a73bd dvb psi: change the NIT parser to handle correctly new mux descriptors, fixes #2605 2015-01-14 13:16:46 +01:00
Jaroslav Kysela
eb5cfaa6e9 dvb network: Add EIT local time option, fixes #2617 2015-01-14 09:47:12 +01:00
Jaroslav Kysela
04fcdce37d SAT>IP: remove debug printf 2015-01-13 20:33:49 +01:00
Jaroslav Kysela
6f6f20eac0 packet: return zero for pktbuf_len() when pktbuf ptr is NULL 2015-01-13 20:22:06 +01:00
Carlo Landmeter
4949382c87 lock: posix needs fcntl.h 2015-01-13 18:52:03 +01:00
Carlo Landmeter
0d5194c17c queue: check if list_move is already defined 2015-01-13 18:52:03 +01:00
Joakim Gissberg
b497168ab4 transcoding: Code cosmetics 2015-01-13 18:51:33 +01:00
Joakim Gissberg
b2132d72c2 transcoding: Remove deprecated bandwith property. Code cosmetics 2015-01-13 18:51:33 +01:00
Joakim Gissberg
baec61b674 transcoding: Fix mpeg2 video bitrate limiter/quantizer. Tweaking of misc default settings 2015-01-13 18:51:33 +01:00
Joakim Gissberg
7b01d9c1f0 Transcoding: Add audio bitrate limiter, video quantizer, tweaking of default encoder settings 2015-01-13 18:51:33 +01:00
Joakim Gissberg
ad8b61ba5a transcoding: Remove redundant double default definition 2015-01-13 18:51:33 +01:00
Joakim Gissberg
619654f1f4 transcoding: Add video bitrate limiter for h.264/VP8/MPEG2 2015-01-13 18:51:33 +01:00
Jaroslav Kysela
ef9556efff SAT>IP: Remove double teardown workaround (wasn't working) 2015-01-13 16:38:20 +01:00
Jaroslav Kysela
d9b781771d SAT>IP: implement extra shutdown (teardown) workaround for IDL4K firmware (gssbox) 2015-01-13 16:32:25 +01:00
Jaroslav Kysela
0818adf576 httpc: add more traces 2015-01-13 16:31:48 +01:00
Jaroslav Kysela
1723e733f5 AAC ADTS header - fix comments for AOT 2015-01-13 16:07:42 +01:00
Jaroslav Kysela
e9eadac057 Another ADTS header fix in transcoding (length) 2015-01-13 10:22:16 +01:00
Jaroslav Kysela
26b18fe141 Another ADTS header fix (AOT) 2015-01-13 10:18:27 +01:00
Jaroslav Kysela
33f72a3483 dvb psi: fix build without DVB 2015-01-13 10:06:49 +01:00
Jaroslav Kysela
940ab268e5 AAC ADTS header fixes (reported by Simon Bond) 2015-01-12 22:05:36 +01:00
Jaroslav Kysela
45eec90f11 transcode: fix the video timeout in globalheaders, including AAC ADTS header fix 2015-01-12 22:00:44 +01:00
Jaroslav Kysela
0a7ce2caf5 subscription: fix the mux data timeout 2015-01-12 14:37:13 +01:00
Joakim Gissberg
98b5304be6 hdhomerun: Upgrade libhdhomerun 2015-01-12 13:50:49 +01:00
Damian Gołda
0196790734 Issue 1625 - Option for Windows-compatible filenames - trim trailing spaces and dots, fixes #1625 2015-01-12 09:54:20 +01:00
Damian Gołda
028826b7df Issue 1625 - Option for Windows-compatible filenames * trim trailing spaces and dots 2015-01-12 09:51:23 +01:00
Jaroslav Kysela
76558d286a httpc: add hc_id to distinguish connections 2015-01-11 22:23:22 +01:00
Jaroslav Kysela
bfa463df8b dvb_psi: prefer HD simulcast LCNs (if present), fixes #2610 2015-01-11 21:52:51 +01:00
Jaroslav Kysela
eed4d8cdc5 dvb_psi: nit,sdt,vct callbacks - operate on all muxes with same tsid, fixes #2605 2015-01-11 21:43:22 +01:00
Sam Stenvall
f5c8e4ce86 accept "%c" placeholder for channel icons to use unmangled channel
name
2015-01-09 13:34:03 +01:00
Sam Stenvall
396700797b [utils] add url_encode() function 2015-01-09 13:34:03 +01:00
Jaroslav Kysela
f1631752c4 SAT>IP: Do not use SHUTDOWN for immediate tune requests 2015-01-09 12:39:11 +01:00
Damian Gołda
022e8d45a5 Issue 1625 - Option for Windows-compatible filenames 2015-01-08 22:22:03 +01:00
Damian Gołda
f8de301910 Issue 1625 - Option for remove unsafe characters from filename while keeping Umlaute and other national characters 2015-01-08 22:22:03 +01:00
Jaroslav Kysela
6f18074477 mpegts mux: improve log for missing in PAT/SDT 2015-01-08 22:14:17 +01:00
Sam Stenvall
c51b16224f [dvr] add "directory" option to auto and time recorder entries to
override directory settings for matching recordings.

Useful for e.g. recording multiple different shows into a single
directory (e.g. news).

Fixes #2123, fixes #2448
2015-01-07 15:56:15 +01:00
Mariusz Bialonczyk
09bd4c4d0e help: add missing information about new OSCam mode 2015-01-07 15:56:15 +01:00
Joakim Gissberg
ba36e4584d Upgrade to FFmpeg 2.5.2 2015-01-07 15:56:15 +01:00
Jaroslav Kysela
0872d71c12 dvb_psi: fix the service re-enable procedure 2015-01-07 15:54:14 +01:00
Jaroslav Kysela
900b20e280 SAT>IP: Trace RTP discontinuity 2015-01-07 15:53:49 +01:00
Jaroslav Kysela
1afee1663a service check: text cleanups, add help text, re-enable service when they're broadcasted again, fixes #2595 2015-01-06 10:33:19 +01:00
Jaroslav Kysela
1027f294d0 transcode: fix AAC encoding (wrong ADTS header - typo in source) 2015-01-05 15:36:44 +01:00
Jaroslav Kysela
6f8b1c5ad6 passthrough muxer: fix pmt packet overflow, fixes #2593 2015-01-05 08:59:13 +01:00
Mariusz Bialonczyk
b85ec59d46 capmt: add support for OSCam network protocol v1 2015-01-04 20:23:59 +01:00
Piotr Kuchciak
93e533ffad Help: clear cosmetic 2015-01-04 20:23:59 +01:00
Piotr Kuchciak
60d41123b5 HELP: Cosmetic update help for Config>General 2015-01-04 20:23:58 +01:00
Piotr Kuchciak
6605d5ec02 HELP: update description. 2015-01-04 16:11:09 +01:00
Piotr Kuchciak
c43c607d5a HELP: add file to button 2015-01-04 16:11:09 +01:00
Piotr Kuchciak
9697eab66c HELP: Add help text for stream profiles
Please add this to button help.
2015-01-04 16:11:09 +01:00
Piotr Kuchciak
a7704655f8 Help: Remove Transcoding from General 2015-01-04 16:11:09 +01:00
Piotr Kuchciak
3af9d86ef8 Help: Update Config>General
@ProfYaffle please update description :)
2015-01-04 16:11:09 +01:00
Glenn-1990
24d24a8107 htsp: add enabled flag to autorec and timerec 2015-01-04 15:59:06 +01:00
Glenn-1990
306f2520a6 htsp: bump version 2015-01-04 15:59:06 +01:00
Glenn-1990
6c8b7d6e48 htsp: add some missing values to dvr/timerec/autorec 2015-01-04 15:59:06 +01:00
Glenn-1990
75ffd0cc5a timerec: fix crash if stop and start times are equal 2015-01-04 15:59:06 +01:00
Glenn-1990
dbd3a1237e add timerec support to htsp server 2015-01-04 15:59:06 +01:00
Jaroslav Kysela
b305d98315 dvb psi: improve the sdt_callback mux selection logic 2015-01-04 15:57:52 +01:00
Jaroslav Kysela
294afe51b1 dvb psi: add Freeview DVB-T private dtag to parse LCN (0x83), fixes #2583 2015-01-04 12:20:41 +01:00
Jaroslav Kysela
c051d0bda5 global headers: improve again the timeout condition 2015-01-03 22:47:36 +01:00
Jaroslav Kysela
86f10581d5 SAT>IP: Fix the possible pointer dereference 2015-01-03 22:06:36 +01:00
Jaroslav Kysela
a890c6bf4b tsfix: cosmetic log issue (new line) 2015-01-03 22:06:19 +01:00
Jaroslav Kysela
c4089cc7a3 dvb_psi: fix sdt parser (when tableid == 0x46 and mux is available), fixes #2586 2015-01-03 21:30:57 +01:00
Jaroslav Kysela
fdbc68abf2 tsfix: add backlog to not drop too many packets (faster start) 2015-01-03 20:30:29 +01:00
Jaroslav Kysela
4dbc7d94ac global headers: analyze queue delays for all streams separately, fixes #2573 2015-01-03 18:01:08 +01:00
Jaroslav Kysela
cb5aed875d mpegts input: fix the wrong variable access 2015-01-02 21:30:02 +01:00
Jaroslav Kysela
4dcdbef28e mpegts: add more logging (mux start, unsubscribe) 2015-01-02 21:18:13 +01:00
Jaroslav Kysela
2635a3d1bf mpegts mux: add more traces to mpegts_mux_start 2015-01-01 19:46:48 +01:00
Jaroslav Kysela
1f62c46d72 mpegts scan: improve dead service detection (add SDT checks) 2015-01-01 16:12:23 +01:00
Jaroslav Kysela
9aa0600b81 udp: implement recvmmsg emulation for older linux kernels 2015-01-01 14:38:53 +01:00
Jaroslav Kysela
2dcf53f71e globalheaders: gh_queue_delay(), fixes #2573
- compute dts difference only for A/V packets
- handle dts wrap
2015-01-01 13:29:30 +01:00
Jaroslav Kysela
c1ed929c31 DVR: add EPG update window doc, fixes #2576 2014-12-30 20:22:06 +01:00
Jaroslav Kysela
cde79eb75f dvb network scan: don't subscribe to already active mux, move Idle Scan to hidden (grid), fixes #2568 2014-12-28 16:16:41 +01:00
Jaroslav Kysela
75fb7a929d DVR autorec: use 'Start After' and 'Start Before' for the start time window 2014-12-26 16:09:02 +01:00
Jaroslav Kysela
e2bd233a4f DVR autorec: change start_window meaning to start up to 2014-12-25 21:43:49 +01:00
ProfYaffle
56d8c8fc2a WebUI: Increase CSS spacing on day of week in EPG/DVR info dialogs 2014-12-25 17:56:15 +01:00
Jaroslav Kysela
0324cf8c8b tsfix: use time offset also for audio streams, sct_type optimization 2014-12-25 17:42:01 +01:00
Jaroslav Kysela
03b88cefc0 DVR autorec: more start,start_window type cleanups 2014-12-25 10:07:57 +01:00
Jaroslav Kysela
e07ee906cc htsp server: correct approxTime, start, startWindow type to handle -1 2014-12-25 10:05:00 +01:00
Jaroslav Kysela
159efe7411 dvr autorec: time window is in minutes, not seconds 2014-12-25 10:00:36 +01:00
Jaroslav Kysela
74090c626a globalheaders: don't disable SCT_TELETEXT 2014-12-24 15:23:07 +01:00
Jaroslav Kysela
b47a14bd8b HTSP server: handle correctly ssc_disabled flag in streaming start structure 2014-12-24 15:22:33 +01:00
Daniel Scheller
74c563ffdf dvb-psi: Fix LCN for UMKBW germany DVB-C 2014-12-21 20:57:29 +01:00
Jaroslav Kysela
8df348fbc3 WEBUI JS DVR: Correct the start column width 2014-12-21 20:53:24 +01:00
ProfYaffle
5dade4fd37 WebUI: Add CSS elements for day of week in EPG and DVR info dialogs 2014-12-21 16:19:18 +00:00
ProfYaffle
31b47ffe00 WebUI: Add error checking to information dialog in Recording grid 2014-12-21 13:08:17 +00:00
ProfYaffle
0748af88cf WebUI: Add day of week to information dialogs in EPG and Recording grid 2014-12-21 13:07:41 +00:00
ProfYaffle
d5ce076150 WebUI: Restore weekday name to Upcoming/Finished/Failed Recordings grids 2014-12-21 12:06:25 +00:00
Jaroslav Kysela
9a5b4db242 mpegts network: move Idle Scan to advanced config 2014-12-20 22:31:17 +01:00
Jaroslav Kysela
c040ab0542 linuxdvb satconf: voltage settings cleanups, fixes #2566 2014-12-20 18:22:34 +01:00
Jaroslav Kysela
c6793b3bab config channels: improve docs (User Icon), fixes #2564 2014-12-19 18:41:03 +01:00
Jaroslav Kysela
3c5d40916d linuxdvb satconf: handle correctly LNB power off 2014-12-19 16:35:57 +01:00
Jaroslav Kysela
eae66e1424 Makefile.ffmpeg: upgrade libx264 and use different mirror, vlc ftp is not accessible, fixes #2567 2014-12-19 12:11:56 +01:00
Niels Ole Salscheider
ba4e998cf4 Fix build with libressl 2014-12-19 11:41:45 +01:00
Jaroslav Kysela
5a9b31e7da ACL: use createdefault in access_init() 2014-12-18 18:15:04 +01:00
Jaroslav Kysela
a67c6bbc1b subscriptions: changed 'no transponder available' to 'no input source available' 2014-12-18 15:15:02 +01:00
Jaroslav Kysela
75084b79e6 mkmux: fix crash when wrong localtime argument was used 2014-12-18 12:26:38 +01:00
Jaroslav Kysela
7a16e29293 linuxdvb rotor: Add Min Rotor Move 2014-12-18 09:04:46 +01:00
Jaroslav Kysela
c8885ce5cb channel tags: add sort index, fixes #2560 2014-12-18 08:50:01 +01:00
Jaroslav Kysela
8db7c13281 CAPMT: Correct type for port property, fixes #2559 2014-12-17 15:42:54 +01:00
Jaroslav Kysela
066b273387 DVR: Add owner (user) field 2014-12-16 20:29:44 +01:00
Jaroslav Kysela
932ade39e8 linuxdvb rotor: Fix a little typo in USALS formula 2014-12-16 20:27:59 +01:00
Jaroslav Kysela
abea3ddee0 DVR: Implement autorec start window instead Starting Around (approx) 2014-12-16 16:10:27 +01:00
Jaroslav Kysela
34be15898f muxer: fix the meta write errors, fixes #2557 2014-12-16 14:01:12 +01:00
Jaroslav Kysela
cfa7cd0577 linuxdvb rotor: add ROTOR_TEST code 2014-12-16 08:55:10 +01:00
Jaroslav Kysela
f39f7b87ab linuxdvb satconf: tone fix..., fixes #2547 2014-12-15 19:58:34 +01:00
Jaroslav Kysela
7a707122e9 linuxdvb satconf: another diseqc tone optimization 2nd, fixes #2547 2014-12-15 18:31:01 +01:00
Jaroslav Kysela
32ad45b47c linuxdvb satconf: another diseqc tone optimization, fixes #2547 2014-12-15 18:29:46 +01:00
Jaroslav Kysela
d02f23302b linuxdvb rotor: change Max Rotor Movement to Init Rotor Time 2014-12-15 18:19:43 +01:00
Piotr Kuchciak
3184c8b01d EPGGRABER: cosmetic changes to show more name grabber 2014-12-15 18:06:31 +01:00
Jaroslav Kysela
c7be037d0c webui: fix the css clash for date picker, fixes #2553 2014-12-15 18:03:29 +01:00
Jaroslav Kysela
774323d002 linuxdvb satconf: tone off - move log 2014-12-15 16:00:19 +01:00
Jaroslav Kysela
5d7039c6da linuxdvb: tone off optimization - 3rd, fixes #2547 2014-12-15 15:58:14 +01:00
Jaroslav Kysela
6de939111d linuxdvb: tone off optimization - 2nd, fixes #2547 2014-12-15 15:55:52 +01:00
Jaroslav Kysela
a7754e8009 linuxdvb: tone off optimization, fixes #2547 2014-12-15 15:54:16 +01:00
Jaroslav Kysela
1827ef1347 mpegts input: introduce linuxdvb_filter_close() 2014-12-15 15:42:10 +01:00
Jaroslav Kysela
cfc78aba5f linuxdvb rotor: reshuffle code again 2014-12-15 15:23:02 +01:00
Jaroslav Kysela
080dfb4ef9 linuxdvb rotor: cleanups, move site config to frontend, fixes 2014-12-15 14:57:42 +01:00
Jaroslav Kysela
948de597c3 tvhlog: improve subsystem parser 2nd, fixes #2552 2014-12-15 14:44:47 +01:00
Jaroslav Kysela
85a5d7c4d2 tvhlog: improve subsystem parser, fixes #2552 2014-12-15 14:42:32 +01:00
Jaroslav Kysela
c08adfdaef linuxdvb: add linuxdvb_frontend_clear(), fixes #2547 2014-12-15 13:13:53 +01:00
Jaroslav Kysela
82cb4f7f42 linuxdvb: add Tune Repeats for frontend 2014-12-15 09:38:59 +01:00
Jaroslav Kysela
70c65a3fba linuxdvb rotor: another fix for last orbital position caching, code shuffle 2014-12-15 08:14:56 +01:00
Jaroslav Kysela
1145484e9f linuxdvb rotor: fix orbital position check 2014-12-15 00:22:34 +01:00
Jaroslav Kysela
f1fda287d8 ACL: fixed ACCESS_FULL (added ACCESS_HTSP_RECORDER), fixes #2551 2014-12-14 22:37:29 +01:00
Jaroslav Kysela
d9bb16eea6 linuxdvb rotor: do not rely on NIT, use site position value only 2014-12-14 22:21:33 +01:00
Jaroslav Kysela
920cb3582c ACL: add HTSP DVR rights for the initial user, fixes #2551 2014-12-14 20:21:52 +01:00
Jaroslav Kysela
40a2e93c8a linuxdvb satconf: another tone fix 2014-12-14 18:33:28 +01:00
Jaroslav Kysela
49e22d400a linuxdvb satconf: move voltage settings as last (for LNB polarization) 2014-12-14 17:34:34 +01:00
Jaroslav Kysela
32540dd18b linuxdvb rotor: fix the polarization caching 2014-12-14 17:26:19 +01:00
Jaroslav Kysela
d4fed8839b linuxdvb diseqc: improve log 2014-12-14 16:00:47 +01:00
Jaroslav Kysela
4fadb70767 linuxdvb diseqc: handle 22khz tone better for unicable 2014-12-14 15:57:13 +01:00
Jaroslav Kysela
811eeb4376 linuxdvb diseqc: reset cache values when frontend is closed, fixes #2547 2014-12-14 13:32:25 +01:00
Jaroslav Kysela
70a17dfb42 linuxdvb diseqc: fix and improve command caching, fixes #2547 2014-12-14 13:28:44 +01:00
Jaroslav Kysela
450c2be234 linuxdvb: remove debug line 2014-12-14 11:34:49 +01:00
Jaroslav Kysela
5ba47ff711 linuxdvb: implement 'Full DiseqC', fixes #2547 2014-12-14 11:28:37 +01:00
Jaroslav Kysela
c4ce18c47d linuxdvb: do not send switch command twice for rotor, add 'Switch Then Rotor' config 2014-12-14 11:20:13 +01:00
Jaroslav Kysela
3a6088cfd9 linuxdvb: fixed typo in the sat config (Longitude Direction West) 2014-12-14 11:10:28 +01:00
Jaroslav Kysela
388f57ecc3 linuxdvb rotor: fix USALS formula (removed debug code) 2014-12-14 10:24:14 +01:00
Jaroslav Kysela
27e3b34d8c mkv: add DESCRIPTION tag, fixes #2510 2014-12-13 21:53:26 +01:00
Jaroslav Kysela
98ed29fb67 DVR: write comments also for entries not based on EPG, fixes #2510 2014-12-13 21:44:46 +01:00
Jaroslav Kysela
ad16b3dbe7 linuxdvb rotor: change the USALS formula 2014-12-13 21:29:36 +01:00
Jaroslav Kysela
b0b48bdf85 rtsp: correct the timeout range (20-3600), warn user
- fix also small typo in linuxdvb rotor
2014-12-13 18:51:57 +01:00
Jaroslav Kysela
39d985be1c linux rotor: add more debug info to the USALS routine 2014-12-12 23:03:34 +01:00
Jaroslav Kysela
19dbe37cc0 mkmux: fix language selection for comment when primary languages are not set, fixes #2549 2014-12-12 22:45:21 +01:00
Jaroslav Kysela
9ce0c5542d linuxdvb rotor: small cleanups 2014-12-12 19:00:18 +01:00
Jaroslav Kysela
129bb84bf6 htsp: move I/O calls outside global lock 2014-12-12 16:38:30 +01:00
Jaroslav Kysela
9469ef0ddf dvbpsi: add hack for broken PMT tables 2014-12-12 16:25:15 +01:00
Jaroslav Kysela
c6d2a0912f DVR: Add episode check to the fuzzy EPG match 2014-12-12 14:16:04 +01:00
Jaroslav Kysela
a609cd2edb HTTP dvrfile streaming: add subscription handling - fixes #2540 2014-12-12 13:41:47 +01:00
Jaroslav Kysela
5ec120c378 ACL: Add HTSP Recorder permission, fixes #2541 2014-12-12 12:01:21 +01:00
Jaroslav Kysela
d6b59b9ee3 mpegts network: create new muxes when delivery system does not match, fixes #2544 2014-12-12 11:45:50 +01:00
Jaroslav Kysela
07a8f5ff06 SAT>IP: Do not enable the second RTSP SHUTDOWN sequence by default 2014-12-12 11:28:04 +01:00
Jaroslav Kysela
d760c4cb7c implement TSS_TUNING message for late tuning error notification (SAT>IP) 2014-12-12 11:19:26 +01:00
Jaroslav Kysela
0b416b3e29 fix SM_CODE_NO_VALID_ADAPTER constant 2014-12-12 08:56:37 +01:00
Jaroslav Kysela
d18fc1a422 tsdebug: avoid I/O when file descriptors are not set 2014-12-12 08:50:43 +01:00
Jaroslav Kysela
f98f580ee0 SAT>IP: Move complete I/O to the separate thread 2014-12-12 08:50:06 +01:00
Jaroslav Kysela
b0e030b326 descrambler: move the key change code to proper location 2014-12-08 20:39:21 +01:00
Jaroslav Kysela
67938bc9d9 descrambler: implement proper TS queue flush before key change, fixes #2171 2014-12-08 20:20:39 +01:00
Jaroslav Kysela
3535d7d22e globalheaders: reduce scan time, add warning for disabled streams 2014-12-08 16:13:56 +01:00
Jaroslav Kysela
0e8647a534 tsfix, htsp: wait only for first video stream 2014-12-08 15:51:05 +01:00
Jaroslav Kysela
8668734eff mpegts parser: speed & PCR extraction improvements 2014-12-08 15:27:28 +01:00
Jaroslav Kysela
2b2cb3236e tsdebug: add possibility to save the whole input mux with decrambler keys 2014-12-07 21:18:42 +01:00
Jaroslav Kysela
b87eb898c8 tvhpoll: cosmetic chage for cppcheck 2014-12-06 16:30:47 +01:00
Jaroslav Kysela
b659bce22d spawn: fix thinko in spawn_parse_args() 2014-12-06 16:29:55 +01:00
Jaroslav Kysela
75e4e787ed webui: fix hostpath memory leaks 2014-12-06 16:27:40 +01:00
Jaroslav Kysela
d254f520a6 utils: trivial cleanups 2014-12-05 22:46:01 +01:00
Jaroslav Kysela
5ba389bd86 WEBUI DVR: Add more informative questions for the delete operation 2014-12-05 20:11:40 +01:00
Jaroslav Kysela
da2ae270f5 profile: use pass as default profile when config files are corrupted 2014-12-05 19:41:10 +01:00
Jaroslav Kysela
2d7cda7eef idnode: implement sort for simple enums 2014-12-05 19:18:17 +01:00
Jaroslav Kysela
f20bab4aed service: Add possibility to disable automatic service checking per service 2014-12-05 13:20:40 +01:00
Jaroslav Kysela
a9d3e4a7d9 linuxdvb rotor: Zero Sat Longitude should be for USALS not for GOTOX 2014-12-05 11:07:08 +01:00
Jaroslav Kysela
5339e1a956 htsp server: fix NULL dereference in addDvrEntry 2014-12-04 21:00:16 +01:00
Jaroslav Kysela
c65dfa1884 service: use --- prefix for disabled services in GUI 2014-12-04 20:38:14 +01:00
Jaroslav Kysela
6391e7b6c5 create <CFG>/.lock file to protect sharing config directory 2014-12-04 17:11:18 +01:00
Jaroslav Kysela
89baf880d6 mpegts service: auto-disable services not seen in the PAT table 2014-12-04 16:24:37 +01:00
Jaroslav Kysela
95ef1c4c0b mpegts service: add created field (timestamp) 2014-12-04 15:14:50 +01:00
Jaroslav Kysela
4fcc326b98 bouquet: do not map disabled services, fixes #2525 2014-12-04 14:11:23 +01:00
Jaroslav Kysela
64eec14399 http: allow ACL with HTSP streaming to fetch icons from imagecache via HTTP, fixes #2529 2014-12-03 18:27:52 +01:00
Jaroslav Kysela
360dd56661 h264: handle NAL delimiter type, fixes #2456 2014-12-03 18:20:31 +01:00
Jaroslav Kysela
5caf1202fa tsfile: disable EPG by default 2014-12-03 18:13:18 +01:00
Jaroslav Kysela
a95ef0b237 transcode: fix for previous memory leak fix 2014-12-02 21:07:27 +01:00
Jaroslav Kysela
4e42e041c5 mpegts dvb: implement last seen, fixes #2528 2014-12-02 20:52:26 +01:00
Jaroslav Kysela
7db9f44a30 epggrab: xmltv - do not use any arguments by default 2014-12-02 19:58:01 +01:00
Jaroslav Kysela
638b870a19 transcode: fix memory leaks (audio, video) 2014-12-02 19:55:15 +01:00
Jaroslav Kysela
1b193a3f7d linuxdvb rotor: USALS addr zero longtitute 2014-12-02 17:16:39 +01:00
Jaroslav Kysela
6967a9a281 linuxdvb rotor: improve GOTOX mode (separate sat longtitute and position byte) 2014-12-02 17:10:39 +01:00
Jaroslav Kysela
3e505d77d9 epggrab: try to fix the thread kill & wrong memory access 2014-12-01 21:25:15 +01:00
Jaroslav Kysela
70b3ef658b IPTV: mux - fix simple memory leak 2014-12-01 20:50:29 +01:00
Jaroslav Kysela
bbed9a309e tsfile: keep valgrind happy (fix memory leaks) 2014-12-01 20:48:28 +01:00
Jaroslav Kysela
f63d16f605 htsmsg_xml: fix uninitialized variable access 2014-12-01 20:32:27 +01:00
Jaroslav Kysela
6b152b942d XMTTV: allow to specify arguments for grabbers, fixes #2516 2014-12-01 20:32:09 +01:00
Jaroslav Kysela
bb31907761 spawn: move argument parser from iptv_pipe 2014-12-01 17:03:28 +01:00
Jaroslav Kysela
bd20a5393c mkvmux: handle COMMENT tag in metadata, fixes #2511, fixes #2510 2014-11-30 18:44:00 +01:00
Jaroslav Kysela
8c743ba44b HTSP: add comment to DVR entry and all related APIs 2014-11-30 18:39:01 +01:00
Jaroslav Kysela
abfae4c9f8 channels: add Auto EPG Channel, fixes #2520 2014-11-30 17:03:54 +01:00
Jaroslav Kysela
01e7cc1478 xmltv: use --quiet argument for grabber binary 2014-11-30 15:32:30 +01:00
Jaroslav Kysela
1d9a3483a5 access: initialize correctly ACCESS_HTSP_STREAMING bitmask 2014-11-30 15:15:35 +01:00
Jaroslav Kysela
8265dbd902 ACL: add HTSP streaming, reshuffle fields, fixes #2518 2014-11-30 13:29:05 +01:00
Jaroslav Kysela
9778bca323 channel,tags: differentiate between user and admin (config) requests - use all argument, fixes #2517 2014-11-30 12:47:51 +01:00
Jaroslav Kysela
452ca85ff6 epg query: fix thinko 2014-11-28 17:45:26 +01:00
Jaroslav Kysela
1f1b36a9e2 imagecache: fix global mutex deadlock 2014-11-28 14:20:06 +01:00
Jaroslav Kysela
2ae32e61af tcp: fix error msg 2014-11-28 13:48:06 +01:00
Jaroslav Kysela
cf827862dc httpc: fix the EOC test in http_client_data_chunked() 2014-11-28 11:40:21 +01:00
Jaroslav Kysela
ea7c5b5056 Makefile.ffmpeg: do not try to use pkg-config to look for system libs 2014-11-27 21:19:36 +01:00
Jaroslav Kysela
36a06a66e0 uuid: use tvh_open instead direct open (O_CLOEXEC issue) 2014-11-27 12:17:14 +01:00
Jaroslav Kysela
c2c5b81330 transcode: improve mpeg2 global (meta) data parser 2014-11-27 09:08:02 +01:00
Jaroslav Kysela
60574bf3e4 mkmux: found another stupid bug in mk_mux_write_pkt 2014-11-26 20:53:23 +01:00
Jaroslav Kysela
39a4db4994 mkmux: always initialize component index in track and use only disabled (remove enabled) variable 2014-11-26 11:29:16 +01:00
Adam Sutton
3e4c0a39d2 support: updated list of apt dist builds 2014-11-26 09:50:02 +00:00
Jaroslav Kysela
3579d04f48 profile: fix sharing (missing STOP messages) 2014-11-25 13:56:45 +01:00
Jaroslav Kysela
fbe1f1f302 httpc: accept only LF (instead CR/LF) for HTTP headers to be tolerant according RFC 2014-11-25 12:34:41 +01:00
Jaroslav Kysela
448b0a4acd mkmux; Fix the cluster start (keyframe) 2014-11-25 10:56:02 +01:00
Jaroslav Kysela
4d6c1bba0e transcoding: fix the resample output buffer allocation 2014-11-25 10:15:30 +01:00
Jaroslav Kysela
bb07ead99b transcode: h264 - merge the meta data to output packet 2014-11-24 18:08:54 +01:00
Jaroslav Kysela
d37a0c6df4 doc: tags - improve private field documetation 2014-11-24 17:45:01 +01:00
Jaroslav Kysela
f4d0859337 fix libav muxer to keep sync with the internal H264 packet changes 2014-11-24 17:23:18 +01:00
Jaroslav Kysela
fe24184ded htsp server: review channel access verification, fixes #2505 2014-11-24 09:01:59 +01:00
Jaroslav Kysela
d6bfd1ffe6 access: optimization for the webroot ticket patch 2014-11-23 19:03:27 +01:00
Patrick Gaubatz
25e48734c9 minor code clean-up in access_ticket_verify2() 2014-11-23 19:03:27 +01:00
Patrick Gaubatz
3539dbf13d fix access_ticket_verify2() when using non-default webroot 2014-11-23 19:03:27 +01:00
Jaroslav Kysela
fe995bda63 channel tags: added private flag, fixes #2501 2014-11-23 18:41:19 +01:00
Patrick Gaubatz
d1bdce093d minor optimizations in http_get_hostpath() 2014-11-23 17:53:59 +01:00
Patrick Gaubatz
664d24308d http: this fixes streaming when tvheadend is behind a https reverse proxy *and* a non-default webroot (e.g. /tvheadend) 2014-11-23 17:53:59 +01:00
Jaroslav Kysela
085fd89465 service: another STOP/START msg cleanup 2014-11-23 17:47:42 +01:00
Jaroslav Kysela
3ba0ae6e62 service: try to fix the service_restart START/STOP msg mismatch 2014-11-23 17:01:31 +01:00
Jaroslav Kysela
44633b8f74 access: fix copy-and-paste error for the destroy fcns 2014-11-23 16:39:03 +01:00
Jaroslav Kysela
e874526824 channel tags: fix permissions code (for HTSP and HTTP API), fixes #2501 2014-11-23 16:13:17 +01:00
Jaroslav Kysela
3081776240 H264 parser: add the rest of frame (after 01Ex start code), fixes#2482 2014-11-22 22:07:36 +01:00
Jaroslav Kysela
1a622c1b67 xmltv: add --quiet argument to the internal search 2014-11-22 18:42:20 +01:00
Jaroslav Kysela
ea23369c5d H264 parser: do not merge header NAL units twice 2014-11-22 18:20:37 +01:00
Jaroslav Kysela
da3d9ecb48 channel enhancements (enabled), fixed min/max chnum handling (access) 2014-11-21 18:02:27 +01:00
Prof Yaffle
00f3cc0e8a WebUI: Enable animated rollup on idnode panels for UI consistency 2014-11-21 15:13:00 +01:00
Prof Yaffle
701e6b373d WebUI: Add rollup-on-create to idnode panels; set readonly panels to rolled up by default 2014-11-21 15:13:00 +01:00
Christian Karrié
ba4bb99489 40px should be ok 2014-11-21 15:13:00 +01:00
Christian Karrié
daae05eb75 small UI update to see values greater than 999 2014-11-21 15:13:00 +01:00
Jaroslav Kysela
3ba5bb9d0b bouquets: add --nobat debug option 2014-11-21 15:10:49 +01:00
Jaroslav Kysela
6bcecff056 epggrab: ota - add more traces 2014-11-21 14:22:36 +01:00
Jaroslav Kysela
a670ec1eee dvb_psi: fix build error for \!DVB config 2014-11-21 08:59:12 +01:00
Jaroslav Kysela
a6420f9713 DVR config: allow admin access (without DVR permissins) to the DVR config entries 2014-11-20 20:21:44 +01:00
Jaroslav Kysela
d0a6684f40 dvb psi: LCN updates 2014-11-20 20:09:43 +01:00
Jaroslav Kysela
898f88a098 dvb psi: fix the table completion notification 2014-11-20 16:53:18 +01:00
Jaroslav Kysela
98854eaacd IPTV: pipe - add respawn option and environment option 2014-11-20 15:04:17 +01:00
Jaroslav Kysela
68b07236d0 mpegts network scan: do not be too frequency strict for the forced bouquet scan 2014-11-20 13:22:21 +01:00
Jaroslav Kysela
f7d356cff2 bouquets: fastscan - do not check the frequency too strictly 2014-11-20 12:28:13 +01:00
Jaroslav Kysela
1ddd261627 iptv pipe: skip the character after backslash 2014-11-19 23:20:42 +01:00
Jaroslav Kysela
dbd31acc3c DVR: improve the episode name saving, fixes #2357 2014-11-19 20:04:32 +01:00
Jaroslav Kysela
c7d08fb316 avc parser: little optimization, pkt_meta is not used in avc_convert_pkt 2014-11-19 17:39:09 +01:00
Jaroslav Kysela
bf1c902fbf H264: move avc parsing from globalheaders to mkv mux 2014-11-19 17:29:16 +01:00
Jaroslav Kysela
b70fa32a71 Partial revert "htsp: another fix for H264 - headers merge"
This reverts commit 33792bf601.
2014-11-19 16:50:20 +01:00
Jaroslav Kysela
207fcbfb10 channel: fix channel_get_icon() - memory leak and handling 2014-11-19 15:23:27 +01:00
Piotr Kuchciak
a0a26513ff WEBUI EPG: small fix name 2014-11-19 14:47:09 +01:00
Jaroslav Kysela
f34f872cb9 SAT>IP: Change the default number of positions for IPLNB to 1 2014-11-19 14:44:43 +01:00
Jaroslav Kysela
b0e31dd880 DVR: Move freeing of some variables to dvr_entry_dec_ref() 2014-11-19 13:50:54 +01:00
Jaroslav Kysela
6ddbd16704 DVR: Store episode string to storage, fixes #2357 2014-11-19 13:47:52 +01:00
Jaroslav Kysela
f15b8a6978 epggrab: ota - om_complete load fix 2014-11-19 13:17:50 +01:00
Richard Kunze
e59a8b41e7 Fixed iconv handle type
Signed-off-by: Richard Kunze <richard.kunze@web.de>
2014-11-19 13:04:54 +01:00
Jaroslav Kysela
a2dfb056ea WEBUI JS: Aligh time properly in EPG/DVR dialogs 2014-11-19 13:03:41 +01:00
Piotr Kuchciak
8aee57930d WEBUI JS: cosmetic changes for dvr and epg dialogs 2014-11-19 12:45:24 +01:00
Jaroslav Kysela
33792bf601 htsp: another fix for H264 - headers merge
The real headers merge is in avc_convert_pkt() but this is called
only from the global headers plugin. Add the global headers plugin
to all HTSP chains.

This patch removes the merging from the TS parser.
2014-11-19 11:39:46 +01:00
Jaroslav Kysela
7743670045 iptv pipe: fix the backslash handling 2014-11-19 08:45:12 +01:00
Jaroslav Kysela
5df9cdf97d epggrab: ota - fix om_requeue handling 2014-11-18 21:01:14 +01:00
Jaroslav Kysela
4f68d03875 iptv pipe: handle the backslash character, fixes#2487 2014-11-18 20:44:00 +01:00
Jaroslav Kysela
fa22f18dfc dvb support: revert dvb_sat_position_to_str() change (missing direction character) 2014-11-18 15:07:22 +01:00
Jaroslav Kysela
a0e69f8c7e linuxdvb: add more traces to linuxdvb_frontend_network_list() 2014-11-18 13:39:00 +01:00
Jaroslav Kysela
d49e965738 http: improve the request dump (for post) 2014-11-18 13:34:29 +01:00
Jaroslav Kysela
46f7e41e62 bouquet: extend help 2014-11-18 13:32:32 +01:00
Jaroslav Kysela
ccf64a0c57 hdhomerun: remove hf_master code - not used 2014-11-18 10:48:54 +01:00
Jaroslav Kysela
c119b442dd linuxdvb,satip,hdhomerun: make uuid persistent per tuner 2014-11-18 10:47:20 +01:00
Jaroslav Kysela
a2d7e1797c spawn: implement spawn_kill, improve IPTV pipe (busy loop) 2014-11-18 09:45:17 +01:00
Jaroslav Kysela
4290f79748 spawn: allow spawnv stdout/stderr redirection to logs 2014-11-18 09:10:48 +01:00
Jaroslav Kysela
fc7e0f4a36 spawn: reshuffle code to get proper stdin for spawned process 2014-11-17 23:26:18 +01:00
Jaroslav Kysela
29ccb30ff6 spawn: another cleanups in spawn_and_give_stdout() 2014-11-17 23:17:19 +01:00
Jaroslav Kysela
2cee0bfeb6 spawn: more CLOEXEC fixes 2014-11-17 22:01:33 +01:00
Jaroslav Kysela
cee7147eef spawn: improve the pipe log reading 2014-11-17 17:13:16 +01:00
Jaroslav Kysela
9cabe1ad55 fix DVR docs - fixes#2483 2014-11-17 15:07:13 +01:00
Jaroslav Kysela
7c1ff4740b spawn: remove spawn_and_store_stdout() 2014-11-17 14:57:46 +01:00
Jaroslav Kysela
c4f30d1e51 iptv pipe: fix url / arg parsing bug, add ${service_name} substitution 2014-11-17 14:57:35 +01:00
Jaroslav Kysela
7548fc5c3e spawn: introduce tvh_fopen() to close fds for spawned processes 2014-11-17 14:22:40 +01:00
Jaroslav Kysela
a563d786e9 spawn: add thread for fast info/error messages, fix spawn_pipe_read() 2014-11-17 14:14:43 +01:00
Jaroslav Kysela
3f7ac5b5e0 spawn: fix a compiler warning (unused-variable) 2014-11-17 08:57:35 +01:00
Jaroslav Kysela
0d10db2514 IPTV: add pipe:// handler to read MPEG-TS stream from an external program 2014-11-16 22:30:39 +01:00
Jaroslav Kysela
003c4a2699 spawn: implement spawn_and_give_stdout() 2014-11-16 21:17:28 +01:00
Jaroslav Kysela
b2290d30a8 spawn: do not use syslog calls in the child process
The syslog routines uses private mutex so it can cause
various crashes and other misbehaviour.

Use pipes to send messages from the child processes.
2014-11-16 21:06:02 +01:00
Jaroslav Kysela
0bb2356a5f simplify file_readall() 2014-11-16 20:32:03 +01:00
Jaroslav Kysela
114298ad15 spawn: fix possible NULL dereferences 2014-11-16 19:41:02 +01:00
240 changed files with 22117 additions and 6591 deletions

View file

@ -39,7 +39,7 @@
],
"buildcmd": [
"./configure --disable-dvbscan --enable-libffmpeg_static --enable-hdhomerun_static",
"./configure --disable-dvbscan --enable-libffmpeg_static --disable-libffmpeg_static_x264 --enable-hdhomerun_static",
"make -j ${PARALLEL}"
]
},
@ -82,7 +82,7 @@
],
"buildcmd": [
"./configure --disable-dvbscan --enable-libffmpeg_static --enable-hdhomerun_static",
"./configure --disable-dvbscan --enable-libffmpeg_static --disable-libffmpeg_static_x264 --enable-hdhomerun_static",
"make -j ${PARALLEL}"
]
}

View file

@ -61,7 +61,7 @@ LDFLAGS += -L${ROOTDIR}/libav_static/build/ffmpeg/lib -Wl,-Bstatic \
-lavresample -lswresample -lswscale \
-lavutil -lavformat -lavcodec -lavutil \
-lvorbisenc -lvorbis -logg -lx264 -lvpx \
-Wl,-Bdynamic
-Wl,-Bdynamic -ldl
endif
ifeq ($(CONFIG_HDHOMERUN_STATIC),yes)
CFLAGS += -I${ROOTDIR}/libhdhomerun_static
@ -153,11 +153,18 @@ SRCS = src/version.c \
src/esfilter.c \
src/intlconv.c \
src/profile.c \
src/bouquet.c
src/bouquet.c \
src/lock.c
SRCS-${CONFIG_UPNP} += \
src/upnp.c
# SATIP Server
SRCS-${CONFIG_SATIP_SERVER} += \
src/satip/server.c \
src/satip/rtsp.c \
src/satip/rtp.c
SRCS += \
src/api.c \
src/api/api_status.c \
@ -218,12 +225,15 @@ SRCS += src/muxer.c \
# Optional code
#
# MPEGTS core
# MPEGTS core, order by usage (psi lib, tsdemux)
SRCS-$(CONFIG_MPEGTS) += \
src/descrambler/descrambler.c \
src/descrambler/caclient.c \
src/input/mpegts.c \
src/input/mpegts/mpegts_pid.c \
src/input/mpegts/mpegts_input.c \
src/input/mpegts/tsdemux.c \
src/input/mpegts/dvb_psi_lib.c \
src/input/mpegts/mpegts_network.c \
src/input/mpegts/mpegts_mux.c \
src/input/mpegts/mpegts_service.c \
@ -232,9 +242,8 @@ SRCS-$(CONFIG_MPEGTS) += \
src/input/mpegts/dvb_charset.c \
src/input/mpegts/dvb_psi.c \
src/input/mpegts/fastscan.c \
src/input/mpegts/tsdemux.c \
src/input/mpegts/mpegts_mux_sched.c \
src/input/mpegts/mpegts_network_scan.c \
src/input/mpegts/mpegts_network_scan.c
# MPEGTS DVB
SRCS-${CONFIG_MPEGTS_DVB} += \
@ -260,7 +269,7 @@ SRCS-${CONFIG_LINUXDVB} += \
src/input/mpegts/linuxdvb/linuxdvb_rotor.c \
src/input/mpegts/linuxdvb/linuxdvb_en50494.c
# SATIP
# SATIP Client
SRCS-${CONFIG_SATIP_CLIENT} += \
src/input/mpegts/satip/satip.c \
src/input/mpegts/satip/satip_frontend.c \
@ -280,6 +289,8 @@ SRCS-${CONFIG_IPTV} += \
src/input/mpegts/iptv/iptv_service.c \
src/input/mpegts/iptv/iptv_http.c \
src/input/mpegts/iptv/iptv_udp.c \
src/input/mpegts/iptv/iptv_rtsp.c \
src/input/mpegts/iptv/iptv_pipe.c
# TSfile
SRCS-$(CONFIG_TSFILE) += \
@ -327,6 +338,15 @@ SRCS-${CONFIG_CAPMT} += \
SRCS-${CONFIG_CONSTCW} += \
src/descrambler/constcw.c
# DVB CAM
SRCS-${CONFIG_LINUXDVB_CA} += \
src/input/mpegts/linuxdvb/linuxdvb_ca.c \
src/descrambler/dvbcam.c
# TSDEBUGCW
SRCS-${CONFIG_TSDEBUG} += \
src/descrambler/tsdebugcw.c
# FFdecsa
ifneq ($(CONFIG_DVBCSA),yes)
FFDECSA-$(CONFIG_CAPMT) = yes
@ -456,7 +476,8 @@ ${BUILDDIR}/libffmpeg_stamp: ${ROOTDIR}/libav_static/build/ffmpeg/lib/libavcodec
@touch $@
${ROOTDIR}/libav_static/build/ffmpeg/lib/libavcodec.a:
$(MAKE) -f Makefile.ffmpeg build
CONFIG_LIBFFMPEG_STATIC_X264=$(CONFIG_LIBFFMPEG_STATIC_X264) \
$(MAKE) -f Makefile.ffmpeg build
# Static HDHOMERUN library

View file

@ -48,10 +48,12 @@ export PATH := $(LIBAVDIR)/build/ffmpeg/bin:$(PATH)
ECFLAGS = -I$(LIBAVDIR)/build/ffmpeg/include
ELIBS = -L$(LIBAVDIR)/build/ffmpeg/lib -ldl
FFMPEG = ffmpeg-2.4.2
CONFIGURE = PKG_CONFIG=/tmp/nobin/pkg-config ./configure
FFMPEG = ffmpeg-2.6.2
FFMPEG_TB = $(FFMPEG).tar.bz2
FFMPEG_URL = http://ffmpeg.org/releases/$(FFMPEG_TB)
FFMPEG_SHA1 = 8fedc6f235d8510f716bca1784faa8cbe5d9cf78
FFMPEG_SHA1 = 65470c9b967485f72f81758a7bad44cf7a1763db
EXTLIBS = libx264 libvorbis libvpx
COMPONENTS = avutil avformat avcodec swresample swscale avresample
@ -70,10 +72,10 @@ LIBVORBIS_TB = $(LIBVORBIS).tar.gz
LIBVORBIS_URL = http://downloads.xiph.org/releases/vorbis/$(LIBVORBIS_TB)
LIBVORBIS_SHA1 = 1602716c187593ffe4302124535240cec2079df3
LIBX264 = x264-snapshot-20141012-2245
LIBX264 = x264-snapshot-20141218-2245
LIBX264_TB = $(LIBX264).tar.bz2
LIBX264_URL = ftp://ftp.videolan.org/pub/x264/snapshots/$(LIBX264_TB)
LIBX264_SHA1 = 392cd6b0e723192e009d731fe44db01b55c97fba
LIBX264_URL = http://ftp.via.ecp.fr/pub/videolan/x264/snapshots/$(LIBX264_TB)
LIBX264_SHA1 = 24a3b20e2c49a112e40df9f64885cbd81250298a
LIBVPX = libvpx-v1.3.0
LIBVPX_TB = $(LIBVPX).tar.bz2
@ -99,10 +101,10 @@ $(LIBAVDIR)/$(YASM)/.tvh_download:
$(LIBAVDIR)/$(YASM)/.tvh_build: \
$(LIBAVDIR)/$(YASM)/.tvh_download
cd $(LIBAVDIR)/$(YASM) && ./configure \
cd $(LIBAVDIR)/$(YASM) && $(CONFIGURE) \
--prefix=/ffmpeg
DESTDIR=$(LIBAVDIR)/build \
make -C $(LIBAVDIR)/$(YASM) install
$(MAKE) -C $(LIBAVDIR)/$(YASM) install
@touch $@
#
@ -117,12 +119,12 @@ $(LIBAVDIR)/$(LIBOGG)/.tvh_download:
$(LIBAVDIR)/$(LIBOGG)/.tvh_build: \
$(LIBAVDIR)/$(YASM)/.tvh_build \
$(LIBAVDIR)/$(LIBOGG)/.tvh_download
cd $(LIBAVDIR)/$(LIBOGG) && ./configure \
cd $(LIBAVDIR)/$(LIBOGG) && $(CONFIGURE) \
--prefix=/ffmpeg \
--enable-static \
--disable-shared
DESTDIR=$(LIBAVDIR)/build \
make -C $(LIBAVDIR)/$(LIBOGG) install
$(MAKE) -C $(LIBAVDIR)/$(LIBOGG) install
@touch $@
$(LIBAVDIR)/$(LIBVORBIS)/.tvh_download: \
@ -135,25 +137,23 @@ $(LIBAVDIR)/$(LIBVORBIS)/.tvh_build: \
$(LIBAVDIR)/$(LIBVORBIS)/.tvh_download \
$(LIBAVDIR)/$(YASM)/.tvh_build \
$(LIBAVDIR)/$(LIBOGG)/.tvh_build
cd $(LIBAVDIR)/$(LIBVORBIS) && ./configure \
cd $(LIBAVDIR)/$(LIBVORBIS) && $(CONFIGURE) \
--prefix=/ffmpeg \
--enable-static \
--disable-shared \
--with-ogg=$(LIBAVDIR)/build/ffmpeg
DESTDIR=$(LIBAVDIR)/build \
make -C $(LIBAVDIR)/$(LIBVORBIS) install
$(MAKE) -C $(LIBAVDIR)/$(LIBVORBIS) install
@touch $@
#
# libx264
#
ARCH = $(shell $(CC) -dumpmachine | cut -d '-' -f 1)
ifneq (,$(filter i386 i486 i586 i686 pentium,$(ARCH)))
ifneq (yes,$(CONFIG_LIBFFMPEG_STATIC_X264))
$(LIBAVDIR)/$(LIBX264)/.tvh_download:
@echo "***** PLEASE FIX !!!! libx264 build for i386 *****"
@echo "***** LIBX264 STATIC BUILD IS DISABLED, USING INSTALLED PACKAGE *****"
@mkdir -p $(LIBAVDIR)/$(LIBX264)
@touch $@
@ -172,7 +172,7 @@ $(LIBAVDIR)/$(LIBX264)/.tvh_download:
$(LIBAVDIR)/$(LIBX264)/.tvh_build: \
$(LIBAVDIR)/$(LIBX264)/.tvh_download \
$(LIBAVDIR)/$(YASM)/.tvh_build
cd $(LIBAVDIR)/$(LIBX264) && ./configure \
cd $(LIBAVDIR)/$(LIBX264) && $(CONFIGURE) \
--prefix=/ffmpeg \
--enable-static \
--disable-shared \
@ -183,7 +183,7 @@ $(LIBAVDIR)/$(LIBX264)/.tvh_build: \
--disable-gpac \
--disable-lsmash
DESTDIR=$(LIBAVDIR)/build \
make -C $(LIBAVDIR)/$(LIBX264) install
$(MAKE) -C $(LIBAVDIR)/$(LIBX264) install
@touch $@
endif
@ -201,12 +201,12 @@ $(LIBAVDIR)/$(LIBVPX)/.tvh_download:
$(LIBAVDIR)/$(LIBVPX)/.tvh_build: \
$(LIBAVDIR)/$(LIBVPX)/.tvh_download \
$(LIBAVDIR)/$(YASM)/.tvh_build
cd $(LIBAVDIR)/$(LIBVPX) && ./configure \
cd $(LIBAVDIR)/$(LIBVPX) && $(CONFIGURE) \
--prefix=/ffmpeg \
--enable-static \
--disable-shared
DIST_DIR=$(LIBAVDIR)/build/ffmpeg \
make -C $(LIBAVDIR)/$(LIBVPX) install
$(MAKE) -C $(LIBAVDIR)/$(LIBVPX) install
@touch $@
#
@ -225,7 +225,7 @@ $(LIBAVDIR)/$(FFMPEG)/.tvh_build: \
$(LIBAVDIR)/$(LIBX264)/.tvh_build \
$(LIBAVDIR)/$(LIBVPX)/.tvh_build \
$(LIBAVDIR)/$(FFMPEG)/.tvh_download
cd $(LIBAVDIR)/$(FFMPEG) && ./configure \
cd $(LIBAVDIR)/$(FFMPEG) && $(CONFIGURE) \
--prefix=/ffmpeg \
--disable-all \
--enable-static \
@ -240,7 +240,7 @@ $(LIBAVDIR)/$(FFMPEG)/.tvh_build: \
$(foreach muxer,$(MUXERS),--enable-muxer=$(muxer)) \
$(foreach bsf,$(BSFS),--enable-bsf=$(bsf))
DESTDIR=$(LIBAVDIR)/build \
make -C $(LIBAVDIR)/$(FFMPEG) install
$(MAKE) -C $(LIBAVDIR)/$(FFMPEG) install
@touch $@
.PHONY: static_libav_clean

View file

@ -45,10 +45,10 @@ LIBHDHRDIR = $(ROOTDIR)/libhdhomerun_static
export PATH := $(LIBHDHRDIR)/build/bin:$(PATH)
LIBHDHR = libhdhomerun_20140604
LIBHDHR = libhdhomerun_20150406
LIBHDHR_TB = $(LIBHDHR).tgz
LIBHDHR_URL = http://download.silicondust.com/hdhomerun/$(LIBHDHR_TB)
LIBHDHR_SHA1 = b8d910a5721c484a30b81662fd6991e3546ed67c
LIBHDHR_SHA1 = f0d5da744d981a80becea6cc862b5e2519e1c3c6
.PHONY: build
build: $(LIBHDHRDIR)/$(LIBHDHR)/.tvh_build

View file

@ -1,6 +1,6 @@
Tvheadend
====================================
(c) 2006 - 2014 Tvheadend Foundation CIC
========================================
(c) 2006 - 2015 Tvheadend Foundation CIC
What it is
@ -10,14 +10,22 @@ Tvheadend is a TV streaming server and digital video recorder.
It supports the following inputs:
* DVB-C
* DVB-C(2)
* DVB-T(2)
* DVB-S(2)
* ATSC
* SAT>IP
* HDHomeRun
* IPTV
* UDP
* HTTP
It support the following outputs:
* HTTP
* HTSP (own protocol)
* SAT>IP
How to build for Linux
----------------------

84
configure vendored
View file

@ -10,6 +10,7 @@
# ###########################################################################
ROOTDIR=$(cd "$(dirname "$0")"; pwd)
test -z "$PKG_CONFIG" && PKG_CONFIG=pkg-config
#
# Options
@ -19,8 +20,8 @@ OPTIONS=(
"cwc:yes"
"capmt:yes"
"constcw:yes"
"v4l:no"
"linuxdvb:yes"
"satip_server:yes"
"satip_client:yes"
"hdhomerun_client:auto"
"hdhomerun_static:no"
@ -34,6 +35,7 @@ OPTIONS=(
"zlib:auto"
"libav:auto"
"libffmpeg_static:no"
"libffmpeg_static_x264:yes"
"inotify:auto"
"epoll:auto"
"uriparser:auto"
@ -41,9 +43,12 @@ OPTIONS=(
"tvhcsa:auto"
"bundle:no"
"dvbcsa:no"
"dvben50221:auto"
"kqueue:no"
"dbus_1:auto"
"android:no"
"tsdebug:no"
"gtimer_check:no"
)
#
@ -160,6 +165,17 @@ int test(void)
}
'
check_cc_snippet stime '
#include <time.h>
#define TEST test
int test(void)
{
time_t t = 1;
stime(&t);
return 0;
}
'
check_cc_snippet recvmmsg '
#define _GNU_SOURCE
#include <stdlib.h>
@ -172,6 +188,18 @@ int test(void)
}
'
check_cc_snippet sendmmsg '
#define _GNU_SOURCE
#include <stdlib.h>
#include <sys/socket.h>
#define TEST test
int test(void)
{
sendmmsg(0, NULL, 0, 0);
return 0;
}
'
check_cc_snippet libiconv '
#include <iconv.h>
int test(void)
@ -181,6 +209,23 @@ int test(void)
}
' -liconv
if enabled_or_auto dvben50221; then
check_cc_snippet libdvben50221 '
#include <libdvben50221/en50221_session.h>
#define TEST test
int test(void)
{
struct en50221_transport_layer *tl = en50221_tl_create(5, 32);
return 0;
}
' '-ldvben50221 -ldvbapi -lucsi'
if enabled libdvben50221; then
enable dvben50221
else
disable dvben50221
fi
fi
#
# Python
#
@ -256,6 +301,13 @@ if enabled_or_auto zlib; then
fi
fi
#
# SAT>IP server
#
if enabled_or_auto satip_server; then
enable upnp
fi
#
# SAT>IP client
#
@ -307,7 +359,8 @@ else
if enabled_or_auto libav; then
has_libav=true
ffmpeg=$(pkg-config --modversion libavcodec | cut -d '.' -f 3)
ffmpeg=$(${PKG_CONFIG} --modversion libavcodec | cut -d '.' -f 3)
test -z "$ffmpeg" && ffmpeg=0
printf "$TAB" "checking for ffmpeg libraries ..."
if test $ffmpeg -lt 100; then
ffmpeg=
@ -409,6 +462,14 @@ if enabled cwc || enabled capmt || enabled constcw; then
fi
fi
#
# libdvben50221
#
if enabled libdvben50221; then
LDFLAGS="$LDFLAGS -ldvben50221 -ldvbapi -lucsi"
enable linuxdvb_ca
fi
#
# Icon caching
#
@ -444,23 +505,19 @@ if [ ${PLATFORM} = "freebsd" ] || [ ${PLATFORM} = "darwin" ]; then
fi
#
# MPEGTS/PS support
# MPEGTS support
#
disable mpegts
disable mpegps
disable mpegts_dvb
if enabled linuxdvb || enabled iptv || enabled tsfile || enabled satip_client || enabled hdhomerun_client;
if enabled linuxdvb || enabled iptv || enabled tsfile || enabled satip_client || \
enabled hdhomerun_client || enabled satip_server;
then
enable mpegts
fi
if enabled linuxdvb || enabled satip_client || enabled hdhomerun_client; then
if enabled linuxdvb || enabled satip_client || enabled hdhomerun_client || enabled satip_server; then
enable mpegts_dvb
fi
if enabled v4l; then
enable mpegps
fi
#
# DBus
#
@ -472,6 +529,13 @@ if enabled_or_auto dbus_1; then
fi
fi
#
# TSDebug
#
if enabled_or_auto tsdebug; then
enable mpegts_dvb
fi
# ###########################################################################
# Write config

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,257 @@
[ 0, 0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
16,
17,
18,
19,
20,
16,
22,
23,
23,
16,
19,
23,
23,
23,
18,
16,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
32,
33,
34,
35,
36,
144,
35,
144,
32,
145,
115,
146,
118,
148,
144,
32,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
48,
49,
51,
51,
48,
48,
48,
48,
48,
48,
48,
48,
49,
48,
51,
50,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
64,
65,
66,
67,
68,
69,
70,
71,
72,
73,
74,
75,
69,
64,
64,
64,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
80,
81,
82,
83,
84,
85,
80,
80,
84,
85,
80,
80,
80,
80,
80,
80,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
96,
97,
98,
99,
100,
101,
102,
96,
96,
96,
96,
96,
96,
96,
96,
96,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
112,
113,
114,
115,
116,
117,
118,
119,
120,
121,
122,
123,
0,
0,
0,
112,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
]

View file

@ -5,6 +5,7 @@
"nid": 4096,
"tsid": 17,
"sid": 17008,
"bouquetid": 0,
"channel" : [
17
],

View file

@ -5,6 +5,7 @@
"nid": 64511,
"tsid": 5800,
"sid": 3635,
"bouquetid": 0,
"channel" : [
17
],

View file

@ -0,0 +1,24 @@
{
"name": "Sky NZ",
"dict": "skynz",
"genre": "skynz",
"nid": 169,
"tsid": 3,
"sid": 9003,
"bouquetid": 0,
"channel" : [
17
],
"title": [
48, 49, 50, 51, 52, 53, 54, 55
],
"summary": [
64, 65, 66, 67, 68, 69, 70, 71
],
"season_num": [
" *\\(S ?([0-9]+),? Ep? ?[0-9]+\\)"
],
"episode_num": [
" *\\(S ?[0-9]+,? Ep? ?([0-9]+)\\)"
]
}

View file

@ -5,6 +5,7 @@
"nid": 2,
"tsid": 2004,
"sid": 4152,
"bouquetid": 4101,
"channel" : [
17
],

View file

@ -1,50 +1,155 @@
[
{
"name": "Canal Digitaal",
"name": "Canal Digitaal SD",
"position": 192,
"frequency": 12515000,
"symbolrate" : 22000000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 900
},
{
"name": "TV Vlaanderen",
"name": "Canal Digitaal HD",
"position": 192,
"frequency": 12515000,
"symbolrate" : 22000000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 901
},
{
"name": "TéléSAT",
"name": "TV Vlaanderen SD",
"position": 192,
"frequency": 12515000,
"symbolrate" : 22000000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 910
},
{
"name": "TV Vlaanderen HD",
"position": 192,
"frequency": 12515000,
"symbolrate" : 22000000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 911
},
{
"name": "TéléSAT SD",
"position": 192,
"frequency": 12515000,
"symbolrate" : 22000000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 920
},
{
"name": "Mobistar NL",
"name": "TéléSAT HD",
"position": 192,
"frequency": 12515000,
"symbolrate" : 22000000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 921
},
{
"name": "Mobistar NL Astra1",
"position": 192,
"frequency": 12515000,
"symbolrate" : 22000000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 930
},
{
"name": "Mobistar FR",
"name": "Mobistar FR Astra1",
"position": 192,
"frequency": 12515000,
"symbolrate" : 22000000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 940
},
{
"name": "AustriaSat",
"name": "AustriaSat Astra1",
"position": 192,
"frequency": 12515000,
"symbolrate" : 22000000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 950
},
{
"name": "Canal Digitaal HD",
"position": 235,
"frequency": 12187000,
"symbolrate" : 27500000,
"polarisation" : "H",
"delsys" : "dvbs2",
"pid": 901
},
{
"name": "TV Vlaanderen HD",
"position": 235,
"frequency": 12187000,
"symbolrate" : 27500000,
"polarisation" : "H",
"delsys" : "dvbs2",
"pid": 911
},
{
"name": "TéléSAT HD",
"position": 235,
"frequency": 12187000,
"symbolrate" : 27500000,
"polarisation" : "H",
"delsys" : "dvbs2",
"pid": 921
},
{
"name": "Mobistar NL Astra3",
"position": 235,
"frequency": 12187000,
"symbolrate" : 27500000,
"polarisation" : "H",
"delsys" : "dvbs2",
"pid": 930
},
{
"name": "Mobistar FR Astra3",
"position": 235,
"frequency": 12187000,
"symbolrate" : 27500000,
"polarisation" : "H",
"delsys" : "dvbs2",
"pid": 940
},
{
"name": "AustriaSat Astra3",
"position": 235,
"frequency": 12187000,
"symbolrate" : 27500000,
"polarisation" : "H",
"delsys" : "dvbs2",
"pid": 950
},
{
"name": "Skylink: Czech Republic",
"position": 235,
"frequency": 12070000,
"symbolrate" : 27500000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 30
},
{
"name": "Skylink: Slovak Republic",
"position": 235,
"frequency": 12070000,
"symbolrate" : 27500000,
"polarisation" : "H",
"delsys" : "dvbs",
"pid": 31
}
]

638
data/conf/satellites Normal file
View file

@ -0,0 +1,638 @@
[
{
"name": "NSS 9",
"pos": -1770
},
{
"name": "AMC 8",
"pos": -1390
},
{
"name": "AMC 7",
"pos": -1370
},
{
"name": "AMC 10",
"pos": -1350
},
{
"name": "Galaxy 15",
"pos": -1330
},
{
"name": "AMC 11",
"pos": -1310
},
{
"name": "Ciel 2/Galaxy 12",
"pos": -1290
},
{
"name": "Galaxy 13/Horizons 1",
"pos": -1270
},
{
"name": "AMC 21/Galaxy 14",
"pos": -1250
},
{
"name": "Galaxy 18",
"pos": -1230
},
{
"name": "EchoStar 9/Galaxy 23",
"pos": -1210
},
{
"name": "Anik F3/DirecTV 7S/EchoStar 14",
"pos": -1190
},
{
"name": "Eutelsat 117 West A",
"pos": -1168
},
{
"name": "Eutelsat 115 West A",
"pos": -1149
},
{
"name": "Eutelsat 113 West A",
"pos": -1130
},
{
"name": "Anik F2",
"pos": -1111
},
{
"name": "DirecTV 5/EchoStar 10/11",
"pos": -1100
},
{
"name": "Anik F1R/G1",
"pos": -1073
},
{
"name": "AMC 15/18",
"pos": -1050
},
{
"name": "AMC 1/SES 3/Spaceway 1 &amp; DirecTV 10/12",
"pos": -1030
},
{
"name": "DirecTV 4S/8/SES 1",
"pos": -1010
},
{
"name": "DirecTV 14/Galaxy 16/Spaceway 2 &amp; DirecTV 11",
"pos": -992
},
{
"name": "Galaxy 19",
"pos": -970
},
{
"name": "Galaxy 3C/Spaceway 3",
"pos": -950
},
{
"name": "Galaxy 25",
"pos": -931
},
{
"name": "Galaxy 17/Nimiq 6",
"pos": -910
},
{
"name": "Galaxy 28",
"pos": -890
},
{
"name": "TKSat 1",
"pos": -872
},
{
"name": "SES 2",
"pos": -870
},
{
"name": "AMC 16",
"pos": -850
},
{
"name": "Brasilsat B4",
"pos": -840
},
{
"name": "AMC 9",
"pos": -830
},
{
"name": "Nimiq 4",
"pos": -820
},
{
"name": "Simon Bolivar",
"pos": -780
},
{
"name": "EchoStar 8/QuetzSat 1",
"pos": -770
},
{
"name": "Star One C3",
"pos": -750
},
{
"name": "Nimiq 5",
"pos": -727
},
{
"name": "AMC 6",
"pos": -720
},
{
"name": "Star One C2",
"pos": -700
},
{
"name": "AMC 4",
"pos": -670
},
{
"name": "Star One C1",
"pos": -650
},
{
"name": "Telstar 14R",
"pos": -630
},
{
"name": "EchoStar 12/16",
"pos": -615
},
{
"name": "Amazonas 2/3/4A",
"pos": -610
},
{
"name": "Intelsat 21",
"pos": -580
},
{
"name": "Amazonas 1/Galaxy 11/Intelsat 805",
"pos": -555
},
{
"name": "Intelsat 23",
"pos": -530
},
{
"name": "Intelsat 1R",
"pos": -500
},
{
"name": "NSS 806",
"pos": -475
},
{
"name": "Intelsat 14",
"pos": -450
},
{
"name": "Intelsat 9/11",
"pos": -431
},
{
"name": "SES 6",
"pos": -405
},
{
"name": "NSS 10/Telstar 11N",
"pos": -375
},
{
"name": "Intelsat 903",
"pos": -345
},
{
"name": "Hylas 1",
"pos": -335
},
{
"name": "Intelsat 25",
"pos": -315
},
{
"name": "Hispasat 1D/1E",
"pos": -300
},
{
"name": "Intelsat 907",
"pos": -275
},
{
"name": "Intelsat 905",
"pos": -245
},
{
"name": "SES 4",
"pos": -220
},
{
"name": "NSS 7",
"pos": -200
},
{
"name": "Intelsat 901",
"pos": -180
},
{
"name": "Telstar 12",
"pos": -150
},
{
"name": "Express A4",
"pos": -140
},
{
"name": "Eutelsat 12 West A",
"pos": -125
},
{
"name": "Express AM44",
"pos": -110
},
{
"name": "Eutelsat 8 West A",
"pos": -80
},
{
"name": "Eutelsat 7 West A/Eutelsat 8 West C/Nilesat 102/201",
"pos": -73
},
{
"name": "Eutelsat 5 West A",
"pos": -50
},
{
"name": "Amos 2/3",
"pos": -40
},
{
"name": "Thor 5/6/Intelsat 10-02",
"pos": -8
},
{
"name": "Eutelsat 3B/Rascom QAF 1R",
"pos": 31
},
{
"name": "Astra 4A/SES 5",
"pos": 49
},
{
"name": "Eutelsat 7A/7B",
"pos": 70
},
{
"name": "Eutelsat 9A/Ka-Sat 9A",
"pos": 90
},
{
"name": "Eutelsat 10A",
"pos": 100
},
{
"name": "Eutelsat Hot Bird 13B/13C/13D",
"pos": 130
},
{
"name": "Eutelsat 16A",
"pos": 160
},
{
"name": "Amos 5",
"pos": 170
},
{
"name": "Astra 1KR/1L/1M/1N",
"pos": 192
},
{
"name": "Arabsat 5C",
"pos": 200
},
{
"name": "Eutelsat 21B",
"pos": 215
},
{
"name": "Astra 3B",
"pos": 235
},
{
"name": "Eutelsat 25B/Es'hail 1",
"pos": 255
},
{
"name": "Badr 4/5/6",
"pos": 260
},
{
"name": "Astra 2A/2C/2E/2F/Eutelsat 28A",
"pos": 282
},
{
"name": "Arabsat 5A",
"pos": 305
},
{
"name": "Eutelsat 31A",
"pos": 308
},
{
"name": "Astra 5B",
"pos": 315
},
{
"name": "Eutelsat 33B/Intelsat 28",
"pos": 330
},
{
"name": "Eutelsat 36A/36B",
"pos": 360
},
{
"name": "Paksat 1R",
"pos": 380
},
{
"name": "Hellas Sat 2",
"pos": 390
},
{
"name": "Türksat 2A/3A/4A",
"pos": 420
},
{
"name": "Intelsat 12",
"pos": 450
},
{
"name": "AzerSpace 1/Africasat 1a",
"pos": 460
},
{
"name": "Intelsat 10",
"pos": 475
},
{
"name": "Afghansat 1",
"pos": 480
},
{
"name": "Yamal 202",
"pos": 490
},
{
"name": "NSS 5",
"pos": 505
},
{
"name": "Y1A",
"pos": 525
},
{
"name": "Express AM22",
"pos": 530
},
{
"name": "G-Sat 8/Yamal 402",
"pos": 549
},
{
"name": "Express AT1",
"pos": 560
},
{
"name": "NSS 12",
"pos": 570
},
{
"name": "Intelsat 904",
"pos": 600
},
{
"name": "Intelsat 902",
"pos": 620
},
{
"name": "Intelsat 906",
"pos": 642
},
{
"name": "Intelsat 17",
"pos": 660
},
{
"name": "Intelsat 20",
"pos": 685
},
{
"name": "Eutelsat 70B",
"pos": 705
},
{
"name": "Intelsat 22",
"pos": 721
},
{
"name": "Insat 3C/4CR",
"pos": 740
},
{
"name": "ABS 2",
"pos": 750
},
{
"name": "Apstar 7",
"pos": 765
},
{
"name": "Thaicom 5/6",
"pos": 785
},
{
"name": "G-Sat 10/Insat 4A",
"pos": 830
},
{
"name": "Horizons 2/Intelsat 15",
"pos": 851
},
{
"name": "KazSat 2",
"pos": 865
},
{
"name": "ChinaSat 12",
"pos": 875
},
{
"name": "ST 2",
"pos": 880
},
{
"name": "Yamal 300K/401",
"pos": 900
},
{
"name": "Measat 3/3a/3b",
"pos": 915
},
{
"name": "ChinaSat 9",
"pos": 922
},
{
"name": "Insat 3A/4B",
"pos": 935
},
{
"name": "NSS 6/SES 8",
"pos": 950
},
{
"name": "Express AM33",
"pos": 965
},
{
"name": "AsiaSat 5",
"pos": 1005
},
{
"name": "Express AM3",
"pos": 1030
},
{
"name": "AsiaSat 7/8",
"pos": 1055
},
{
"name": "NSS 11/SES 7/Telkom 1",
"pos": 1082
},
{
"name": "BSAT 3A/3C/JCSAT 110R/N-Sat 110",
"pos": 1100
},
{
"name": "ChinaSat 10",
"pos": 1105
},
{
"name": "Koreasat 5/Palapa D",
"pos": 1130
},
{
"name": "ChinaSat 6B",
"pos": 1155
},
{
"name": "ABS 7/Koreasat 6",
"pos": 1160
},
{
"name": "AsiaSat 3S/Telkom 2",
"pos": 1180
},
{
"name": "Thaicom 4",
"pos": 1195
},
{
"name": "AsiaSat 4",
"pos": 1222
},
{
"name": "JCSAT 4B",
"pos": 1240
},
{
"name": "ChinaSat 6A",
"pos": 1250
},
{
"name": "JCSAT 3A",
"pos": 1280
},
{
"name": "JCSAT 5A/Vinasat 1/2",
"pos": 1320
},
{
"name": "Apstar 6",
"pos": 1340
},
{
"name": "Telstar 18",
"pos": 1380
},
{
"name": "Express AM5/AT2",
"pos": 1400
},
{
"name": "Superbird C2",
"pos": 1440
},
{
"name": "JCSAT 1B",
"pos": 1500
},
{
"name": "Optus D2",
"pos": 1520
},
{
"name": "JCSAT 2A",
"pos": 1540
},
{
"name": "Optus C1/D3",
"pos": 1560
},
{
"name": "ABS 6",
"pos": 1590
},
{
"name": "Optus D1",
"pos": 1600
},
{
"name": "Superbird B2",
"pos": 1620
},
{
"name": "Optus 10/B3",
"pos": 1640
},
{
"name": "Intelsat 19",
"pos": 1660
},
{
"name": "Intelsat 8",
"pos": 1690
},
{
"name": "Eutelsat 172A",
"pos": 1720
},
{
"name": "Intelsat 18",
"pos": 1800
}
]

View file

@ -55,29 +55,7 @@ The columns have the following functions:
<dd>
IPv4 prefix for matching based on source IP address.
If set to 0.0.0.0/0 it will match everything.
<dt><b>Streaming</b>
<dd>
Enables access to streaming functionality. This permission is enough to stream over HTSP to VLC, Showtime and similar.
<dt><b>Advanced Streaming</b>
<dd>
Enables access to advanced streaming function for HTTP - like direct
service or whole MPEG-TS stream (mux)..
<dt><b>Streaming Profile</b>
<dd>
Specify a streaming profile to be used when this user logs in; use the (default) stream if not specified.
<dt><b>Video Recorder</b>
<dd>
Enables access to all video recording functions. This also include administration of the auto recordings.
<dt><b>DVR Config Profile</b>
<dd>
If set, the user will only be able to use the DVR config profile
equal to this value.
Note that this field is unset when the DVR Config Profile is removed.
The multiple networks can be delimited using comma or semicolon.
<dt><b>Web interface</b>
<dd>
@ -86,11 +64,51 @@ The columns have the following functions:
<dt><b>Admin</b>
<dd>
Enables access to the Configuration tab.
<dt><b>Streaming</b>
<dd>
Enables access to streaming functionality for HTTP (web).
<dt><b>Advanced Streaming</b>
<dd>
Enables access to advanced streaming function for HTTP (web) - like direct
service or whole MPEG-TS stream (mux)..
<dt><b>HTSP Streaming</b>
<dd>
Enables access to streaming for the HTSP protocol (Movian, Kodi etc.).
<dt><b>Streaming Profile</b>
<dd>
Specify a streaming profile to be used when this user logs in; use the (default) stream if not specified.
<dt><b>Limit Connections</b>
<dd>
If set, this will limit the number of concurrent streaming connections a user is permitted to have. 0=disabled
<dt><b>Video Recorder</b>
<dd>
Enables access to all video recording functions. This also include administration of the auto recordings.
<dt><b>HTSP DVR</b>
<dd>
Enables access to video recording functions for the HTSP protocol (Movian, Kodi etc.).
<dt><b>All DVR</b>
<dd>
Enable to access to DVR entries created by other users (read-only).
<dt><b>All DVR (rw)</b>
<dd>
Enable to access to DVR entries created by other users with the ability to
remove the DVR entries.
<dt><b>DVR Config Profile</b>
<dd>
If set, the user will only be able to use the DVR config profile
equal to this value.
Note that this field is unset when the DVR Config Profile is removed.
<dt><b>Min Channel Num</b>
<dd>
If non-zero, this sets the lower limit of the channels accessible by a user, i.e. the user will only be able to access channels where the channel number is equal to or greater than this value.
@ -120,7 +138,7 @@ Let's also take a look at an example:
<p>
First line gives clients originating from 192.168.0.0 - 192.168.0.255 network
access to streaming functions. Typically you would use this for your
local media players at home (All though Showtime can prompt for username & password
local media players at home (All though Movian can prompt for username & password
in its HTSP client)
<p>
The second line adds a user with world wide access who might want to modify

View file

@ -1,5 +1,10 @@
<div class="hts-doc-text">
<p>
To use bouquets, ensure to add and scan all available muxes using
the predefined muxes or manual configuration.
</p>
<p>
The bouquets are obtained automatically from the DVB source during
the mux scan period. Note that bouquets may use more muxes and only

View file

@ -155,6 +155,18 @@
pmt_mode = 4
</dl>
<dt>mode 5 (new OSCam since revision 10087)
<dd>Similar to mode 3 (TCP), but uses a new network protocol which also added
client/server greeting messages and protocol version information (to be able
to smoothly detect enhancements in the future).<br><br>
<b>This is currently the preferred mode (the other may be removed in future)!</b><br><br>
The following lines are required in <b>[dvbapi]</b> section of oscam.conf file:
<dl>
<dt>boxtype = pc<br>
pmt_mode = 4<br>
listen_port = 9000 # or your preferred port<br>
</dl>
<dt>Comment
<dd>Allows the administrator to set a comment only visible in this editor.
It does not serve any active purpose.

View file

@ -21,6 +21,23 @@
The columns have the following functions:
<dl>
<dt>Play
<dd>Direct play link using the HTTP streaming.
<dl>
<dt>URL to the stream using a channel name
<dd>http://<i>host:port</i>/play/stream/channelname/<i>channel_name</i>
<dt>URL to the stream using a channel number
<dd>http://<i>host:port</i>/play/stream/channelnumber/<i>channel_number</i>
</dl>
<dt>Enabled
<dd>Whether or not the mux is enabled and thus available.
<dd>Channel number. This is not used by Tvheadend internally, but rather
intended to be used by HTSP clients for mapping to remote control
buttons, presentation order, etc
<dt>Number
<dd>Channel number. This is not used by Tvheadend internally, but rather
intended to be used by HTSP clients for mapping to remote control
@ -36,6 +53,12 @@
display a direct link that can be used to open in preferred media
player.
<dt>Auto EPG Channel
<dd>Auto-link EPG channels from XMLTV and OpenTV EPG grabbers using
the channel name for matching. If you turn this option off, only
OTA EPG grabber will be used for this channel unless the EPG Grab
Source option (bellow) is not set manually.
<dt>EPG Grab Source
<dd>Name of the Internet-based EPG provider (typically XMLTV) channel
that should be used to update this channels EPG info.
@ -54,10 +77,13 @@
in the EPG if you have many channels. The tags are also presented
in a Media player.
<dt>Icon
<dt>User Icon
<dd>An URL pointing to an image representing the channel.
The icon URL will be set automatically when importing data from
XMLTV. This field allows the user to edit it manually.
XMLTV, when picon path is set or when channel icon path is set
in the general config. This field allows the user to edit it manually. The
reset icon action allows to re-set the automatic URL for
selected channel (e.g. after configuration change).
<dt>DVR Pre-Start
<dd>Allows the user to specify an amount of extra time that should

View file

@ -60,8 +60,13 @@
<dd>If checked, broadcasts with matching title and matching non-zero episode number
are considered duplicates.
<dt>EPG update window
<dd>Maximum difference between event start times when the EPG event is changed.
TVHeadend uses a fuzzy match logic (using title, start times,
duration, episode) to check when the event was changed.
<dt>Post-processor command
<dd>Command to run after finishing a recording. The command will be run in background and is executed even if a recording is aborted or an error occurred. Use the %e error formatting string to check for errors, the error string is empty if recording finished successfully.
<dd>Command to run after finishing a recording. The command will be run in background and is executed even if a recording is aborted or an error occurred. Use the %e error formatting string to check for errors, the error string is "OK" if recording finished successfully.
<br><br>
Support format strings:<br>
<table class="hts-doc-text" border="0">
@ -171,10 +176,19 @@
<dd>If checked, insert the episode number before the data and time rather than after (assumes <i>Include date</i>, <i>Include time</i> and <i>Include episode</i> options are set).
<dt>Remove all unsafe characters from filename
<dd>If checked, all characters that could possibly cause problems for filenaming will be removed.
<dd>If checked, all characters that could possibly cause problems for filenaming will be replaced with '_'.<br>
Applies to characters:<br>
* not supported by Windows characters: <i>/ : \ < > | * ? ' "</i><br>
* control characters (ASCII code below 32)<br>
* control and national characters (ASCII code above 122)<br>
<dt>Replace whitespace in title with '-'
<dd>If checked, all whitespace characters will be replaced with '-'.
<dd>If checked, whitespace characters (spaces and tabs) will be replaced with '-'.
<dt>Use Windows-compatible filenames
<dd>If checked:<br>
* special characters not supported by Windows like: <i>/ : \ < > | * ? ' "</i> will be replaced with '_'<br>
* trailing spaces ' ' and dots '.' will be removed
</dl>
Changes to any of these settings must be confirmed by pressing the 'Save configuration' button before taking effect.

View file

@ -1,7 +1,7 @@
<div class="hts-doc-text">
This table defines rules to filter and order the elementary streams
like video or audio from the input feed.
(PIDs) like video or audio from the input feed.
<p>
The execution order of commands is granted. It means that first rule
@ -90,10 +90,20 @@ The columns have the following functions:
<dt>USE
<dd>Use this elementary stream.
<dt>ONCE
<dd>Use this elementary stream only once per service type.
The language is distinguished, too.
The first successfully compared rule wins.
<dt>ONE_TIME
<dd>Use this elementary stream only one time per service type (like
video, audio, subtitles) and language. The first sucessfully
compared rule wins. For example, when one AC3 elementary stream
is marked to be used with 'eng' language and another rule with
the ONE_TIME action was matched, the new AC3 elementary stream
will not be added if the language for new AC3 elementary stream
is 'eng'. Note that the second rule might not have the language
filter (column) set.
<p>
For the CA filter, this rule means that the new CA elementary stream
is added only if another CA is not already used.
</p>
<dt>EXCLUSIVE
<dd>Use only this elementary stream. No other elementary streams

View file

@ -60,16 +60,44 @@
<dt>Update tolerance (milliseconds)</dt>
<dd>Only update the system clock (doesn't affect NTP driver) if the delta
between the system clock and DVB time is greater than this. This can help
stop horrible oscillations on the system clock.</dd>
stop excessive oscillations on the system clock.</dd>
<br><br>
<hr>
<b>Picon</b>
<hr>
<dd>Picons (from p ersonal icons) are collections of similar icons that can be automatically
matched against your channels based on a number of technical parameters that will uniquely
define a channel. The use of these parameters (e.g. mux, frequency, orbital position)
removes the ambiguity of using names - it's not case sensitive, it doesn't care if there
are spaces or not, and so on.
You can generate picons yourself from existing images, or you can usually find sets
pre-made on the Internet if you search for them. They're a good way to get large numbers
of icons matched quickly, and usually in a similar style (such as square, x * y pixels, with
a consistent highlight/reflection effect).</dd>
<dl>
<dt>Prefer picons over channel name:</dt>
<dd>If both a picon and a channel-specific (e.g. channelname.jpg) icon are defined, use the picon.</dd>
<dt>Channel icon path</dt>
<dd>Path to an icon for this channel. This can be named however you wish, as a local (file://) or remote (http://) image.
The following placeholders are available:<br>
<ul>
<li>%C - the transliterated channel name in ASCII (safe characters, no spaces etc.)</li>
<li>%c - the channel name (URL encoded ASCII)</li>
</ul>
Example: file:///tmp/icons/%C.png or http://example.com/%c.png</dd>
<dt>Picon path</dt>
<dd>Path to a directory (folder) containing your picon collection. This can be named however
you wish, as a local (file://) or remote (http://) location - however, remember that it's pointing
to a directory as the picon names are automatically generated from the service parameters
frequency, orbital position, etc.).<br>
Example: file:///home/hts/picons</dd>
</dl>
<br><br>
<hr>
<b>Transcoding</b>
<hr>
<dd>If enabled at build time (src/plumbing/transcoding.c), this allows you to switch transcoding support on and off.</dd>
<br><br>
<hr>
@ -103,5 +131,72 @@
</dl>
<br><br>
<hr>
<b>SAT&gt;IP Server</b>
<hr>
<dd>SAT&gt;IP Server is something like DVB network tuner. TVHeadend can
forward mpegts input streams including on-the-fly descramling to SAT&gt;IP
clients.</dd>
<dd>Only networks with the "SAT>IP Source" field set are exported
through the SAT&gt;IP protocol. This field is matched through the "src"
parameter asked from the SAT>IP client. Usually (and by default) this value is 1.
For satellite tuners, this value determines the satellite source (dish).
By specification position 1 = DiseqC AA, 2 = DiseqC AB, 3 = DiseqC BA,
4 = DiseqC BB, but any numbers may be used - depends on the SAT&gt;IP client.
Note that if you use a similar number for multiple networks, the first matched
network containing the mux with requested parameters will win
(also for unknown mux).</dd>
<dl>
<dt>RTSP Port
<dd>
Select RTSP port (TCP) for realtime commands from SAT&gt;IP clients. Usually
(as defined in the specification) this port is 554. But as extension,
TVHeadend can use any TCP port value (which is default 9983 for non-root
users). But the SAT&gt;IP client must allow to set this value (TVHeadend
client will obtain the RTSP port number automatically using the XML
description). If the RTSP port value is zero, the SAT&gt;IP server
functionality is not enabled.
<dt>Subscription Weight
<dd>
Subscription weight value. Default value is 100 (standard streaming). Note
that the default value for DVR is 300 (normal priority).
<dt>Descramble Services
<dd>
The maximum limit of services descrambled per a mux. If zero, the
descrambling functionality is disabled.
<dt>Muxes Handling
<dd>
When SAT&gt;IP client requests new mux configuration, tvheadend can handle it
in three ways. The auto (0) configuration means that if the mux does not exists,
a temporary mux is created and removed when the client closes the
connection. The keep (1) configuration will remember all successfuly scanned muxes.
The reject (2) configuration will reject unknown muxes.
<dt>Exported DVB-T/T2 Tuners
<dd>
Exported DVB-T/T2 tuners - streaming instances.
<dt>Exported DVB-S/S2 Tuners
<dd>
Exported DVB-S/S2 tuners - streaming instances.
<dt>Exported DVB-C/C2 Tuners
<dd>
Exported DVB-C/C2 tuners - streaming instances.
<dt>Exported ATSC/DVB-C(AnnexB) Tuners
<dd>
Exported ATSC/DVB-C(AnnexB) - streaming instances.
</dl>
</dl>
</div>

View file

@ -73,6 +73,35 @@
<dt>URL
<dd>Mux URL.
<dl>
<dt>udp://
<dd>Raw MPEG-TS UDP packets
<dt>rtp://
<dd>MPEG-TS UDP packets with RTP header
<dt>http://
<dd>HTTP stream (MPEG-TS)
<dt>https://
<dd>Secure HTTP stream (MPEG-TS)
<dt>pipe://
<dd>Read standard output from an external program.
If the program name does not have the first
character '/', the PATH environment variable
is used to find the program name in all
directories specified by PATH.
Additional arguments may be separated
using spaces. A raw MPEG-TS stream is
expected. The string ${service_name}
is substituted with the service name
field contents. The \ (backslash) character means
"take the next character asis" (usually
space or the backslash itself.
</dl>
<dt># Services
<dd>The number of services found on this mux.
@ -96,5 +125,12 @@
<dt>Streaming Priority
<dd>IPTV : The mux priority value for streamed channels through HTTP or HTSP (higher value = higher priority to use services from this mux). Value 0 means use the standard streaming network priority value.
<dt>Environment (pipe)
<dd>IPTV : List of environment variables for pipe (like PATH=/bin:/sbin)
separated by spaces. The backslash character is handled like
in URL.
<dt>Respawn (pipe)
<dd>IPTV : Respawn the executed process when it dies.
</dl>
</div>

View file

@ -55,7 +55,11 @@ Buttons have the following functions:
<dd>Whether automatic discovery is enabled for this network, i.e. whether Tvheadend looks for muxes or simply stays with the list of muxes as defined initially.
<p>
<dt><b>Skip initial Scan</b>
<dd>Don't scan this network for muxes at Tvheadend start.
<dd>Don't scan all muxes in this network at Tvheadend start.
The initial scan procedure is not a blind scan. Only known muxes
registered to this network are scanned. If Network Discovery
is enabled and new muxes are discovered using DVB
descriptors, these muxes will be scanned too.
<p>
<dt><b>Idle Scan Muxes</b>
<dd>When nothing else happens Tvheadend will continuously rotate among all muxes and tune to them to verify that they are still working
@ -79,6 +83,23 @@ Buttons have the following functions:
<dd>If you experience problems caused by overlaps between multiple network
providers this option can be used to filter which network ID is received
by a given adapter.
<p>
<dt><b>Ignore Provider's Channel Numbers</b>
<dd>Do not use the local channel numbers defined by provider.
<p>
<dt><b>SAT&gt;IP Source Number</b>
<dd>This field is matched through the "src" parameter asked from the
SAT&gt;IP client. Usually (and by default) this value is 1.
For satellite tuners, this value determines the satellite source (dish).
By specification position 1 = DiseqC AA, 2 = DiseqC AB, 3 = DiseqC BA,
4 = DiseqC BB, but any numbers may be used - depends on the SAT&gt;IP
client. Note that if you use same number for multiple networks,
the first matched network containing the mux with requested parameters
will win (also for unknown mux). If this field is set to zero,
the network cannot be used by the SAT&gt;IP server.</dd>
<p>
<dt><b>EIT Local Time</b>
<dd>EPG (EIT) events uses local time instead UTC.
<p>
<dt><b>Character Set</b>
<dd>The character encoding for this network (e.g. UTF-8).

View file

@ -18,6 +18,17 @@
<dt>Enabled
<dd>Whether or not this service is available for use
<dt>Automatic Checking
<dd>Check for the service presence. If service is no longer broadcasted,
this field will become as "Missing In PAT/SDT". The
check can be also disabled for given service using this
column.
<dt>Priority
<dd>Define priority (range 0-10) for this service. The higher value means more preferred.
Note that this value is _added_ to the input (tuner) priority.
Take this in account when you set the input priorities.
<dt>Channel
<dd>The channel to which the service is mapped

View file

@ -0,0 +1,41 @@
<div class="hts-doc-text">
<p>
<br>
<hr>
<b>Buttons</b>
<hr>
Buttons have the following functions:
<br><br>
<dl>
<dt><b>Add</b>
<dd>
Add a new profile. You can choose from any of the types from the list.
<p>
<dt><b>Delete</b>
<dd>
Delete an existing profile.
<p>
<dt><b>Save</b>
<dd>
Saves any changes.
<p>
<dt><b>Undo</b>
<dd>
Undoes any changes.
<p>
</dl>
<p>
<br>
<hr>
<b>Columns</b>
<hr>
The columns have the following functions:
<dl>
<dt><b>Stream Profile Name</b>
<dd>This column contains the name of Stream Profile.
</dl>
</div>

View file

@ -7,12 +7,12 @@
Tags are used to define a set of channels.
Notice that nothing prohibits a channel to be a member of multiple tags.
Also, there is no requirement to configure tags for running Tvheadend
itself. It is, however, required if you run Tvheadend together with Showtime.
itself. It is, however, required if you run Tvheadend together with Movian.
<p>
The tag-sets are used for:
<ul>
<li>Searches in the EPG.
<li>Display of channel groups in the Showtime Media player.
<li>Display of channel groups in the Movian Media player.
</ul>
<p>
@ -48,9 +48,16 @@
automatic recordings, groups, etc.
<dt>Internal
<dd>Tags are exported via HTSP (to the Showtime Media player) and used
there for grouping of TV channels. If you do not wish to export a
tag you can flag it as internal only.
<dd>Tags are exported via HTSP/HTTP and used there for grouping of
TV channels. If you do not wish to export a tag you can flag
it as internal only.
<dt>Private
<dd>Tags are exported via HTSP/HTTP and used there for grouping of TV
channels. If you do not wish to export a tag to other users you can
flag it as private only. Only users with this tag configured
in the access configuration (or users with not set tags) can
use it.
<dt>Icon
<dd>Full path to an icon used to depict the tag. This can be a TV network

View file

@ -36,6 +36,11 @@
specify an unlimited period its highly recommended you specifying a value
here.
<dt>Max. RAM Size (MegaBytes)
<dd>Specifies the maximum RAM (system memory) size for timeshift buffers.
When free RAM buffers are available, they are used instead storage to
save the timeshift data.
<dt>Unlimited:
<dd>If checked, this allows the combined size of all timeshift buffers to
potentially grow unbounded until your storage media runs out of space

View file

@ -30,20 +30,6 @@ The rows have the following functions
<p>
<dt><b>Networks</b></dt>
<dd>Associate this device with one or more networks.</dd>
<p>
<dt><b>Init Scan</b></dt>
<dd>Allow the initial scan tuning on this device.</dd>
<p>
<dt><b>Idle Scan</b></dt>
<dd>Allow the idle scan tuning on this device.</dd>
<p>
<dt><b>Power Save</b></dt>
<dd>If enabled, allows the tuner to go to sleep when idle.</dd>
<p>
<dt><b>Skip Initial Bytes</b></dt>
<dd>If set, first bytes from the MPEG-TS stream are discarded. It may be
required for some drivers / hardware which does not flush completely
the MPEG-TS buffers after a frequency/parameters change.</dd>
<p>
</dl>
<dt><u><i><b>Advanced Settings</b></i></u></dt>
@ -56,9 +42,90 @@ The rows have the following functions
<dd>The tuner priority value for streamed channels through HTTP or HTSP
(higher value = higher priority to use this tuner). If not set (zero),
the standard priority value is used.</dd>
<p>
<dt><b>Init Scan</b></dt>
<dd>Allow the initial scan tuning on this device. See to
Skip initial Scan in the network settings for the further description.</dd>
<p>
<dt><b>Idle Scan</b></dt>
<dd>Allow the idle scan tuning on this device.</dd>
<p>
<dt><b>Linked Input</b></dt>
<dd>Always make alive also the linked input. The subscriptions are named as "keep".</dd>
</dl>
</p>
<br>
<hr>
<b>LinuxDVB Specific Rows</b>
<hr>
<dl>
<dt><b>Power Save</b></dt>
<dd>If enabled, allows the tuner to go to sleep when idle.</dd>
<p>
<dt><b>Tune Before DiseqC</b></dt>
<dd>If set, one tune request (setup) is proceed before the DiseqC
sequence (voltage, tone settings). Some linux drivers require this
procedure.</dd>
<p>
<dt><b>Tune Repeats</b></dt>
<dd>If set, the tune requests are repeated using this number. Zero means
one tune requests, one two tune requests etc.</dd>
<p>
<dt><b>Skip Initial Bytes</b></dt>
<dd>If set, first bytes from the MPEG-TS stream are discarded. It may be
required for some drivers / hardware which does not flush completely
the MPEG-TS buffers after a frequency/parameters change.</dd>
<p>
<dt><b>Input Buffer (Bytes)</b></dt>
<dd>By default, linuxdvb input buffer is 18800 bytes long. The accepted
range is 18800-1880000 bytes.</dd>
<p>
<dt><b>Status Period</b></dt>
<dd>By default, linuxdvb status read period is 1000ms (one second). The
accepted range is 250ms to 8000ms. Note that for some hardware /
drivers (like USB), the status operations takes too much time and CPU.
In this case, increase the default value. For fast hardware, this value
might be descreased to make the decision of the re-tune algorithm
based on the signal status faster.</dd>
<p>
<dt><b>Force old status</b></dt>
<dd>Always use the old ioctls to read the linuxdvb status (signal strenght,
SNR, error counters). Some drivers are not matured enough to provide
the correct values using the new v5 linuxdvb API.</dd>
</dl>
<br>
<hr>
<b>LinuxDVB Satellite Config Rows</b>
<hr>
<dl>
<dt><b>DiseqC repeats</b></dt>
<dd>Number of repeats for the DiseqC commands (default is zero - no DiseqC repeats).</dd>
<p>
<dt><b>Full DiseqC</b></dt>
<dd>Always sent the whole DiseqC sequence including LNB setup (voltage, tone).
If this is not checked, only changed settings is set. It may cause
issues with some drivers. If the tuning is not reliable, try to
activate this option.</dd>
<p>
<dt><b>Turn off LNB when idle</b></dt>
<dd>Turn off LNB when it is not used. It may save some power.</dd>
<p>
<dt><b>Switch Then Rotor</b></dt>
<dd>If the DiseqC switch is before rotor (tuner - switch - rotor), enable this.</dd>
<p>
<dt><b>Init Rotor Time (seconds)</b></dt>
<dd>Upon new start, tvheadend does not know the last rotor position. This
value defined the initial rotor movement. TVHeadend waits the
specified seconds when the first movement is requested.</dd>
<dt><b>Min Rotor Time (seconds)</b></dt>
<dd>The minimum delay after the rotor movement command is send.</dd>
</dl>
<p>
<br>
<hr>
<b>SAT>IP Specific Rows</b>
@ -85,6 +152,9 @@ setting this to 100.</dd>
<p>
<dt><b>PIDs in setup</b></dt>
<dd>Enable, if the SAT>IP box requires pids=0 parameter in the SETUP RTSP command.</dd>
<p>
<dt><b>Double RTSP Shutdown</b></dt>
<dd>Enable, if the SAT>IP box might require to send twice the RTSP SHUTDOWN command.</dd>
<p>
<dt><b>Force pilot for DVB-S2</b></dt>
<dd>Enable, if the SAT>IP box requiest plts=on parameter in the SETUP RTSP
@ -124,5 +194,6 @@ setting this to 100.</dd>
quick continuous tuning.</dd>
</dl>
</p>
</div>

View file

@ -51,10 +51,22 @@ Check or clear this box to enable or disable this rule.
<dd>
The name you've given to the rule, e.g. 'Stuff involving Jeremy Clarkson'.
<p>
<dt><b>Directory</b>
<dd>
When specified, this setting overrides the subdirectory rules (except the base directory) specified by the DVR configuration
and puts all recordings done by this entry into the specified subdirectory. Useful for e.g. recording multiple different news
broadcasts into one common subdirectory called "News". The backshlash and other special characters are escaped, so it is possible
to create only one sublevel subdirectories (the base path for the target directory is always taken from the DVR configuration).
<p>
<dt><b>Title (Regexp)</b>
<dd>
The title of the programme to look for. Note that this accepts case-insensitive regular expressions, so you can use pattern matching as Tvheadend scans the EPG for programmes to record.
<p>
<dt><b>Fulltext</b>
<dd>
When the fulltext is checked, the title pattern is matched against title,
subtitle, summary and description.
<p>
<dt><b>Channel</b>
<dd>
The channel on which this rule applies, i.e. the channel you're aiming to record.
@ -79,15 +91,13 @@ The maximal duration of a matching event - in other words, only match programmes
<dd>
On which specific days of the week to find matching programmes.
<p>
<dt><b>Starting Around</b>
<dt><b>Start After</b>
<dd>
An approximate starting time for matching programmes.
<br>
<br>
I'd need to check the code to see how this works to expand on this any further. It used to be:
<br>
<br>
Only record events if they are scheduled +-15 minutes from this given time.
An event which starts between this "start after" and "start before" will be matched (including boundary values).
<p>
<dt><b>Start Before</b>
<dd>
An event which starts between this "start after" and "start before" will be matched (including boundary values).
<p>
<dt><b>Priority</b>
<dd>

View file

@ -50,16 +50,25 @@ Check or clear this box to enable or disable this rule.
<dd>
The name you've given to the rule, e.g. 'Evening cartoons for the children'.
<p>
<dt><b>Directory</b>
<dd>
When specified, this setting overrides the subdirectory rules specified by the DVR configuration and puts all recordings done by this entry into the specified subdirectory. Useful for e.g. recording multiple different news broadcasts into one common subdirectory called "News".
<p>
<dt><b>Title</b>
<dd>
Not sure how this differs from **Name* *
The title is used in the filename that is created for the recording
<br>
<br>
The default format string suggests Time-[date]-[time]:
The default format string suggests Time-[date]_[time]:
<br>
<br>
%x The local date, formatted according to your locale settings
%R The time in HH;MM format
The escape-codes use <a href="http://man7.org/linux/man-pages/man3/strftime.3.html">strftime</a> format. Examples:
<br>
%F The date in ISO-format (e.g. 2015-02-28)
<br>
%R The time in 24h HH:MM format (e.g. 19:45)
<br>
%x The date, formatted according to your locale settings
<p>
<dt><b>Channel</b>
<dd>

View file

@ -20,7 +20,8 @@ sorted based on start time.</p>
Only display events that match the given title. The filter uses case-insensitive
regular expressions. If you don't know what a regular expression is, this simply
means that you can type just parts of the title and filter on that - there's no need
for full, exact matching.</dd>
for full, exact matching. If the fulltext checkbox is checked, the title
text is matched against title, subtitle, summary and description.</dd>
<dt>[Filter channel...]</dt>
<dd>
Only display events from the selected channel. Channels in the drop down are

View file

@ -2,12 +2,12 @@
<dl>
<dt>Why does Tvheadend deliver data over TCP to Showtime? I thought it was
<dt>Why does Tvheadend deliver data over TCP to Movian? I thought it was
bad to use TCP for realtime sensitive traffic?
<dd>
'HTSP' - the protocol used for streaming TV, sending meta information
updates and RPC between Tvheadend and Showtime uses a transmission
updates and RPC between Tvheadend and Movian uses a transmission
scheduler with multiple queues on the Tvheadend side. This means that
Tvheadend can measure the available bandwidth between itself and the
mediaplayer and when congestion happens it's even capable of dropping
@ -15,7 +15,7 @@
links and DSL connections with zero picture/audio artifacts.
<p>
It's possible to get view drop statistics and bitrates directly in
Showtime. (Open the menu when watching a TV-channel and switch on
Movian. (Open the menu when watching a TV-channel and switch on
'Detailed Information')
</div>

View file

@ -7,7 +7,7 @@
<dt>Input sources
<dl>
<dt>DVB-T, DVB-C, DVB-S, DVB-S2 and ATSC.
<dt>DVB-T, DVB-C, DVB-S, DVB-S2, ATSC and SAT>IP.
<dd>
Multiple adapters are supported.
Each adapter can receive all programs available on the currently
@ -20,13 +20,17 @@
</dd>
<dt>Analog TV
<dd>
Using the Video4Linux2 API. Currently, only PAL is supported.
The IPTV extension URL - pipe:// allow to process any MPEG-TS input.
FFMPEG or LIBAV library can be used to produce analog to digital
conversion.
</dd>
</dl>
<dt>Output targets
<dl>
<dt>HTSP (Home TV Streaming Protocol), supported by Showtime Media player and <a href="http://www.xbmc.org/">XBMC</a>
<dt>HTTP (Web Protocol), supported by <a href="http://www.videolan.org/vlc/">VLC</a>, <a href="http://www.mplayerhq.hu">MPlayer</a>
<dt>HTSP (Home TV Streaming Protocol), supported by <a href="http://movian.tv">Movian Media player</a> and <a href="http://kodi.tv/">Kodi</a>
<dt>SAT>IP Server
<dt>The Built-in Digital Video Recorder
</dl>
@ -40,7 +44,7 @@
All setup and configuration is done from the built in web user interface.
Even so, all settings are stored in human readable text files.
<dt>Fully integrated with HTS Showtime Media player.
<dt>Fully integrated with HTS Movian Media player or Kodi HTS PVR addon.
<dd>
All channel data and their grouping, EPG and TV streaming is conducted over a
single TCP connection.
@ -79,6 +83,10 @@
<dd>
Requires a card server (newcamd and capmt protocol is supported).
<dt>Hardware based CSA descrambling
<dd>
Requires the standard dvben50221 linuxdvb library.
<dt>Internationalization
<dd>
All text is encoded in UTF-8 to provide full international support. All major

View file

@ -32,7 +32,11 @@ to Showtime, XBMC and various other clients.
%build
echo %{version} > %{_builddir}/%{buildsubdir}/rpm/version
%configure --disable-lockowner --enable-bundle --enable-libffmpeg_static
%ifarch %arm
%configure --disable-lockowner --enable-bundle --disable-libffmpeg_static
%else
%configure --disable-lockowner --enable-bundle --enable-libffmpeg_static
%endif
%{__make}
%install
@ -70,3 +74,18 @@ exit 0
%{_bindir}/*
%{_sysconfdir}/sysconfig/*
%{_unitdir}/*
%changelog
* Wed Mar 25 2015 Bob Lightfoot <boblfoot@gmail.com> 3.9-2658-gb427d7e
- Patching rpm spec file so the arm architecture builds properly
* Mon Oct 13 2014 Jaroslav Kysela <perex@perex.cz> 3.9-1806-g6f3324e
- RPM: Typo fixes
* Mon Oct 13 2014 Jaroslav Kysela <perex@perex.cz> 3.9-1805-g14a7de8
- RPM build - config fixes
* Mon Oct 13 2014 Jaroslav Kysela <perex@perex.cz> 3.9-1803-g392dec0
- Add basic RPM build support
~

View file

@ -149,11 +149,19 @@ access_t *
access_ticket_verify2(const char *id, const char *resource)
{
access_ticket_t *at;
char buf[256], *r;
if((at = access_ticket_find(id)) == NULL)
return NULL;
if(strcmp(at->at_resource, resource))
if (tvheadend_webroot) {
snprintf(buf, sizeof(buf), "%s%s", tvheadend_webroot, at->at_resource);
r = buf;
} else {
r = at->at_resource;
}
if(strcmp(r, resource))
return NULL;
return access_copy(at->at_access);
@ -331,7 +339,9 @@ access_verify(const char *username, const char *password,
bits = 0;
}
return (mask & bits) == mask ? 0 : -1;
return (mask & ACCESS_OR) ?
((mask & bits) ? 0 : -1) :
((mask & bits) == mask ? 0 : -1);
}
/*
@ -342,20 +352,25 @@ static void
access_dump_a(access_t *a)
{
htsmsg_field_t *f;
size_t l = 0;
char buf[1024];
int first;
snprintf(buf, sizeof(buf),
"%s:%s [%s%s%s%s%s], conn=%u, chmin=%u, chmax=%u%s",
tvh_strlcatf(buf, sizeof(buf), l,
"%s:%s [%c%c%c%c%c%c%c%c%c], conn=%u, chmin=%llu, chmax=%llu%s",
a->aa_representative ?: "<no-id>",
a->aa_username ?: "<no-user>",
a->aa_rights & ACCESS_STREAMING ? "S" : "",
a->aa_rights & ACCESS_ADVANCED_STREAMING ? "A" : "",
a->aa_rights & ACCESS_WEB_INTERFACE ? "W" : "",
a->aa_rights & ACCESS_RECORDER ? "R" : "",
a->aa_rights & ACCESS_ADMIN ? "*" : "",
a->aa_rights & ACCESS_STREAMING ? 'S' : ' ',
a->aa_rights & ACCESS_ADVANCED_STREAMING ? 'A' : ' ',
a->aa_rights & ACCESS_HTSP_STREAMING ? 'T' : ' ',
a->aa_rights & ACCESS_WEB_INTERFACE ? 'W' : ' ',
a->aa_rights & ACCESS_RECORDER ? 'R' : ' ',
a->aa_rights & ACCESS_HTSP_RECORDER ? 'E' : ' ',
a->aa_rights & ACCESS_ALL_RECORDER ? 'L' : ' ',
a->aa_rights & ACCESS_ALL_RW_RECORDER ? 'D' : ' ',
a->aa_rights & ACCESS_ADMIN ? '*' : ' ',
a->aa_conn_limit,
a->aa_chmin, a->aa_chmax,
(long long)a->aa_chmin, (long long)a->aa_chmax,
a->aa_match ? ", matched" : "");
if (a->aa_profiles) {
@ -364,14 +379,14 @@ access_dump_a(access_t *a)
profile_t *pro = profile_find_by_uuid(htsmsg_field_get_str(f) ?: "");
if (pro) {
if (first)
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ", profile=");
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s'%s'",
tvh_strlcatf(buf, sizeof(buf), l, ", profile=");
tvh_strlcatf(buf, sizeof(buf), l, "%s'%s'",
first ? "" : ",", pro->pro_name ?: "");
first = 0;
}
}
} else {
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ", profile=ANY");
tvh_strlcatf(buf, sizeof(buf), l, ", profile=ANY");
}
if (a->aa_dvrcfgs) {
@ -380,14 +395,14 @@ access_dump_a(access_t *a)
dvr_config_t *cfg = dvr_config_find_by_uuid(htsmsg_field_get_str(f) ?: "");
if (cfg) {
if (first)
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ", dvr=");
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s'%s'",
tvh_strlcatf(buf, sizeof(buf), l, ", dvr=");
tvh_strlcatf(buf, sizeof(buf), l, "%s'%s'",
first ? "" : ",", cfg->dvr_config_name ?: "");
first = 0;
}
}
} else {
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ", dvr=ANY");
tvh_strlcatf(buf, sizeof(buf), l, ", dvr=ANY");
}
if (a->aa_chtags) {
@ -396,14 +411,14 @@ access_dump_a(access_t *a)
channel_tag_t *ct = channel_tag_find_by_uuid(htsmsg_field_get_str(f) ?: "");
if (ct) {
if (first)
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ", tags=");
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s'%s'",
tvh_strlcatf(buf, sizeof(buf), l, ", tags=");
tvh_strlcatf(buf, sizeof(buf), l, "%s'%s'",
first ? "" : ",", ct->ct_name ?: "");
first = 0;
}
}
} else {
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ", tag=ANY");
tvh_strlcatf(buf, sizeof(buf), l, ", tag=ANY");
}
tvhtrace("access", "%s", buf);
@ -793,12 +808,20 @@ access_entry_update_rights(access_entry_t *ae)
r |= ACCESS_STREAMING;
if (ae->ae_adv_streaming)
r |= ACCESS_ADVANCED_STREAMING;
if (ae->ae_htsp_streaming)
r |= ACCESS_HTSP_STREAMING;
if (ae->ae_dvr)
r |= ACCESS_RECORDER;
if (ae->ae_htsp_dvr)
r |= ACCESS_HTSP_RECORDER;
if (ae->ae_all_dvr)
r |= ACCESS_ALL_RECORDER;
if (ae->ae_webui)
r |= ACCESS_WEB_INTERFACE;
if (ae->ae_admin)
r |= ACCESS_ADMIN;
if (ae->ae_all_rw_dvr)
r |= ACCESS_ALL_RW_RECORDER;
ae->ae_rights = r;
}
@ -829,6 +852,10 @@ access_entry_create(const char *uuid, htsmsg_t *conf)
TAILQ_INIT(&ae->ae_ipmasks);
if (conf) {
/* defaults */
ae->ae_htsp_streaming = 1;
ae->ae_htsp_dvr = 1;
ae->ae_all_dvr = 1;
idnode_load(&ae->ae_id, conf);
/* note password has PO_NOSAVE, thus it must be set manually */
if ((s = htsmsg_get_str(conf, "password")) != NULL)
@ -899,7 +926,7 @@ access_destroy_by_profile(profile_t *pro, int delconf)
while ((ae = LIST_FIRST(&pro->pro_accesses)) != NULL) {
LIST_REMOVE(ae, ae_profile_link);
ae->ae_dvr_config = NULL;
ae->ae_profile = NULL;
if (delconf)
access_entry_save(ae);
}
@ -915,7 +942,7 @@ access_destroy_by_dvr_config(dvr_config_t *cfg, int delconf)
while ((ae = LIST_FIRST(&cfg->dvr_accesses)) != NULL) {
LIST_REMOVE(ae, ae_dvr_config_link);
ae->ae_profile = profile_find_by_name(NULL, NULL);
ae->ae_dvr_config = NULL;
if (delconf)
access_entry_save(ae);
}
@ -1048,7 +1075,7 @@ access_entry_class_prefix_get(void *o)
s_addr = htonl(ai->ai_network);
inet_ntop(AF_INET, &s_addr, addrbuf, sizeof(addrbuf));
}
pos += snprintf(buf+pos, sizeof(buf)-pos, ",%s/%d", addrbuf, ai->ai_prefixlen);
tvh_strlcatf(buf, sizeof(buf), pos, ",%s/%d", addrbuf, ai->ai_prefixlen);
}
return &ret;
}
@ -1252,6 +1279,12 @@ const idclass_t access_entry_class = {
.name = "Advanced Streaming",
.off = offsetof(access_entry_t, ae_adv_streaming),
},
{
.type = PT_BOOL,
.id = "htsp_streaming",
.name = "HTSP Streaming",
.off = offsetof(access_entry_t, ae_htsp_streaming),
},
{
.type = PT_STR,
.id = "profile",
@ -1266,6 +1299,24 @@ const idclass_t access_entry_class = {
.name = "Video Recorder",
.off = offsetof(access_entry_t, ae_dvr),
},
{
.type = PT_BOOL,
.id = "htsp_dvr",
.name = "HTSP DVR",
.off = offsetof(access_entry_t, ae_htsp_dvr),
},
{
.type = PT_BOOL,
.id = "all_dvr",
.name = "All DVR",
.off = offsetof(access_entry_t, ae_all_dvr),
},
{
.type = PT_BOOL,
.id = "all_rw_dvr",
.name = "All DVR (rw)",
.off = offsetof(access_entry_t, ae_all_rw_dvr),
},
{
.type = PT_STR,
.id = "dvr_config",
@ -1293,13 +1344,15 @@ const idclass_t access_entry_class = {
.off = offsetof(access_entry_t, ae_conn_limit),
},
{
.type = PT_U32,
.type = PT_S64,
.intsplit = CHANNEL_SPLIT,
.id = "channel_min",
.name = "Min Channel Num",
.off = offsetof(access_entry_t, ae_chmin),
},
{
.type = PT_U32,
.type = PT_S64,
.intsplit = CHANNEL_SPLIT,
.id = "channel_max",
.name = "Max Channel Num",
.off = offsetof(access_entry_t, ae_chmax),
@ -1359,19 +1412,23 @@ access_init(int createdefault, int noacl)
access_entry_reindex();
}
if(TAILQ_FIRST(&access_entries) == NULL) {
if(createdefault && TAILQ_FIRST(&access_entries) == NULL) {
/* No records available */
ae = access_entry_create(NULL, NULL);
free(ae->ae_comment);
ae->ae_comment = strdup("Default access entry");
ae->ae_enabled = 1;
ae->ae_streaming = 1;
ae->ae_adv_streaming = 1;
ae->ae_dvr = 1;
ae->ae_webui = 1;
ae->ae_admin = 1;
ae->ae_enabled = 1;
ae->ae_streaming = 1;
ae->ae_adv_streaming = 1;
ae->ae_htsp_streaming = 1;
ae->ae_dvr = 1;
ae->ae_htsp_dvr = 1;
ae->ae_all_dvr = 1;
ae->ae_all_rw_dvr = 1;
ae->ae_webui = 1;
ae->ae_admin = 1;
access_entry_update_rights(ae);
TAILQ_INIT(&ae->ae_ipmasks);

View file

@ -57,6 +57,7 @@ typedef struct access_entry {
int ae_streaming;
int ae_adv_streaming;
int ae_htsp_streaming;
struct profile *ae_profile;
LIST_ENTRY(access_entry) ae_profile_link;
@ -64,14 +65,17 @@ typedef struct access_entry {
uint32_t ae_conn_limit;
int ae_dvr;
int ae_htsp_dvr;
int ae_all_dvr;
int ae_all_rw_dvr;
struct dvr_config *ae_dvr_config;
LIST_ENTRY(access_entry) ae_dvr_config_link;
int ae_webui;
int ae_admin;
uint32_t ae_chmin;
uint32_t ae_chmax;
uint64_t ae_chmin;
uint64_t ae_chmax;
struct channel_tag *ae_chtag;
LIST_ENTRY(access_entry) ae_channel_tag_link;
@ -89,8 +93,8 @@ typedef struct access {
uint32_t aa_rights;
htsmsg_t *aa_profiles;
htsmsg_t *aa_dvrcfgs;
uint32_t aa_chmin;
uint32_t aa_chmax;
uint64_t aa_chmin;
uint64_t aa_chmax;
htsmsg_t *aa_chtags;
int aa_match;
uint32_t aa_conn_limit;
@ -113,13 +117,20 @@ typedef struct access_ticket {
#define ACCESS_ANONYMOUS 0
#define ACCESS_STREAMING (1<<0)
#define ACCESS_ADVANCED_STREAMING (1<<1)
#define ACCESS_WEB_INTERFACE (1<<2)
#define ACCESS_RECORDER (1<<3)
#define ACCESS_ADMIN (1<<4)
#define ACCESS_HTSP_STREAMING (1<<2)
#define ACCESS_WEB_INTERFACE (1<<3)
#define ACCESS_RECORDER (1<<4)
#define ACCESS_HTSP_RECORDER (1<<5)
#define ACCESS_ALL_RECORDER (1<<6)
#define ACCESS_ADMIN (1<<7)
#define ACCESS_ALL_RW_RECORDER (1<<8)
#define ACCESS_OR (1<<30)
#define ACCESS_FULL \
(ACCESS_STREAMING | ACCESS_ADVANCED_STREAMING | \
ACCESS_WEB_INTERFACE | ACCESS_RECORDER | ACCESS_ADMIN)
ACCESS_HTSP_STREAMING | ACCESS_WEB_INTERFACE | \
ACCESS_RECORDER | ACCESS_HTSP_RECORDER | \
ACCESS_ALL_RECORDER | ACCESS_ADMIN | ACCESS_ALL_RW_RECORDER)
/**
* Create a new ticket for the requested resource and generate a id for it
@ -153,7 +164,9 @@ int access_verify(const char *username, const char *password,
struct sockaddr *src, uint32_t mask);
static inline int access_verify2(access_t *a, uint32_t mask)
{ return (a->aa_rights & mask) == mask ? 0 : -1; }
{ return (mask & ACCESS_OR) ?
((a->aa_rights & mask) ? 0 : -1) :
((a->aa_rights & mask) == mask ? 0 : -1); }
int access_verify_list(htsmsg_t *list, const char *item);

View file

@ -25,21 +25,42 @@
#include "access.h"
#include "api.h"
static void
api_channel_key_val(htsmsg_t *dst, const char *key, const char *val)
{
htsmsg_t *e = htsmsg_create_map();
htsmsg_add_str(e, "key", key);
htsmsg_add_str(e, "val", val ?: "");
htsmsg_add_msg(dst, NULL, e);
}
static int
api_channel_is_all(access_t *perm, htsmsg_t *args)
{
return htsmsg_get_bool_or_default(args, "all", 0) &&
!access_verify2(perm, ACCESS_ADMIN);
}
// TODO: this will need converting to an idnode system
static int
api_channel_list
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
channel_t *ch;
htsmsg_t *l, *e;
htsmsg_t *l;
int cfg = api_channel_is_all(perm, args);
char buf[128];
l = htsmsg_create_list();
pthread_mutex_lock(&global_lock);
CHANNEL_FOREACH(ch) {
e = htsmsg_create_map();
htsmsg_add_str(e, "key", idnode_uuid_as_str(&ch->ch_id));
htsmsg_add_str(e, "val", channel_get_name(ch));
htsmsg_add_msg(l, NULL, e);
if (!cfg && !channel_access(ch, perm, 0)) continue;
if (!ch->ch_enabled) {
snprintf(buf, sizeof(buf), "{%s}", channel_get_name(ch));
api_channel_key_val(l, idnode_uuid_as_str(&ch->ch_id), buf);
} else {
api_channel_key_val(l, idnode_uuid_as_str(&ch->ch_id), channel_get_name(ch));
}
}
pthread_mutex_unlock(&global_lock);
*resp = htsmsg_create_map();
@ -50,12 +71,14 @@ api_channel_list
static void
api_channel_grid
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf )
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
{
channel_t *ch;
int cfg = api_channel_is_all(perm, args);
CHANNEL_FOREACH(ch)
idnode_set_add(ins, (idnode_t*)ch, &conf->filter);
if (cfg || channel_access(ch, perm, 0))
idnode_set_add(ins, (idnode_t*)ch, &conf->filter);
}
static int
@ -82,15 +105,20 @@ api_channel_tag_list
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
channel_tag_t *ct;
htsmsg_t *l, *e;
htsmsg_t *l;
int cfg = api_channel_is_all(perm, args);
char buf[128];
l = htsmsg_create_list();
TAILQ_FOREACH(ct, &channel_tags, ct_link) {
e = htsmsg_create_map();
htsmsg_add_str(e, "key", idnode_uuid_as_str(&ct->ct_id));
htsmsg_add_str(e, "val", ct->ct_name);
htsmsg_add_msg(l, NULL, e);
}
TAILQ_FOREACH(ct, &channel_tags, ct_link)
if (cfg || channel_tag_access(ct, perm, 0)) {
if (ct->ct_enabled) {
api_channel_key_val(l, idnode_uuid_as_str(&ct->ct_id), ct->ct_name);
} else {
snprintf(buf, sizeof(buf), "{%s}", ct->ct_name);
api_channel_key_val(l, idnode_uuid_as_str(&ct->ct_id), buf);
}
}
*resp = htsmsg_create_map();
htsmsg_add_msg(*resp, "entries", l);
return 0;
@ -98,12 +126,14 @@ api_channel_tag_list
static void
api_channel_tag_grid
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf )
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
{
channel_tag_t *ct;
int cfg = api_channel_is_all(perm, args);
TAILQ_FOREACH(ct, &channel_tags, ct_link)
idnode_set_add(ins, (idnode_t*)ct, &conf->filter);
if (cfg || channel_tag_access(ct, perm, 0))
idnode_set_add(ins, (idnode_t*)ct, &conf->filter);
}
static int

View file

@ -160,16 +160,19 @@ static htsmsg_t *
api_dvr_entry_create_from_single(htsmsg_t *args)
{
htsmsg_t *entries, *m;
const char *s1, *s2;
const char *s1, *s2, *s3;
if (!(s1 = htsmsg_get_str(args, "config_uuid")))
return NULL;
if (!(s2 = htsmsg_get_str(args, "event_id")))
return NULL;
s3 = htsmsg_get_str(args, "comment");
entries = htsmsg_create_list();
m = htsmsg_create_map();
htsmsg_add_str(m, "config_uuid", s1);
htsmsg_add_str(m, "event_id", s2);
if (s3)
htsmsg_add_str(m, "comment", s3);
htsmsg_add_msg(entries, NULL, m);
return entries;
}
@ -179,7 +182,7 @@ api_dvr_entry_create_by_event
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
dvr_entry_t *de;
const char *config_uuid;
const char *config_uuid, *comment;
epg_broadcast_t *e;
htsmsg_t *entries, *entries2 = NULL, *m;
htsmsg_field_t *f;
@ -199,14 +202,17 @@ api_dvr_entry_create_by_event
continue;
config_uuid = htsmsg_get_str(m, "config_uuid");
comment = htsmsg_get_str(m, "comment");
pthread_mutex_lock(&global_lock);
if ((e = epg_broadcast_find_by_id(strtoll(s, NULL, 10)))) {
dvr_config_t *cfg = dvr_config_find_by_list(perm->aa_dvrcfgs, config_uuid);
if (cfg) {
de = dvr_entry_create_by_event(idnode_uuid_as_str(&cfg->dvr_id),
e, 0, 0, perm->aa_representative,
NULL, DVR_PRIO_NORMAL, 0);
e, 0, 0,
perm->aa_username,
perm->aa_representative,
NULL, DVR_PRIO_NORMAL, 0, comment);
if (de)
dvr_entry_save(de);
}
@ -253,6 +259,8 @@ api_dvr_autorec_create
if (!(conf = htsmsg_get_map(args, "conf")))
return EINVAL;
if (perm->aa_username)
htsmsg_set_str(conf, "owner", perm->aa_username);
if (perm->aa_representative)
htsmsg_set_str(conf, "creator", perm->aa_representative);
@ -297,7 +305,9 @@ api_dvr_autorec_create_by_series
dvr_config_t *cfg = dvr_config_find_by_list(perm->aa_dvrcfgs, config_uuid);
if (cfg) {
dae = dvr_autorec_add_series_link(idnode_uuid_as_str(&cfg->dvr_id),
e, perm->aa_representative,
e,
perm->aa_username,
perm->aa_representative,
"Created from EPG query");
if (dae) {
dvr_autorec_save(dae);
@ -334,6 +344,8 @@ api_dvr_timerec_create
if (!(conf = htsmsg_get_map(args, "conf")))
return EINVAL;
if (perm->aa_username)
htsmsg_set_str(conf, "owner", perm->aa_username);
if (perm->aa_representative)
htsmsg_set_str(conf, "creator", perm->aa_representative);
@ -351,8 +363,10 @@ api_dvr_timerec_create
void api_dvr_init ( void )
{
static api_hook_t ah[] = {
{ "dvr/config/class", ACCESS_RECORDER, api_idnode_class, (void*)&dvr_config_class },
{ "dvr/config/grid", ACCESS_RECORDER, api_idnode_grid, api_dvr_config_grid },
{ "dvr/config/class", ACCESS_OR|ACCESS_ADMIN|ACCESS_RECORDER,
api_idnode_class, (void*)&dvr_config_class },
{ "dvr/config/grid", ACCESS_OR|ACCESS_ADMIN|ACCESS_RECORDER,
api_idnode_grid, api_dvr_config_grid },
{ "dvr/config/create", ACCESS_ADMIN, api_dvr_config_create, NULL },
{ "dvr/entry/class", ACCESS_RECORDER, api_idnode_class, (void*)&dvr_entry_class },

View file

@ -289,11 +289,12 @@ api_epg_grid
memset(&eq, 0, sizeof(eq));
lang = htsmsg_get_str(args, "lang");
eq.lang = lang ? strdup(lang) : NULL;
if (lang)
eq.lang = strdup(lang);
str = htsmsg_get_str(args, "title");
if (str)
eq.stitle = strdup(str);
eq.fulltext = htsmsg_get_bool_or_default(args, "fulltext", 0);
str = htsmsg_get_str(args, "channel");
if (str)
eq.channel = strdup(str);
@ -422,7 +423,7 @@ api_epg_grid
/* Query the EPG */
pthread_mutex_lock(&global_lock);
epg_query(&eq);
epg_query(&eq, perm);
/* Build response */
start = MIN(eq.entries, start);

View file

@ -49,6 +49,8 @@ api_mpegts_input_network_list
if (!mi)
goto exit;
tvhtrace("mpegts", "network-list: found input '%s'", mi->mi_name ?: "");
htsmsg_t *l = htsmsg_create_list();
if ((is = mi->mi_network_list(mi))) {
for (i = 0; i < is->is_count; i++) {
@ -134,6 +136,43 @@ api_mpegts_network_create
return err;
}
static int
api_mpegts_network_scan
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
htsmsg_field_t *f;
htsmsg_t *uuids;
mpegts_network_t *mn;
const char *uuid;
if (!(f = htsmsg_field_find(args, "uuid")))
return -EINVAL;
if ((uuids = htsmsg_field_get_list(f))) {
HTSMSG_FOREACH(f, uuids) {
if (!(uuid = htsmsg_field_get_str(f))) continue;
mn = mpegts_network_find(uuid);
if (mn) {
pthread_mutex_lock(&global_lock);
mpegts_network_scan(mn);
pthread_mutex_unlock(&global_lock);
}
}
} else if ((uuid = htsmsg_field_get_str(f))) {
mn = mpegts_network_find(uuid);
if (mn) {
pthread_mutex_lock(&global_lock);
mpegts_network_scan(mn);
pthread_mutex_unlock(&global_lock);
}
else
return -ENOENT;
} else {
return -EINVAL;
}
return 0;
}
static int
api_mpegts_network_muxclass
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
@ -288,6 +327,43 @@ api_mpegts_mux_sched_create
return err;
}
#if ENABLE_MPEGTS_DVB
static int
api_dvb_orbitalpos_list
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
htsmsg_t *l, *e, *c;
htsmsg_field_t *f;
const char *s;
int satpos, i;
char buf[128];
if (!satellites)
return 0;
l = htsmsg_create_list();
HTSMSG_FOREACH(f, satellites) {
if((c = htsmsg_get_map_by_field(f)) == NULL)
continue;
if(htsmsg_get_s32(c, "pos", &satpos))
continue;
if((s = htsmsg_get_str(c, "name")) == NULL)
continue;
e = htsmsg_create_map();
dvb_sat_position_to_str(satpos, buf, sizeof(buf));
htsmsg_add_str(e, "key", buf);
i = strlen(buf);
snprintf(buf + i, sizeof(buf) - i, " : %s", s);
htsmsg_add_str(e, "val", buf);
htsmsg_add_msg(l, NULL, e);
}
*resp = htsmsg_create_map();
htsmsg_add_msg(*resp, "entries", l);
return 0;
}
#endif
#if ENABLE_MPEGTS_DVB
static int
api_dvb_scanfile_list
@ -295,6 +371,7 @@ api_dvb_scanfile_list
{
char buf[512];
const char *type = htsmsg_get_str(args, "type");
int satpos = htsmsg_get_s32_or_default(args, "satpos", INT_MAX);
scanfile_region_list_t *list = NULL;
htsmsg_t *l, *e;
scanfile_region_t *r;
@ -317,6 +394,7 @@ api_dvb_scanfile_list
l = htsmsg_create_list();
LIST_FOREACH(r, list, sfr_link) {
LIST_FOREACH(n, &r->sfr_networks, sfn_link) {
if (satpos != INT_MAX && n->sfn_satpos != satpos) continue;
e = htsmsg_create_map();
sprintf(buf, "%s/%s/%s", type, r->sfr_id, n->sfn_id);
htsmsg_add_str(e, "key", buf);
@ -354,6 +432,7 @@ api_mpegts_init ( void )
{ "mpegts/network/create", ACCESS_ADMIN, api_mpegts_network_create, NULL },
{ "mpegts/network/mux_class", ACCESS_ADMIN, api_mpegts_network_muxclass, NULL },
{ "mpegts/network/mux_create", ACCESS_ADMIN, api_mpegts_network_muxcreate, NULL },
{ "mpegts/network/scan", ACCESS_ADMIN, api_mpegts_network_scan, NULL },
{ "mpegts/mux/grid", ACCESS_ADMIN, api_idnode_grid, api_mpegts_mux_grid },
{ "mpegts/mux/class", ACCESS_ADMIN, api_idnode_class, (void*)&mpegts_mux_class },
{ "mpegts/service/grid", ACCESS_ADMIN, api_idnode_grid, api_mpegts_service_grid },
@ -362,6 +441,7 @@ api_mpegts_init ( void )
{ "mpegts/mux_sched/grid", ACCESS_ADMIN, api_idnode_grid, api_mpegts_mux_sched_grid },
{ "mpegts/mux_sched/create", ACCESS_ADMIN, api_mpegts_mux_sched_create, NULL },
#if ENABLE_MPEGTS_DVB
{ "dvb/orbitalpos/list", ACCESS_ADMIN, api_dvb_orbitalpos_list, NULL },
{ "dvb/scanfile/list", ACCESS_ADMIN, api_dvb_scanfile_list, NULL },
#endif
{ NULL },

View file

@ -37,8 +37,10 @@ api_status_inputs
tvh_input_stream_t *st;
tvh_input_stream_list_t stl = { 0 };
pthread_mutex_lock(&global_lock);
TVH_INPUT_FOREACH(ti)
ti->ti_get_streams(ti, &stl);
pthread_mutex_unlock(&global_lock);
l = htsmsg_create_list();
while ((st = LIST_FIRST(&stl))) {
@ -67,11 +69,13 @@ api_status_subscriptions
l = htsmsg_create_list();
c = 0;
pthread_mutex_lock(&global_lock);
LIST_FOREACH(ths, &subscriptions, ths_global_link) {
e = subscription_create_msg(ths);
htsmsg_add_msg(l, NULL, e);
c++;
}
pthread_mutex_unlock(&global_lock);
*resp = htsmsg_create_map();
htsmsg_add_msg(*resp, "entries", l);
@ -120,12 +124,48 @@ api_connections_cancel
return 0;
}
static void
input_clear_stats(const char *uuid)
{
tvh_input_instance_t *tii;
pthread_mutex_lock(&global_lock);
if ((tii = tvh_input_instance_find_by_uuid(uuid)) != NULL)
if (tii->tii_clear_stats)
tii->tii_clear_stats(tii);
pthread_mutex_unlock(&global_lock);
}
static int
api_status_input_clear_stats
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
htsmsg_field_t *f;
htsmsg_t *ids;
const char *uuid;
if (!(f = htsmsg_field_find(args, "uuid")))
return EINVAL;
if (!(ids = htsmsg_field_get_list(f))) {
if ((uuid = htsmsg_field_get_str(f)) == NULL)
return EINVAL;
input_clear_stats(uuid);
} else {
HTSMSG_FOREACH(f, ids) {
if ((uuid = htsmsg_field_get_str(f)) == NULL) continue;
input_clear_stats(uuid);
}
}
return 0;
}
void api_status_init ( void )
{
static api_hook_t ah[] = {
{ "status/connections", ACCESS_ADMIN, api_status_connections, NULL },
{ "status/subscriptions", ACCESS_ADMIN, api_status_subscriptions, NULL },
{ "status/inputs", ACCESS_ADMIN, api_status_inputs, NULL },
{ "status/inputclrstats", ACCESS_ADMIN, api_status_input_clear_stats, NULL },
{ "connections/cancel", ACCESS_ADMIN, api_connections_cancel, NULL },
{ NULL },
};

View file

@ -163,6 +163,7 @@ bouquet_find_by_source(const char *name, const char *src, int create)
tvhwarn("bouquet", "bouquet name '%s' changed to '%s'", bq->bq_name ?: "", name);
free(bq->bq_name);
bq->bq_name = strdup(name);
bouquet_save(bq, 1);
}
return bq;
}
@ -225,6 +226,8 @@ bouquet_map_channel(bouquet_t *bq, service_t *t)
channel_t *ch = NULL;
channel_service_mapping_t *csm;
if (!t->s_enabled)
return;
if (!bq->bq_mapradio && service_is_radio(t))
return;
if (!bq->bq_mapnolcn &&
@ -319,6 +322,25 @@ bouquet_unmap_channel(bouquet_t *bq, service_t *t)
}
}
/**
*
*/
void
bouquet_notify_service_enabled(service_t *t)
{
bouquet_t *bq;
lock_assert(&global_lock);
RB_FOREACH(bq, &bouquets, bq_link)
if (idnode_set_exists(bq->bq_services, &t->s_id)) {
if (!t->s_enabled)
bouquet_unmap_channel(bq, t);
else if (bq->bq_enabled && bq->bq_maptoch)
bouquet_map_channel(bq, t);
}
}
/*
*
*/

View file

@ -73,6 +73,8 @@ bouquet_t * bouquet_create(const char *uuid, htsmsg_t *conf,
void bouquet_destroy_by_service(service_t *t);
void bouquet_destroy_by_channel_tag(channel_tag_t *ct);
void bouquet_notify_service_enabled(service_t *t);
static inline bouquet_t *
bouquet_find_by_uuid(const char *uuid)
{ return (bouquet_t *)idnode_find(uuid, &bouquet_class, NULL); }

View file

@ -1,6 +1,6 @@
/*
* tvheadend, channel functions
* Copyright (C) 2007 Andreas Öman
* Copyright (C) 2007 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -45,6 +45,8 @@
#include "bouquet.h"
#include "intlconv.h"
#define CHANNEL_BLANK_NAME "{name-not-set}"
struct channel_tree channels;
struct channel_tag_queue channel_tags;
@ -193,9 +195,12 @@ htsmsg_t *
channel_class_get_list(void *o)
{
htsmsg_t *m = htsmsg_create_map();
htsmsg_t *p = htsmsg_create_map();
htsmsg_add_str(m, "type", "api");
htsmsg_add_str(m, "uri", "channel/list");
htsmsg_add_str(m, "event", "channel");
htsmsg_add_u32(p, "all", 1);
htsmsg_add_msg(m, "params", p);
return m;
}
@ -245,9 +250,11 @@ channel_class_epggrab_set ( void *o, const void *v )
}
/* Link */
HTSMSG_FOREACH(f, l) {
if ((ec = epggrab_channel_find_by_id(htsmsg_field_get_str(f))))
save |= epggrab_channel_link(ec, ch);
if (l) {
HTSMSG_FOREACH(f, l) {
if ((ec = epggrab_channel_find_by_id(htsmsg_field_get_str(f))))
save |= epggrab_channel_link(ec, ch);
}
}
/* Delete */
@ -309,14 +316,12 @@ const idclass_t channel_class = {
.ic_get_title = channel_class_get_title,
.ic_delete = channel_class_delete,
.ic_properties = (const property_t[]){
#if 0
{
.type = PT_BOOL,
.id = "enabled",
.name = "Enabled",
.off = offsetof(channel_t, ch_enabled),
},
#endif
{
.type = PT_STR,
.id = "name",
@ -347,6 +352,12 @@ const idclass_t channel_class = {
.get = channel_class_get_icon,
.opts = PO_RDONLY | PO_NOSAVE | PO_HIDDEN,
},
{
.type = PT_BOOL,
.id = "epgauto",
.name = "Auto EPG Channel",
.off = offsetof(channel_t, ch_epgauto),
},
{
.type = PT_STR,
.islist = 1,
@ -417,7 +428,7 @@ channel_find_by_name ( const char *name )
if (name == NULL)
return NULL;
CHANNEL_FOREACH(ch)
if (!strcmp(channel_get_name(ch), name))
if (ch->ch_enabled && !strcmp(channel_get_name(ch), name))
break;
return ch;
}
@ -457,17 +468,23 @@ channel_find_by_number ( const char *no )
* Check if user can access the channel
*/
int
channel_access(channel_t *ch, access_t *a, const char *username)
channel_access(channel_t *ch, access_t *a, int disabled)
{
if (!ch)
return 0;
if (!disabled && !ch->ch_enabled)
return 0;
/* Channel number check */
if (ch && (a->aa_chmin || a->aa_chmax)) {
int chnum = channel_get_number(ch);
if (a->aa_chmin || a->aa_chmax) {
int64_t chnum = channel_get_number(ch);
if (chnum < a->aa_chmin || chnum > a->aa_chmax)
return 0;
}
/* Channel tag check */
if (ch && a->aa_chtags) {
if (a->aa_chtags) {
channel_tag_mapping_t *ctm;
htsmsg_field_t *f;
HTSMSG_FOREACH(f, a->aa_chtags) {
@ -551,7 +568,7 @@ channel_set_tags_by_list ( channel_t *ch, htsmsg_t *tags )
const char *
channel_get_name ( channel_t *ch )
{
static const char *blank = "";
static const char *blank = CHANNEL_BLANK_NAME;
const char *s;
channel_service_mapping_t *csm;
if (ch->ch_name && *ch->ch_name) return ch->ch_name;
@ -561,6 +578,19 @@ channel_get_name ( channel_t *ch )
return blank;
}
int
channel_set_name ( channel_t *ch, const char *name )
{
int save = 0;
if (!ch || !name) return 0;
if (!ch->ch_name || strcmp(ch->ch_name, name) ) {
if (ch->ch_name) free(ch->ch_name);
ch->ch_name = strdup(name);
save = 1;
}
return save;
}
int64_t
channel_get_number ( channel_t *ch )
{
@ -585,6 +615,19 @@ channel_get_number ( channel_t *ch )
return 0;
}
int
channel_set_number ( channel_t *ch, uint32_t major, uint32_t minor )
{
int save = 0;
int64_t chnum = (uint64_t)major * CHANNEL_SPLIT + (uint64_t)minor;
if (!ch || !chnum) return 0;
if (!ch->ch_number || ch->ch_number != chnum) {
ch->ch_number = chnum;
save = 1;
}
return save;
}
static int
check_file( const char *url )
{
@ -618,20 +661,18 @@ channel_get_icon ( channel_t *ch )
/* No user icon - try to get the channel icon by name */
if (!pick && chicon && chicon[0] >= ' ' && chicon[0] <= 122 &&
(chname = channel_get_name(ch)) != NULL && chname[0]) {
(chname = channel_get_name(ch)) != NULL && chname[0] &&
strcmp(chname, CHANNEL_BLANK_NAME)) {
const char *chi, *send, *sname, *s;
chi = strdup(chicon);
send = strstr(chi, "%C");
if (send == NULL) {
buf[0] = '\0';
sname = "";
} else {
*(char *)send = '\0';
send += 2;
/* Check for and replace placeholders */
if ((send = strstr(chi, "%C"))) {
sname = intlconv_utf8safestr(intlconv_charset_id("ASCII", 1, 1),
chname, strlen(chname) * 2);
if (sname == NULL)
sname = strdup(chname);
/* Remove problematic characters */
s = sname;
while (s && *s) {
@ -641,6 +682,26 @@ channel_get_icon ( channel_t *ch )
s++;
}
}
else if((send = strstr(chi, "%c"))) {
char *aname = intlconv_utf8safestr(intlconv_charset_id("ASCII", 1, 1),
chname, strlen(chname) * 2);
if (aname == NULL)
aname = strdup(chname);
sname = url_encode(aname);
free((char *)aname);
}
else {
buf[0] = '\0';
sname = "";
}
if (send) {
*(char *)send = '\0';
send += 2;
}
snprintf(buf, sizeof(buf), "%s%s%s", chi, sname ?: "", send ?: "");
if (send)
free((char *)sname);
@ -662,7 +723,7 @@ channel_get_icon ( channel_t *ch )
continue;
snprintf(buf2, sizeof(buf2), "%s/%s", picon, icn+8);
if (i > 1 || check_file(buf2)) {
ch->ch_icon = strdup(icn);
icon = ch->ch_icon = strdup(icn);
channel_save(ch);
idnode_notify_simple(&ch->ch_id);
break;
@ -694,6 +755,18 @@ channel_get_icon ( channel_t *ch )
return buf;
}
int channel_set_icon ( channel_t *ch, const char *icon )
{
int save = 0;
if (!ch || !icon) return 0;
if (!ch->ch_icon || strcmp(ch->ch_icon, icon) ) {
if (ch->ch_icon) free(ch->ch_icon);
ch->ch_icon = strdup(icon);
save = 1;
}
return save;
}
/* **************************************************************************
* Creation/Deletion
* *************************************************************************/
@ -722,6 +795,10 @@ channel_create0
abort();
}
/* Defaults */
ch->ch_enabled = 1;
ch->ch_epgauto = 1;
if (conf) {
ch->ch_load = 1;
idnode_load(&ch->ch_id, conf);
@ -1020,6 +1097,7 @@ channel_tag_save(channel_tag_t *ct)
idnode_save(&ct->ct_id, c);
hts_settings_save(c, "channel/tag/%s", idnode_uuid_as_str(&ct->ct_id));
htsmsg_destroy(c);
htsp_tag_update(ct);
}
@ -1041,6 +1119,35 @@ channel_tag_get_icon(channel_tag_t *ct)
return icon;
}
/**
* Check if user can access the channel tag
*/
int
channel_tag_access(channel_tag_t *ct, access_t *a, int disabled)
{
if (!ct)
return 0;
if (!disabled && (!ct->ct_enabled || ct->ct_internal))
return 0;
if (!ct->ct_private)
return 1;
/* Channel tag check */
if (a->aa_chtags) {
htsmsg_field_t *f;
const char *uuid = idnode_uuid_as_str(&ct->ct_id);
HTSMSG_FOREACH(f, a->aa_chtags)
if (!strcmp(htsmsg_field_get_str(f) ?: "", uuid))
goto chtags_ok;
return 0;
}
chtags_ok:
return 1;
}
/* **************************************************************************
* Channel Tag Class definition
* **************************************************************************/
@ -1083,9 +1190,12 @@ htsmsg_t *
channel_tag_class_get_list(void *o)
{
htsmsg_t *m = htsmsg_create_map();
htsmsg_t *p = htsmsg_create_map();
htsmsg_add_str(m, "type", "api");
htsmsg_add_str(m, "uri", "channeltag/list");
htsmsg_add_str(m, "event", "channeltag");
htsmsg_add_u32(p, "all", 1);
htsmsg_add_msg(m, "params", p);
return m;
}
@ -1103,6 +1213,12 @@ const idclass_t channel_tag_class = {
.name = "Enabled",
.off = offsetof(channel_tag_t, ct_enabled),
},
{
.type = PT_U32,
.id = "index",
.name = "Sort Index",
.off = offsetof(channel_tag_t, ct_index),
},
{
.type = PT_STR,
.id = "name",
@ -1115,6 +1231,12 @@ const idclass_t channel_tag_class = {
.name = "Internal",
.off = offsetof(channel_tag_t, ct_internal),
},
{
.type = PT_BOOL,
.id = "private",
.name = "Private",
.off = offsetof(channel_tag_t, ct_private),
},
{
.type = PT_STR,
.id = "icon",

View file

@ -1,6 +1,6 @@
/*
* tvheadend, channel functions
* Copyright (C) 2007 Andreas Öman
* Copyright (C) 2007 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -43,12 +43,13 @@ typedef struct channel
idnode_t ch_id;
RB_ENTRY(channel) ch_link;
int ch_refcount;
int ch_zombie;
int ch_load;
/* Channel info */
int ch_enabled;
char *ch_name; // Note: do not access directly!
int64_t ch_number;
char *ch_icon;
@ -67,6 +68,7 @@ typedef struct channel
gtimer_t ch_epg_timer_head;
gtimer_t ch_epg_timer_current;
int ch_epgauto;
LIST_HEAD(,epggrab_channel_link) ch_epggrab;
/* DVR */
@ -89,7 +91,9 @@ typedef struct channel_tag {
TAILQ_ENTRY(channel_tag) ct_link;
int ct_enabled;
uint32_t ct_index;
int ct_internal;
int ct_private;
int ct_titled_icon;
char *ct_name;
char *ct_comment;
@ -176,15 +180,17 @@ htsmsg_t * channel_tag_class_get_list(void *o);
const char * channel_tag_get_icon(channel_tag_t *ct);
int channel_access(channel_t *ch, struct access *a, const char *username);
int channel_access(channel_t *ch, struct access *a, int disabled);
int channel_tag_map(channel_t *ch, channel_tag_t *ct);
void channel_tag_unmap(channel_t *ch, channel_tag_t *ct);
int channel_tag_access(channel_tag_t *ct, struct access *a, int disabled);
void channel_save(channel_t *ch);
const char *channel_get_name ( channel_t *ch );
int channel_set_name ( channel_t *ch, const char *s );
int channel_set_name ( channel_t *ch, const char *name );
#define CHANNEL_SPLIT 1000000
@ -192,6 +198,7 @@ static inline uint32_t channel_get_major ( int64_t chnum ) { return chnum / CHAN
static inline uint32_t channel_get_minor ( int64_t chnum ) { return chnum % CHANNEL_SPLIT; }
int64_t channel_get_number ( channel_t *ch );
int channel_set_number ( channel_t *ch, uint32_t major, uint32_t minor );
const char *channel_get_icon ( channel_t *ch );
int channel_set_icon ( channel_t *ch, const char *icon );

View file

@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
#include <string.h>
#include <sys/stat.h>
@ -25,12 +26,16 @@
#include "uuid.h"
#include "htsbuf.h"
#include "spawn.h"
#include "lock.h"
#include "profile.h"
/* *************************************************************************
* Global data
* ************************************************************************/
static htsmsg_t *config;
static char config_lock[PATH_MAX];
static int config_lock_fd;
/* *************************************************************************
* Config migration
@ -1099,6 +1104,87 @@ config_migrate_v15 ( void )
}
}
static int
config_dvr_autorec_start_set(const char *s, int *tm)
{
int t;
if(s == NULL || s[0] == '\0' || !isdigit(s[0]))
t = -1;
else if(strchr(s, ':') != NULL)
// formatted time string - convert
t = (atoi(s) * 60) + atoi(s + 3);
else {
t = atoi(s);
}
if (t >= 24 * 60)
t = -1;
if (t != *tm) {
*tm = t;
return 1;
}
return 0;
}
static void
config_modify_dvrauto( htsmsg_t *c )
{
int tm = -1, tw = -1;
char buf[16];
if (config_dvr_autorec_start_set(htsmsg_get_str(c, "start"), &tm) > 0 && tm >= 0) {
tm -= 15;
if (tm < 0)
tm += 24 * 60;
tw = tm + 30;
if (tw >= 24 * 60)
tw -= 24 * 60;
snprintf(buf, sizeof(buf), "%02d:%02d", tm / 60, tm % 60);
htsmsg_set_str(c, "start", buf);
snprintf(buf, sizeof(buf), "%02d:%02d", tw / 60, tw % 60);
htsmsg_set_str(c, "start_window", buf);
} else {
htsmsg_delete_field(c, "start");
}
}
static void
config_migrate_v16 ( void )
{
htsmsg_t *c, *e;
htsmsg_field_t *f;
if ((c = hts_settings_load("dvr/autorec")) != NULL) {
HTSMSG_FOREACH(f, c) {
if (!(e = htsmsg_field_get_map(f))) continue;
config_modify_dvrauto(e);
hts_settings_save(e, "dvr/autorec/%s", f->hmf_name);
}
htsmsg_destroy(c);
}
}
static void
config_migrate_v17 ( void )
{
htsmsg_t *c, *e;
htsmsg_field_t *f;
int i, p;
if ((c = hts_settings_load("profile")) != NULL) {
HTSMSG_FOREACH(f, c) {
if (!(e = htsmsg_field_get_map(f))) continue;
if (htsmsg_get_s32(e, "priority", &i)) {
p = PROFILE_SPRIO_NORMAL;
if (strcmp(htsmsg_get_str(e, "name") ?: "", "htsp") == 0)
p = PROFILE_SPRIO_IMPORTANT;
htsmsg_set_s32(e, "priority", p);
hts_settings_save(e, "profile/%s", f->hmf_name);
}
}
}
}
/*
* Perform backup
*/
@ -1114,8 +1200,11 @@ dobackup(const char *oldver)
const char *root = hts_settings_get_root();
char errtxt[128];
const char **arg;
pid_t pid;
int code;
assert(root);
tvhinfo("config", "backup: migrating config from %s (running %s)",
oldver, tvheadend_version);
@ -1136,7 +1225,7 @@ dobackup(const char *oldver)
}
snprintf(outfile, sizeof(outfile), "%s/backup", root);
if (makedirs(outfile, 0700))
if (makedirs(outfile, 0700, -1, -1))
goto fatal;
if (chdir(root)) {
tvherror("config", "unable to find directory '%s'", root);
@ -1147,10 +1236,14 @@ dobackup(const char *oldver)
root, oldver);
tvhinfo("config", "backup: running, output file %s", outfile);
spawnv(argv[0], (void *)argv);
while ((code = spawn_reap(errtxt, sizeof(errtxt))) == -EAGAIN)
usleep(20000);
if (spawnv(argv[0], (void *)argv, &pid, 1, 1)) {
code = -ENOENT;
} else {
while ((code = spawn_reap(pid, errtxt, sizeof(errtxt))) == -EAGAIN)
usleep(20000);
if (code == -ECHILD)
code = 0;
}
if (code) {
htsbuf_queue_t q;
@ -1202,7 +1295,9 @@ static const config_migrate_t config_migrate_table[] = {
config_migrate_v12,
config_migrate_v13,
config_migrate_v14,
config_migrate_v15
config_migrate_v15,
config_migrate_v16,
config_migrate_v17
};
/*
@ -1293,24 +1388,32 @@ config_check ( void )
* Initialisation / Shutdown / Saving
* *************************************************************************/
static int config_newcfg = 0;
void
config_init ( const char *path, int backup )
config_boot ( const char *path, gid_t gid, uid_t uid )
{
struct stat st;
char buf[1024];
const char *homedir = getenv("HOME");
int new = 0;
htsmsg_t *config2;
config = htsmsg_create_map();
/* Generate default */
if (!path) {
const char *homedir = getenv("HOME");
if (homedir == NULL) {
tvherror("START", "environment variable HOME is not set");
exit(EXIT_FAILURE);
}
snprintf(buf, sizeof(buf), "%s/.hts/tvheadend", homedir);
path = buf;
}
/* Ensure directory exists */
if (stat(path, &st)) {
new = 1;
if (makedirs(path, 0700)) {
config_newcfg = 1;
if (makedirs(path, 0700, gid, uid)) {
tvhwarn("START", "failed to create settings directory %s,"
" settings will not be saved", path);
return;
@ -1329,15 +1432,39 @@ config_init ( const char *path, int backup )
/* Configure settings routines */
hts_settings_init(path);
/* Lock it */
hts_settings_buildpath(config_lock, sizeof(config_lock), ".lock");
if ((config_lock_fd = file_lock(config_lock, 3)) < 0)
exit(78); /* config error */
if (chown(config_lock, uid, gid))
tvhwarn("config", "unable to chown lock file %s UID:%d GID:%d", config_lock, uid, gid);
/* Load global settings */
config = hts_settings_load("config");
if (!config) {
config2 = hts_settings_load("config");
if (!config2) {
tvhlog(LOG_DEBUG, "config", "no configuration, loading defaults");
config = htsmsg_create_map();
} else {
htsmsg_destroy(config);
config = config2;
}
}
void
config_init ( int backup )
{
const char *path = hts_settings_get_root();
if (path == NULL || access(path, R_OK | W_OK)) {
tvhwarn("START", "configuration path %s is not r/w"
" for UID:%d GID:%d [e=%s],"
" settings will not be saved",
path, getuid(), getgid(), strerror(errno));
return;
}
/* Store version number */
if (new) {
if (config_newcfg) {
htsmsg_set_u32(config, "version", ARRAY_SIZE(config_migrate_table));
htsmsg_set_str(config, "fullversion", tvheadend_version);
config_save();
@ -1347,11 +1474,14 @@ config_init ( const char *path, int backup )
if (config_migrate(backup))
config_check();
}
tvhinfo("config", "loaded");
}
void config_done ( void )
{
/* note: tvhlog is inactive !!! */
htsmsg_destroy(config);
file_unlock(config_lock, config_lock_fd);
}
void config_save ( void )
@ -1368,8 +1498,14 @@ htsmsg_t *config_get_all ( void )
return htsmsg_copy(config);
}
static int
_config_set_str ( const char *fld, const char *val )
const char *
config_get_str ( const char *fld )
{
return htsmsg_get_str(config, fld);
}
int
config_set_str ( const char *fld, const char *val )
{
const char *c = htsmsg_get_str(config, fld);
if (!c || strcmp(c, val)) {
@ -1380,6 +1516,26 @@ _config_set_str ( const char *fld, const char *val )
return 0;
}
int
config_get_int ( const char *fld, int deflt )
{
return htsmsg_get_s32_or_default(config, fld, deflt);
}
int
config_set_int ( const char *fld, int val )
{
const char *c = htsmsg_get_str(config, fld);
char buf[16];
snprintf(buf, sizeof(buf), "%d", val);
if (!c || strcmp(c, buf)) {
if (c) htsmsg_delete_field(config, fld);
htsmsg_add_s32(config, fld, val);
return 1;
}
return 0;
}
const char *config_get_language ( void )
{
return htsmsg_get_str(config, "language");
@ -1387,7 +1543,7 @@ const char *config_get_language ( void )
int config_set_language ( const char *lang )
{
return _config_set_str("language", lang);
return config_set_str("language", lang);
}
const char *config_get_muxconfpath ( void )
@ -1397,7 +1553,7 @@ const char *config_get_muxconfpath ( void )
int config_set_muxconfpath ( const char *path )
{
return _config_set_str("muxconfpath", path);
return config_set_str("muxconfpath", path);
}
int config_get_prefer_picon ( void )
@ -1409,7 +1565,7 @@ int config_get_prefer_picon ( void )
int config_set_prefer_picon ( const char *str )
{
return _config_set_str("prefer_picon", str);
return config_set_str("prefer_picon", str);
}
const char *config_get_chicon_path ( void )
@ -1419,7 +1575,7 @@ const char *config_get_chicon_path ( void )
int config_set_chicon_path ( const char *str )
{
return _config_set_str("chiconpath", str);
return config_set_str("chiconpath", str);
}
const char *config_get_picon_path ( void )
@ -1429,5 +1585,5 @@ const char *config_get_picon_path ( void )
int config_set_picon_path ( const char *str )
{
return _config_set_str("piconpath", str);
return config_set_str("piconpath", str);
}

View file

@ -21,14 +21,21 @@
#ifndef __TVH_CONFIG__H__
#define __TVH_CONFIG__H__
#include <unistd.h>
#include "htsmsg.h"
void config_init ( const char *path, int backup );
void config_boot ( const char *path, gid_t gid, uid_t uid );
void config_init ( int backup );
void config_done ( void );
void config_save ( void );
htsmsg_t *config_get_all ( void );
const char *config_get_str ( const char *fld );
int config_set_str ( const char *fld, const char *val );
int config_get_int ( const char *fld, int dflt );
int config_set_int ( const char *fld, int val );
const char *config_get_muxconfpath ( void );
int config_set_muxconfpath ( const char *str )
__attribute__((warn_unused_result));

5
src/descrambler.h Executable file → Normal file
View file

@ -66,6 +66,7 @@ typedef struct th_descrambler_runtime {
uint32_t dr_key_first:1;
uint8_t dr_key_index;
uint8_t dr_key_valid;
uint8_t dr_key_changed;
time_t dr_key_start;
time_t dr_key_timestamp[2];
time_t dr_ecm_start;
@ -73,6 +74,8 @@ typedef struct th_descrambler_runtime {
time_t dr_last_err;
sbuf_t dr_buf;
tvhlog_limit_t dr_loglimit_key;
uint8_t dr_key_even[16];
uint8_t dr_key_odd[16];
} th_descrambler_runtime_t;
typedef void (*descrambler_section_callback_t)
@ -164,7 +167,7 @@ void descrambler_keys ( th_descrambler_t *t, int type,
const uint8_t *even, const uint8_t *odd );
int descrambler_descramble ( struct service *t,
struct elementary_stream *st,
const uint8_t *tsb );
const uint8_t *tsb, int len );
int descrambler_open_pid ( struct mpegts_mux *mux, void *opaque, int pid,
descrambler_section_callback_t callback,
struct service *service );

View file

@ -296,6 +296,9 @@ caclient_start ( struct service *t )
if (cac->cac_enabled)
cac->cac_start(cac, t);
pthread_mutex_unlock(&caclients_mutex);
#if ENABLE_TSDEBUG
tsdebugcw_service_start(t);
#endif
}
void
@ -344,6 +347,9 @@ caclient_init(void)
pthread_mutex_init(&caclients_mutex, NULL);
TAILQ_INIT(&caclients);
#if ENABLE_TSDEBUG
tsdebugcw_init();
#endif
if (!(c = hts_settings_load("caclient")))
return;

View file

@ -75,8 +75,14 @@ const char *caclient_get_status(caclient_t *cac);
void caclient_init(void);
void caclient_done(void);
void tsdebugcw_service_start(struct service *t);
void tsdebugcw_new_keys(struct service *t, int type, uint8_t *odd, uint8_t *even);
void tsdebugcw_go(void);
void tsdebugcw_init(void);
caclient_t *cwc_create(void);
caclient_t *capmt_create(void);
caclient_t *constcw_create(void);
caclient_t *tsdebugcw_create(void);
#endif /* __TVH_CACLIENT_H__ */

View file

@ -67,6 +67,8 @@ typedef struct dmx_filter {
uint8_t mode[DMX_FILTER_SIZE];
} dmx_filter_t;
#define DVBAPI_PROTOCOL_VERSION 1
#define CA_SET_DESCR 0x40106f86
#define CA_SET_DESCR_X 0x866f1040
#define CA_SET_DESCR_AES 0x40106f87
@ -77,6 +79,10 @@ typedef struct dmx_filter {
#define DMX_STOP_X 0x2a6f0000
#define DMX_SET_FILTER 0x403c6f2b
#define DMX_SET_FILTER_X 0x2b6f3c40
#define DVBAPI_FILTER_DATA 0xFFFF0000
#define DVBAPI_CLIENT_INFO 0xFFFF0001
#define DVBAPI_SERVER_INFO 0xFFFF0002
// ca_pmt_list_management values:
#define CAPMT_LIST_MORE 0x00 // append a 'MORE' CAPMT object the list and start receiving the next object
@ -102,18 +108,20 @@ typedef struct dmx_filter {
#define CAPMT_MSG_CLEAR 0x02
// limits
#define MAX_CA 16
#define MAX_INDEX 64
#define MAX_FILTER 64
#define MAX_SOCKETS 16 // max sockets (simultaneous channels) per demux
#define MAX_PIDS 64 // max opened pids
#define MAX_CA 16
#define MAX_INDEX 64
#define MAX_FILTER 64
#define MAX_SOCKETS 16 // max sockets (simultaneous channels) per demux
#define MAX_PIDS 64 // max opened pids
#define MAX_INFO_LEN 255
typedef enum {
CAPMT_OSCAM_SO_WRAPPER,
CAPMT_OSCAM_OLD,
CAPMT_OSCAM_MULTILIST,
CAPMT_OSCAM_TCP,
CAPMT_OSCAM_UNIX_SOCKET
CAPMT_OSCAM_UNIX_SOCKET,
CAPMT_OSCAM_NET_PROTO
} capmt_oscam_mode_t;
/**
@ -280,6 +288,7 @@ static void capmt_notify_server(capmt_t *capmt, capmt_service_t *ct, int force);
static void capmt_send_request(capmt_service_t *ct, int lm);
static void capmt_table_input(void *opaque, int pid,
const uint8_t *data, int len);
static void capmt_send_client_info(capmt_t *capmt);
/**
*
@ -440,7 +449,7 @@ capmt_connect(capmt_t *capmt, int i)
if (!capmt->capmt_running)
return -1;
if (capmt->capmt_oscam == CAPMT_OSCAM_TCP) {
if (capmt->capmt_oscam == CAPMT_OSCAM_TCP || capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO) {
char errbuf[256];
@ -483,6 +492,8 @@ capmt_connect(capmt_t *capmt, int i)
tvhlog(LOG_DEBUG, "capmt", "%s: Created socket %d", capmt_name(capmt), fd);
capmt->capmt_sock[i] = fd;
capmt->capmt_sock_reconnect[i]++;
if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO)
capmt_send_client_info(capmt);
capmt_poll_add(capmt, fd, i + 1);
}
@ -728,6 +739,27 @@ capmt_send_stop(capmt_service_t *t)
}
}
/**
*
*/
static void
capmt_send_stop_descrambling(capmt_t *capmt)
{
uint8_t buf[8];
buf[0] = 0x9F;
buf[1] = 0x80;
buf[2] = 0x3F;
buf[3] = 0x04;
buf[4] = 0x83;
buf[5] = 0x02;
buf[6] = 0x00;
buf[7] = 0xFF; //wildcard demux id
capmt_write_msg(capmt, 0, 0, buf, 8);
}
/**
* global_lock is held
* s_stream_mutex is held
@ -776,6 +808,21 @@ capmt_service_destroy(th_descrambler_t *td)
free(ct);
}
static void
capmt_send_client_info(capmt_t *capmt)
{
char buf[MAX_INFO_LEN + 7];
*(uint32_t *)(buf + 0) = htonl(DVBAPI_CLIENT_INFO);
*(uint16_t *)(buf + 4) = htons(DVBAPI_PROTOCOL_VERSION); //supported protocol version
int len = snprintf(buf + 7, sizeof(buf) - 7, "Tvheadend %s", tvheadend_version);
if (len >= sizeof(buf) - 7)
len = sizeof(buf) - 7 - 1;
buf[6] = len;
capmt_queue_msg(capmt, 0, 0, (uint8_t *)&buf, len + 7, CAPMT_MSG_FAST);
}
static void
capmt_filter_data(capmt_t *capmt, uint8_t adapter, uint8_t demux_index,
uint8_t filter_index, const uint8_t *data, int len,
@ -783,8 +830,7 @@ capmt_filter_data(capmt_t *capmt, uint8_t adapter, uint8_t demux_index,
{
uint8_t *buf = alloca(len + 6);
buf[0] = buf[1] = 0xff;
buf[2] = buf[3] = 0;
*(uint32_t *)(buf + 0) = htonl(DVBAPI_FILTER_DATA);
buf[4] = demux_index;
buf[5] = filter_index;
memcpy(buf + 6, data, len);
@ -821,6 +867,8 @@ capmt_set_filter(capmt_t *capmt, int adapter, sbuf_t *sb, int offset)
if (filter->pid && pid != filter->pid)
capmt_pid_remove(capmt, adapter, filter->pid);
filter->pid = pid;
if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO)
offset = 1; //filter data starts +1 byte because of adapter no
memcpy(&filter->filter, sbuf_peek(sb, offset + 8), sizeof(filter->filter));
tvhlog_hexdump("capmt", filter->filter.filter, DMX_FILTER_SIZE);
tvhlog_hexdump("capmt", filter->filter.mask, DMX_FILTER_SIZE);
@ -854,10 +902,15 @@ capmt_stop_filter(capmt_t *capmt, int adapter, sbuf_t *sb, int offset)
{
uint8_t demux_index = sbuf_peek_u8 (sb, offset + 4);
uint8_t filter_index = sbuf_peek_u8 (sb, offset + 5);
int16_t pid = sbuf_peek_s16be(sb, offset + 6);
int16_t pid;
capmt_dmx_t *filter;
capmt_filters_t *cf;
if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO)
pid = sbuf_peek_s16 (sb, offset + 6);
else
pid = sbuf_peek_s16be(sb, offset + 6);
tvhtrace("capmt", "%s: stopping filter: adapter=%d, demux=%d, filter=%d, pid=%d",
capmt_name(capmt), adapter, demux_index, filter_index, pid);
if (adapter >= MAX_CA ||
@ -886,6 +939,10 @@ capmt_stop_filter(capmt_t *capmt, int adapter, sbuf_t *sb, int offset)
static void
capmt_notify_server(capmt_t *capmt, capmt_service_t *ct, int force)
{
/* flush out the greeting */
if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO)
capmt_flush_queue(capmt, 0);
pthread_mutex_lock(&capmt->capmt_mutex);
if (capmt_oscam_new(capmt)) {
if (!LIST_EMPTY(&capmt->capmt_services))
@ -972,32 +1029,40 @@ static int
capmt_msg_size(capmt_t *capmt, sbuf_t *sb, int offset)
{
uint32_t cmd;
uint8_t adapter_byte = 0;
int oscam_new = capmt_oscam_new(capmt);
if (sb->sb_ptr - offset < 4)
return 0;
cmd = sbuf_peek_u32(sb, offset);
if (!sb->sb_bswap && !sb->sb_err) {
if (cmd == CA_SET_PID_X ||
cmd == CA_SET_DESCR_X ||
cmd == CA_SET_DESCR_AES_X ||
cmd == DMX_SET_FILTER_X ||
cmd == DMX_STOP_X) {
sb->sb_bswap = 1;
cmd = sbuf_peek_u32(sb, offset);
if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO) {
adapter_byte = 1; //we need to take into account the adapter index byte which is now after the cmd
} else {
if (!sb->sb_bswap && !sb->sb_err) {
if (cmd == CA_SET_PID_X ||
cmd == CA_SET_DESCR_X ||
cmd == CA_SET_DESCR_AES_X ||
cmd == DMX_SET_FILTER_X ||
cmd == DMX_STOP_X) {
sb->sb_bswap = 1;
cmd = sbuf_peek_u32(sb, offset);
}
}
}
sb->sb_err = 1; /* "first seen" flag for the moment */
if (cmd == CA_SET_PID)
return 4 + 8;
return 4 + 8 + adapter_byte;
else if (cmd == CA_SET_DESCR)
return 4 + 16;
return 4 + 16 + adapter_byte;
else if (cmd == CA_SET_DESCR_AES)
return 4 + 32;
else if (oscam_new && cmd == DMX_SET_FILTER)
return 4 + 2 + 60;
//when using network protocol the dmx_sct_filter_params fields are added seperately to avoid padding problems, so we substract 2 bytes:
return 4 + 2 + 60 + adapter_byte + (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO ? -2 : 0);
else if (oscam_new && cmd == DMX_STOP)
return 4 + 4;
return 4 + 4 + adapter_byte;
else if (oscam_new && cmd == DVBAPI_SERVER_INFO && sb->sb_ptr > 6)
return 4 + 2 + 1 + sbuf_peek_u8(sb, 6);
else {
sb->sb_err = 0;
return -1; /* fatal */
@ -1012,6 +1077,11 @@ capmt_analyze_cmd(capmt_t *capmt, int adapter, sbuf_t *sb, int offset)
cmd = sbuf_peek_u32(sb, offset);
if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO && cmd != DVBAPI_SERVER_INFO) {
adapter = sbuf_peek_u8(sb, 4);
offset = 1;
}
if (cmd == CA_SET_PID) {
uint32_t seq = sbuf_peek_u32(sb, offset + 4);
@ -1088,13 +1158,23 @@ capmt_analyze_cmd(capmt_t *capmt, int adapter, sbuf_t *sb, int offset)
capmt_stop_filter(capmt, adapter, sb, offset);
} else if (cmd == DVBAPI_SERVER_INFO) {
uint16_t protocol_version = sbuf_peek_u16(sb, offset + 4);
uint8_t len = sbuf_peek_u8(sb, offset + 4 + 2);
unsigned char oscam_info[len+1];
memcpy(&oscam_info, sbuf_peek(sb, offset + 4 + 2 + 1), len);
oscam_info[len] = 0; //null-terminating the string
tvhlog(LOG_INFO, "capmt", "%s: connected to %s, using network protocol_version = %d", capmt_name(capmt), oscam_info, protocol_version);
}
}
static void
show_connection(capmt_t *capmt, const char *what)
{
if (capmt->capmt_oscam == CAPMT_OSCAM_TCP) {
if (capmt->capmt_oscam == CAPMT_OSCAM_TCP || capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO) {
tvhlog(LOG_INFO, "capmt",
"%s: mode %i connected to %s:%i (%s)",
capmt_name(capmt),
@ -1222,7 +1302,7 @@ handle_ca0(capmt_t *capmt) {
static void
handle_single(capmt_t *capmt)
{
int ret, recvsock, adapter, nfds, cmd_size, reconnect;
int ret, recvsock, adapter, nfds, cmd_size, reconnect, offset;
uint8_t buf[256];
sbuf_t buffer;
tvhpoll_event_t ev;
@ -1284,18 +1364,24 @@ handle_single(capmt_t *capmt)
while (buffer.sb_ptr > 0) {
cmd_size = 0;
adapter = -1;
offset = 0;
while (buffer.sb_ptr > 0) {
if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO) {
buffer.sb_bswap = 1;
} else {
adapter = buffer.sb_data[0];
offset = 1;
}
if (adapter < MAX_CA) {
cmd_size = capmt_msg_size(capmt, &buffer, 1);
if (cmd_size >= 0)
cmd_size = capmt_msg_size(capmt, &buffer, offset);
if (cmd_size > 0)
break;
}
sbuf_cut(&buffer, 1);
}
if (cmd_size + 1 <= buffer.sb_ptr) {
capmt_analyze_cmd(capmt, adapter, &buffer, 1);
sbuf_cut(&buffer, cmd_size + 1);
if (cmd_size + offset <= buffer.sb_ptr) {
capmt_analyze_cmd(capmt, adapter, &buffer, offset);
sbuf_cut(&buffer, cmd_size + offset);
} else {
break;
}
@ -1402,6 +1488,7 @@ capmt_thread(void *aux)
/* Accessible */
if (capmt->capmt_sockfile && capmt->capmt_oscam != CAPMT_OSCAM_TCP &&
capmt->capmt_oscam != CAPMT_OSCAM_NET_PROTO &&
!access(capmt->capmt_sockfile, R_OK | W_OK))
caclient_set_status((caclient_t *)capmt, CACLIENT_STATUS_NONE);
else
@ -1459,6 +1546,7 @@ capmt_thread(void *aux)
}
#else
if (capmt->capmt_oscam == CAPMT_OSCAM_TCP ||
capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO ||
capmt->capmt_oscam == CAPMT_OSCAM_UNIX_SOCKET) {
handle_single(capmt);
} else {
@ -1578,7 +1666,7 @@ capmt_caid_change(th_descrambler_t *td)
lock_assert(&t->s_stream_mutex);
TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
if (t->s_dvb_prefcapid_lock == 2 &&
if (t->s_dvb_prefcapid_lock == PREFCAPID_FORCE &&
t->s_dvb_prefcapid != st->es_pid)
continue;
LIST_FOREACH(c, &st->es_caids, link) {
@ -1711,11 +1799,13 @@ capmt_send_request(capmt_service_t *ct, int lm)
}
memcpy(&buf[pos], &cad, cad.cad_length + 2);
pos += cad.cad_length + 2;
tvhlog(LOG_DEBUG, "capmt", "%s: adding ECMPID=0x%X (%d), CAID=0x%X (%d) PROVID=0x%X (%d)",
tvhlog(LOG_DEBUG, "capmt", "%s: adding ECMPID=0x%X (%d), "
"CAID=0x%X (%d) PROVID=0x%X (%d), SID=%d, ADAPTER=%d",
capmt_name(capmt),
cce2->cce_ecmpid, cce2->cce_ecmpid,
cce2->cce_caid, cce2->cce_caid,
cce2->cce_providerid, cce2->cce_providerid);
cce2->cce_providerid, cce2->cce_providerid,
sid, adapter_num);
}
uint8_t end[] = {
@ -1763,7 +1853,12 @@ capmt_enumerate_services(capmt_t *capmt, int force)
tvhlog(LOG_DEBUG, "capmt", "%s: %s: no subscribed services, closing socket, fd=%d", capmt_name(capmt), __FUNCTION__, capmt->capmt_sock[0]);
if (capmt->capmt_sock[0] >= 0)
caclient_set_status((caclient_t *)capmt, CACLIENT_STATUS_READY);
capmt_socket_close(capmt, 0);
if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO) {
capmt_send_stop_descrambling(capmt);
capmt_pid_flush(capmt);
}
else
capmt_socket_close(capmt, 0);
}
else if (force || (res_srv_count != all_srv_count)) {
LIST_FOREACH(ct, &capmt->capmt_services, ct_link) {
@ -1817,6 +1912,7 @@ capmt_service_start(caclient_t *cac, service_t *s)
goto fin;
if (tuner < 0 && capmt->capmt_oscam != CAPMT_OSCAM_TCP &&
capmt->capmt_oscam != CAPMT_OSCAM_NET_PROTO &&
capmt->capmt_oscam != CAPMT_OSCAM_UNIX_SOCKET) {
tvhlog(LOG_WARNING, "capmt",
"%s: Virtual adapters are supported only in modes 3 and 4 (service \"%s\")",
@ -1862,7 +1958,7 @@ capmt_service_start(caclient_t *cac, service_t *s)
TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
caid_t *c;
if (t->s_dvb_prefcapid_lock == 2 &&
if (t->s_dvb_prefcapid_lock == PREFCAPID_FORCE &&
t->s_dvb_prefcapid != st->es_pid)
continue;
LIST_FOREACH(c, &st->es_caids, link) {
@ -1920,7 +2016,7 @@ capmt_free(caclient_t *cac)
tvhlog(LOG_INFO, "capmt", "%s: mode %i %s %s port %i destroyed",
capmt_name(capmt),
capmt->capmt_oscam,
capmt->capmt_oscam == CAPMT_OSCAM_TCP ? "IP address" : "sockfile",
capmt->capmt_oscam == CAPMT_OSCAM_TCP || capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO ? "IP address" : "sockfile",
capmt->capmt_sockfile, capmt->capmt_port);
capmt_flush_queue(capmt, 1);
free(capmt->capmt_sockfile);
@ -1971,11 +2067,12 @@ static htsmsg_t *
caclient_capmt_class_oscam_mode_list ( void *o )
{
static const struct strtab tab[] = {
{ "OSCam pc-nodmx (rev >= 9756)", CAPMT_OSCAM_UNIX_SOCKET},
{ "OSCam TCP (rev >= 9574)", CAPMT_OSCAM_TCP },
{ "OSCam (rev >= 9095)", CAPMT_OSCAM_MULTILIST },
{ "Older OSCam", CAPMT_OSCAM_OLD },
{ "Wrapper (capmt_ca.so)", CAPMT_OSCAM_SO_WRAPPER },
{ "OSCam net protocol (rev >= 10389)", CAPMT_OSCAM_NET_PROTO },
{ "OSCam pc-nodmx (rev >= 9756)", CAPMT_OSCAM_UNIX_SOCKET },
{ "OSCam TCP (rev >= 9574)", CAPMT_OSCAM_TCP },
{ "OSCam (rev >= 9095)", CAPMT_OSCAM_MULTILIST },
{ "Older OSCam", CAPMT_OSCAM_OLD },
{ "Wrapper (capmt_ca.so)", CAPMT_OSCAM_SO_WRAPPER },
};
return strtab2htsmsg(tab);
}
@ -2002,7 +2099,7 @@ const idclass_t caclient_capmt_class =
.def.s = "/tmp/camd.socket",
},
{
.type = PT_U16,
.type = PT_INT,
.id = "port",
.name = "Listen/Connect Port",
.off = offsetof(capmt_t, capmt_port),

157
src/descrambler/cwc.c Executable file → Normal file
View file

@ -272,6 +272,7 @@ typedef struct cwc {
*
*/
static void cwc_service_pid_free(cwc_service_t *ct);
static void cwc_service_destroy(th_descrambler_t *td);
void cwc_emm_conax(cwc_t *cwc, struct cs_card_data *pcard, const uint8_t *data, int len);
void cwc_emm_irdeto(cwc_t *cwc, struct cs_card_data *pcard, const uint8_t *data, int len);
@ -666,16 +667,20 @@ static int
cwc_ecm_reset(th_descrambler_t *th)
{
cwc_service_t *ct = (cwc_service_t *)th;
cwc_t *cwc = ct->cs_cwc;
ecm_pid_t *ep;
ecm_section_t *es;
if (ct->cs_constcw)
return 1; /* keys will not change */
pthread_mutex_lock(&cwc->cwc_mutex);
ct->td_keystate = DS_UNKNOWN;
LIST_FOREACH(ep, &ct->cs_pids, ep_link)
LIST_FOREACH(es, &ep->ep_sections, es_link)
es->es_keystate = ES_UNKNOWN;
ct->ecm_state = ECM_RESET;
pthread_mutex_unlock(&cwc->cwc_mutex);
return 0;
}
@ -683,13 +688,16 @@ static void
cwc_ecm_idle(th_descrambler_t *th)
{
cwc_service_t *ct = (cwc_service_t *)th;
cwc_t *cwc = ct->cs_cwc;
ecm_pid_t *ep;
ecm_section_t *es;
pthread_mutex_lock(&cwc->cwc_mutex);
LIST_FOREACH(ep, &ct->cs_pids, ep_link)
LIST_FOREACH(es, &ep->ep_sections, es_link)
es->es_keystate = ES_IDLE;
ct->ecm_state = ECM_RESET;
pthread_mutex_unlock(&cwc->cwc_mutex);
}
static void
@ -697,6 +705,7 @@ handle_ecm_reply(cwc_service_t *ct, ecm_section_t *es, uint8_t *msg,
int len, int seq)
{
mpegts_service_t *t = (mpegts_service_t *)ct->td_service;
cwc_t *cwc = ct->cs_cwc;
ecm_pid_t *ep;
ecm_section_t *es2;
char chaninfo[32];
@ -769,7 +778,7 @@ forbid:
ct->td_keystate = DS_FORBIDDEN;
ct->ecm_state = ECM_RESET;
/* this pid is not valid, force full scan */
if (t->s_dvb_prefcapid == ct->cs_channel && t->s_dvb_prefcapid_lock == 0)
if (t->s_dvb_prefcapid == ct->cs_channel && t->s_dvb_prefcapid_lock == PREFCAPID_OFF)
t->s_dvb_prefcapid = 0;
}
return;
@ -782,7 +791,7 @@ forbid:
if(t->s_dvb_prefcapid == 0 ||
(t->s_dvb_prefcapid != ct->cs_channel &&
t->s_dvb_prefcapid_lock == 0)) {
t->s_dvb_prefcapid_lock == PREFCAPID_OFF)) {
t->s_dvb_prefcapid = ct->cs_channel;
tvhlog(LOG_DEBUG, "cwc", "Saving prefered PID %d for %s",
t->s_dvb_prefcapid, ct->td_nicename);
@ -809,7 +818,9 @@ forbid:
es->es_keystate = ES_RESOLVED;
es->es_resolved = 1;
pthread_mutex_unlock(&cwc->cwc_mutex);
descrambler_keys((th_descrambler_t *)ct, DESCRAMBLER_DES, msg + 3, msg + 3 + 8);
pthread_mutex_lock(&cwc->cwc_mutex);
} else {
tvhlog(LOG_DEBUG, "cwc",
"Received ECM reply%s for service \"%s\" "
@ -833,7 +844,9 @@ forbid:
es->es_keystate = ES_RESOLVED;
es->es_resolved = 1;
pthread_mutex_unlock(&cwc->cwc_mutex);
descrambler_keys((th_descrambler_t *)ct, DESCRAMBLER_AES, msg + 3, msg + 3 + 16);
pthread_mutex_lock(&cwc->cwc_mutex);
}
}
}
@ -1606,7 +1619,6 @@ cwc_table_input(void *opaque, int pid, const uint8_t *data, int len)
ecm_section_t *es;
char chaninfo[32];
struct cs_card_data *pcard = NULL;
int i_break;
caid_t *c;
uint16_t caid;
uint32_t providerid;
@ -1614,33 +1626,37 @@ cwc_table_input(void *opaque, int pid, const uint8_t *data, int len)
if (data == NULL)
return;
if (ct->td_keystate == DS_IDLE)
return;
if(len > 4096)
return;
if((data[0] & 0xf0) != 0x80)
return;
LIST_FOREACH(ep, &ct->cs_pids, ep_link) {
if(ep->ep_pid == pid)
break;
pthread_mutex_lock(&t->s_stream_mutex);
pthread_mutex_lock(&cwc->cwc_mutex);
if (ct->td_keystate == DS_IDLE)
goto end;
if (ct->ecm_state == ECM_RESET) {
/* clean all */
cwc_service_pid_free(ct);
/* move to init state */
ct->ecm_state = ECM_INIT;
ct->cs_channel = -1;
t->s_dvb_prefcapid = 0;
tvhlog(LOG_DEBUG, "cwc", "Reset after unexpected or no reply for service \"%s\"", t->s_dvb_svcname);
}
pthread_mutex_lock(&t->s_stream_mutex);
if(ep == NULL) {
tvhlog(LOG_DEBUG, "cwc", "ECM state %i", ct->ecm_state);
if (ct->ecm_state == ECM_RESET) {
ct->ecm_state = ECM_INIT;
ct->cs_channel = -1;
t->s_dvb_prefcapid = 0;
tvhlog(LOG_DEBUG, "cwc", "Reset after unexpected or no reply for service \"%s\"", t->s_dvb_svcname);
}
LIST_FOREACH(ep, &ct->cs_pids, ep_link)
if(ep->ep_pid == pid) break;
if(ep == NULL) {
if (ct->ecm_state == ECM_INIT) {
// Validate prefered ECM PID
if(t->s_dvb_prefcapid != 0) {
tvhlog(LOG_DEBUG, "cwc", "ECM state INIT");
if(t->s_dvb_prefcapid != PREFCAPID_OFF) {
struct elementary_stream *prefca
= service_stream_find((service_t*)t, t->s_dvb_prefcapid);
if (!prefca || prefca->es_type != SCT_CA) {
@ -1649,52 +1665,32 @@ cwc_table_input(void *opaque, int pid, const uint8_t *data, int len)
}
}
if(t->s_dvb_prefcapid == pid) {
if(t->s_dvb_prefcapid == pid || t->s_dvb_prefcapid == 0) {
ep = calloc(1, sizeof(ecm_pid_t));
ep->ep_pid = t->s_dvb_prefcapid;
ep->ep_pid = pid;
LIST_INSERT_HEAD(&ct->cs_pids, ep, ep_link);
tvhlog(LOG_DEBUG, "cwc", "Insert prefered ECM (PID %d) for service \"%s\"", t->s_dvb_prefcapid, t->s_dvb_svcname);
}
else if(t->s_dvb_prefcapid == 0) {
ep = calloc(1, sizeof(ecm_pid_t));
ep->ep_pid = pid;
LIST_INSERT_HEAD(&ct->cs_pids, ep, ep_link);
tvhlog(LOG_DEBUG, "cwc", "Insert new ECM (PID %d) for service \"%s\"", pid, t->s_dvb_svcname);
tvhlog(LOG_DEBUG, "cwc", "Insert %s ECM (PID %d) for service \"%s\"",
t->s_dvb_prefcapid ? "preferred" : "new", pid, t->s_dvb_svcname);
}
}
}
if(ep == NULL)
goto end;
if(ep == NULL) {
pthread_mutex_unlock(&t->s_stream_mutex);
return;
st = service_stream_find((service_t *)t, pid);
if (st) {
LIST_FOREACH(c, &st->es_caids, link)
LIST_FOREACH(pcard, &cwc->cwc_cards, cs_card)
if(pcard->cwc_caid == c->caid && verify_provider(pcard, c->providerid))
goto found;
}
i_break = 0;
c = NULL;
TAILQ_FOREACH(st, &t->s_components, es_link) {
if (st->es_pid != pid) continue;
LIST_FOREACH(c, &st->es_caids, link) {
LIST_FOREACH(pcard,&cwc->cwc_cards, cs_card) {
if(pcard->cwc_caid == c->caid && verify_provider(pcard, c->providerid)){
i_break = 1;
break;
}
}
if (i_break == 1) break;
}
if (i_break == 1) break;
}
if(c == NULL) {
pthread_mutex_unlock(&t->s_stream_mutex);
return;
}
goto end;
found:
caid = c->caid;
providerid = c->providerid;
pthread_mutex_unlock(&t->s_stream_mutex);
switch(data[0]) {
case 0x80:
case 0x81:
@ -1737,7 +1733,7 @@ cwc_table_input(void *opaque, int pid, const uint8_t *data, int len)
if(ct->cs_channel >= 0 && channel != -1 &&
ct->cs_channel != channel) {
tvhlog(LOG_DEBUG, "cwc", "Filtering ECM (PID %d)", channel);
return;
goto end;
}
es->es_seq = cwc_send_msg(cwc, data, len, sid, 1, caid, providerid);
@ -1754,6 +1750,10 @@ cwc_table_input(void *opaque, int pid, const uint8_t *data, int len)
cwc_send_msg(cwc, data, len, sid, 1, 0, 0);
break;
}
end:
pthread_mutex_unlock(&cwc->cwc_mutex);
pthread_mutex_unlock(&t->s_stream_mutex);
}
/**
@ -1947,16 +1947,10 @@ cwc_emm_bulcrypt(cwc_t *cwc, struct cs_card_data *pcard, const uint8_t *data, in
* s_stream_mutex is held
*/
static void
cwc_service_destroy(th_descrambler_t *td)
cwc_service_pid_free(cwc_service_t *ct)
{
cwc_service_t *ct = (cwc_service_t *)td;
ecm_pid_t *ep;
ecm_section_t *es;
int i;
for (i = 0; i < CWC_ES_PIDS; i++)
if (ct->cs_epids[i])
descrambler_close_pid(ct->cs_mux, ct, ct->cs_epids[i]);
while((ep = LIST_FIRST(&ct->cs_pids)) != NULL) {
while ((es = LIST_FIRST(&ep->ep_sections)) != NULL) {
@ -1966,6 +1960,23 @@ cwc_service_destroy(th_descrambler_t *td)
LIST_REMOVE(ep, ep_link);
free(ep);
}
}
/**
* cwc_mutex is held
* s_stream_mutex is held
*/
static void
cwc_service_destroy(th_descrambler_t *td)
{
cwc_service_t *ct = (cwc_service_t *)td;
int i;
for (i = 0; i < CWC_ES_PIDS; i++)
if (ct->cs_epids[i])
descrambler_close_pid(ct->cs_mux, ct, ct->cs_epids[i]);
cwc_service_pid_free(ct);
LIST_REMOVE(td, td_service_link);
@ -1996,16 +2007,16 @@ cwc_service_start(caclient_t *cac, service_t *t)
if (!idnode_is_instance(&t->s_id, &mpegts_service_class))
return;
pthread_mutex_lock(&t->s_stream_mutex);
pthread_mutex_lock(&cwc->cwc_mutex);
LIST_FOREACH(ct, &cwc->cwc_services, cs_link) {
if (ct->td_service == t && ct->cs_cwc == cwc)
break;
}
pthread_mutex_lock(&t->s_stream_mutex);
LIST_FOREACH(pcard, &cwc->cwc_cards, cs_card) {
if (pcard->cwc_caid == 0) continue;
TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
if (((mpegts_service_t *)t)->s_dvb_prefcapid_lock == 2 &&
if (((mpegts_service_t *)t)->s_dvb_prefcapid_lock == PREFCAPID_FORCE &&
((mpegts_service_t *)t)->s_dvb_prefcapid != st->es_pid)
continue;
LIST_FOREACH(c, &st->es_caids, link) {
@ -2020,16 +2031,10 @@ cwc_service_start(caclient_t *cac, service_t *t)
}
if (!pcard) {
if (ct) cwc_service_destroy((th_descrambler_t*)ct);
pthread_mutex_unlock(&t->s_stream_mutex);
pthread_mutex_unlock(&cwc->cwc_mutex);
return;
}
pthread_mutex_unlock(&t->s_stream_mutex);
if (ct) {
pthread_mutex_unlock(&cwc->cwc_mutex);
return;
goto end;
}
if (ct)
goto end;
ct = calloc(1, sizeof(cwc_service_t));
ct->cs_cwc = cwc;
@ -2039,7 +2044,7 @@ cwc_service_start(caclient_t *cac, service_t *t)
ct->cs_constcw = pcard->cwc_caid == 0x2600;
td = (th_descrambler_t *)ct;
snprintf(buf, sizeof(buf), "cwc-%s-%i", cwc->cwc_hostname, cwc->cwc_port);
snprintf(buf, sizeof(buf), "cwc-%s-%i-%04X", cwc->cwc_hostname, cwc->cwc_port, pcard->cwc_caid);
td->td_nicename = strdup(buf);
td->td_service = t;
td->td_stop = cwc_service_destroy;
@ -2049,7 +2054,6 @@ cwc_service_start(caclient_t *cac, service_t *t)
LIST_INSERT_HEAD(&cwc->cwc_services, ct, cs_link);
pthread_mutex_lock(&t->s_stream_mutex);
i = 0;
TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
LIST_FOREACH(c, &st->es_caids, link)
@ -2059,7 +2063,6 @@ cwc_service_start(caclient_t *cac, service_t *t)
}
if (i == CWC_ES_PIDS) break;
}
pthread_mutex_unlock(&t->s_stream_mutex);
for (i = 0; i < CWC_ES_PIDS; i++)
if (ct->cs_epids[i])
@ -2070,7 +2073,9 @@ cwc_service_start(caclient_t *cac, service_t *t)
tvhlog(LOG_DEBUG, "cwc", "%s using CWC %s:%d",
service_nicename(t), cwc->cwc_hostname, cwc->cwc_port);
end:
pthread_mutex_unlock(&cwc->cwc_mutex);
pthread_mutex_unlock(&t->s_stream_mutex);
}
@ -2088,7 +2093,9 @@ cwc_free(caclient_t *cac)
while((ct = LIST_FIRST(&cwc->cwc_services)) != NULL) {
t = (mpegts_service_t *)ct->td_service;
pthread_mutex_lock(&t->s_stream_mutex);
pthread_mutex_lock(&cwc->cwc_mutex);
cwc_service_destroy((th_descrambler_t *)&ct);
pthread_mutex_lock(&cwc->cwc_mutex);
pthread_mutex_unlock(&t->s_stream_mutex);
}

211
src/descrambler/descrambler.c Executable file → Normal file
View file

@ -21,6 +21,8 @@
#include "caclient.h"
#include "ffdecsa/FFdecsa.h"
#include "input.h"
#include "input/mpegts/tsdemux.h"
#include "dvbcam.h"
struct caid_tab {
const char *name;
@ -83,6 +85,9 @@ descrambler_init ( void )
ffdecsa_init();
#endif
caclient_init();
#if ENABLE_LINUXDVB_CA
dvbcam_init();
#endif
}
void
@ -122,6 +127,10 @@ descrambler_service_start ( service_t *t )
tvhcsa_init(&dr->dr_csa);
}
caclient_start(t);
#if ENABLE_LINUXDVB_CA
dvbcam_service_start(t);
#endif
}
void
@ -130,6 +139,10 @@ descrambler_service_stop ( service_t *t )
th_descrambler_t *td;
th_descrambler_runtime_t *dr = t->s_descramble;
#if ENABLE_LINUXDVB_CA
dvbcam_service_stop(t);
#endif
while ((td = LIST_FIRST(&t->s_descramblers)) != NULL)
td->td_stop(td);
t->s_descramble = NULL;
@ -166,10 +179,11 @@ void
descrambler_keys ( th_descrambler_t *td, int type,
const uint8_t *even, const uint8_t *odd )
{
static uint8_t empty[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
service_t *t = td->td_service;
th_descrambler_runtime_t *dr;
th_descrambler_t *td2;
int i, j = 0;
int j = 0;
if (t == NULL || (dr = t->s_descramble) == NULL) {
td->td_keystate = DS_FORBIDDEN;
@ -195,22 +209,20 @@ descrambler_keys ( th_descrambler_t *td, int type,
goto fin;
}
for (i = 0; i < dr->dr_csa.csa_keylen; i++)
if (even[i]) {
j++;
tvhcsa_set_key_even(&dr->dr_csa, even);
dr->dr_key_valid |= 0x40;
dr->dr_key_timestamp[0] = dispatch_clock;
break;
}
for (i = 0; i < dr->dr_csa.csa_keylen; i++)
if (odd[i]) {
j++;
tvhcsa_set_key_odd(&dr->dr_csa, odd);
dr->dr_key_valid |= 0x80;
dr->dr_key_timestamp[1] = dispatch_clock;
break;
}
if (memcmp(empty, even, dr->dr_csa.csa_keylen)) {
j++;
memcpy(dr->dr_key_even, even, dr->dr_csa.csa_keylen);
dr->dr_key_changed |= 1;
dr->dr_key_valid |= 0x40;
dr->dr_key_timestamp[0] = dispatch_clock;
}
if (memcmp(empty, odd, dr->dr_csa.csa_keylen)) {
j++;
memcpy(dr->dr_key_odd, odd, dr->dr_csa.csa_keylen);
dr->dr_key_changed |= 2;
dr->dr_key_valid |= 0x80;
dr->dr_key_timestamp[1] = dispatch_clock;
}
if (j) {
if (td->td_keystate != DS_RESOLVED)
@ -252,6 +264,40 @@ descrambler_keys ( th_descrambler_t *td, int type,
fin:
pthread_mutex_unlock(&t->s_stream_mutex);
#if ENABLE_TSDEBUG
if (j) {
tsdebug_packet_t *tp = malloc(sizeof(*tp));
uint16_t keylen = dr->dr_csa.csa_keylen;
uint16_t sid = ((mpegts_service_t *)td->td_service)->s_dvb_service_id;
uint32_t pos = 0, crc;
mpegts_mux_t *mm = ((mpegts_service_t *)td->td_service)->s_dvb_mux;
if (!mm->mm_active)
return;
pthread_mutex_lock(&mm->mm_active->mmi_input->mi_output_lock);
tp->pos = mm->mm_tsdebug_pos;
memset(tp->pkt, 0xff, sizeof(tp->pkt));
tp->pkt[pos++] = 0x47; /* sync byte */
tp->pkt[pos++] = 0x1f; /* PID MSB */
tp->pkt[pos++] = 0xff; /* PID LSB */
tp->pkt[pos++] = 0x00; /* CC */
memcpy(tp->pkt + pos, "TVHeadendDescramblerKeys", 24);
pos += 24;
tp->pkt[pos++] = type & 0xff;
tp->pkt[pos++] = keylen & 0xff;
tp->pkt[pos++] = (sid >> 8) & 0xff;
tp->pkt[pos++] = sid & 0xff;
memcpy(tp->pkt + pos, even, keylen);
memcpy(tp->pkt + pos + keylen, odd, keylen);
pos += 2 * keylen;
crc = tvh_crc32(tp->pkt, pos, 0x859aa5ba);
tp->pkt[pos++] = (crc >> 24) & 0xff;
tp->pkt[pos++] = (crc >> 16) & 0xff;
tp->pkt[pos++] = (crc >> 8) & 0xff;
tp->pkt[pos++] = crc & 0xff;
TAILQ_INSERT_HEAD(&mm->mm_tsdebug_packets, tp, link);
pthread_mutex_unlock(&mm->mm_active->mmi_input->mi_output_lock);
}
#endif
}
static void
@ -288,7 +334,7 @@ key_update( th_descrambler_runtime_t *dr, uint8_t key )
if (dr->dr_key_start)
dr->dr_key_start = dispatch_clock;
else
/* We don't knoe the exact start key switch time */
/* We don't know the exact start key switch time */
dr->dr_key_start = dispatch_clock - 60;
}
@ -314,37 +360,82 @@ key_late( th_descrambler_runtime_t *dr, uint8_t ki )
return dr->dr_ecm_key_time + 2 < dr->dr_key_start;
}
static int
ecm_reset( service_t *t, th_descrambler_runtime_t *dr )
{
th_descrambler_t *td;
int ret = 0;
/* reset the reader ECM state */
LIST_FOREACH(td, &t->s_descramblers, td_service_link) {
if (!td->td_ecm_reset(td)) {
dr->dr_key_valid = 0;
ret = 1;
}
}
return ret;
}
int
descrambler_descramble ( service_t *t,
elementary_stream_t *st,
const uint8_t *tsb )
const uint8_t *tsb,
int len )
{
th_descrambler_t *td;
th_descrambler_runtime_t *dr = t->s_descramble;
int count, failed, off, size, flush_data = 0;
uint8_t *tsb2, ki;
int count, failed, resolved, off, len2, len3, flush_data = 0;
const uint8_t *tsb2;
uint8_t ki;
lock_assert(&t->s_stream_mutex);
if (dr == NULL)
if (dr == NULL) {
if ((tsb[3] & 0x80) == 0) {
ts_recv_packet2((mpegts_service_t *)t, tsb, len);
return 1;
}
return -1;
count = failed = 0;
}
if (dr->dr_csa.csa_type == DESCRAMBLER_NONE && dr->dr_buf.sb_ptr == 0)
if ((tsb[3] & 0x80) == 0) {
ts_recv_packet2((mpegts_service_t *)t, tsb, len);
return 1;
}
count = failed = resolved = 0;
LIST_FOREACH(td, &t->s_descramblers, td_service_link) {
count++;
if (td->td_keystate == DS_FORBIDDEN) {
failed++;
continue;
switch (td->td_keystate) {
case DS_FORBIDDEN: failed++; break;
case DS_RESOLVED : resolved++; break;
default: break;
}
if (td->td_keystate != DS_RESOLVED)
continue;
}
if (resolved) {
/* update the keys */
if (dr->dr_key_changed) {
dr->dr_csa.csa_flush(&dr->dr_csa, (mpegts_service_t *)t);
if (dr->dr_key_changed & 1)
tvhcsa_set_key_even(&dr->dr_csa, dr->dr_key_even);
if (dr->dr_key_changed & 2)
tvhcsa_set_key_odd(&dr->dr_csa, dr->dr_key_odd);
dr->dr_key_changed = 0;
}
/* process the queued TS packets */
if (dr->dr_buf.sb_ptr > 0) {
for (off = 0, size = dr->dr_buf.sb_ptr; off < size; off += 188) {
tsb2 = dr->dr_buf.sb_data + off;
for (tsb2 = dr->dr_buf.sb_data, len2 = dr->dr_buf.sb_ptr;
len2 > 0; tsb2 += len3, len2 -= len3) {
ki = tsb2[3];
if ((ki & 0x80) != 0x00) {
if (key_valid(dr, ki) == 0) {
sbuf_cut(&dr->dr_buf, off);
goto next2;
sbuf_cut(&dr->dr_buf, tsb2 - dr->dr_buf.sb_data);
flush_data = 1;
goto next;
}
if (dr->dr_key_index != (ki & 0x40) &&
dr->dr_key_start + 2 < dispatch_clock) {
@ -352,23 +443,24 @@ descrambler_descramble ( service_t *t,
(ki & 0x40) ? "odd" : "even",
((mpegts_service_t *)t)->s_dvb_svcname);
if (key_late(dr, ki)) {
sbuf_cut(&dr->dr_buf, off);
if (!td->td_ecm_reset(td)) {
dr->dr_key_valid = 0;
if (ecm_reset(t, dr)) {
sbuf_cut(&dr->dr_buf, tsb2 - dr->dr_buf.sb_data);
flush_data = 1;
goto next;
}
}
key_update(dr, ki);
}
}
dr->dr_csa.csa_descramble(&dr->dr_csa,
(mpegts_service_t *)td->td_service,
tsb2);
len3 = mpegts_word_count(tsb2, len2, 0xFF0000C0);
dr->dr_csa.csa_descramble(&dr->dr_csa, (mpegts_service_t *)t, tsb2, len3);
}
if (off > 0)
if (len2 == 0)
service_reset_streaming_status_flags(t, TSS_NO_ACCESS);
sbuf_free(&dr->dr_buf);
}
/* check for key change */
ki = tsb[3];
if ((ki & 0x80) != 0x00) {
if (key_valid(dr, ki) == 0) {
@ -377,7 +469,7 @@ descrambler_descramble ( service_t *t,
((mpegts_service_t *)t)->s_dvb_svcname,
(ki & 0x40) ? "odd stream key is not valid" :
"even stream key is not valid");
continue;
goto next;
}
if (dr->dr_key_index != (ki & 0x40) &&
dr->dr_key_start + 2 < dispatch_clock) {
@ -385,27 +477,22 @@ descrambler_descramble ( service_t *t,
(ki & 0x40) ? "odd" : "even",
((mpegts_service_t *)t)->s_dvb_svcname);
if (key_late(dr, ki)) {
tvhtrace("descrambler", "ECM late (%ld seconds) for service \"%s\"",
tvherror("descrambler", "ECM late (%ld seconds) for service \"%s\"",
dispatch_clock - dr->dr_ecm_key_time,
((mpegts_service_t *)t)->s_dvb_svcname);
if (!td->td_ecm_reset(td)) {
dr->dr_key_valid = 0;
if (ecm_reset(t, dr)) {
flush_data = 1;
goto next;
}
}
key_update(dr, ki);
}
}
dr->dr_csa.csa_descramble(&dr->dr_csa,
(mpegts_service_t *)td->td_service,
tsb);
dr->dr_csa.csa_descramble(&dr->dr_csa, (mpegts_service_t *)t, tsb, len);
service_reset_streaming_status_flags(t, TSS_NO_ACCESS);
return 1;
next:
flush_data = 1;
next2:
continue;
}
next:
if (dr->dr_ecm_start) { /* ECM sent */
ki = tsb[3];
if ((ki & 0x80) != 0x00) {
@ -448,7 +535,7 @@ next2:
((mpegts_service_t *)t)->s_dvb_svcname);
}
}
sbuf_append(&dr->dr_buf, tsb, 188);
sbuf_append(&dr->dr_buf, tsb, len);
service_set_streaming_status_flags(t, TSS_NO_ACCESS);
}
} else {
@ -469,17 +556,22 @@ descrambler_table_callback
descrambler_section_t *ds;
descrambler_ecmsec_t *des;
th_descrambler_runtime_t *dr;
int emm = (mt->mt_flags & MT_FAST) == 0;
if (len < 6)
return 0;
pthread_mutex_lock(&mt->mt_mux->mm_descrambler_lock);
TAILQ_FOREACH(ds, &dt->sections, link) {
LIST_FOREACH(des, &ds->ecmsecs, link)
if (des->number == ptr[4])
break;
if (!emm) {
LIST_FOREACH(des, &ds->ecmsecs, link)
if (des->number == ptr[4])
break;
} else {
des = LIST_FIRST(&ds->ecmsecs);
}
if (des == NULL) {
des = calloc(1, sizeof(*des));
des->number = ptr[4];
des->number = emm ? 0 : ptr[4];
LIST_INSERT_HEAD(&ds->ecmsecs, des, link);
}
if (des->last_data == NULL || len != des->last_data_len ||
@ -493,7 +585,7 @@ descrambler_table_callback
des->last_data_len = 0;
}
ds->callback(ds->opaque, mt->mt_pid, ptr, len);
if ((mt->mt_flags & MT_FAST) != 0) { /* ECM */
if (!emm) { /* ECM */
mpegts_service_t *t = mt->mt_service;
if (t) {
/* The keys are requested from this moment */
@ -506,6 +598,8 @@ descrambler_table_callback
} else
tvhtrace("descrambler", "Unknown fast table message (section %d, len %d, pid %d)",
des->number, len, mt->mt_pid);
} else {
tvhtrace("descrambler", "EMM message (len %d, pid %d)", len, mt->mt_pid);
}
}
}
@ -542,7 +636,8 @@ descrambler_open_pid_( mpegts_mux_t *mux, void *opaque, int pid,
TAILQ_INIT(&dt->sections);
dt->table = mpegts_table_add(mux, 0, 0, descrambler_table_callback,
dt, "descrambler",
MT_FULL | MT_DEFER | flags, pid);
MT_FULL | MT_DEFER | flags, pid,
MPS_WEIGHT_CA);
if (dt->table)
dt->table->mt_service = (mpegts_service_t *)service;
TAILQ_INSERT_TAIL(&mux->mm_descrambler_tables, dt, link);
@ -702,7 +797,7 @@ next:
caid = emm->caid;
pid = emm->pid;
tvhtrace("descrambler", "close emm caid %04X (%i) pid %04X (%i)", caid, caid, pid, pid);
descrambler_close_pid(mux, emm->opaque, pid);
descrambler_close_pid_(mux, emm->opaque, pid);
}
TAILQ_REMOVE(&mux->mm_descrambler_emms, emm, link);
TAILQ_INSERT_TAIL(&removing, emm, link);

215
src/descrambler/dvbcam.c Normal file
View file

@ -0,0 +1,215 @@
/*
* tvheadend, DVB CAM interface
* Copyright (C) 2014 Damjan Marion
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
#include "tvheadend.h"
#include "caclient.h"
#include "service.h"
#include "input.h"
#include "dvbcam.h"
#include "input/mpegts/linuxdvb/linuxdvb_private.h"
#if ENABLE_LINUXDVB_CA
#define CAIDS_PER_CA_SLOT 16
typedef struct dvbcam_active_service {
TAILQ_ENTRY(dvbcam_active_service) link;
service_t *t;
uint8_t *last_pmt;
int last_pmt_len;
linuxdvb_ca_t *ca;
uint8_t slot;
} dvbcam_active_service_t;
typedef struct dvbcam_active_caid {
TAILQ_ENTRY(dvbcam_active_caid) link;
uint16_t caids[CAIDS_PER_CA_SLOT];
int num_caids;
linuxdvb_ca_t *ca;
uint8_t slot;
} dvbcam_active_caid_t;
TAILQ_HEAD(,dvbcam_active_service) dvbcam_active_services;
TAILQ_HEAD(,dvbcam_active_caid) dvbcam_active_caids;
pthread_mutex_t dvbcam_mutex;
void
dvbcam_register_cam(linuxdvb_ca_t * lca, uint8_t slot, uint16_t * caids,
int num_caids)
{
dvbcam_active_caid_t *ac;
num_caids = MIN(CAIDS_PER_CA_SLOT, num_caids);
if ((ac = malloc(sizeof(*ac))) == NULL)
return;
ac->ca = lca;
ac->slot = slot;
memcpy(ac->caids, caids, num_caids * sizeof(uint16_t));
ac->num_caids = num_caids;
pthread_mutex_lock(&dvbcam_mutex);
TAILQ_INSERT_TAIL(&dvbcam_active_caids, ac, link);
pthread_mutex_unlock(&dvbcam_mutex);
}
void
dvbcam_unregister_cam(linuxdvb_ca_t * lca, uint8_t slot)
{
dvbcam_active_caid_t *ac, *ac_tmp;
dvbcam_active_service_t *as;
pthread_mutex_lock(&dvbcam_mutex);
/* remove pointer to this CAM in all active services */
TAILQ_FOREACH(as, &dvbcam_active_services, link)
if (as->ca == lca && as->slot == slot)
as->ca = NULL;
/* delete entry */
for (ac = TAILQ_FIRST(&dvbcam_active_caids); ac != NULL; ac = ac_tmp) {
ac_tmp = TAILQ_NEXT(ac, link);
if(ac && ac->ca == lca && ac->slot == slot) {
TAILQ_REMOVE(&dvbcam_active_caids, ac, link);
free(ac);
}
}
pthread_mutex_unlock(&dvbcam_mutex);
}
void
dvbcam_pmt_data(mpegts_service_t *s, const uint8_t *ptr, int len)
{
linuxdvb_frontend_t *lfe;
dvbcam_active_caid_t *ac;
dvbcam_active_service_t *as = NULL, *as2;
elementary_stream_t *st;
caid_t *c;
int i;
lfe = (linuxdvb_frontend_t*) s->s_dvb_active_input;
if (!lfe)
return;
pthread_mutex_lock(&dvbcam_mutex);
TAILQ_FOREACH(as2, &dvbcam_active_services, link)
if (as2->t == (service_t *) s) {
as = as2;
break;
}
pthread_mutex_unlock(&dvbcam_mutex);
if (!as)
return;
if(as->last_pmt)
free(as->last_pmt);
as->last_pmt = malloc(len + 3);
memcpy(as->last_pmt, ptr-3, len + 3);
as->last_pmt_len = len + 3;
as->ca = NULL;
pthread_mutex_lock(&dvbcam_mutex);
/* check all ellementary streams for CAIDs, if any send PMT to CAM */
TAILQ_FOREACH(st, &s->s_components, es_link) {
LIST_FOREACH(c, &st->es_caids, link) {
TAILQ_FOREACH(ac, &dvbcam_active_caids, link) {
for(i=0;i<ac->num_caids;i++) {
if(ac->ca && ac->ca->lca_adapter == lfe->lfe_adapter &&
ac->caids[i] == c->caid)
{
as->ca = ac->ca;
as->slot = ac->slot;
break;
}
}
}
}
}
pthread_mutex_unlock(&dvbcam_mutex);
/* this service doesn't have assigned CAM */
if (!as->ca)
return;
linuxdvb_ca_send_capmt(as->ca, as->slot, as->last_pmt, as->last_pmt_len);
}
void
dvbcam_service_start(service_t *t)
{
dvbcam_active_service_t *as;
TAILQ_FOREACH(as, &dvbcam_active_services, link)
if (as->t == t)
return;
if ((as = malloc(sizeof(*as))) == NULL)
return;
as->t = t;
as->last_pmt = NULL;
as->last_pmt_len = 0;
pthread_mutex_lock(&dvbcam_mutex);
TAILQ_INSERT_TAIL(&dvbcam_active_services, as, link);
pthread_mutex_unlock(&dvbcam_mutex);
}
void
dvbcam_service_stop(service_t *t)
{
dvbcam_active_service_t *as, *as_tmp;
pthread_mutex_lock(&dvbcam_mutex);
for (as = TAILQ_FIRST(&dvbcam_active_services); as != NULL; as = as_tmp) {
as_tmp = TAILQ_NEXT(as, link);
if(as && as->t == t) {
TAILQ_REMOVE(&dvbcam_active_services, as, link);
if(as->last_pmt)
free(as->last_pmt);
free(as);
}
}
pthread_mutex_unlock(&dvbcam_mutex);
}
void
dvbcam_init(void)
{
pthread_mutex_init(&dvbcam_mutex, NULL);
TAILQ_INIT(&dvbcam_active_services);
TAILQ_INIT(&dvbcam_active_caids);
}
#endif /* ENABLE_LINUXDVB_CA */

37
src/descrambler/dvbcam.h Normal file
View file

@ -0,0 +1,37 @@
/*
* tvheadend - CSA wrapper
* Copyright (C) 2013 Adam Sutton
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __DVBCAM_H__
#define __DVBCAM_H__
struct mpegts_service;
struct elementary_stream;
#if ENABLE_LINUXDVB_CA
typedef struct linuxdvb_ca linuxdvb_ca_t;
void dvbcam_init(void);
void dvbcam_service_start(struct service *t);
void dvbcam_service_stop(struct service *t);
void dvbcam_register_cam(linuxdvb_ca_t * lca, uint8_t slot, uint16_t * caids, int num_caids);
void dvbcam_unregister_cam(linuxdvb_ca_t * lca, uint8_t slot);
void dvbcam_pmt_data(mpegts_service_t *s, const uint8_t *ptr, int len);
#endif
#endif /* __DVBCAM_H__ */

0
src/descrambler/libaesdec/libaesdec.c Executable file → Normal file
View file

0
src/descrambler/libaesdec/libaesdec.h Executable file → Normal file
View file

169
src/descrambler/tsdebugcw.c Normal file
View file

@ -0,0 +1,169 @@
/*
* tvheadend, tsdebug code word interface
* Copyright (C) 2014 Jaroslav Kysela
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
#include "tvheadend.h"
#include "caclient.h"
#include "service.h"
#include "input.h"
/**
*
*/
typedef struct tsdebugcw_service {
th_descrambler_t;
int tdcw_type;
uint8_t tdcw_key_even[16]; /* DES or AES key */
uint8_t tdcw_key_odd [16]; /* DES or AES key */
} tsdebugcw_service_t;
typedef struct tsdebugcw_request {
TAILQ_ENTRY(tsdebugcw_request) link;
tsdebugcw_service_t *ct;
} tsdebugcw_request_t;
pthread_mutex_t tsdebugcw_mutex;
TAILQ_HEAD(,tsdebugcw_request) tsdebugcw_requests;
/*
*
*/
static int
tsdebugcw_ecm_reset(th_descrambler_t *th)
{
return 1;
}
/**
* s_stream_mutex is held
*/
static void
tsdebugcw_service_destroy(th_descrambler_t *td)
{
tsdebugcw_service_t *ct = (tsdebugcw_service_t *)td;
tsdebugcw_request_t *ctr, *ctrnext;
pthread_mutex_lock(&tsdebugcw_mutex);
for (ctr = TAILQ_FIRST(&tsdebugcw_requests); ctr; ctr = ctrnext) {
ctrnext = TAILQ_NEXT(ctr, link);
if (ctr->ct == ct) {
TAILQ_REMOVE(&tsdebugcw_requests, ctr, link);
free(ctr);
}
}
pthread_mutex_unlock(&tsdebugcw_mutex);
LIST_REMOVE(td, td_service_link);
free(ct->td_nicename);
free(ct);
}
/**
* global_lock is held. Not that we care about that, but either way, it is.
*/
void
tsdebugcw_service_start(service_t *t)
{
tsdebugcw_service_t *ct;
th_descrambler_t *td;
char buf[128];
extern const idclass_t mpegts_service_class;
if (!idnode_is_instance(&t->s_id, &mpegts_service_class))
return;
LIST_FOREACH(td, &t->s_descramblers, td_service_link)
if (td->td_stop == tsdebugcw_service_destroy)
break;
if (td)
return;
ct = calloc(1, sizeof(tsdebugcw_service_t));
td = (th_descrambler_t *)ct;
snprintf(buf, sizeof(buf), "tsdebugcw");
td->td_nicename = strdup(buf);
td->td_service = t;
td->td_stop = tsdebugcw_service_destroy;
td->td_ecm_reset = tsdebugcw_ecm_reset;
LIST_INSERT_HEAD(&t->s_descramblers, td, td_service_link);
}
/*
*
*/
void
tsdebugcw_new_keys(service_t *t, int type, uint8_t *odd, uint8_t *even)
{
static char empty[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
th_descrambler_t *td;
tsdebugcw_service_t *ct;
tsdebugcw_request_t *ctr;
int keylen = type == DESCRAMBLER_AES ? 16 : 8;
LIST_FOREACH(td, &t->s_descramblers, td_service_link)
if (td->td_stop == tsdebugcw_service_destroy)
break;
if (!td)
return;
ct = (tsdebugcw_service_t *)td;
ct->tdcw_type = type;
if (memcmp(empty, odd, keylen))
memcpy(ct->tdcw_key_odd, odd, keylen);
if (memcmp(empty, even, keylen))
memcpy(ct->tdcw_key_even, even, keylen);
ctr = malloc(sizeof(*ctr));
ctr->ct = ct;
pthread_mutex_lock(&tsdebugcw_mutex);
TAILQ_INSERT_TAIL(&tsdebugcw_requests, ctr, link);
pthread_mutex_unlock(&tsdebugcw_mutex);
}
/*
*
*/
void
tsdebugcw_go(void)
{
tsdebugcw_request_t *ctr;
tsdebugcw_service_t *ct;
while (1) {
pthread_mutex_lock(&tsdebugcw_mutex);
ctr = TAILQ_FIRST(&tsdebugcw_requests);
if (ctr)
TAILQ_REMOVE(&tsdebugcw_requests, ctr, link);
pthread_mutex_unlock(&tsdebugcw_mutex);
if (!ctr) break;
ct = ctr->ct;
descrambler_keys((th_descrambler_t *)ct, ct->tdcw_type,
ct->tdcw_key_odd, ct->tdcw_key_even);
free(ctr);
}
}
/*
*
*/
void
tsdebugcw_init(void)
{
pthread_mutex_init(&tsdebugcw_mutex, NULL);
TAILQ_INIT(&tsdebugcw_requests);
}

View file

@ -25,70 +25,28 @@
#include <assert.h>
static void
tvhcsa_aes_descramble
( tvhcsa_t *csa, struct mpegts_service *s, const uint8_t *tsb )
tvhcsa_aes_flush
( tvhcsa_t *csa, struct mpegts_service *s )
{
aes_decrypt_packet(csa->csa_aes_keys, (unsigned char *)tsb);
ts_recv_packet2(s, tsb);
/* empty - no queue */
}
static void
tvhcsa_des_descramble
( tvhcsa_t *csa, struct mpegts_service *s, const uint8_t *tsb )
tvhcsa_aes_descramble
( tvhcsa_t *csa, struct mpegts_service *s, const uint8_t *tsb, int len )
{
const uint8_t *tsb2, *end2;
for (tsb2 = tsb, end2 = tsb + len; tsb2 < end2; tsb2 += 188)
aes_decrypt_packet(csa->csa_aes_keys, (unsigned char *)tsb2);
ts_recv_packet2(s, tsb, len);
}
static void
tvhcsa_des_flush
( tvhcsa_t *csa, struct mpegts_service *s )
{
#if ENABLE_DVBCSA
uint8_t *pkt;
int xc0;
int ev_od;
int len;
int offset;
int n;
int i;
const uint8_t *t0;
pkt = csa->csa_tsbcluster + csa->csa_fill * 188;
memcpy(pkt, tsb, 188);
csa->csa_fill++;
do { // handle this packet
xc0 = pkt[3] & 0xc0;
if(xc0 == 0x00) { // clear
break;
}
if(xc0 == 0x40) { // reserved
break;
}
if(xc0 == 0x80 || xc0 == 0xc0) { // encrypted
ev_od = (xc0 & 0x40) >> 6; // 0 even, 1 odd
pkt[3] &= 0x3f; // consider it decrypted now
if(pkt[3] & 0x20) { // incomplete packet
offset = 4 + pkt[4] + 1;
len = 188 - offset;
n = len >> 3;
// FIXME: //residue = len - (n << 3);
if(n == 0) { // decrypted==encrypted!
break; // this doesn't need more processing
}
} else {
len = 184;
offset = 4;
// FIXME: //n = 23;
// FIXME: //residue = 0;
}
if(ev_od == 0) {
csa->csa_tsbbatch_even[csa->csa_fill_even].data = pkt + offset;
csa->csa_tsbbatch_even[csa->csa_fill_even].len = len;
csa->csa_fill_even++;
} else {
csa->csa_tsbbatch_odd[csa->csa_fill_odd].data = pkt + offset;
csa->csa_tsbbatch_odd[csa->csa_fill_odd].len = len;
csa->csa_fill_odd++;
}
}
} while(0);
if(csa->csa_fill != csa->csa_cluster_size)
return;
if(csa->csa_fill_even) {
csa->csa_tsbbatch_even[csa->csa_fill_even].data = NULL;
@ -101,51 +59,112 @@ tvhcsa_des_descramble
csa->csa_fill_odd = 0;
}
t0 = csa->csa_tsbcluster;
ts_recv_packet2(s, csa->csa_tsbcluster, csa->csa_fill * 188);
for(i = 0; i < csa->csa_fill; i++) {
ts_recv_packet2(s, t0);
t0 += 188;
}
csa->csa_fill = 0;
#else
int r;
int r, l;
unsigned char *vec[3];
memcpy(csa->csa_tsbcluster + csa->csa_fill * 188, tsb, 188);
csa->csa_fill++;
vec[0] = csa->csa_tsbcluster;
vec[1] = csa->csa_tsbcluster + csa->csa_fill * 188;
vec[2] = NULL;
if(csa->csa_fill != csa->csa_cluster_size)
return;
r = decrypt_packets(csa->csa_keys, vec);
if(r > 0) {
ts_recv_packet2(s, csa->csa_tsbcluster, r * 188);
while(1) {
l = csa->csa_fill - r;
assert(l >= 0);
vec[0] = csa->csa_tsbcluster;
vec[1] = csa->csa_tsbcluster + csa->csa_fill * 188;
vec[2] = NULL;
r = decrypt_packets(csa->csa_keys, vec);
if(r > 0) {
int i;
const uint8_t *t0 = csa->csa_tsbcluster;
for(i = 0; i < r; i++) {
ts_recv_packet2(s, t0);
t0 += 188;
}
r = csa->csa_fill - r;
assert(r >= 0);
if(r > 0)
memmove(csa->csa_tsbcluster, t0, r * 188);
csa->csa_fill = r;
} else {
csa->csa_fill = 0;
}
break;
if(l > 0)
memmove(csa->csa_tsbcluster, csa->csa_tsbcluster + r * 188, l * 188);
csa->csa_fill = l;
} else {
csa->csa_fill = 0;
}
#endif
}
static void
tvhcsa_des_descramble
( tvhcsa_t *csa, struct mpegts_service *s, const uint8_t *tsb, int tsb_len )
{
const uint8_t *tsb_end = tsb + tsb_len;
assert(csa->csa_fill >= 0 && csa->csa_fill < csa->csa_cluster_size);
#if ENABLE_DVBCSA
uint8_t *pkt;
int xc0;
int ev_od;
int len;
int offset;
int n;
for ( ; tsb < tsb_end; tsb += 188) {
pkt = csa->csa_tsbcluster + csa->csa_fill * 188;
memcpy(pkt, tsb, 188);
csa->csa_fill++;
do { // handle this packet
xc0 = pkt[3] & 0xc0;
if(xc0 == 0x00) { // clear
break;
}
if(xc0 == 0x40) { // reserved
break;
}
if(xc0 == 0x80 || xc0 == 0xc0) { // encrypted
ev_od = (xc0 & 0x40) >> 6; // 0 even, 1 odd
pkt[3] &= 0x3f; // consider it decrypted now
if(pkt[3] & 0x20) { // incomplete packet
offset = 4 + pkt[4] + 1;
len = 188 - offset;
n = len >> 3;
// FIXME: //residue = len - (n << 3);
if(n == 0) { // decrypted==encrypted!
break; // this doesn't need more processing
}
} else {
len = 184;
offset = 4;
// FIXME: //n = 23;
// FIXME: //residue = 0;
}
if(ev_od == 0) {
csa->csa_tsbbatch_even[csa->csa_fill_even].data = pkt + offset;
csa->csa_tsbbatch_even[csa->csa_fill_even].len = len;
csa->csa_fill_even++;
} else {
csa->csa_tsbbatch_odd[csa->csa_fill_odd].data = pkt + offset;
csa->csa_tsbbatch_odd[csa->csa_fill_odd].len = len;
csa->csa_fill_odd++;
}
}
} while(0);
if(csa->csa_fill == csa->csa_cluster_size)
tvhcsa_des_flush(csa, s);
}
#else
for ( ; tsb < tsb_end; tsb += 188 ) {
memcpy(csa->csa_tsbcluster + csa->csa_fill * 188, tsb, 188);
csa->csa_fill++;
if(csa->csa_fill == csa->csa_cluster_size)
tvhcsa_des_flush(csa, s);
}
#endif
}
@ -159,10 +178,12 @@ tvhcsa_set_type( tvhcsa_t *csa, int type )
switch (type) {
case DESCRAMBLER_DES:
csa->csa_descramble = tvhcsa_des_descramble;
csa->csa_flush = tvhcsa_des_flush;
csa->csa_keylen = 8;
break;
case DESCRAMBLER_AES:
csa->csa_descramble = tvhcsa_aes_descramble;
csa->csa_flush = tvhcsa_aes_flush;
csa->csa_keylen = 16;
break;
default:
@ -220,7 +241,10 @@ tvhcsa_init ( tvhcsa_t *csa )
#else
csa->csa_cluster_size = get_suggested_cluster_size();
#endif
csa->csa_tsbcluster = malloc(csa->csa_cluster_size * 188);
/* Note: the optimized routines might read memory after last TS packet */
/* allocate safe memory and fill it with zeros */
csa->csa_tsbcluster = malloc((csa->csa_cluster_size + 1) * 188);
memset(csa->csa_tsbcluster + csa->csa_cluster_size * 188, 0, 188);
#if ENABLE_DVBCSA
csa->csa_tsbbatch_even = malloc((csa->csa_cluster_size + 1) *
sizeof(struct dvbcsa_bs_batch_s));

View file

@ -41,7 +41,10 @@ typedef struct tvhcsa
int csa_type; /*< see DESCRAMBLER_* defines */
int csa_keylen;
void (*csa_descramble)
( struct tvhcsa *csa, struct mpegts_service *s, const uint8_t *tsb );
( struct tvhcsa *csa, struct mpegts_service *s,
const uint8_t *tsb, int len );
void (*csa_flush)
( struct tvhcsa *csa, struct mpegts_service *s );
int csa_cluster_size;
uint8_t *csa_tsbcluster;
@ -62,6 +65,8 @@ typedef struct tvhcsa
} tvhcsa_t;
#if ENABLE_TVHCSA
int tvhcsa_set_type( tvhcsa_t *csa, int type );
void tvhcsa_set_key_even( tvhcsa_t *csa, const uint8_t *even );
@ -70,4 +75,16 @@ void tvhcsa_set_key_odd ( tvhcsa_t *csa, const uint8_t *odd );
void tvhcsa_init ( tvhcsa_t *csa );
void tvhcsa_destroy ( tvhcsa_t *csa );
#else
static inline int tvhcsa_set_type( tvhcsa_t *csa, int type ) { return -1; }
static inline void tvhcsa_set_key_even( tvhcsa_t *csa, const uint8_t *even ) { };
static inline void tvhcsa_set_key_odd ( tvhcsa_t *csa, const uint8_t *odd ) { };
static inline void tvhcsa_init ( tvhcsa_t *csa ) { };
static inline void tvhcsa_destroy ( tvhcsa_t *csa ) { };
#endif
#endif /* __TVH_CSA_H__ */

View file

@ -63,7 +63,7 @@ typedef struct dvr_config {
int dvr_skip_commercials;
int dvr_subtitle_in_title;
int dvr_episode_before_date;
int dvr_episode_duplicate;
int dvr_windows_compatible_filenames;
/* Series link support */
int dvr_sl_brand_lock;
@ -154,10 +154,15 @@ typedef struct dvr_entry {
time_t de_start_extra;
time_t de_stop_extra;
char *de_owner;
char *de_creator;
char *de_comment;
char *de_filename; /* Initially null if no filename has been
generated yet */
char *de_directory; /* Can be set for autorec entries, will override any
directory setting from the configuration */
lang_str_t *de_title; /* Title in UTF-8 (from EPG) */
lang_str_t *de_subtitle; /* Subtitle in UTF-8 (from EPG) */
lang_str_t *de_desc; /* Description in UTF-8 (from EPG) */
uint32_t de_content_type; /* Content type (from EPG) (only code) */
@ -172,6 +177,7 @@ typedef struct dvr_entry {
* EPG information / links
*/
epg_broadcast_t *de_bcast;
char *de_episode;
/**
* Major State
@ -188,6 +194,11 @@ typedef struct dvr_entry {
*/
uint32_t de_errors;
/**
* Number of data errors (only to be modified by the recording thread)
*/
uint32_t de_data_errors;
/**
* Last error, see SM_CODE_ defines
*/
@ -224,10 +235,25 @@ typedef struct dvr_entry {
LIST_ENTRY(dvr_entry) de_inotify_link;
#endif
/**
* Entry change notification timer
*/
time_t de_last_notify;
} dvr_entry_t;
#define DVR_CH_NAME(e) ((e)->de_channel == NULL ? (e)->de_channel_name : channel_get_name((e)->de_channel))
typedef enum {
DVR_AUTOREC_RECORD_ALL = 0,
DVR_AUTOREC_RECORD_DIFFERENT_EPISODE_NUMBER = 1,
DVR_AUTOREC_RECORD_DIFFERENT_SUBTITLE = 2,
DVR_AUTOREC_RECORD_DIFFERENT_DESCRIPTION = 3,
DVR_AUTOREC_RECORD_ONCE_PER_WEEK = 4,
DVR_AUTOREC_RECORD_ONCE_PER_DAY = 5
} dvr_autorec_dedup_t;
/**
* Autorec entry
*/
@ -237,19 +263,23 @@ typedef struct dvr_autorec_entry {
TAILQ_ENTRY(dvr_autorec_entry) dae_link;
char *dae_name;
char *dae_directory;
dvr_config_t *dae_config;
LIST_ENTRY(dvr_autorec_entry) dae_config_link;
int dae_enabled;
char *dae_owner;
char *dae_creator;
char *dae_comment;
char *dae_title;
regex_t dae_title_preg;
int dae_fulltext;
uint32_t dae_content_type;
int dae_start; /* Minutes from midnight */
int dae_start; /* Minutes from midnight */
int dae_start_window; /* Minutes (duration) */
uint32_t dae_weekdays;
@ -274,6 +304,9 @@ typedef struct dvr_autorec_entry {
time_t dae_start_extra;
time_t dae_stop_extra;
int dae_record;
} dvr_autorec_entry_t;
TAILQ_HEAD(dvr_autorec_entry_queue, dvr_autorec_entry);
@ -289,10 +322,12 @@ typedef struct dvr_timerec_entry {
TAILQ_ENTRY(dvr_timerec_entry) dte_link;
char *dte_name;
char *dte_directory;
dvr_config_t *dte_config;
LIST_ENTRY(dvr_timerec_entry) dte_config_link;
int dte_enabled;
char *dte_owner;
char *dte_creator;
char *dte_comment;
@ -401,22 +436,25 @@ dvr_entry_t *
dvr_entry_create_by_event( const char *dvr_config_uuid,
epg_broadcast_t *e,
time_t start_extra, time_t stop_extra,
const char *creator,
const char *owner, const char *creator,
dvr_autorec_entry_t *dae,
dvr_prio_t pri, int retention );
dvr_prio_t pri, int retention,
const char *comment );
dvr_entry_t *
dvr_entry_create_htsp( const char *dvr_config_uuid,
channel_t *ch, time_t start, time_t stop,
time_t start_extra, time_t stop_extra,
const char *title, const char *description,
const char *title, const char* subtitle, const char *description,
const char *lang, epg_genre_t *content_type,
const char *creator, dvr_autorec_entry_t *dae,
dvr_prio_t pri, int retention );
const char *owner, const char *creator,
dvr_autorec_entry_t *dae,
dvr_prio_t pri, int retention,
const char *comment );
dvr_entry_t *
dvr_entry_update( dvr_entry_t *de,
const char* de_title, const char *de_desc, const char *lang,
const char* de_title, const char* de_subtitle, const char *de_desc, const char *lang,
time_t de_start, time_t de_stop,
time_t de_start_extra, time_t de_stop_extra,
dvr_prio_t pri, int retention );
@ -457,6 +495,19 @@ htsmsg_t *dvr_entry_class_pri_list(void *o);
htsmsg_t *dvr_entry_class_config_name_list(void *o);
htsmsg_t *dvr_entry_class_duration_list(void *o, const char *not_set, int max, int step);
static inline int dvr_entry_verify(dvr_entry_t *de, access_t *a, int readonly)
{
if (readonly && !access_verify2(a, ACCESS_ALL_RECORDER))
return 0;
if (!access_verify2(a, ACCESS_ALL_RW_RECORDER))
return 0;
if (strcmp(de->de_owner ?: "", a->aa_username ?: ""))
return -1;
return 0;
}
/**
*
*/
@ -468,24 +519,26 @@ dvr_entry_t *
dvr_entry_create_(const char *config_uuid, epg_broadcast_t *e,
channel_t *ch, time_t start, time_t stop,
time_t start_extra, time_t stop_extra,
const char *title, const char *description,
const char *title, const char* subtitle, const char *description,
const char *lang, epg_genre_t *content_type,
const char *creator, dvr_autorec_entry_t *dae,
dvr_timerec_entry_t *tae,
dvr_prio_t pri, int retention);
const char *owner, const char *creator,
dvr_autorec_entry_t *dae, dvr_timerec_entry_t *tae,
dvr_prio_t pri, int retention, const char *comment);
dvr_autorec_entry_t*
dvr_autorec_create_htsp(const char *dvr_config_name, const char *title,
channel_t *ch, uint32_t aroundTime, uint32_t days,
time_t start_extra, time_t stop_extra,
dvr_prio_t pri, int retention,
int min_duration, int max_duration,
const char *creator, const char *comment);
dvr_autorec_entry_t *
dvr_autorec_create_htsp(const char *dvr_config_name, const char *title, int fulltext,
channel_t *ch, uint32_t enabled, int32_t start,
int32_t start_window, uint32_t days, time_t start_extra,
time_t stop_extra, dvr_prio_t pri, int retention,
int min_duration, int max_duration, dvr_autorec_dedup_t dup_detect,
const char *owner, const char *creator,
const char *comment, const char *name, const char *directory);
dvr_autorec_entry_t *
dvr_autorec_add_series_link(const char *dvr_config_name,
epg_broadcast_t *event,
const char *creator, const char *comment);
const char *owner, const char *creator,
const char *comment);
void dvr_autorec_save(dvr_autorec_entry_t *dae);
@ -520,6 +573,13 @@ void dvr_autorec_done(void);
void dvr_autorec_update(void);
static inline int dvr_autorec_entry_verify(dvr_autorec_entry_t *dae, access_t *a)
{
if (strcmp(dae->dae_owner ?: "", a->aa_username ?: ""))
return -1;
return 0;
}
/**
*
*/
@ -527,6 +587,13 @@ void dvr_autorec_update(void);
dvr_timerec_entry_t *
dvr_timerec_create(const char *uuid, htsmsg_t *conf);
dvr_timerec_entry_t*
dvr_timerec_create_htsp(const char *dvr_config_name, const char *title,
channel_t *ch, uint32_t enabled, uint32_t start, uint32_t stop,
uint32_t weekdays, dvr_prio_t pri, int retention,
const char *owner, const char *creator, const char *comment,
const char *name, const char *directory);
static inline dvr_timerec_entry_t *
dvr_timerec_find_by_uuid(const char *uuid)
{ return (dvr_timerec_entry_t*)idnode_find(uuid, &dvr_timerec_entry_class, NULL); }
@ -548,6 +615,13 @@ void dvr_timerec_done(void);
void dvr_timerec_update(void);
static inline int dvr_timerec_entry_verify(dvr_timerec_entry_t *dte, access_t *a)
{
if (strcmp(dte->dte_owner ?: "", a->aa_username ?: ""))
return -1;
return 0;
}
/**
*
*/

View file

@ -1,6 +1,6 @@
/*
* tvheadend, Automatic recordings
* Copyright (C) 2010 Andreas Öman
* Copyright (C) 2010 Andreas <EFBFBD>man
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -88,17 +88,33 @@ autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e)
// if configured
if(dae->dae_serieslink) {
if (!e->serieslink || dae->dae_serieslink != e->serieslink) return 0;
return 1;
} else {
if(dae->dae_season)
if (!e->episode->season || dae->dae_season != e->episode->season) return 0;
if(dae->dae_brand)
if (!e->episode->brand || dae->dae_brand != e->episode->brand) return 0;
}
if(dae->dae_season)
if (!e->episode->season || dae->dae_season != e->episode->season) return 0;
if(dae->dae_brand)
if (!e->episode->brand || dae->dae_brand != e->episode->brand) return 0;
if(dae->dae_title != NULL && dae->dae_title[0] != '\0') {
lang_str_ele_t *ls;
if(!e->episode->title) return 0;
RB_FOREACH(ls, e->episode->title, link)
if (!regexec(&dae->dae_title_preg, ls->str, 0, NULL, 0)) break;
if (!dae->dae_fulltext) {
if(!e->episode->title) return 0;
RB_FOREACH(ls, e->episode->title, link)
if (!regexec(&dae->dae_title_preg, ls->str, 0, NULL, 0)) break;
} else {
ls = NULL;
if (e->episode->title)
RB_FOREACH(ls, e->episode->title, link)
if (!regexec(&dae->dae_title_preg, ls->str, 0, NULL, 0)) break;
if (!ls && e->episode->subtitle)
RB_FOREACH(ls, e->episode->subtitle, link)
if (!regexec(&dae->dae_title_preg, ls->str, 0, NULL, 0)) break;
if (!ls && e->summary)
RB_FOREACH(ls, e->summary, link)
if (!regexec(&dae->dae_title_preg, ls->str, 0, NULL, 0)) break;
if (!ls && e->description)
RB_FOREACH(ls, e->description, link)
if (!regexec(&dae->dae_title_preg, ls->str, 0, NULL, 0)) break;
}
if (!ls) return 0;
}
@ -106,9 +122,13 @@ autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e)
if ((cfg = dae->dae_config) == NULL)
return 0;
if (cfg->dvr_sl_quality_lock)
if(dae->dae_channel != NULL &&
dae->dae_channel != e->channel)
return 0;
if(dae->dae_channel != NULL) {
if (dae->dae_channel != e->channel &&
dae->dae_channel->ch_enabled)
return 0;
if (!dae->dae_channel->ch_enabled)
return 0;
}
if(dae->dae_channel_tag != NULL) {
LIST_FOREACH(ctm, &dae->dae_channel_tag->ct_ctms, ctm_tag_link)
@ -126,15 +146,29 @@ autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e)
return 0;
}
if(dae->dae_start >= 0) {
struct tm a_time;
struct tm ev_time;
if(dae->dae_start >= 0 && dae->dae_start_window >= 0 &&
dae->dae_start < 24*60 && dae->dae_start_window < 24*60) {
struct tm a_time, ev_time;
time_t ta, te, tad;
localtime_r(&e->start, &a_time);
localtime_r(&e->start, &ev_time);
ev_time = a_time;
a_time.tm_min = dae->dae_start % 60;
a_time.tm_hour = dae->dae_start / 60;
if(abs(mktime(&a_time) - mktime(&ev_time)) > 900)
return 0;
ta = mktime(&a_time);
te = mktime(&ev_time);
if(dae->dae_start > dae->dae_start_window) {
ta -= 24 * 3600; /* 24 hours */
tad = ((24 * 60) - dae->dae_start + dae->dae_start_window) * 60;
if(ta > te || te > ta + tad) {
ta += 24 * 3600;
if(ta > te || te > ta + tad)
return 0;
}
} else {
tad = (dae->dae_start_window - dae->dae_start) * 60;
if(ta > te || te > ta + tad)
return 0;
}
}
duration = difftime(e->stop,e->start);
@ -176,6 +210,7 @@ dvr_autorec_create(const char *uuid, htsmsg_t *conf)
dae->dae_weekdays = 0x7f;
dae->dae_pri = DVR_PRIO_NORMAL;
dae->dae_start = -1;
dae->dae_start_window = -1;
dae->dae_config = dvr_config_find_by_name_default(NULL);
LIST_INSERT_HEAD(&dae->dae_config->dvr_autorec_entries, dae, dae_config_link);
@ -190,12 +225,13 @@ dvr_autorec_create(const char *uuid, htsmsg_t *conf)
dvr_autorec_entry_t*
dvr_autorec_create_htsp(const char *dvr_config_name, const char *title,
channel_t *ch, uint32_t aroundTime, uint32_t weekdays,
time_t start_extra, time_t stop_extra,
dvr_autorec_create_htsp(const char *dvr_config_name, const char *title, int fulltext,
channel_t *ch, uint32_t enabled, int32_t start, int32_t start_window,
uint32_t weekdays, time_t start_extra, time_t stop_extra,
dvr_prio_t pri, int retention,
int min_duration, int max_duration,
const char *creator, const char *comment)
int min_duration, int max_duration, dvr_autorec_dedup_t dup_detect,
const char *owner, const char *creator, const char *comment,
const char *name, const char *directory)
{
dvr_autorec_entry_t *dae;
htsmsg_t *conf, *days;
@ -203,7 +239,7 @@ dvr_autorec_create_htsp(const char *dvr_config_name, const char *title,
conf = htsmsg_create_map();
days = htsmsg_create_list();
htsmsg_add_u32(conf, "enabled", 1);
htsmsg_add_u32(conf, "enabled", enabled > 0 ? 1 : 0);
htsmsg_add_u32(conf, "retention", retention);
htsmsg_add_u32(conf, "pri", pri);
htsmsg_add_u32(conf, "minduration", min_duration);
@ -211,12 +247,19 @@ dvr_autorec_create_htsp(const char *dvr_config_name, const char *title,
htsmsg_add_s64(conf, "start_extra", start_extra);
htsmsg_add_s64(conf, "stop_extra", stop_extra);
htsmsg_add_str(conf, "title", title);
htsmsg_add_u32(conf, "fulltext", fulltext);
htsmsg_add_u32(conf, "record", dup_detect);
htsmsg_add_str(conf, "config_name", dvr_config_name ?: "");
htsmsg_add_str(conf, "owner", owner ?: "");
htsmsg_add_str(conf, "creator", creator ?: "");
htsmsg_add_str(conf, "comment", comment ?: "");
htsmsg_add_str(conf, "name", name ?: "");
htsmsg_add_str(conf, "directory", directory ?: "");
if (aroundTime)
htsmsg_add_u32(conf, "start", (aroundTime-1));
if (start >= 0)
htsmsg_add_s32(conf, "start", start);
if (start_window >= 0)
htsmsg_add_s32(conf, "start_window", start_window);
if (ch)
htsmsg_add_str(conf, "channel", idnode_uuid_as_str(&ch->ch_id));
@ -244,7 +287,8 @@ dvr_autorec_create_htsp(const char *dvr_config_name, const char *title,
dvr_autorec_entry_t *
dvr_autorec_add_series_link(const char *dvr_config_name,
epg_broadcast_t *event,
const char *creator, const char *comment)
const char *owner, const char *creator,
const char *comment)
{
dvr_autorec_entry_t *dae;
htsmsg_t *conf;
@ -260,6 +304,7 @@ dvr_autorec_add_series_link(const char *dvr_config_name,
htsmsg_add_str(conf, "channel", channel_get_name(event->channel));
if (event->serieslink)
htsmsg_add_str(conf, "serieslink", event->serieslink->uri);
htsmsg_add_str(conf, "owner", owner ?: "");
htsmsg_add_str(conf, "creator", creator ?: "");
htsmsg_add_str(conf, "comment", comment ?: "");
dae = dvr_autorec_create(NULL, conf);
@ -287,6 +332,8 @@ autorec_entry_destroy(dvr_autorec_entry_t *dae, int delconf)
LIST_REMOVE(dae, dae_config_link);
free(dae->dae_name);
free(dae->dae_directory);
free(dae->dae_owner);
free(dae->dae_creator);
free(dae->dae_comment);
@ -344,6 +391,20 @@ dvr_autorec_entry_class_delete(idnode_t *self)
autorec_entry_destroy((dvr_autorec_entry_t *)self, 1);
}
static int
dvr_autorec_entry_class_perm(idnode_t *self, access_t *a, htsmsg_t *msg_to_write)
{
dvr_autorec_entry_t *dae = (dvr_autorec_entry_t *)self;
if (access_verify2(a, ACCESS_OR|ACCESS_ADMIN|ACCESS_RECORDER))
return -1;
if (!access_verify2(a, ACCESS_ADMIN))
return 0;
if (dvr_autorec_entry_verify(dae, a))
return -1;
return 0;
}
static const char *
dvr_autorec_entry_class_get_title (idnode_t *self)
{
@ -490,6 +551,13 @@ dvr_autorec_entry_class_start_set(void *o, const void *v)
return dvr_autorec_entry_class_time_set(o, v, &dae->dae_start);
}
static int
dvr_autorec_entry_class_start_window_set(void *o, const void *v)
{
dvr_autorec_entry_t *dae = (dvr_autorec_entry_t *)o;
return dvr_autorec_entry_class_time_set(o, v, &dae->dae_start_window);
}
static const void *
dvr_autorec_entry_class_time_get(void *o, int tm)
{
@ -510,6 +578,13 @@ dvr_autorec_entry_class_start_get(void *o)
return dvr_autorec_entry_class_time_get(o, dae->dae_start);
}
static const void *
dvr_autorec_entry_class_start_window_get(void *o)
{
dvr_autorec_entry_t *dae = (dvr_autorec_entry_t *)o;
return dvr_autorec_entry_class_time_get(o, dae->dae_start_window);
}
htsmsg_t *
dvr_autorec_entry_class_time_list(void *o, const char *null)
{
@ -793,6 +868,20 @@ dvr_autorec_entry_class_content_type_list(void *o)
return m;
}
static htsmsg_t *
dvr_autorec_entry_class_dedup_list ( void *o )
{
static const struct strtab tab[] = {
{ "Record all", DVR_AUTOREC_RECORD_ALL },
{ "Record if different episode number", DVR_AUTOREC_RECORD_DIFFERENT_EPISODE_NUMBER },
{ "Record if different subtitle", DVR_AUTOREC_RECORD_DIFFERENT_SUBTITLE },
{ "Record if different description", DVR_AUTOREC_RECORD_DIFFERENT_DESCRIPTION },
{ "Record once per week", DVR_AUTOREC_RECORD_ONCE_PER_WEEK },
{ "Record once per day", DVR_AUTOREC_RECORD_ONCE_PER_DAY },
};
return strtab2htsmsg(tab);
}
const idclass_t dvr_autorec_entry_class = {
.ic_class = "dvrautorec",
.ic_caption = "DVR Auto-Record Entry",
@ -800,6 +889,7 @@ const idclass_t dvr_autorec_entry_class = {
.ic_save = dvr_autorec_entry_class_save,
.ic_get_title = dvr_autorec_entry_class_get_title,
.ic_delete = dvr_autorec_entry_class_delete,
.ic_perm = dvr_autorec_entry_class_perm,
.ic_properties = (const property_t[]) {
{
.type = PT_BOOL,
@ -812,6 +902,12 @@ const idclass_t dvr_autorec_entry_class = {
.id = "name",
.name = "Name",
.off = offsetof(dvr_autorec_entry_t, dae_name),
},
{
.type = PT_STR,
.id = "directory",
.name = "Directory",
.off = offsetof(dvr_autorec_entry_t, dae_directory),
},
{
.type = PT_STR,
@ -820,6 +916,12 @@ const idclass_t dvr_autorec_entry_class = {
.set = dvr_autorec_entry_class_title_set,
.off = offsetof(dvr_autorec_entry_t, dae_title),
},
{
.type = PT_BOOL,
.id = "fulltext",
.name = "Fulltext",
.off = offsetof(dvr_autorec_entry_t, dae_fulltext),
},
{
.type = PT_STR,
.id = "channel",
@ -841,12 +943,21 @@ const idclass_t dvr_autorec_entry_class = {
{
.type = PT_STR,
.id = "start",
.name = "Starting Around",
.name = "Start After",
.set = dvr_autorec_entry_class_start_set,
.get = dvr_autorec_entry_class_start_get,
.list = dvr_autorec_entry_class_time_list_,
.opts = PO_SORTKEY
},
{
.type = PT_STR,
.id = "start_window",
.name = "Start Before",
.set = dvr_autorec_entry_class_start_window_set,
.get = dvr_autorec_entry_class_start_window_get,
.list = dvr_autorec_entry_class_time_list_,
.opts = PO_SORTKEY,
},
{
.type = PT_TIME,
.id = "start_extra",
@ -903,6 +1014,14 @@ const idclass_t dvr_autorec_entry_class = {
.def.i = DVR_PRIO_NORMAL,
.off = offsetof(dvr_autorec_entry_t, dae_pri),
},
{
.type = PT_U32,
.id = "record",
.name = "Duplicate Handling",
.def.i = DVR_AUTOREC_RECORD_ALL,
.off = offsetof(dvr_autorec_entry_t, dae_record),
.list = dvr_autorec_entry_class_dedup_list,
},
{
.type = PT_INT,
.id = "retention",
@ -942,6 +1061,13 @@ const idclass_t dvr_autorec_entry_class = {
.get = dvr_autorec_entry_class_series_link_get,
.opts = PO_RDONLY,
},
{
.type = PT_STR,
.id = "owner",
.name = "Owner",
.off = offsetof(dvr_autorec_entry_t, dae_owner),
.opts = PO_RDONLY,
},
{
.type = PT_STR,
.id = "creator",
@ -1047,6 +1173,7 @@ dvr_autorec_changed(dvr_autorec_entry_t *dae, int purge)
dvr_autorec_purge_spawns(dae, 1);
CHANNEL_FOREACH(ch) {
if (!ch->ch_enabled) continue;
RB_FOREACH(e, &ch->ch_epg_schedule, sched_link) {
if(autorec_cmp(dae, e))
dvr_entry_create_by_autorec(e, dae);

View file

@ -316,7 +316,7 @@ dvr_config_class_perm(idnode_t *self, access_t *a, htsmsg_t *msg_to_write)
htsmsg_field_t *f;
const char *uuid, *my_uuid;
if (access_verify2(a, ACCESS_RECORDER))
if (access_verify2(a, ACCESS_OR|ACCESS_ADMIN|ACCESS_RECORDER))
return -1;
if (!access_verify2(a, ACCESS_ADMIN))
return 0;
@ -571,13 +571,6 @@ const idclass_t dvr_config_class = {
.list = dvr_config_class_extra_list,
.group = 1,
},
{
.type = PT_BOOL,
.id = "episode-duplicate-detection",
.name = "Episode Duplicate Detect",
.off = offsetof(dvr_config_t, dvr_episode_duplicate),
.group = 1,
},
{
.type = PT_U32,
.id = "epg-update-window",
@ -727,6 +720,13 @@ const idclass_t dvr_config_class = {
.off = offsetof(dvr_config_t, dvr_whitespace_in_title),
.group = 5,
},
{
.type = PT_BOOL,
.id = "windows-compatible-filenames",
.name = "Use Windows-compatible filenames",
.off = offsetof(dvr_config_t, dvr_windows_compatible_filenames),
.group = 5,
},
{}
},
};

View file

@ -145,7 +145,7 @@ dvr_parse_file
dvr_cutpoint_t *cp = NULL;
float frate = 0.0;
char line[DVR_MAX_CUTPOINT_LINE];
FILE *file = fopen(path, "r");
FILE *file = tvh_fopen(path, "r");
if (file == NULL)
return -1;

View file

@ -44,6 +44,7 @@ static void dvr_timer_expire(void *aux);
static void dvr_timer_start_recording(void *aux);
static void dvr_timer_stop_recording(void *aux);
static int dvr_entry_class_disp_title_set(void *o, const void *v);
static int dvr_entry_class_disp_subtitle_set(void *o, const void *v);
/*
* Start / stop time calculators
@ -253,35 +254,32 @@ dvr_make_title(char *output, size_t outlen, dvr_entry_t *de)
snprintf(output + strlen(output), outlen - strlen(output),
"%s", lang_str_get(de->de_title, NULL));
if(cfg->dvr_episode_before_date) {
if(cfg->dvr_episode_in_title) {
if(de->de_bcast && de->de_bcast->episode)
epg_episode_number_format(de->de_bcast->episode,
output + strlen(output),
outlen - strlen(output),
".", "S%02d", NULL, "E%02d", NULL);
}
if (cfg->dvr_episode_before_date) {
if (cfg->dvr_episode_in_title && de->de_bcast && de->de_bcast->episode)
epg_episode_number_format(de->de_bcast->episode,
output + strlen(output),
outlen - strlen(output),
".", "S%02d", NULL, "E%02d", NULL);
}
if(cfg->dvr_subtitle_in_title) {
if(de->de_bcast && de->de_bcast->episode && de->de_bcast->episode->subtitle)
if (cfg->dvr_subtitle_in_title && de->de_subtitle) {
snprintf(output + strlen(output), outlen - strlen(output),
".%s", lang_str_get(de->de_bcast->episode->subtitle, NULL));
".%s", lang_str_get(de->de_subtitle, NULL));
}
localtime_r(&de->de_start, &tm);
if(cfg->dvr_date_in_title) {
if (cfg->dvr_date_in_title) {
strftime(buf, sizeof(buf), "%F", &tm);
snprintf(output + strlen(output), outlen - strlen(output), ".%s", buf);
}
if(cfg->dvr_time_in_title) {
if (cfg->dvr_time_in_title) {
strftime(buf, sizeof(buf), "%H-%M", &tm);
snprintf(output + strlen(output), outlen - strlen(output), ".%s", buf);
}
if(!cfg->dvr_episode_before_date) {
if (!cfg->dvr_episode_before_date) {
if(cfg->dvr_episode_in_title) {
if(de->de_bcast && de->de_bcast->episode)
epg_episode_number_format(de->de_bcast->episode,
@ -308,14 +306,14 @@ dvr_entry_set_timer(dvr_entry_t *de)
de->de_sched_state = DVR_MISSED_TIME;
else
_dvr_entry_completed(de);
gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de,
gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de,
de->de_stop + dvr_entry_get_retention(de) * 86400);
} else if (de->de_sched_state == DVR_RECORDING) {
gtimer_arm_abs(&de->de_timer, dvr_timer_stop_recording, de, stop);
} else if (de->de_channel) {
} else if (de->de_channel && de->de_channel->ch_enabled) {
de->de_sched_state = DVR_SCHEDULED;
@ -333,6 +331,21 @@ dvr_entry_set_timer(dvr_entry_t *de)
}
/**
* Get episode name
*/
static char *
dvr_entry_get_episode(epg_broadcast_t *bcast, char *buf, int len)
{
if (!bcast || !bcast->episode)
return NULL;
if (epg_episode_number_format(bcast->episode,
buf, len, NULL,
"Season %d", ".", "Episode %d", "/%d"))
return buf;
return NULL;
}
/**
* Find dvr entry using 'fuzzy' search
*/
@ -341,6 +354,7 @@ dvr_entry_fuzzy_match(dvr_entry_t *de, epg_broadcast_t *e)
{
time_t t1, t2;
const char *title1, *title2;
char buf[64];
/* Matching ID */
if (de->de_dvb_eid && de->de_dvb_eid == e->dvb_eid)
@ -359,11 +373,19 @@ dvr_entry_fuzzy_match(dvr_entry_t *de, epg_broadcast_t *e)
return 0;
/* Outside of window */
if ( abs(e->start - de->de_start) > de->de_config->dvr_update_window )
if (abs(e->start - de->de_start) > de->de_config->dvr_update_window)
return 0;
/* Title match (or contains?) */
return strcmp(title1, title2) == 0;
if (strcmp(title1, title2))
return 0;
/* episode check */
if (dvr_entry_get_episode(e, buf, sizeof(buf)) && de->de_episode)
if (strcmp(buf, de->de_episode))
return 0;
return 1;
}
/**
@ -407,6 +429,11 @@ dvr_entry_create(const char *uuid, htsmsg_t *conf)
(s = htsmsg_get_str(conf, "disp_title")) != NULL)
dvr_entry_class_disp_title_set(de, s);
/* special case, becaous PO_NOSAVE, load ignores it */
if (de->de_subtitle == NULL &&
(s = htsmsg_get_str(conf, "disp_subtitle")) != NULL)
dvr_entry_class_disp_subtitle_set(de, s);
de->de_refcnt = 1;
LIST_INSERT_HEAD(&dvrentries, de, de_global_link);
@ -434,14 +461,16 @@ dvr_entry_t *
dvr_entry_create_(const char *config_uuid, epg_broadcast_t *e,
channel_t *ch, time_t start, time_t stop,
time_t start_extra, time_t stop_extra,
const char *title, const char *description,
const char *title, const char* subtitle, const char *description,
const char *lang, epg_genre_t *content_type,
const char *owner,
const char *creator, dvr_autorec_entry_t *dae,
dvr_timerec_entry_t *dte,
dvr_prio_t pri, int retention)
dvr_prio_t pri, int retention,
const char *comment)
{
dvr_entry_t *de;
char tbuf[64];
char tbuf[64], *s;
struct tm tm;
time_t t;
lang_str_t *l;
@ -456,11 +485,15 @@ dvr_entry_create_(const char *config_uuid, epg_broadcast_t *e,
htsmsg_add_str(conf, "config_name", config_uuid ?: "");
htsmsg_add_s64(conf, "start_extra", start_extra);
htsmsg_add_s64(conf, "stop_extra", stop_extra);
htsmsg_add_str(conf, "owner", owner ?: "");
htsmsg_add_str(conf, "creator", creator ?: "");
htsmsg_add_str(conf, "comment", comment ?: "");
if (e) {
htsmsg_add_u32(conf, "dvb_eid", e->dvb_eid);
if (e->episode && e->episode->title)
lang_str_serialize(e->episode->title, conf, "title");
if (e->episode && e->episode->subtitle)
lang_str_serialize(e->episode->subtitle, conf, "subtitle");
if (e->description)
lang_str_serialize(e->description, conf, "description");
else if (e->episode && e->episode->description)
@ -469,6 +502,8 @@ dvr_entry_create_(const char *config_uuid, epg_broadcast_t *e,
lang_str_serialize(e->summary, conf, "description");
else if (e->episode && e->episode->summary)
lang_str_serialize(e->episode->summary, conf, "description");
if (e->episode && (s = dvr_entry_get_episode(e, tbuf, sizeof(tbuf))))
htsmsg_add_str(conf, "episode", s);
} else if (title) {
l = lang_str_create();
lang_str_add(l, title, lang, 0);
@ -480,15 +515,27 @@ dvr_entry_create_(const char *config_uuid, epg_broadcast_t *e,
lang_str_serialize(l, conf, "description");
lang_str_destroy(l);
}
if (subtitle) {
l = lang_str_create();
lang_str_add(l, subtitle, lang, 0);
lang_str_serialize(l, conf, "subtitle");
lang_str_destroy(l);
}
}
if (content_type)
htsmsg_add_u32(conf, "content_type", content_type->code / 16);
if (e)
htsmsg_add_u32(conf, "broadcast", e->id);
if (dae)
{
htsmsg_add_str(conf, "autorec", idnode_uuid_as_str(&dae->dae_id));
htsmsg_add_str(conf, "directory", dae->dae_directory ?: "");
}
if (dte)
{
htsmsg_add_str(conf, "timerec", idnode_uuid_as_str(&dte->dte_id));
htsmsg_add_str(conf, "directory", dte->dte_directory ?: "");
}
de = dvr_entry_create(NULL, conf);
@ -518,11 +565,13 @@ dvr_entry_t *
dvr_entry_create_htsp(const char *config_uuid,
channel_t *ch, time_t start, time_t stop,
time_t start_extra, time_t stop_extra,
const char *title,
const char *title, const char* subtitle,
const char *description, const char *lang,
epg_genre_t *content_type,
const char *owner,
const char *creator, dvr_autorec_entry_t *dae,
dvr_prio_t pri, int retention)
dvr_prio_t pri, int retention,
const char *comment)
{
dvr_config_t *cfg = dvr_config_find_by_uuid(config_uuid);
if (!cfg)
@ -530,8 +579,9 @@ dvr_entry_create_htsp(const char *config_uuid,
return dvr_entry_create_(cfg ? idnode_uuid_as_str(&cfg->dvr_id) : NULL,
NULL,
ch, start, stop, start_extra, stop_extra,
title, description, lang, content_type,
creator, dae, NULL, pri, retention);
title, subtitle, description, lang, content_type,
owner, creator, dae, NULL, pri, retention,
comment);
}
/**
@ -541,8 +591,10 @@ dvr_entry_t *
dvr_entry_create_by_event(const char *config_uuid,
epg_broadcast_t *e,
time_t start_extra, time_t stop_extra,
const char *owner,
const char *creator, dvr_autorec_entry_t *dae,
dvr_prio_t pri, int retention)
dvr_prio_t pri, int retention,
const char *comment)
{
if(!e->channel || !e->episode || !e->episode->title)
return NULL;
@ -550,46 +602,100 @@ dvr_entry_create_by_event(const char *config_uuid,
return dvr_entry_create_(config_uuid, e,
e->channel, e->start, e->stop,
start_extra, stop_extra,
NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
LIST_FIRST(&e->episode->genre),
creator, dae, NULL, pri, retention);
owner, creator, dae, NULL, pri, retention,
comment);
}
/**
*
*/
static int _dvr_duplicate_event ( epg_broadcast_t *e )
static dvr_entry_t* _dvr_duplicate_event(dvr_entry_t* de)
{
dvr_entry_t *de;
epg_episode_num_t empty_epnum;
int has_epnum = 1;
if (!de->de_autorec)
return NULL;
/* skip episode duplicate check below if no episode number */
memset(&empty_epnum, 0, sizeof(empty_epnum));
if (epg_episode_number_cmp(&empty_epnum, &e->episode->epnum) == 0)
has_epnum = 0;
int record = de->de_autorec->dae_record;
LIST_FOREACH(de, &dvrentries, de_global_link) {
if (de->de_bcast) {
if (de->de_bcast->episode == e->episode) return 1;
struct tm de_start;
localtime_r(&de->de_start, &de_start);
if (has_epnum) {
int ep_dup_det = de->de_config->dvr_episode_duplicate;
switch (record) {
case DVR_AUTOREC_RECORD_ALL:
return NULL;
case DVR_AUTOREC_RECORD_DIFFERENT_EPISODE_NUMBER:
if (strempty(de->de_episode))
return NULL;
break;
case DVR_AUTOREC_RECORD_DIFFERENT_SUBTITLE:
if (lang_str_empty(de->de_subtitle))
return NULL;
break;
case DVR_AUTOREC_RECORD_DIFFERENT_DESCRIPTION:
if (lang_str_empty(de->de_desc))
return NULL;
break;
case DVR_AUTOREC_RECORD_ONCE_PER_WEEK:
de_start.tm_mday -= (de_start.tm_wday + 6) % 7; // week = mon-sun
mktime(&de_start); // adjusts de_start
break;
}
if (ep_dup_det) {
const char* de_title = lang_str_get(de->de_bcast->episode->title, NULL);
const char* e_title = lang_str_get(e->episode->title, NULL);
// title not defined, can't be deduped
if (lang_str_empty(de->de_title))
return NULL;
/* duplicate if title and episode match */
if (de_title && e_title && strcmp(de_title, e_title) == 0
&& epg_episode_number_cmp(&de->de_bcast->episode->epnum, &e->episode->epnum) == 0) {
return 1;
}
}
dvr_entry_t *de2;
LIST_FOREACH(de2, &dvrentries, de_global_link) {
if (de == de2)
continue;
// only earlier recordings qualify as master
if (de2->de_start > de->de_start)
continue;
// only successful earlier recordings qualify as master
if (de2->de_sched_state == DVR_MISSED_TIME || (de2->de_sched_state == DVR_COMPLETED && de2->de_last_error != SM_CODE_OK))
continue;
// if titles are not defined or do not match, don't dedup
if (lang_str_compare(de->de_title, de2->de_title))
continue;
switch (record) {
case DVR_AUTOREC_RECORD_DIFFERENT_EPISODE_NUMBER:
if (!strcmp(de->de_episode, de2->de_episode))
return de2;
break;
case DVR_AUTOREC_RECORD_DIFFERENT_SUBTITLE:
if (!lang_str_compare(de->de_subtitle, de2->de_subtitle))
return de2;
break;
case DVR_AUTOREC_RECORD_DIFFERENT_DESCRIPTION:
if (!lang_str_compare(de->de_desc, de2->de_desc))
return de2;
break;
case DVR_AUTOREC_RECORD_ONCE_PER_WEEK: {
struct tm de2_start;
localtime_r(&de2->de_start, &de2_start);
de2_start.tm_mday -= (de2_start.tm_wday + 6) % 7; // week = mon-sun
mktime(&de2_start); // adjusts de2_start
if (de_start.tm_year == de2_start.tm_year && de_start.tm_yday == de2_start.tm_yday)
return de2;
break;
}
case DVR_AUTOREC_RECORD_ONCE_PER_DAY: {
struct tm de2_start;
localtime_r(&de2->de_start, &de2_start);
if (de_start.tm_year == de2_start.tm_year && de_start.tm_yday == de2_start.tm_yday)
return de2;
break;
}
}
}
return 0;
return NULL;
}
/**
@ -600,17 +706,22 @@ dvr_entry_create_by_autorec(epg_broadcast_t *e, dvr_autorec_entry_t *dae)
{
char buf[200];
/* Dup detection */
if (_dvr_duplicate_event(e)) return;
if(dae->dae_creator) {
snprintf(buf, sizeof(buf), "Auto recording by: %s", dae->dae_creator);
} else {
snprintf(buf, sizeof(buf), "Auto recording");
/* Identical duplicate detection
NOTE: Semantic duplicate detection is deferred to the start time of recording and then done using _dvr_duplicate_event by dvr_timer_start_recording. */
dvr_entry_t* de;
LIST_FOREACH(de, &dvrentries, de_global_link) {
if (de->de_bcast == e || (de->de_bcast && de->de_bcast->episode == e->episode))
return;
}
snprintf(buf, sizeof(buf), "Auto recording%s%s",
dae->dae_creator ? " by: " : "",
dae->dae_creator ?: "");
dvr_entry_create_by_event(idnode_uuid_as_str(&dae->dae_config->dvr_id), e,
dae->dae_start_extra, dae->dae_stop_extra,
buf, dae, dae->dae_pri, dae->dae_retention);
dae->dae_owner, buf, dae, dae->dae_pri, dae->dae_retention,
dae->dae_comment);
}
/**
@ -640,10 +751,15 @@ dvr_entry_dec_ref(dvr_entry_t *de)
LIST_REMOVE(de, de_config_link);
free(de->de_filename);
free(de->de_owner);
free(de->de_creator);
free(de->de_comment);
if (de->de_title) lang_str_destroy(de->de_title);
if (de->de_subtitle) lang_str_destroy(de->de_subtitle);
if (de->de_desc) lang_str_destroy(de->de_desc);
if (de->de_bcast) de->de_bcast->putref((epg_object_t*)de->de_bcast);
free(de->de_channel_name);
free(de->de_episode);
free(de);
}
@ -672,8 +788,6 @@ dvr_entry_destroy(dvr_entry_t *de, int delconf)
LIST_REMOVE(de, de_channel_link);
LIST_REMOVE(de, de_global_link);
de->de_channel = NULL;
free(de->de_channel_name);
de->de_channel_name = NULL;
dvr_entry_dec_ref(de);
}
@ -727,10 +841,11 @@ dvr_timer_expire(void *aux)
}
static dvr_entry_t *_dvr_entry_update
( dvr_entry_t *de, epg_broadcast_t *e, const char *title,
( dvr_entry_t *de, epg_broadcast_t *e, const char *title, const char* subtitle,
const char *desc, const char *lang, time_t start, time_t stop,
time_t start_extra, time_t stop_extra, dvr_prio_t pri, int retention )
{
char buf[40];
int save = 0;
if (!dvr_entry_is_editable(de))
@ -776,7 +891,13 @@ static dvr_entry_t *_dvr_entry_update
if (!de->de_title) de->de_title = lang_str_create();
save = lang_str_add(de->de_title, title, lang, 1);
}
/* Subtitle*/
if (subtitle) {
if (!de->de_subtitle) de->de_subtitle = lang_str_create();
save = lang_str_add(de->de_subtitle, subtitle, lang, 1);
}
/* EID */
if (e && e->dvb_eid != de->de_dvb_eid) {
de->de_dvb_eid = e->dvb_eid;
@ -801,6 +922,16 @@ static dvr_entry_t *_dvr_entry_update
de->de_bcast = e;
e->getref(e);
save = 1;
}
/* Episode */
if (dvr_entry_get_episode(de->de_bcast, buf, sizeof(buf))) {
if (strcmp(de->de_episode ?: "", buf)) {
free(de->de_episode);
de->de_episode = strdup(buf);
save = 1;
}
}
/* Save changes */
@ -820,12 +951,12 @@ static dvr_entry_t *_dvr_entry_update
dvr_entry_t *
dvr_entry_update
(dvr_entry_t *de,
const char* de_title, const char *de_desc, const char *lang,
const char* de_title, const char* de_subtitle, const char *de_desc, const char *lang,
time_t de_start, time_t de_stop,
time_t de_start_extra, time_t de_stop_extra,
dvr_prio_t pri, int retention)
{
return _dvr_entry_update(de, NULL, de_title, de_desc, lang,
return _dvr_entry_update(de, NULL, de_title, de_subtitle, de_desc, lang,
de_start, de_stop, de_start_extra, de_stop_extra,
pri, retention);
}
@ -861,7 +992,7 @@ dvr_event_replaced(epg_broadcast_t *e, epg_broadcast_t *new_e)
e->putref(e);
de->de_bcast = NULL;
/* If this was craeted by autorec - just remove it, it'll get recreated */
/* If this was created by autorec - just remove it, it'll get recreated */
if (de->de_autorec) {
dvr_entry_destroy(de, 1);
@ -877,7 +1008,7 @@ dvr_event_replaced(epg_broadcast_t *e, epg_broadcast_t *new_e)
e->start, e->stop);
e->getref(e);
de->de_bcast = e;
_dvr_entry_update(de, e, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0);
_dvr_entry_update(de, e, NULL, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0);
break;
}
}
@ -890,7 +1021,7 @@ void dvr_event_updated ( epg_broadcast_t *e )
dvr_entry_t *de;
de = dvr_entry_find_by_event(e);
if (de)
_dvr_entry_update(de, e, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0);
_dvr_entry_update(de, e, NULL, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0);
else {
LIST_FOREACH(de, &dvrentries, de_global_link) {
if (de->de_sched_state != DVR_SCHEDULED) continue;
@ -906,7 +1037,7 @@ void dvr_event_updated ( epg_broadcast_t *e )
e->start, e->stop);
e->getref(e);
de->de_bcast = e;
_dvr_entry_update(de, e, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0);
_dvr_entry_update(de, e, NULL, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0);
break;
}
}
@ -962,8 +1093,20 @@ dvr_timer_start_recording(void *aux)
{
dvr_entry_t *de = aux;
if (de->de_channel == NULL || !de->de_channel->ch_enabled) {
de->de_sched_state = DVR_NOSTATE;
return;
}
// if duplicate, then delete it now, don't record!
if (_dvr_duplicate_event(de)) {
dvr_entry_cancel_delete(de);
return;
}
de->de_sched_state = DVR_RECORDING;
de->de_rec_state = DVR_RS_PENDING;
de->de_last_error = SM_CODE_OK;
tvhlog(LOG_INFO, "dvr", "\"%s\" on \"%s\" recorder starting",
lang_str_get(de->de_title, NULL), DVR_CH_NAME(de));
@ -1055,6 +1198,20 @@ dvr_entry_class_delete(idnode_t *self)
dvr_entry_cancel_delete((dvr_entry_t *)self);
}
static int
dvr_entry_class_perm(idnode_t *self, access_t *a, htsmsg_t *msg_to_write)
{
dvr_entry_t *de = (dvr_entry_t *)self;
if (access_verify2(a, ACCESS_OR|ACCESS_ADMIN|ACCESS_RECORDER))
return -1;
if (!access_verify2(a, ACCESS_ADMIN))
return 0;
if (dvr_entry_verify(de, a, msg_to_write == NULL ? 1 : 0))
return -1;
return 0;
}
static const char *
dvr_entry_class_get_title (idnode_t *self)
{
@ -1467,14 +1624,32 @@ dvr_entry_class_disp_title_get(void *o)
return &s;
}
static int
dvr_entry_class_disp_subtitle_set(void *o, const void *v)
{
dvr_entry_t *de = (dvr_entry_t *)o;
const char *s = "";
if (v == NULL || *((char *)v) == '\0')
v = "UnknownSubtitle";
if (de->de_subtitle)
s = lang_str_get(de->de_subtitle, NULL);
if (strcmp(s, v)) {
lang_str_destroy(de->de_subtitle);
de->de_subtitle = lang_str_create();
lang_str_add(de->de_subtitle, v, NULL, 0);
return 1;
}
return 0;
}
static const void *
dvr_entry_class_disp_description_get(void *o)
dvr_entry_class_disp_subtitle_get(void *o)
{
dvr_entry_t *de = (dvr_entry_t *)o;
static const char *s;
s = "";
if (de->de_title) {
s = lang_str_get(de->de_desc, NULL);
if (de->de_subtitle) {
s = lang_str_get(de->de_subtitle, NULL);
if (s == NULL)
s = "";
}
@ -1482,17 +1657,16 @@ dvr_entry_class_disp_description_get(void *o)
}
static const void *
dvr_entry_class_episode_get(void *o)
dvr_entry_class_disp_description_get(void *o)
{
dvr_entry_t *de = (dvr_entry_t *)o;
static const char *s;
static char buf[100];
s = "";
if (de->de_bcast && de->de_bcast->episode)
if (epg_episode_number_format(de->de_bcast->episode,
buf, sizeof(buf), NULL,
"Season %d", ".", "Episode %d", "/%d"))
s = buf;
if (de->de_desc) {
s = lang_str_get(de->de_desc, NULL);
if (s == NULL)
s = "";
}
return &s;
}
@ -1503,7 +1677,8 @@ dvr_entry_class_url_get(void *o)
static const char *s;
static char buf[100];
s = "";
if (de->de_sched_state == DVR_COMPLETED) {
if (de->de_sched_state == DVR_COMPLETED ||
de->de_sched_state == DVR_RECORDING) {
snprintf(buf, sizeof(buf), "dvrfile/%s", idnode_uuid_as_str(&de->de_id));
s = buf;
}
@ -1515,9 +1690,12 @@ dvr_entry_class_filesize_get(void *o)
{
static int64_t size;
dvr_entry_t *de = (dvr_entry_t *)o;
if (de->de_sched_state == DVR_COMPLETED)
if (de->de_sched_state == DVR_COMPLETED ||
de->de_sched_state == DVR_RECORDING) {
size = dvr_get_filesize(de);
else
if (size < 0)
size = 0;
} else
size = 0;
return &size;
}
@ -1593,6 +1771,15 @@ dvr_entry_class_channel_icon_url_get(void *o)
return &s;
}
static const void *
dvr_entry_class_duplicate_get(void *o)
{
static time_t null = 0;
dvr_entry_t *de = (dvr_entry_t *)o;
de = _dvr_duplicate_event(de);
return de ? &de->de_start : &null;
}
htsmsg_t *
dvr_entry_class_duration_list(void *o, const char *not_set, int max, int step)
{
@ -1642,10 +1829,10 @@ const idclass_t dvr_entry_class = {
.ic_class = "dvrentry",
.ic_caption = "DVR Entry",
.ic_event = "dvrentry",
.ic_perm_def = ACCESS_RECORDER,
.ic_save = dvr_entry_class_save,
.ic_get_title = dvr_entry_class_get_title,
.ic_delete = dvr_entry_class_delete,
.ic_perm = dvr_entry_class_perm,
.ic_properties = (const property_t[]) {
{
.type = PT_TIME,
@ -1725,6 +1912,7 @@ const idclass_t dvr_entry_class = {
.get = dvr_entry_class_channel_name_get,
.set = dvr_entry_class_channel_name_set,
.off = offsetof(dvr_entry_t, de_channel_name),
.opts = PO_RDONLY,
},
{
.type = PT_LANGSTR,
@ -1741,6 +1929,21 @@ const idclass_t dvr_entry_class = {
.set = dvr_entry_class_disp_title_set,
.opts = PO_NOSAVE,
},
{
.type = PT_LANGSTR,
.id = "subtitle",
.name = "Subtitle",
.off = offsetof(dvr_entry_t, de_subtitle),
.opts = PO_RDONLY,
},
{
.type = PT_STR,
.id = "disp_subtitle",
.name = "Subtitle",
.get = dvr_entry_class_disp_subtitle_get,
.set = dvr_entry_class_disp_subtitle_set,
.opts = PO_NOSAVE,
},
{
.type = PT_LANGSTR,
.id = "description",
@ -1792,6 +1995,13 @@ const idclass_t dvr_entry_class = {
.rend = dvr_entry_class_config_name_rend,
.get_opts = dvr_entry_class_start_opts,
},
{
.type = PT_STR,
.id = "owner",
.name = "Owner",
.off = offsetof(dvr_entry_t, de_owner),
.opts = PO_RDONLY,
},
{
.type = PT_STR,
.id = "creator",
@ -1806,6 +2016,13 @@ const idclass_t dvr_entry_class = {
.off = offsetof(dvr_entry_t, de_filename),
.opts = PO_RDONLY,
},
{
.type = PT_STR,
.id = "directory",
.name = "Directory",
.off = offsetof(dvr_entry_t, de_directory),
.opts = PO_RDONLY,
},
{
.type = PT_U32,
.id = "errorcode",
@ -1820,6 +2037,13 @@ const idclass_t dvr_entry_class = {
.off = offsetof(dvr_entry_t, de_errors),
.opts = PO_RDONLY,
},
{
.type = PT_U32,
.id = "data_errors",
.name = "Data Errors",
.off = offsetof(dvr_entry_t, de_data_errors),
.opts = PO_RDONLY,
},
{
.type = PT_U16,
.id = "dvb_eid",
@ -1870,8 +2094,8 @@ const idclass_t dvr_entry_class = {
.type = PT_STR,
.id = "episode",
.name = "Episode",
.get = dvr_entry_class_episode_get,
.opts = PO_RDONLY | PO_NOSAVE | PO_HIDDEN,
.off = offsetof(dvr_entry_t, de_episode),
.opts = PO_RDONLY | PO_HIDDEN,
},
{
.type = PT_STR,
@ -1901,6 +2125,19 @@ const idclass_t dvr_entry_class = {
.get = dvr_entry_class_sched_status_get,
.opts = PO_RDONLY | PO_NOSAVE | PO_HIDDEN,
},
{
.type = PT_TIME,
.id = "duplicate",
.name = "Rerun of",
.get = dvr_entry_class_duplicate_get,
.opts = PO_RDONLY | PO_NOSAVE,
},
{
.type = PT_STR,
.id = "comment",
.name = "Comment",
.off = offsetof(dvr_entry_t, de_comment),
},
{}
}
};
@ -1949,7 +2186,7 @@ static struct strtab priotab[] = {
{ "high", DVR_PRIO_HIGH },
{ "normal", DVR_PRIO_NORMAL },
{ "low", DVR_PRIO_LOW },
{ "unimportant", DVR_PRIO_UNIMPORTANT },
{ "unimportant", DVR_PRIO_UNIMPORTANT }
};
dvr_prio_t
@ -1971,9 +2208,11 @@ dvr_val2pri(dvr_prio_t v)
void
dvr_entry_delete(dvr_entry_t *de)
{
dvr_config_t *cfg = de->de_config;
time_t t;
struct tm tm;
char tbuf[64];
char tbuf[64], *rdir;
int r;
t = dvr_entry_get_start_time(de);
localtime_r(&t, &tm);
@ -1991,37 +2230,14 @@ dvr_entry_delete(dvr_entry_t *de)
#if ENABLE_INOTIFY
dvr_inotify_del(de);
#endif
if(unlink(de->de_filename) && errno != ENOENT)
rdir = NULL;
if(cfg->dvr_title_dir || cfg->dvr_channel_dir || cfg->dvr_dir_per_day || de->de_directory)
rdir = cfg->dvr_storage;
r = deferred_unlink(de->de_filename, rdir);
if(r && r != -ENOENT)
tvhlog(LOG_WARNING, "dvr", "Unable to remove file '%s' from disk -- %s",
de->de_filename, strerror(errno));
/* Also delete directories, if they were created for the recording and if they are empty */
dvr_config_t *cfg = de->de_config;
char path[500];
snprintf(path, sizeof(path), "%s", cfg->dvr_storage);
if(cfg->dvr_title_dir || cfg->dvr_channel_dir || cfg->dvr_dir_per_day) {
char *p;
int l;
l = strlen(de->de_filename);
p = alloca(l + 1);
memcpy(p, de->de_filename, l);
p[l--] = 0;
for(; l >= 0; l--) {
if(p[l] == '/') {
p[l] = 0;
if(strncmp(p, cfg->dvr_storage, strlen(p)) == 0)
break;
if(rmdir(p) == -1)
break;
}
}
}
de->de_filename, strerror(-errno));
}
dvr_entry_destroy(de, 1);
}

View file

@ -63,7 +63,7 @@ pthread_t dvr_inotify_tid;
void dvr_inotify_init ( void )
{
_inot_fd = inotify_init();
_inot_fd = inotify_init1(IN_CLOEXEC);
if (_inot_fd < 0) {
tvhlog(LOG_ERR, "dvr", "failed to initialise inotify (err=%s)",
strerror(errno));
@ -93,12 +93,11 @@ void dvr_inotify_add ( dvr_entry_t *de )
{
dvr_inotify_entry_t *e;
char *path;
struct stat st;
if (_inot_fd < 0)
return;
if (!de->de_filename || stat(de->de_filename, &st))
if (!de->de_filename)
return;
path = strdup(de->de_filename);
@ -106,11 +105,6 @@ void dvr_inotify_add ( dvr_entry_t *de )
SKEL_ALLOC(dvr_inotify_entry_skel);
dvr_inotify_entry_skel->path = dirname(path);
if (stat(dvr_inotify_entry_skel->path, &st)) {
free(path);
return;
}
e = RB_INSERT_SORTED(&_inot_tree, dvr_inotify_entry_skel, link, _str_cmp);
if (!e) {
e = dvr_inotify_entry_skel;

View file

@ -69,7 +69,7 @@ dvr_rec_subscribe(dvr_entry_t *de)
assert(de->de_s == NULL);
assert(de->de_chain == NULL);
if(de->de_pri < ARRAY_SIZE(prio2weight))
if(de->de_pri > 0 && de->de_pri < ARRAY_SIZE(prio2weight))
weight = prio2weight[de->de_pri];
else
weight = 300;
@ -85,9 +85,9 @@ dvr_rec_subscribe(dvr_entry_t *de)
return;
}
de->de_s = subscription_create_from_channel(prch, weight,
de->de_s = subscription_create_from_channel(prch, NULL, weight,
buf, prch->prch_flags,
NULL, NULL, NULL);
NULL, NULL, NULL, NULL);
if (de->de_s == NULL) {
tvherror("dvr", "unable to create new channel subcription for '%s'",
channel_get_name(de->de_channel));
@ -117,7 +117,7 @@ dvr_rec_unsubscribe(dvr_entry_t *de, int stopcode)
pthread_join(de->de_thread, NULL);
subscription_unsubscribe(de->de_s);
subscription_unsubscribe(de->de_s, 0);
de->de_s = NULL;
de->de_chain = NULL;
@ -134,7 +134,7 @@ dvr_rec_unsubscribe(dvr_entry_t *de, int stopcode)
static char *
cleanup_filename(char *s, dvr_config_t *cfg)
{
int i, len = strlen(s);
int i, len = strlen(s), len2;
char *s1;
s1 = intlconv_utf8safestr(cfg->dvr_charset_id, s, len * 2);
@ -151,7 +151,8 @@ cleanup_filename(char *s, dvr_config_t *cfg)
if (s[0] == '.')
s[0] = '_';
for (i = 0, len = strlen(s); i < len; i++) {
len2 = strlen(s);
for (i = 0; i < len2; i++) {
if(s[i] == '/')
s[i] = '-';
@ -164,6 +165,18 @@ cleanup_filename(char *s, dvr_config_t *cfg)
((s[i] < 32) || (s[i] > 122) ||
(strchr("/:\\<>|*?'\"", s[i]) != NULL)))
s[i] = '_';
else if(cfg->dvr_windows_compatible_filenames &&
(strchr("/:\\<>|*?'\"", s[i]) != NULL))
s[i] = '_';
}
if(cfg->dvr_windows_compatible_filenames) {
// trim trailing spaces and dots
for (i = len2 - 1; i >= 0; i--) {
if((s[i] != ' ') && (s[i] != '.'))
break;
s[i] = '\0';
}
}
return s;
@ -198,42 +211,53 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
if (path[strlen(path)-1] == '/')
path[strlen(path)-1] = '\0';
/* Append per-day directory */
if (cfg->dvr_dir_per_day) {
localtime_r(&de->de_start, &tm);
strftime(fullname, sizeof(fullname), "%F", &tm);
s = cleanup_filename(fullname, cfg);
/* Use the specified directory if set, otherwise construct it from the DVR
configuration */
if (de->de_directory) {
char *directory = strdup(de->de_directory);
s = cleanup_filename(directory, cfg);
if (s == NULL)
return -1;
snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
free(s);
}
/* Append per-channel directory */
if (cfg->dvr_channel_dir) {
char *chname = strdup(DVR_CH_NAME(de));
s = cleanup_filename(chname, cfg);
free(chname);
if (s == NULL)
} else {
/* Append per-day directory */
if (cfg->dvr_dir_per_day) {
localtime_r(&de->de_start, &tm);
strftime(fullname, sizeof(fullname), "%F", &tm);
s = cleanup_filename(fullname, cfg);
if (s == NULL)
return -1;
snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
free(s);
}
snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
free(s);
}
// TODO: per-brand, per-season
/* Append per-title directory */
if (cfg->dvr_title_dir) {
char *title = strdup(lang_str_get(de->de_title, NULL));
s = cleanup_filename(title, cfg);
free(title);
if (s == NULL)
/* Append per-channel directory */
if (cfg->dvr_channel_dir) {
char *chname = strdup(DVR_CH_NAME(de));
s = cleanup_filename(chname, cfg);
free(chname);
if (s == NULL)
return -1;
snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
free(s);
snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
free(s);
}
// TODO: per-brand, per-season
/* Append per-title directory */
if (cfg->dvr_title_dir) {
char *title = strdup(lang_str_get(de->de_title, NULL));
s = cleanup_filename(title, cfg);
free(title);
if (s == NULL)
return -1;
snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
free(s);
}
}
if (makedirs(path, cfg->dvr_muxcnf.m_directory_permissions) != 0)
if (makedirs(path, cfg->dvr_muxcnf.m_directory_permissions, -1, -1) != 0)
return -1;
/* Construct final name */
@ -285,6 +309,18 @@ dvr_rec_fatal_error(dvr_entry_t *de, const char *fmt, ...)
de->de_filename ?: lang_str_get(de->de_title, NULL), msgbuf);
}
/**
*
*/
static void
dvr_notify(dvr_entry_t *de, int now)
{
if (now || de->de_last_notify + 5 < dispatch_clock) {
idnode_notify_simple(&de->de_id);
de->de_last_notify = dispatch_clock;
htsp_dvr_entry_update(de);
}
}
/**
*
@ -304,7 +340,7 @@ dvr_rec_set_state(dvr_entry_t *de, dvr_rs_state_t newstate, int error)
de->de_errors++;
}
if (notify)
idnode_notify_simple(&de->de_id);
dvr_notify(de, 1);
}
/**
@ -358,8 +394,8 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss)
return -1;
}
if(cfg->dvr_tag_files && de->de_bcast) {
if(muxer_write_meta(muxer, de->de_bcast)) {
if(cfg->dvr_tag_files) {
if(muxer_write_meta(muxer, de->de_bcast, de->de_comment)) {
dvr_rec_fatal_error(de, "Unable to write meta data");
return -1;
}
@ -459,6 +495,7 @@ dvr_thread(void *aux)
profile_chain_t *prch = de->de_chain;
streaming_queue_t *sq = &prch->prch_sq;
streaming_message_t *sm;
th_subscription_t *ts;
th_pkt_t *pkt;
int run = 1;
int started = 0;
@ -474,14 +511,24 @@ dvr_thread(void *aux)
continue;
}
if (de->de_s && started) {
if ((ts = de->de_s) != NULL && started) {
pktbuf_t *pb = NULL;
if (sm->sm_type == SMT_PACKET)
if (sm->sm_type == SMT_PACKET) {
pb = ((th_pkt_t*)sm->sm_data)->pkt_payload;
else if (sm->sm_type == SMT_MPEGTS)
if (((th_pkt_t*)sm->sm_data)->pkt_err) {
de->de_data_errors += ((th_pkt_t*)sm->sm_data)->pkt_err;
dvr_notify(de, 0);
}
}
else if (sm->sm_type == SMT_MPEGTS) {
pb = sm->sm_data;
if (pb->pb_err) {
de->de_data_errors += pb->pb_err;
dvr_notify(de, 0);
}
}
if (pb)
atomic_add(&de->de_s->ths_bytes_out, pktbuf_len(pb));
atomic_add(&ts->ths_bytes_out, pktbuf_len(pb));
}
TAILQ_REMOVE(&sq->sq_queue, sm, sm_link);
@ -508,6 +555,7 @@ dvr_thread(void *aux)
if(started) {
muxer_write_pkt(prch->prch_muxer, sm->sm_type, sm->sm_data);
sm->sm_data = NULL;
dvr_notify(de, 0);
}
break;
@ -516,6 +564,7 @@ dvr_thread(void *aux)
dvr_rec_set_state(de, DVR_RS_RUNNING, 0);
muxer_write_pkt(prch->prch_muxer, sm->sm_type, sm->sm_data);
sm->sm_data = NULL;
dvr_notify(de, 0);
}
break;
@ -551,7 +600,7 @@ dvr_thread(void *aux)
} else if(sm->sm_code == 0) {
// Recording is completed
de->de_last_error = 0;
de->de_last_error = SM_CODE_OK;
tvhlog(LOG_INFO,
"dvr", "Recording completed: \"%s\"",
de->de_filename ?: lang_str_get(de->de_title, NULL));
@ -576,11 +625,10 @@ dvr_thread(void *aux)
case SMT_SERVICE_STATUS:
if(sm->sm_code & TSS_PACKETS) {
} else if(sm->sm_code & (TSS_GRACEPERIOD | TSS_ERRORS)) {
} else if(sm->sm_code & TSS_ERRORS) {
int code = SM_CODE_UNDEFINED_ERROR;
if(sm->sm_code & TSS_NO_DESCRAMBLER)
code = SM_CODE_NO_DESCRAMBLER;
@ -679,7 +727,7 @@ dvr_spawn_postproc(dvr_entry_t *de, const char *dvr_postproc)
args[i] = s;
}
spawnv(args[0], (void *)args);
spawnv(args[0], (void *)args, NULL, 1, 1);
htsstr_argsplit_free(args);
}
@ -695,6 +743,7 @@ dvr_thread_epilog(dvr_entry_t *de)
muxer_close(prch->prch_muxer);
muxer_destroy(prch->prch_muxer);
prch->prch_muxer = NULL;
dvr_notify(de, 1);
dvr_config_t *cfg = de->de_config;
if(cfg && cfg->dvr_postproc && de->de_filename)

View file

@ -32,6 +32,7 @@
#include "settings.h"
#include "dvr.h"
#include "epg.h"
#include "htsp_server.h"
struct dvr_timerec_entry_queue timerec_entries;
@ -132,7 +133,7 @@ dvr_timerec_check(dvr_timerec_entry_t *dte)
/* day boundary correction */
if (start > stop)
stop += 24 * 60 * 60;
assert(start < stop);
assert(start <= stop);
if(dte->dte_weekdays != 0x7f) {
localtime_r(&start, &tm_start);
@ -154,9 +155,10 @@ dvr_timerec_check(dvr_timerec_entry_t *dte)
dte->dte_creator ?: "");
de = dvr_entry_create_(idnode_uuid_as_str(&dte->dte_config->dvr_id),
NULL, dte->dte_channel,
start, stop, 0, 0, title,
NULL, NULL, NULL, buf,
NULL, dte, dte->dte_pri, dte->dte_retention);
start, stop, 0, 0, title, NULL,
NULL, NULL, NULL, dte->dte_owner, buf,
NULL, dte, dte->dte_pri, dte->dte_retention,
dte->dte_comment);
return;
@ -181,7 +183,7 @@ dvr_timerec_create(const char *uuid, htsmsg_t *conf)
return NULL;
}
dte->dte_title = strdup("Time-%x-%R");
dte->dte_title = strdup("Time-%F_%R");
dte->dte_weekdays = 0x7f;
dte->dte_pri = DVR_PRIO_NORMAL;
dte->dte_start = -1;
@ -193,6 +195,56 @@ dvr_timerec_create(const char *uuid, htsmsg_t *conf)
idnode_load(&dte->dte_id, conf);
htsp_timerec_entry_add(dte);
return dte;
}
dvr_timerec_entry_t*
dvr_timerec_create_htsp(const char *dvr_config_name, const char *title,
channel_t *ch, uint32_t enabled, uint32_t start, uint32_t stop,
uint32_t weekdays, dvr_prio_t pri, int retention,
const char *owner, const char *creator, const char *comment,
const char *name, const char *directory)
{
dvr_timerec_entry_t *dte;
htsmsg_t *conf, *days;
conf = htsmsg_create_map();
days = htsmsg_create_list();
htsmsg_add_u32(conf, "enabled", enabled > 0 ? 1 : 0);
htsmsg_add_u32(conf, "retention", retention);
htsmsg_add_u32(conf, "pri", pri);
htsmsg_add_str(conf, "title", title);
htsmsg_add_str(conf, "config_name", dvr_config_name ?: "");
htsmsg_add_str(conf, "owner", owner ?: "");
htsmsg_add_str(conf, "creator", creator ?: "");
htsmsg_add_str(conf, "comment", comment ?: "");
htsmsg_add_str(conf, "name", name ?: "");
htsmsg_add_str(conf, "directory", directory ?: "");
htsmsg_add_u32(conf, "start", start);
htsmsg_add_u32(conf, "stop", stop);
if (ch)
htsmsg_add_str(conf, "channel", idnode_uuid_as_str(&ch->ch_id));
int i;
for (i = 0; i < 7; i++)
if (weekdays & (1 << i))
htsmsg_add_u32(days, NULL, i + 1);
htsmsg_add_msg(conf, "weekdays", days);
dte = dvr_timerec_create(NULL, conf);
htsmsg_destroy(conf);
if (dte)
{
dvr_timerec_save(dte);
dvr_timerec_check(dte);
}
return dte;
}
@ -207,6 +259,8 @@ timerec_entry_destroy(dvr_timerec_entry_t *dte, int delconf)
if (delconf)
hts_settings_remove("dvr/timerec/%s", idnode_uuid_as_str(&dte->dte_id));
htsp_timerec_entry_delete(dte);
TAILQ_REMOVE(&timerec_entries, dte, dte_link);
idnode_unlink(&dte->dte_id);
@ -215,6 +269,7 @@ timerec_entry_destroy(dvr_timerec_entry_t *dte, int delconf)
free(dte->dte_name);
free(dte->dte_title);
free(dte->dte_owner);
free(dte->dte_creator);
free(dte->dte_comment);
@ -249,6 +304,7 @@ dvr_timerec_entry_class_save(idnode_t *self)
dvr_timerec_entry_t *dte = (dvr_timerec_entry_t *)self;
dvr_timerec_save(dte);
dvr_timerec_check(dte);
htsp_timerec_entry_update(dte);
}
static void
@ -257,6 +313,20 @@ dvr_timerec_entry_class_delete(idnode_t *self)
timerec_entry_destroy((dvr_timerec_entry_t *)self, 1);
}
static int
dvr_timerec_entry_class_perm(idnode_t *self, access_t *a, htsmsg_t *msg_to_write)
{
dvr_timerec_entry_t *dte = (dvr_timerec_entry_t *)self;
if (access_verify2(a, ACCESS_OR|ACCESS_ADMIN|ACCESS_RECORDER))
return -1;
if (!access_verify2(a, ACCESS_ADMIN))
return 0;
if (dvr_timerec_entry_verify(dte, a))
return -1;
return 0;
}
static const char *
dvr_timerec_entry_class_get_title (idnode_t *self)
{
@ -468,6 +538,7 @@ const idclass_t dvr_timerec_entry_class = {
.ic_save = dvr_timerec_entry_class_save,
.ic_get_title = dvr_timerec_entry_class_get_title,
.ic_delete = dvr_timerec_entry_class_delete,
.ic_perm = dvr_timerec_entry_class_perm,
.ic_properties = (const property_t[]) {
{
.type = PT_BOOL,
@ -486,7 +557,13 @@ const idclass_t dvr_timerec_entry_class = {
.id = "title",
.name = "Title",
.off = offsetof(dvr_timerec_entry_t, dte_title),
.def.s = "Time-%x-%R",
.def.s = "Time-%F_%R",
},
{
.type = PT_STR,
.id = "directory",
.name = "Directory",
.off = offsetof(dvr_timerec_entry_t, dte_directory),
},
{
.type = PT_STR,
@ -552,6 +629,13 @@ const idclass_t dvr_timerec_entry_class = {
.rend = dvr_timerec_entry_class_config_name_rend,
.list = dvr_entry_class_config_name_list,
},
{
.type = PT_STR,
.id = "owner",
.name = "Owner",
.off = offsetof(dvr_timerec_entry_t, dte_creator),
.opts = PO_RDONLY,
},
{
.type = PT_STR,
.id = "creator",
@ -608,8 +692,9 @@ dvr_timerec_timer_cb(void *aux)
tvhtrace("dvr", "timerec update");
/* check all entries */
TAILQ_FOREACH(dte, &timerec_entries, dte_link)
TAILQ_FOREACH(dte, &timerec_entries, dte_link) {
dvr_timerec_check(dte);
}
/* load the timer */
gtimer_arm(&dvr_timerec_timer, dvr_timerec_timer_cb, NULL, 3550);

View file

@ -1101,19 +1101,19 @@ size_t epg_episode_number_format
epg_episode_num_t num;
epg_episode_get_epnum(episode, &num);
if ( num.e_num ) {
if (pre) i += snprintf(&buf[i], len-i, "%s", pre);
if (pre) tvh_strlcatf(buf, len, i, "%s", pre);
if ( sfmt && num.s_num ) {
i += snprintf(&buf[i], len-i, sfmt, num.s_num);
tvh_strlcatf(buf, len, i, sfmt, num.s_num);
if ( cfmt && num.s_cnt )
i += snprintf(&buf[i], len-i, cfmt, num.s_cnt);
if (sep) i += snprintf(&buf[i], len-i, "%s", sep);
tvh_strlcatf(buf, len, i, cfmt, num.s_cnt);
if (sep) tvh_strlcatf(buf, len, i, "%s", sep);
}
i += snprintf(&buf[i], len-i, efmt, num.e_num);
tvh_strlcatf(buf, len, i, efmt, num.e_num);
if ( cfmt && num.e_cnt )
i+= snprintf(&buf[i], len-i, cfmt, num.e_cnt);
tvh_strlcatf(buf, len, i, cfmt, num.e_cnt);
} else if ( num.text ) {
if (pre) i += snprintf(&buf[i], len-i, "%s", pre);
i += snprintf(&buf[i], len-i, "%s", num.text);
if (pre) tvh_strlcatf(buf, len, i, "%s", pre);
tvh_strlcatf(buf, len, i, "%s", num.text);
}
return i;
}
@ -1289,7 +1289,7 @@ const char *epg_episode_get_subtitle
return lang_str_get(e->subtitle, lang);
}
const char *epg_episode_get_summary
const char *epg_episode_get_summary
( const epg_episode_t *e, const char *lang )
{
if (!e || !e->summary) return NULL;
@ -2118,12 +2118,11 @@ size_t epg_genre_get_str ( const epg_genre_t *genre, int major_only,
if (!_epg_genre_names[maj][0]) return 0;
min = major_only ? 0 : (genre->code & 0xf);
if (!min || major_prefix ) {
ret = snprintf(buf, len, "%s", _epg_genre_names[maj][0]);
if (min) ret += snprintf(buf+ret, len-ret, " : ");
}
if (min && _epg_genre_names[maj][min]) {
ret += snprintf(buf+ret, len-ret, "%s", _epg_genre_names[maj][min]);
tvh_strlcatf(buf, len, ret, "%s", _epg_genre_names[maj][0]);
if (min) tvh_strlcatf(buf, len, ret, " : ");
}
if (min && _epg_genre_names[maj][min])
tvh_strlcatf(buf, len, ret, "%s", _epg_genre_names[maj][min]);
return ret;
}
@ -2263,6 +2262,7 @@ _eq_add ( epg_query_t *eq, epg_broadcast_t *e )
{
const char *s, *lang = eq->lang;
epg_episode_t *ep;
int fulltext = eq->stitle && eq->fulltext;
/* Filtering */
if (e == NULL) return;
@ -2292,22 +2292,36 @@ _eq_add ( epg_query_t *eq, epg_broadcast_t *e )
}
if (!r) return;
}
if (eq->title.comp != EC_NO || eq->stitle) {
if (fulltext) {
if ((s = epg_episode_get_title(ep, lang)) == NULL ||
regexec(&eq->stitle_re, s, 0, NULL, 0)) {
if ((s = epg_episode_get_subtitle(ep, lang)) == NULL ||
regexec(&eq->stitle_re, s, 0, NULL, 0)) {
if ((s = epg_broadcast_get_summary(e, lang)) == NULL ||
regexec(&eq->stitle_re, s, 0, NULL, 0)) {
if ((s = epg_broadcast_get_description(e, lang)) == NULL ||
regexec(&eq->stitle_re, s, 0, NULL, 0)) {
return;
}
}
}
}
}
if (eq->title.comp != EC_NO || (eq->stitle && !fulltext)) {
if ((s = epg_episode_get_title(ep, lang)) == NULL) return;
if (eq->stitle)
if (regexec(&eq->stitle_re, s, 0, NULL, 0)) return;
if (_eq_comp_str(&eq->title, s)) return;
if (eq->stitle && !fulltext && regexec(&eq->stitle_re, s, 0, NULL, 0)) return;
if (eq->title.comp != EC_NO && _eq_comp_str(&eq->title, s)) return;
}
if (eq->subtitle.comp != EC_NO) {
if ((s = epg_episode_get_subtitle(ep, lang)) == NULL) return;
if (_eq_comp_str(&eq->subtitle, s)) return;
}
if (eq->summary.comp != EC_NO) {
if ((s = epg_episode_get_summary(ep, lang)) == NULL) return;
if ((s = epg_broadcast_get_summary(e, lang)) == NULL) return;
if (_eq_comp_str(&eq->summary, s)) return;
}
if (eq->description.comp != EC_NO) {
if ((s = epg_episode_get_description(ep, lang)) == NULL) return;
if ((s = epg_broadcast_get_description(e, lang)) == NULL) return;
if (_eq_comp_str(&eq->description, s)) return;
}
@ -2517,7 +2531,7 @@ static int _epg_sort_genre_descending ( const void *a, const void *b, void *eq )
}
epg_broadcast_t **
epg_query ( epg_query_t *eq )
epg_query ( epg_query_t *eq, access_t *perm )
{
channel_t *channel;
channel_tag_t *tag;
@ -2542,20 +2556,25 @@ epg_query ( epg_query_t *eq )
/* Single channel */
if (channel && tag == NULL) {
_eq_add_channel(eq, channel);
if (channel_access(channel, perm, 0))
_eq_add_channel(eq, channel);
/* Tag based */
} else if (tag) {
channel_tag_mapping_t *ctm;
channel_t *ch2;
LIST_FOREACH(ctm, &tag->ct_ctms, ctm_tag_link) {
if(channel == NULL || ctm->ctm_channel == channel)
_eq_add_channel(eq, ctm->ctm_channel);
ch2 = ctm->ctm_channel;
if(ch2 == channel || channel == NULL)
if (channel_access(ch2, perm, 0))
_eq_add_channel(eq, ch2);
}
/* All channels */
} else {
CHANNEL_FOREACH(channel)
_eq_add_channel(eq, channel);
if (channel_access(channel, perm, 0))
_eq_add_channel(eq, channel);
}
switch (eq->sort_dir) {

View file

@ -22,6 +22,7 @@
#include <regex.h>
#include "settings.h"
#include "lang_str.h"
#include "access.h"
/*
* External forward decls
@ -578,6 +579,7 @@ typedef struct epg_query {
epg_filter_num_t channel_num;
char *stitle;
regex_t stitle_re;
int fulltext;
char *channel;
char *channel_tag;
uint32_t genre_count;
@ -609,7 +611,7 @@ typedef struct epg_query {
uint32_t allocated;
} epg_query_t;
epg_broadcast_t **epg_query(epg_query_t *eq);
epg_broadcast_t **epg_query(epg_query_t *eq, access_t *perm);
void epg_query_free(epg_query_t *eq);
/* ************************************************************************

View file

@ -31,6 +31,7 @@
#include "epggrab.h"
#define EPG_DB_VERSION 2
#define EPG_DB_ALLOC_STEP (1024*1024)
extern epg_object_tree_t epg_brands;
extern epg_object_tree_t epg_seasons;
@ -261,7 +262,7 @@ void epg_done ( void )
* Save
* *************************************************************************/
static int _epg_write ( int fd, htsmsg_t *m )
static int _epg_write ( sbuf_t *sb, htsmsg_t *m )
{
int ret = 1;
size_t msglen;
@ -270,7 +271,11 @@ static int _epg_write ( int fd, htsmsg_t *m )
int r = htsmsg_binary_serialize(m, &msgdata, &msglen, 0x10000);
htsmsg_destroy(m);
if (!r) {
ret = tvh_write(fd, msgdata, msglen);
ret = 0;
/* allocation helper - we fight with megabytes */
if (sb->sb_size - sb->sb_ptr < 32 * 1024)
sbuf_realloc(sb, (sb->sb_size - (sb->sb_size % EPG_DB_ALLOC_STEP)) + EPG_DB_ALLOC_STEP);
sbuf_append(sb, msgdata, msglen);
free(msgdata);
}
} else {
@ -279,11 +284,31 @@ static int _epg_write ( int fd, htsmsg_t *m )
return ret;
}
static int _epg_write_sect ( int fd, const char *sect )
static int _epg_write_sect ( sbuf_t *sb, const char *sect )
{
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_str(m, "__section__", sect);
return _epg_write(fd, m);
return _epg_write(sb, m);
}
static void epg_save_tsk_callback ( void *p, int dearmed )
{
sbuf_t *sb = p;
int fd, r;
tvhinfo("epgdb", "save start");
fd = hts_settings_open_file(1, "epgdb.v%d", EPG_DB_VERSION);
if (fd >= 0) {
r = tvh_write(fd, sb->sb_data, sb->sb_ptr);
close(fd);
if (r)
tvherror("epgdb", "write error (size %d)", sb->sb_ptr);
else
tvhinfo("epgdb", "stored (size %d)", sb->sb_ptr);
} else
tvherror("epgdb", "unable to open epgdb file");
sbuf_free(sb);
free(sb);
}
void epg_save_callback ( void *p )
@ -293,63 +318,68 @@ void epg_save_callback ( void *p )
void epg_save ( void )
{
int fd;
sbuf_t *sb = malloc(sizeof(*sb));
epg_object_t *eo;
epg_broadcast_t *ebc;
channel_t *ch;
epggrab_stats_t stats;
extern gtimer_t epggrab_save_timer;
if (epggrab_epgdb_periodicsave)
gtimer_arm(&epggrab_save_timer, epg_save_callback, NULL, epggrab_epgdb_periodicsave);
fd = hts_settings_open_file(1, "epgdb.v%d", EPG_DB_VERSION);
if (fd < 0)
if (!sb)
return;
tvhinfo("epgdb", "snapshot start");
sbuf_init_fixed(sb, EPG_DB_ALLOC_STEP);
if (epggrab_epgdb_periodicsave)
gtimer_arm(&epggrab_save_timer, epg_save_callback, NULL, epggrab_epgdb_periodicsave);
memset(&stats, 0, sizeof(stats));
if ( _epg_write_sect(fd, "config") ) goto error;
if (_epg_write(fd, epg_config_serialize())) goto error;
if ( _epg_write_sect(fd, "brands") ) goto error;
if ( _epg_write_sect(sb, "config") ) goto error;
if (_epg_write(sb, epg_config_serialize())) goto error;
if ( _epg_write_sect(sb, "brands") ) goto error;
RB_FOREACH(eo, &epg_brands, uri_link) {
if (_epg_write(fd, epg_brand_serialize((epg_brand_t*)eo))) goto error;
if (_epg_write(sb, epg_brand_serialize((epg_brand_t*)eo))) goto error;
stats.brands.total++;
}
if ( _epg_write_sect(fd, "seasons") ) goto error;
if ( _epg_write_sect(sb, "seasons") ) goto error;
RB_FOREACH(eo, &epg_seasons, uri_link) {
if (_epg_write(fd, epg_season_serialize((epg_season_t*)eo))) goto error;
if (_epg_write(sb, epg_season_serialize((epg_season_t*)eo))) goto error;
stats.seasons.total++;
}
if ( _epg_write_sect(fd, "episodes") ) goto error;
if ( _epg_write_sect(sb, "episodes") ) goto error;
RB_FOREACH(eo, &epg_episodes, uri_link) {
if (_epg_write(fd, epg_episode_serialize((epg_episode_t*)eo))) goto error;
if (_epg_write(sb, epg_episode_serialize((epg_episode_t*)eo))) goto error;
stats.episodes.total++;
}
if ( _epg_write_sect(fd, "serieslinks") ) goto error;
if ( _epg_write_sect(sb, "serieslinks") ) goto error;
RB_FOREACH(eo, &epg_serieslinks, uri_link) {
if (_epg_write(fd, epg_serieslink_serialize((epg_serieslink_t*)eo))) goto error;
if (_epg_write(sb, epg_serieslink_serialize((epg_serieslink_t*)eo))) goto error;
stats.seasons.total++;
}
if ( _epg_write_sect(fd, "broadcasts") ) goto error;
if ( _epg_write_sect(sb, "broadcasts") ) goto error;
CHANNEL_FOREACH(ch) {
RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) {
if (_epg_write(fd, epg_broadcast_serialize(ebc))) goto error;
if (_epg_write(sb, epg_broadcast_serialize(ebc))) goto error;
stats.broadcasts.total++;
}
}
close(fd);
tasklet_arm_alloc(epg_save_tsk_callback, sb);
/* Stats */
tvhlog(LOG_INFO, "epgdb", "saved");
tvhlog(LOG_INFO, "epgdb", " brands %d", stats.brands.total);
tvhlog(LOG_INFO, "epgdb", " seasons %d", stats.seasons.total);
tvhlog(LOG_INFO, "epgdb", " episodes %d", stats.episodes.total);
tvhlog(LOG_INFO, "epgdb", " broadcasts %d", stats.broadcasts.total);
tvhinfo("epgdb", "queued to save (size %d)", sb->sb_ptr);
tvhinfo("epgdb", " brands %d", stats.brands.total);
tvhinfo("epgdb", " seasons %d", stats.seasons.total);
tvhinfo("epgdb", " episodes %d", stats.episodes.total);
tvhinfo("epgdb", " broadcasts %d", stats.broadcasts.total);
return;
error:
tvhlog(LOG_ERR, "epgdb", "failed to store epg to disk");
hts_settings_remove("epgdb.v%d", EPG_DB_VERSION);
close(fd);
sbuf_free(sb);
free(sb);
}

View file

@ -148,9 +148,11 @@ static void _epggrab_load ( void )
htsmsg_get_u32(m, "channel_renumber", &epggrab_channel_renumber);
htsmsg_get_u32(m, "channel_reicon", &epggrab_channel_reicon);
htsmsg_get_u32(m, "epgdb_periodicsave", &epggrab_epgdb_periodicsave);
if (epggrab_epgdb_periodicsave)
if (epggrab_epgdb_periodicsave) {
epggrab_epgdb_periodicsave = MAX(epggrab_epgdb_periodicsave, 3600);
gtimer_arm(&epggrab_save_timer, epg_save_callback, NULL,
epggrab_epgdb_periodicsave);
}
if ((str = htsmsg_get_str(m, "cron")) != NULL)
epggrab_set_cron(str);
htsmsg_get_u32(m, "grab-enabled", &enabled);
@ -295,16 +297,16 @@ int epggrab_set_channel_renumber ( uint32_t e )
int epggrab_set_periodicsave ( uint32_t e )
{
int save = 0;
pthread_mutex_lock(&global_lock);
if ( e != epggrab_epgdb_periodicsave ) {
epggrab_epgdb_periodicsave = e;
pthread_mutex_lock(&global_lock);
epggrab_epgdb_periodicsave = e ? MAX(e, 3600) : 0;
if (!e)
gtimer_disarm(&epggrab_save_timer);
else
epg_save(); // will arm the timer
pthread_mutex_unlock(&global_lock);
save = 1;
}
pthread_mutex_unlock(&global_lock);
return save;
}

View file

@ -83,8 +83,9 @@ typedef struct epggrab_channel
char *name; ///< Channel name
char *icon; ///< Channel icon
int number; ///< Channel number
int major; ///< Channel major number
int minor; ///< Channel minor number
LIST_HEAD(,epggrab_channel_link) channels; ///< Mapped channels
} epggrab_channel_t;
@ -107,7 +108,7 @@ htsmsg_t* epggrab_channel_list ( int ota );
*/
int epggrab_channel_set_name ( epggrab_channel_t *ch, const char *name );
int epggrab_channel_set_icon ( epggrab_channel_t *ch, const char *icon );
int epggrab_channel_set_number ( epggrab_channel_t *ch, int number );
int epggrab_channel_set_number ( epggrab_channel_t *ch, int major, int minor );
/*
* Updated/link
@ -292,6 +293,7 @@ int epggrab_enable_module_by_id ( const char *id, uint8_t e );
int epggrab_ota_set_cron ( const char *cron, int lock );
int epggrab_ota_set_timeout ( uint32_t e );
int epggrab_ota_set_initial ( uint32_t e );
void epggrab_ota_trigger ( int secs );
/*
* Load/Save

View file

@ -36,10 +36,12 @@ SKEL_DECLARE(epggrab_channel_skel, epggrab_channel_t);
/* Check if channels match */
int epggrab_channel_match ( epggrab_channel_t *ec, channel_t *ch )
{
if (!ec || !ch) return 0;
if (!ec || !ch || !ch->ch_epgauto || !ch->ch_enabled) return 0;
if (LIST_FIRST(&ec->channels)) return 0; // ignore already paired
if (ec->name && !strcmp(ec->name, channel_get_name(ch))) return 1;
int64_t number = channel_get_number(ch);
if ((ec->major || ec->minor) && ec->major == channel_get_major(number) && ec->minor == channel_get_minor(number)) return 1;
return 0;
}
@ -59,10 +61,11 @@ epggrab_channel_link_delete
int
epggrab_channel_link ( epggrab_channel_t *ec, channel_t *ch )
{
int save = 0;
epggrab_channel_link_t *ecl;
/* No change */
if (!ch) return 0;
if (!ch || !ch->ch_enabled) return 0;
/* Already linked */
LIST_FOREACH(ecl, &ec->channels, ecl_epg_link) {
@ -80,14 +83,14 @@ epggrab_channel_link ( epggrab_channel_t *ec, channel_t *ch )
ecl->ecl_epggrab = ec;
LIST_INSERT_HEAD(&ec->channels, ecl, ecl_epg_link);
LIST_INSERT_HEAD(&ch->ch_epggrab, ecl, ecl_chn_link);
#if TODO_CHAN_UPDATE
if (ec->name && epggrab_channel_rename)
channel_rename(ch, ec->name);
if (ec->number>0 && epggrab_channel_renumber)
channel_set_number(ch, ec->number);
save |= channel_set_name(ch, ec->name);
if ((ec->major > 0 || ec->minor > 0) && epggrab_channel_renumber)
save |= channel_set_number(ch, ec->major, ec->minor);
if (ec->icon && epggrab_channel_reicon)
channel_set_icon(ch, ec->icon);
#endif
save |= channel_set_icon(ch, ec->icon);
if (save)
channel_save(ch);
/* Save */
if (ec->mod->ch_save) ec->mod->ch_save(ec->mod, ec);
@ -111,13 +114,13 @@ int epggrab_channel_set_name ( epggrab_channel_t *ec, const char *name )
if (!ec->name || strcmp(ec->name, name)) {
if (ec->name) free(ec->name);
ec->name = strdup(name);
#if TODO_CHAN_UPDATE
if (epggrab_channel_rename) {
epggrab_channel_link_t *ecl;
LIST_FOREACH(ecl, &ec->channels, link)
channel_rename(ecl->channel, name);
LIST_FOREACH(ecl, &ec->channels, ecl_epg_link) {
if (channel_set_name(ecl->ecl_channel, name))
channel_save(ecl->ecl_channel);
}
}
#endif
save = 1;
}
return save;
@ -131,32 +134,33 @@ int epggrab_channel_set_icon ( epggrab_channel_t *ec, const char *icon )
if (!ec->icon || strcmp(ec->icon, icon) ) {
if (ec->icon) free(ec->icon);
ec->icon = strdup(icon);
#if TODO_CHAN_UPDATE
if (epggrab_channel_reicon) {
epggrab_channel_link_t *ecl;
LIST_FOREACH(ecl, &ec->channels, link)
channel_set_icon(ecl->channel, icon);
LIST_FOREACH(ecl, &ec->channels, ecl_epg_link) {
if (channel_set_icon(ecl->ecl_channel, icon))
channel_save(ecl->ecl_channel);
}
}
#endif
save = 1;
}
return save;
}
/* Set channel number */
int epggrab_channel_set_number ( epggrab_channel_t *ec, int number )
int epggrab_channel_set_number ( epggrab_channel_t *ec, int major, int minor )
{
int save = 0;
if (!ec || (number <= 0)) return 0;
if (ec->number != number) {
ec->number = number;
#if TODO_CHAN_UPDATE
if (!ec || (major <= 0 && minor <= 0)) return 0;
if (ec->major != major || ec->minor != minor) {
ec->major = major;
ec->minor = minor;
if (epggrab_channel_renumber) {
epggrab_channel_link_t *ecl;
LIST_FOREACH(ecl, &ec->channels, link)
channel_set_number(ecl->channel, number);
LIST_FOREACH(ecl, &ec->channels, ecl_epg_link) {
if (channel_set_number(ecl->ecl_channel, major, minor))
channel_save(ecl->ecl_channel);
}
}
#endif
save = 1;
}
return save;

View file

@ -163,8 +163,10 @@ void epggrab_module_ch_save ( void *_m, epggrab_channel_t *ch )
htsmsg_add_str(a, NULL, channel_get_uuid(ecl->ecl_channel));
}
if (a) htsmsg_add_msg(m, "channels", a);
if (ch->number)
htsmsg_add_u32(m, "number", ch->number);
if (ch->major)
htsmsg_add_u32(m, "major", ch->major);
if (ch->minor)
htsmsg_add_u32(m, "major", ch->minor);
hts_settings_save(m, "epggrab/%s/channels/%s", mod->id, ch->id);
htsmsg_destroy(m);
@ -208,8 +210,10 @@ static void _epggrab_module_channel_load
egc->name = strdup(str);
if ((str = htsmsg_get_str(m, "icon")))
egc->icon = strdup(str);
if(!htsmsg_get_u32(m, "number", &u32))
egc->number = u32;
if(!htsmsg_get_u32(m, "major", &u32))
egc->major = u32;
if(!htsmsg_get_u32(m, "minor", &u32))
egc->minor = u32;
if ((a = htsmsg_get_list(m, "channels"))) {
HTSMSG_FOREACH(f, a) {
if ((str = htsmsg_field_get_str(f))) {
@ -248,6 +252,7 @@ epggrab_module_int_done( void *m )
{
epggrab_module_int_t *mod = m;
free((char *)mod->path);
mod->path = NULL;
}
epggrab_module_int_t *epggrab_module_int_create
@ -279,21 +284,40 @@ epggrab_module_int_t *epggrab_module_int_create
char *epggrab_module_grab_spawn ( void *m )
{
int outlen;
int rd = -1, outlen;
char *outbuf;
epggrab_module_int_t *mod = m;
char **argv = NULL;
/* Debug */
tvhlog(LOG_INFO, mod->id, "grab %s", mod->path);
/* Grab */
outlen = spawn_and_store_stdout(mod->path, NULL, &outbuf);
if ( outlen < 1 ) {
tvhlog(LOG_ERR, mod->id, "no output detected");
/* Arguments */
if (spawn_parse_args(&argv, 64, mod->path, NULL)) {
tvhlog(LOG_ERR, mod->id, "unable to parse arguments");
return NULL;
}
/* Grab */
outlen = spawn_and_give_stdout(argv[0], (char **)argv, NULL, &rd, NULL, 1);
if (outlen < 0)
goto error;
outlen = file_readall(rd, &outbuf);
if (outlen < 1)
goto error;
close(rd);
return outbuf;
error:
spawn_free_args(argv);
if (rd >= 0)
close(rd);
tvhlog(LOG_ERR, mod->id, "no output detected");
return NULL;
}
@ -381,7 +405,7 @@ epggrab_module_done_socket( void *m )
shutdown(sock, SHUT_RDWR);
close(sock);
if (mod->tid) {
pthread_kill(mod->tid, SIGTERM);
pthread_kill(mod->tid, SIGQUIT);
pthread_join(mod->tid, NULL);
}
mod->tid = 0;

View file

@ -395,10 +395,11 @@ static int _eit_desc_crid
* EIT Event
* ***********************************************************************/
static int _eit_process_event
static int _eit_process_event_one
( epggrab_module_t *mod, int tableid,
mpegts_service_t *svc, const uint8_t *ptr, int len,
int *resched, int *save )
mpegts_service_t *svc, channel_t *ch,
const uint8_t *ptr, int len,
int local, int *resched, int *save )
{
int save2 = 0;
int ret, dllen;
@ -409,13 +410,12 @@ static int _eit_process_event
epg_episode_t *ee;
epg_serieslink_t *es;
eit_event_t ev;
channel_t *ch = LIST_FIRST(&svc->s_channels)->csm_chn;
if ( len < 12 ) return -1;
/* Core fields */
eid = ptr[0] << 8 | ptr[1];
start = dvb_convert_date(&ptr[2]);
start = dvb_convert_date(&ptr[2], local);
stop = start + bcdtoint(ptr[7] & 0xff) * 3600 +
bcdtoint(ptr[8] & 0xff) * 60 +
bcdtoint(ptr[9] & 0xff);
@ -446,13 +446,14 @@ static int _eit_process_event
int r;
dtag = ptr[0];
dlen = ptr[1];
tvhtrace(mod->id, " dtag %02X dlen %d", dtag, dlen);
tvhlog_hexdump(mod->id, ptr+2, dlen);
dllen -= 2;
ptr += 2;
if (dllen < dlen) break;
tvhtrace(mod->id, " dtag %02X dlen %d", dtag, dlen);
tvhlog_hexdump(mod->id, ptr, dlen);
switch (dtag) {
case DVB_DESC_SHORT_EVENT:
r = _eit_desc_short_event(mod, ptr, dlen, &ev);
@ -531,6 +532,8 @@ static int _eit_process_event
*save |= epg_episode_set_genre(ee, ev.genre, mod);
if ( ev.parental )
*save |= epg_episode_set_age_rating(ee, ev.parental, mod);
if ( ev.summary )
*save |= epg_episode_set_subtitle2(ee, ev.summary, mod);
#if TODO_ADD_EXTRA
if ( ev.extra )
*save |= epg_episode_set_extra(ee, extra, mod);
@ -549,6 +552,23 @@ static int _eit_process_event
return ret;
}
static int _eit_process_event
( epggrab_module_t *mod, int tableid,
mpegts_service_t *svc, const uint8_t *ptr, int len,
int local, int *resched, int *save )
{
channel_service_mapping_t *csm;
int ret = 0;
if ( len < 12 ) return -1;
LIST_FOREACH(csm, &svc->s_channels, csm_svc_link)
ret = _eit_process_event_one(mod, tableid, svc, csm->csm_chn,
ptr, len, local, resched, save);
return ret;
}
static int
_eit_callback
(mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid)
@ -563,7 +583,7 @@ _eit_callback
epggrab_ota_map_t *map = mt->mt_opaque;
epggrab_module_t *mod = (epggrab_module_t *)map->om_module;
epggrab_ota_mux_t *ota = NULL;
mpegts_table_state_t *st;
mpegts_psi_table_state_t *st;
/* Validate */
if(tableid < 0x4e || tableid > 0x6f || len < 11)
@ -582,7 +602,8 @@ _eit_callback
ota = epggrab_ota_register((epggrab_module_ota_t*)mod, NULL, mm);
/* Begin */
r = dvb_table_begin(mt, ptr, len, tableid, extraid, 11, &st, &sect, &last, &ver);
r = dvb_table_begin((mpegts_psi_table_t *)mt, ptr, len,
tableid, extraid, 11, &st, &sect, &last, &ver);
if (r != 1) return r;
if (st) {
uint32_t mask;
@ -643,6 +664,7 @@ _eit_callback
while (len) {
int r;
if ((r = _eit_process_event(mod, tableid, svc, ptr, len,
mm->mm_network->mn_localtime,
&resched, &save)) < 0)
break;
len -= r;
@ -654,7 +676,7 @@ _eit_callback
if (save) epg_updated();
done:
r = dvb_table_end(mt, st, sect);
r = dvb_table_end((mpegts_psi_table_t *)mt, st, sect);
if (ota && !r)
epggrab_ota_complete((epggrab_module_ota_t*)mod, ota);
@ -683,7 +705,7 @@ static int _eit_start
/* Freesat (3002/3003) */
if (!strcmp("uk_freesat", m->id)) {
mpegts_table_add(dm, 0, 0, dvb_bat_callback, NULL, "bat", MT_CRC, 3002);
mpegts_table_add(dm, 0, 0, dvb_bat_callback, NULL, "bat", MT_CRC, 3002, MPS_WEIGHT_EIT);
pid = 3003;
/* Viasat Baltic (0x39) */
@ -692,10 +714,10 @@ static int _eit_start
/* Standard (0x12) */
} else {
pid = 0x12;
pid = DVB_EIT_PID;
opts = MT_RECORD;
}
mpegts_table_add(dm, 0, 0, _eit_callback, map, m->id, MT_CRC | opts, pid);
mpegts_table_add(dm, 0, 0, _eit_callback, map, m->id, MT_CRC | opts, pid, MPS_WEIGHT_EIT);
// TODO: might want to limit recording to EITpf only
tvhlog(LOG_DEBUG, m->id, "installed table handlers");
return 0;

View file

@ -76,6 +76,8 @@ typedef struct opentv_module_t
int onid;
int tsid;
int sid;
int bouquetid;
int bouquet_auto;
int *channel;
int *title;
int *summary;
@ -212,7 +214,9 @@ static int _opentv_parse_event_record
time_t mjd )
{
uint8_t rtag = buf[0];
uint8_t rlen = buf[1];
int rlen = buf[1];
if (rlen+2 > len)
return -1;
if (rlen+2 <= len) {
switch (rtag) {
case 0xb5: // title
@ -224,8 +228,13 @@ static int _opentv_parse_event_record
ev->cat = buf[6];
if (prov->genre)
ev->cat = prov->genre->map[ev->cat];
if (!ev->title)
if (!ev->title) {
ev->title = _opentv_parse_string(prov, buf+9, rlen-7);
if (!strcmp(prov->dict->id, "skynz")) {
if ((strlen(ev->title) >= 6) && (ev->title[0] == '[') && (ev->title[1] == '[') && (ev->title[4] == ']') && (ev->title[5] == ']'))
memmove(ev->title,ev->title+6,strlen(ev->title)-5);
}
}
}
break;
case 0xb9: // summary
@ -254,13 +263,21 @@ static int _opentv_parse_event
opentv_event_t *ev )
{
int slen = (((int)buf[2] & 0xf) << 8) | buf[3];
int i = 4;
int i = 4, r;
if (slen+4 > len) {
tvhtrace("opentv", "event len (%d) > table len (%d)", slen+4, len);
return -1;
}
ev->eid = ((uint16_t)buf[0] << 8) | buf[1];
/* Process records */
while (i < slen+4) {
i += _opentv_parse_event_record(prov, ev, buf+i, len-i, mjd);
r = _opentv_parse_event_record(prov, ev, buf+i, len-i, mjd);
if (r < 0)
return -1;
i += r;
}
return slen+4;
}
@ -290,35 +307,26 @@ static void *_opentv_apply_pattern_list(char *buf, size_t size_buf, const char *
/* Parse an event section */
static int
opentv_parse_event_section
opentv_parse_event_section_one
( opentv_status_t *sta, int cid, int mjd,
channel_t *ch, const char *lang,
const uint8_t *buf, int len )
{
int i, save = 0;
int i, r, save = 0;
opentv_module_t *mod = sta->os_mod;
epggrab_module_t *src = (epggrab_module_t*)mod;
epggrab_channel_t *ec;
epg_broadcast_t *ebc;
epg_episode_t *ee;
epg_serieslink_t *es;
opentv_event_t ev;
const char *lang = NULL;
epggrab_channel_link_t *ecl;
/* Get language (bit of a hack) */
if (!strcmp(mod->dict->id, "skyit")) lang = "it";
else if (!strcmp(mod->dict->id, "skyeng")) lang = "eng";
/* Channel */
if (!(ec = _opentv_find_epggrab_channel(mod, cid, 0, NULL))) return 0;
if (!(ecl = LIST_FIRST(&ec->channels))) return 0;
/* Loop around event entries */
i = 7;
while (i < len) {
memset(&ev, 0, sizeof(opentv_event_t));
i += _opentv_parse_event(mod, sta, buf+i, len-i, cid, mjd,
&ev);
r = _opentv_parse_event(mod, sta, buf+i, len-i, cid, mjd, &ev);
if (r < 0) break;
i += r;
/*
* Broadcast
@ -326,13 +334,13 @@ opentv_parse_event_section
/* Find broadcast */
if (ev.start && ev.stop) {
ebc = epg_broadcast_find_by_time(ecl->ecl_channel, ev.start, ev.stop,
ebc = epg_broadcast_find_by_time(ch, ev.start, ev.stop,
ev.eid, 1, &save);
tvhdebug("opentv", "find by time start %"PRItime_t " stop "
"%"PRItime_t " eid %d = %p",
ev.start, ev.stop, ev.eid, ebc);
} else {
ebc = epg_broadcast_find_by_eid(ecl->ecl_channel, ev.eid);
ebc = epg_broadcast_find_by_eid(ch, ev.eid);
tvhdebug("opentv", "find by eid %d = %p", ev.eid, ebc);
}
if (!ebc)
@ -355,7 +363,7 @@ opentv_parse_event_section
if (ev.serieslink) {
char suri[257];
snprintf(suri, 256, "opentv://channel-%s/series-%d",
channel_get_uuid(ecl->ecl_channel), ev.serieslink);
channel_get_uuid(ch), ev.serieslink);
if ((es = epg_serieslink_find_by_uri(suri, 1, &save)))
save |= epg_broadcast_set_serieslink(ebc, es, src);
}
@ -424,6 +432,32 @@ done:
if (ev.desc) free(ev.desc);
}
return save;
}
static int
opentv_parse_event_section
( opentv_status_t *sta, int cid, int mjd,
const uint8_t *buf, int len )
{
opentv_module_t *mod = sta->os_mod;
epggrab_channel_t *ec;
epggrab_channel_link_t *ecl;
const char *lang = NULL;
int save = 0;
/* Get language (bit of a hack) */
if (!strcmp(mod->dict->id, "skyit")) lang = "it";
else if (!strcmp(mod->dict->id, "skyeng")) lang = "eng";
else if (!strcmp(mod->dict->id, "skynz")) lang = "eng";
/* Channel */
if (!(ec = _opentv_find_epggrab_channel(mod, cid, 0, NULL))) return 0;
/* Iterate all channels */
LIST_FOREACH(ecl, &ec->channels, ecl_epg_link)
save |= opentv_parse_event_section_one(sta, cid, mjd, ecl->ecl_channel, lang, buf, len);
/* Update EPG */
if (save) epg_updated();
return 0;
@ -435,7 +469,7 @@ done:
static int
opentv_desc_channels
( mpegts_table_t *mt, mpegts_mux_t *mm,
( mpegts_table_t *mt, mpegts_mux_t *mm, uint16_t nbid,
const uint8_t dtag, const uint8_t *buf, int len )
{
opentv_status_t *sta = mt->mt_opaque;
@ -444,23 +478,45 @@ opentv_desc_channels
epggrab_channel_link_t *ecl;
mpegts_service_t *svc;
channel_t *ch;
int sid, cid, cnum;
int sid, cid, cnum, unk;
#if ENABLE_TRACE
int type;
#endif
int save = 0;
int i = 2;
while (i < len) {
sid = ((int)buf[i] << 8) | buf[i+1];
#if ENABLE_TRACE
type = buf[2];
#endif
cid = ((int)buf[i+3] << 8) | buf[i+4];
cnum = ((int)buf[i+5] << 8) | buf[i+6];
tvhtrace(mt->mt_name, " sid %04X cid %04X cnum %d", sid, cid, cnum);
unk = ((int)buf[i+7] << 8) | buf[i+8];
tvhtrace(mt->mt_name, " sid %04X type %02X cid %04X cnum %d unk %04X", sid, type, cid, cnum, unk);
cnum = cnum < 65535 ? cnum : 0;
/* Find the service */
svc = mpegts_service_find(mm, sid, 0, 0, NULL);
tvhtrace(mt->mt_name, " svc %p [%s]", svc, svc ? svc->s_nicename : NULL);
if (svc && svc->s_dvb_opentv_chnum != cnum) {
if (svc && svc->s_dvb_opentv_chnum != cnum &&
(!svc->s_dvb_opentv_id || svc->s_dvb_opentv_id == unk)) {
if (mod->bouquetid != nbid) {
if (mod->bouquet_auto) {
if (nbid < mod->bouquetid) {
tvhwarn(mt->mt_name, "bouquet id set to %d, report this!", nbid);
mod->bouquetid = nbid;
} else
goto skip_chnum;
} else
goto skip_chnum;
}
tvhtrace(mt->mt_name, " cnum changed (%i != %i)", cnum, (int)svc->s_dvb_opentv_chnum);
svc->s_dvb_opentv_chnum = cnum;
svc->s_dvb_opentv_id = unk;
service_request_save((service_t *)svc, 0);
}
skip_chnum:
if (svc && LIST_FIRST(&svc->s_channels)) {
ec =_opentv_find_epggrab_channel(mod, cid, 1, &save);
ecl = LIST_FIRST(&ec->channels);
@ -474,7 +530,7 @@ opentv_desc_channels
if (!ecl)
epggrab_channel_link(ec, ch);
save |= epggrab_channel_set_number(ec, cnum);
save |= epggrab_channel_set_number(ec, cnum, 0);
}
i += 9;
}
@ -488,7 +544,7 @@ opentv_table_callback
{
int r = 1, cid, mjd;
int sect, last, ver;
mpegts_table_state_t *st;
mpegts_psi_table_state_t *st;
opentv_status_t *sta = mt->mt_opaque;
opentv_module_t *mod = sta->os_mod;
epggrab_ota_mux_t *ota = sta->os_ota;
@ -502,7 +558,8 @@ opentv_table_callback
mjd = (mjd - 40587) * 86400;
/* Begin */
r = dvb_table_begin(mt, buf, len, tableid, (uint64_t)cid << 32 | mjd, 7,
r = dvb_table_begin((mpegts_psi_table_t *)mt, buf, len,
tableid, (uint64_t)cid << 32 | mjd, 7,
&st, &sect, &last, &ver);
if (r != 1) goto done;
@ -510,7 +567,7 @@ opentv_table_callback
r = opentv_parse_event_section(sta, cid, mjd, buf, len);
/* End */
r = dvb_table_end(mt, st, sect);
r = dvb_table_end((mpegts_psi_table_t *)mt, st, sect);
/* Complete */
done:
@ -533,7 +590,8 @@ done:
mt2 = mpegts_table_add(mt->mt_mux,
OPENTV_SUMMARY_BASE, OPENTV_TABLE_MASK,
opentv_table_callback, sta,
mod->id, MT_CRC, *t++);
mod->id, MT_CRC, *t++,
MPS_WEIGHT_EIT);
if (mt2) {
sta->os_refcount++;
mt2->mt_destroy = opentv_status_destroy;
@ -581,7 +639,8 @@ opentv_bat_callback
mt2 = mpegts_table_add(mt->mt_mux,
OPENTV_TITLE_BASE, OPENTV_TABLE_MASK,
opentv_table_callback, mt->mt_opaque,
mod->id, MT_CRC, *t++);
mod->id, MT_CRC, *t++,
MPS_WEIGHT_EIT);
if (mt2) {
if (!mt2->mt_destroy) {
sta->os_refcount++;
@ -631,7 +690,8 @@ static int _opentv_start
}
mt = mpegts_table_add(mm, DVB_BAT_BASE, DVB_BAT_MASK,
opentv_bat_callback, sta,
m->id, MT_CRC, *t++);
m->id, MT_CRC, *t++,
MPS_WEIGHT_EIT);
if (mt) {
mt->mt_mux_cb = bat_desc;
if (!mt->mt_destroy) {
@ -677,12 +737,15 @@ static void _opentv_compile_pattern_list ( opentv_pattern_list_t *list, htsmsg_t
{
opentv_pattern_t *pattern;
htsmsg_field_t *f;
const char *s;
TAILQ_INIT(list);
if (!l) return;
HTSMSG_FOREACH(f, l) {
s = htsmsg_field_get_str(f);
if (s == NULL) continue;
pattern = calloc(1, sizeof(opentv_pattern_t));
pattern->text = strdup(htsmsg_field_get_str(f));
pattern->text = strdup(s);
if (regcomp(&pattern->compiled, pattern->text, REG_EXTENDED)) {
tvhlog(LOG_WARNING, "opentv", "error compiling pattern \"%s\"", pattern->text);
free(pattern->text);
@ -815,7 +878,7 @@ static int _opentv_prov_load_one ( const char *id, htsmsg_t *m )
{
char ibuf[100], nbuf[1000];
htsmsg_t *cl, *tl, *sl;
uint32_t tsid, sid, onid;
uint32_t tsid, sid, onid, bouquetid;
const char *str, *name;
opentv_dict_t *dict;
opentv_genre_t *genre;
@ -837,6 +900,7 @@ static int _opentv_prov_load_one ( const char *id, htsmsg_t *m )
if (htsmsg_get_u32(m, "nid", &onid)) return -1;
if (htsmsg_get_u32(m, "tsid", &tsid)) return -1;
if (htsmsg_get_u32(m, "sid", &sid)) return -1;
if (htsmsg_get_u32(m, "bouquetid", &bouquetid)) return -1;
/* Genre map (optional) */
str = htsmsg_get_str(m, "genre");
@ -861,6 +925,8 @@ static int _opentv_prov_load_one ( const char *id, htsmsg_t *m )
mod->onid = onid;
mod->tsid = tsid;
mod->sid = sid;
mod->bouquetid = bouquetid;
mod->bouquet_auto = bouquetid == 0;
mod->channel = _pid_list_to_array(cl);
mod->title = _pid_list_to_array(tl);
mod->summary = _pid_list_to_array(sl);

View file

@ -94,7 +94,7 @@ static int _pyepg_parse_channel
if ((str = htsmsg_xml_get_cdata_str(tags, "image")))
save |= epggrab_channel_set_icon(ch, str);
if ((!htsmsg_xml_get_cdata_u32(tags, "number", &u32)))
save |= epggrab_channel_set_number(ch, u32);
save |= epggrab_channel_set_number(ch, u32, 0);
/* Update */
if (save) {

26
src/epggrab/module/xmltv.c Executable file → Normal file
View file

@ -32,6 +32,7 @@
#include "tvheadend.h"
#include "channels.h"
#include "spawn.h"
#include "file.h"
#include "htsstr.h"
#include "lang_str.h"
@ -675,14 +676,17 @@ static int _xmltv_parse
static void _xmltv_load_grabbers ( void )
{
int outlen;
int outlen = -1, rd = -1;
size_t i, p, n;
char *outbuf;
char name[1000];
char *tmp, *tmp2 = NULL, *path;
/* Load data */
outlen = spawn_and_store_stdout(XMLTV_FIND, NULL, &outbuf);
if (spawn_and_give_stdout(XMLTV_FIND, NULL, NULL, &rd, NULL, 1) >= 0)
outlen = file_readall(rd, &outbuf);
if (rd >= 0)
close(rd);
/* Process */
if ( outlen > 0 ) {
@ -692,8 +696,13 @@ static void _xmltv_load_grabbers ( void )
outbuf[i] = '\0';
sprintf(name, "XMLTV: %s", &outbuf[n]);
epggrab_module_int_create(NULL, &outbuf[p], name, 3, &outbuf[p],
NULL, _xmltv_parse, NULL, NULL);
NULL, _xmltv_parse, NULL, NULL);
p = n = i + 1;
} else if ( outbuf[i] == '\\') {
memmove(outbuf, outbuf + 1, strlen(outbuf));
if (outbuf[i])
i++;
continue;
} else if ( outbuf[i] == '|' ) {
outbuf[i] = '\0';
n = i + 1;
@ -706,10 +715,9 @@ static void _xmltv_load_grabbers ( void )
} else if ((tmp = getenv("PATH"))) {
tvhdebug("epggrab", "using internal grab search");
char bin[256];
char desc[] = "--description";
char *argv[] = {
NULL,
desc,
(char *)"--description",
NULL
};
path = strdup(tmp);
@ -726,12 +734,18 @@ static void _xmltv_load_grabbers ( void )
if (stat(bin, &st)) continue;
if (!(st.st_mode & S_IEXEC)) continue;
if (!S_ISREG(st.st_mode)) continue;
if ((outlen = spawn_and_store_stdout(bin, argv, &outbuf)) > 0) {
rd = -1;
if (spawn_and_give_stdout(bin, argv, NULL, &rd, NULL, 1) >= 0 &&
(outlen = file_readall(rd, &outbuf)) > 0) {
close(rd);
if (outbuf[outlen-1] == '\n') outbuf[outlen-1] = '\0';
snprintf(name, sizeof(name), "XMLTV: %s", outbuf);
epggrab_module_int_create(NULL, bin, name, 3, bin,
NULL, _xmltv_parse, NULL, NULL);
free(outbuf);
} else {
if (rd >= 0)
close(rd);
}
}
closedir(dir);

View file

@ -195,11 +195,16 @@ epggrab_ota_done ( epggrab_ota_mux_t *om, int reason )
if (!om->om_done && om->om_requeue) {
TAILQ_INSERT_HEAD(&epggrab_ota_pending, om, om_q_link);
om->om_q_type = EPGGRAB_OTA_MUX_PENDING;
} else {
om->om_requeue = 0;
}
} else if (reason == EPGGRAB_OTA_DONE_TIMEOUT) {
om->om_requeue = 0;
LIST_FOREACH(map, &om->om_modules, om_link)
if (!map->om_complete)
tvhlog(LOG_WARNING, "epggrab", "%s - data completion timeout for %s", map->om_module->name, name);
} else {
om->om_requeue = 0;
}
/* Remove subscriber */
@ -290,14 +295,18 @@ epggrab_mux_start ( mpegts_mux_t *mm, void *p )
}
static void
epggrab_mux_stop ( mpegts_mux_t *mm, void *p )
epggrab_mux_stop ( mpegts_mux_t *mm, void *p, int reason )
{
epggrab_ota_mux_t *ota;
const char *uuid = idnode_uuid_as_str(&mm->mm_id);
int done = EPGGRAB_OTA_DONE_STOLEN;
if (reason == SM_CODE_NO_INPUT)
done = EPGGRAB_OTA_DONE_NO_DATA;
tvhtrace("epggrab", "mux %p (%s) stop", mm, uuid);
TAILQ_FOREACH(ota, &epggrab_ota_active, om_q_link)
if (!strcmp(ota->om_mux_uuid, uuid)) {
epggrab_ota_done(ota, EPGGRAB_OTA_DONE_STOLEN);
epggrab_ota_done(ota, done);
break;
}
}
@ -429,6 +438,8 @@ epggrab_ota_data_timeout_cb ( void *p )
epggrab_ota_done(om, EPGGRAB_OTA_DONE_NO_DATA);
/* Not completed, but no data - wait for a manual mux tuning */
epggrab_ota_complete_mark(om, 1);
} else {
tvhtrace("epggrab", "data timeout check succeed");
}
}
@ -496,10 +507,13 @@ next_one:
net->failed = 0;
}
epg_flag = mm->mm_is_epg(mm);
if (epg_flag > MM_EPG_LAST)
epg_flag = MM_EPG_ENABLE;
modname = epg_flag >= 0 ? modnames[epg_flag] : NULL;
epg_flag = MM_EPG_DISABLE;
if (mm->mm_is_enabled(mm)) {
epg_flag = mm->mm_is_epg(mm);
if (epg_flag > MM_EPG_LAST)
epg_flag = MM_EPG_ENABLE;
modname = epg_flag >= 0 ? modnames[epg_flag] : NULL;
}
if (epg_flag < 0 || epg_flag == MM_EPG_DISABLE) {
#if ENABLE_TRACE
@ -532,8 +546,11 @@ next_one:
/* Subscribe to the mux */
om->om_requeue = 1;
if ((r = mpegts_mux_subscribe(mm, "epggrab", SUBSCRIPTION_PRIO_EPG,
SUBSCRIPTION_EPG))) {
if ((r = mpegts_mux_subscribe(mm, NULL, "epggrab",
SUBSCRIPTION_PRIO_EPG,
SUBSCRIPTION_EPG |
SUBSCRIPTION_ONESHOT |
SUBSCRIPTION_TABLES))) {
TAILQ_INSERT_TAIL(&epggrab_ota_pending, om, om_q_link);
om->om_q_type = EPGGRAB_OTA_MUX_PENDING;
if (r == SM_CODE_NO_FREE_ADAPTER)
@ -541,6 +558,7 @@ next_one:
if (first == NULL)
first = om;
} else {
tvhtrace("epggrab", "mux %p started", mm);
kick = 0;
/* note: it is possible that the mux_start listener is not called */
/* for reshared mux subscriptions, so call it (maybe second time) here.. */
@ -737,7 +755,7 @@ epggrab_ota_load_one
free(ota);
return;
}
htsmsg_get_u32(c, "complete", (uint32_t *)&ota->om_complete);
ota->om_complete = htsmsg_get_u32_or_default(c, "complete", 0) != 0;
if (!(l = htsmsg_get_list(c, "modules"))) return;
HTSMSG_FOREACH(f, l) {
@ -800,6 +818,16 @@ epggrab_ota_init ( void )
}
}
void
epggrab_ota_trigger ( int secs )
{
/* notify another system layers, that we will do EPG OTA */
secs = MIN(1, MAX(secs, 7*24*3600));
dbus_emit_signal_s64("/epggrab/ota", "next", time(NULL) + secs);
epggrab_ota_pending_flag = 1;
epggrab_ota_kick(secs);
}
void
epggrab_ota_post ( void )
{
@ -807,10 +835,7 @@ epggrab_ota_post ( void )
/* Init timer (call after full init - wait for network tuners) */
if (epggrab_ota_initial) {
/* notify another system layers, that we will do EPG OTA */
dbus_emit_signal_s64("/epggrab/ota", "next", time(NULL) + 15);
epggrab_ota_pending_flag = 1;
epggrab_ota_kick(15);
epggrab_ota_trigger(15);
t = time(NULL);
}

View file

@ -85,7 +85,7 @@ esfilter_txt2class(const char *s)
static struct strtab esfilteractiontab[] = {
{ "NONE", ESFA_NONE },
{ "USE", ESFA_USE },
{ "ONCE", ESFA_ONCE },
{ "ONE_TIME", ESFA_ONE_TIME },
{ "EXCLUSIVE", ESFA_EXCLUSIVE },
{ "EMPTY", ESFA_EMPTY },
{ "IGNORE", ESFA_IGNORE }

View file

@ -73,7 +73,7 @@ extern struct esfilter_entry_queue esfilters[];
typedef enum {
ESFA_NONE = 0,
ESFA_USE, /* use this stream */
ESFA_ONCE, /* use this stream once per language */
ESFA_ONE_TIME, /* use this stream once per language */
ESFA_EXCLUSIVE, /* use this stream exclusively */
ESFA_EMPTY, /* use this stream when no streams were added */
ESFA_IGNORE,

View file

@ -16,65 +16,46 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define MAX_RDBUF_SIZE 8192
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "queue.h"
#include "tvheadend.h"
#include "file.h"
typedef struct file_read_buf {
TAILQ_ENTRY(file_read_buf) link;
int size;
char buf[MAX_RDBUF_SIZE];
} file_read_buf_t;
TAILQ_HEAD(file_read_buf_queue, file_read_buf);
#define MAX_RDBUF_SIZE 8192
size_t file_readall ( int fd, char **outp )
{
int r, totalsize = 0;
struct file_read_buf_queue bufs;
file_read_buf_t *b = NULL;
char *outbuf;
size_t outsize = 0, totalsize = 0;
char *outbuf = NULL, *n;
int r;
TAILQ_INIT(&bufs);
while(1) {
if(b == NULL) {
b = malloc(sizeof(file_read_buf_t));
b->size = 0;
TAILQ_INSERT_TAIL(&bufs, b, link);
while (1) {
if(totalsize == outsize) {
n = realloc(outbuf, outsize += MAX_RDBUF_SIZE);
if (!n) {
free(outbuf);
return 0;
}
outbuf = n;
}
r = read(fd, b->buf + b->size, MAX_RDBUF_SIZE - b->size);
if(r < 1)
r = read(fd, outbuf + totalsize, outsize - totalsize);
if(r < 1) {
if (ERRNO_AGAIN(errno))
continue;
break;
b->size += r;
}
totalsize += r;
if(b->size == MAX_RDBUF_SIZE)
b = NULL;
}
close(fd);
if(totalsize == 0) {
free(b);
*outp = NULL;
return 0;
}
outbuf = malloc(totalsize + 1);
r = 0;
while((b = TAILQ_FIRST(&bufs)) != NULL) {
memcpy(outbuf + r, b->buf, b->size);
r+= b->size;
TAILQ_REMOVE(&bufs, b, link);
free(b);
}
assert(r == totalsize);
*outp = outbuf;
if (totalsize == outsize) {
n = realloc(outbuf, outsize += 1);
if (!n) {
free(outbuf);
return 0;
}
outbuf = n;
}
outbuf[totalsize] = 0;
return totalsize;
}

View file

@ -24,7 +24,11 @@
#include <string.h>
#include <stdio.h>
#if ENABLE_ZLIB
#define ZLIB_CONST 1
#include <zlib.h>
#ifndef z_const
#define z_const
#endif
#endif
#include <sys/types.h>
#include <sys/stat.h>
@ -78,18 +82,16 @@ static uint8_t *_fb_inflate ( const uint8_t *data, size_t size, size_t orig )
{
int err;
z_stream zstr;
uint8_t *bufin, *bufout;
uint8_t *bufout;
/* Setup buffers */
bufin = malloc(size);
bufout = malloc(orig);
memcpy(bufin, data, size);
/* Setup zlib */
memset(&zstr, 0, sizeof(zstr));
inflateInit2(&zstr, 31);
zstr.avail_in = size;
zstr.next_in = bufin;
zstr.next_in = (z_const uint8_t *)data;
zstr.avail_out = orig;
zstr.next_out = bufout;
@ -99,7 +101,6 @@ static uint8_t *_fb_inflate ( const uint8_t *data, size_t size, size_t orig )
free(bufout);
bufout = NULL;
}
free(bufin);
inflateEnd(&zstr);
return bufout;
@ -111,18 +112,16 @@ static uint8_t *_fb_deflate ( const uint8_t *data, size_t orig, size_t *size )
{
int err;
z_stream zstr;
uint8_t *bufin, *bufout;
uint8_t *bufout;
/* Setup buffers */
bufin = malloc(orig);
bufout = malloc(orig);
memcpy(bufin, data, orig);
/* Setup zlib */
memset(&zstr, 0, sizeof(zstr));
err = deflateInit2(&zstr, 9, Z_DEFLATED, 31, 9, Z_DEFAULT_STRATEGY);
zstr.avail_in = orig;
zstr.next_in = bufin;
zstr.next_in = (z_const uint8_t *)data;
zstr.avail_out = orig;
zstr.next_out = bufout;
@ -147,7 +146,6 @@ static uint8_t *_fb_deflate ( const uint8_t *data, size_t orig, size_t *size )
}
break;
}
free(bufin);
deflateEnd(&zstr);
return bufout;
@ -413,7 +411,7 @@ fb_file *fb_open2
} else {
char path[512];
snprintf(path, sizeof(path), "%s/%s", dir->d.root, name);
FILE *fp = fopen(path, "rb");
FILE *fp = tvh_fopen(path, "rb");
if (fp) {
struct stat st;
stat(path, &st);

View file

@ -105,7 +105,7 @@ void
fsmonitor_init ( void )
{
/* Intialise inotify */
fsmonitor_fd = inotify_init();
fsmonitor_fd = inotify_init1(IN_CLOEXEC);
tvhthread_create0(&fsmonitor_tid, NULL, fsmonitor_thread, NULL, "fsmonitor");
}

View file

@ -829,10 +829,14 @@ htsmsg_xml_deserialize(char *src, char *errbuf, size_t errbufsize)
char *src0 = src;
int i;
xp.xp_errmsg[0] = 0;
memset(&xp, 0, sizeof(xp));
xp.xp_encoding = XML_ENCODING_UTF8;
LIST_INIT(&xp.xp_namespaces);
/* check for UTF-8 BOM */
if(src[0] == 0xef && src[1] == 0xbb && src[2] == 0xbf)
memmove(src, src + 3, strlen(src) - 2);
if((src = htsmsg_parse_prolog(&xp, src)) == NULL)
goto err;

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/*
* tvheadend, HTSP interface
* Copyright (C) 2007 Andreas Öman
* Copyright (C) 2007 Andreas <EFBFBD>man
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -44,6 +44,10 @@ void htsp_autorec_entry_add(dvr_autorec_entry_t *dae);
void htsp_autorec_entry_update(dvr_autorec_entry_t *dae);
void htsp_autorec_entry_delete(dvr_autorec_entry_t *dae);
void htsp_timerec_entry_add(dvr_timerec_entry_t *dte);
void htsp_timerec_entry_update(dvr_timerec_entry_t *dte);
void htsp_timerec_entry_delete(dvr_timerec_entry_t *dte);
void htsp_event_add(epg_broadcast_t *ebc);
void htsp_event_update(epg_broadcast_t *ebc);
void htsp_event_delete(epg_broadcast_t *ebc);

View file

@ -37,11 +37,12 @@
#include "notify.h"
#include "channels.h"
static void *http_server;
void *http_server;
static LIST_HEAD(, http_path) http_paths;
static http_path_list_t http_paths;
static struct strtab HTTP_cmdtab[] = {
{ "NONE", HTTP_CMD_NONE },
{ "GET", HTTP_CMD_GET },
{ "HEAD", HTTP_CMD_HEAD },
{ "POST", HTTP_CMD_POST },
@ -61,8 +62,6 @@ static struct strtab HTTP_versiontab[] = {
{ "RTSP/1.0", RTSP_VERSION_1_0 },
};
static void http_parse_get_args(http_connection_t *hc, char *args);
/**
*
*/
@ -113,7 +112,7 @@ http_resolve(http_connection_t *hc, char **remainp, char **argsp)
while (1) {
LIST_FOREACH(hp, &http_paths, hp_link) {
LIST_FOREACH(hp, hc->hc_paths, hp_link) {
if(!strncmp(path, hp->hp_path, hp->hp_len)) {
if(path[hp->hp_len] == 0 ||
path[hp->hp_len] == '/' ||
@ -185,12 +184,16 @@ http_rc2str(int code)
switch(code) {
case HTTP_STATUS_OK: return "OK";
case HTTP_STATUS_PARTIAL_CONTENT: return "Partial Content";
case HTTP_STATUS_NOT_FOUND: return "Not found";
case HTTP_STATUS_UNAUTHORIZED: return "Unauthorized";
case HTTP_STATUS_BAD_REQUEST: return "Bad request";
case HTTP_STATUS_FOUND: return "Found";
case HTTP_STATUS_BAD_REQUEST: return "Bad Request";
case HTTP_STATUS_UNAUTHORIZED: return "Unauthorized";
case HTTP_STATUS_NOT_FOUND: return "Not Found";
case HTTP_STATUS_UNSUPPORTED: return "Unsupported Media Type";
case HTTP_STATUS_BANDWIDTH: return "Not Enough Bandwidth";
case HTTP_STATUS_BAD_SESSION: return "Session Not Found";
case HTTP_STATUS_HTTP_VERSION: return "HTTP/RTSP Version Not Supported";
default:
return "Unknown returncode";
return "Unknown Code";
break;
}
}
@ -212,22 +215,26 @@ http_send_header(http_connection_t *hc, int rc, const char *content,
int64_t contentlen,
const char *encoding, const char *location,
int maxage, const char *range,
const char *disposition)
const char *disposition,
http_arg_list_t *args)
{
struct tm tm0, *tm;
htsbuf_queue_t hdrs;
http_arg_t *ra;
time_t t;
int sess = 0;
htsbuf_queue_init(&hdrs, 0);
htsbuf_qprintf(&hdrs, "%s %d %s\r\n",
val2str(hc->hc_version, HTTP_versiontab),
rc, http_rc2str(rc));
http_ver2str(hc->hc_version), rc, http_rc2str(rc));
htsbuf_qprintf(&hdrs, "Server: HTS/tvheadend\r\n");
if (hc->hc_version != RTSP_VERSION_1_0)
htsbuf_qprintf(&hdrs, "Server: HTS/tvheadend\r\n");
if(maxage == 0) {
htsbuf_qprintf(&hdrs, "Cache-Control: no-cache\r\n");
if (hc->hc_version != RTSP_VERSION_1_0)
htsbuf_qprintf(&hdrs, "Cache-Control: no-cache\r\n");
} else {
time(&t);
@ -258,8 +265,9 @@ http_send_header(http_connection_t *hc, int rc, const char *content,
htsbuf_qprintf(&hdrs, "Set-Cookie: logout=0; Path=\"/logout'\"; expires=Thu, 01 Jan 1970 00:00:00 GMT\r\n");
}
htsbuf_qprintf(&hdrs, "Connection: %s\r\n",
hc->hc_keep_alive ? "Keep-Alive" : "Close");
if (hc->hc_version != RTSP_VERSION_1_0)
htsbuf_qprintf(&hdrs, "Connection: %s\r\n",
hc->hc_keep_alive ? "Keep-Alive" : "Close");
if(encoding != NULL)
htsbuf_qprintf(&hdrs, "Content-Encoding: %s\r\n", encoding);
@ -281,6 +289,22 @@ http_send_header(http_connection_t *hc, int rc, const char *content,
if(disposition != NULL)
htsbuf_qprintf(&hdrs, "Content-Disposition: %s\r\n", disposition);
if(hc->hc_cseq) {
htsbuf_qprintf(&hdrs, "CSeq: %"PRIu64"\r\n", hc->hc_cseq);
if (++hc->hc_cseq == 0)
hc->hc_cseq = 1;
}
if (args) {
TAILQ_FOREACH(ra, args, link) {
if (strcmp(ra->key, "Session") == 0)
sess = 1;
htsbuf_qprintf(&hdrs, "%s: %s\r\n", ra->key, ra->val);
}
}
if(hc->hc_session && !sess)
htsbuf_qprintf(&hdrs, "Session: %s\r\n", hc->hc_session);
htsbuf_qprintf(&hdrs, "\r\n");
tcp_write_queue(hc->hc_fd, &hdrs);
@ -296,7 +320,7 @@ http_send_reply(http_connection_t *hc, int rc, const char *content,
const char *encoding, const char *location, int maxage)
{
http_send_header(hc, rc, content, hc->hc_reply.hq_size,
encoding, location, maxage, 0, NULL);
encoding, location, maxage, 0, NULL, NULL);
if(hc->hc_no_output)
return;
@ -312,32 +336,34 @@ void
http_error(http_connection_t *hc, int error)
{
const char *errtxt = http_rc2str(error);
char addrstr[50];
if (!http_server) return;
tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrstr, 50);
if (error != HTTP_STATUS_FOUND && error != HTTP_STATUS_MOVED)
tvhlog(error < 400 ? LOG_INFO : LOG_ERR, "HTTP", "%s: %s -- %d",
addrstr, hc->hc_url, error);
tvhlog(error < 400 ? LOG_INFO : LOG_ERR, "http", "%s: %s %s %s -- %d",
hc->hc_peer_ipstr, http_ver2str(hc->hc_version),
http_cmd2str(hc->hc_cmd), hc->hc_url, error);
htsbuf_queue_flush(&hc->hc_reply);
if (hc->hc_version != RTSP_VERSION_1_0) {
htsbuf_queue_flush(&hc->hc_reply);
htsbuf_qprintf(&hc->hc_reply,
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
"<HTML><HEAD>\r\n"
"<TITLE>%d %s</TITLE>\r\n"
"</HEAD><BODY>\r\n"
"<H1>%d %s</H1>\r\n",
error, errtxt, error, errtxt);
htsbuf_qprintf(&hc->hc_reply,
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
"<HTML><HEAD>\r\n"
"<TITLE>%d %s</TITLE>\r\n"
"</HEAD><BODY>\r\n"
"<H1>%d %s</H1>\r\n",
error, errtxt, error, errtxt);
if (error == HTTP_STATUS_UNAUTHORIZED)
htsbuf_qprintf(&hc->hc_reply, "<P><A HREF=\"/\">Default Login</A></P>");
if (error == HTTP_STATUS_UNAUTHORIZED)
htsbuf_qprintf(&hc->hc_reply, "<P><A HREF=\"/\">Default Login</A></P>");
htsbuf_qprintf(&hc->hc_reply, "</BODY></HTML>\r\n");
htsbuf_qprintf(&hc->hc_reply, "</BODY></HTML>\r\n");
http_send_reply(hc, error, "text/html", NULL, NULL, 0);
http_send_reply(hc, error, "text/html", NULL, NULL, 0);
} else {
http_send_reply(hc, error, NULL, NULL, NULL, 0);
}
}
@ -419,10 +445,8 @@ http_access_verify_ticket(http_connection_t *hc)
hc->hc_access = access_ticket_verify2(ticket_id, hc->hc_url);
if (hc->hc_access == NULL)
return;
char addrstr[50];
tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrstr, 50);
tvhlog(LOG_INFO, "HTTP", "%s: using ticket %s for %s",
addrstr, ticket_id, hc->hc_url);
tvhlog(LOG_INFO, "http", "%s: using ticket %s for %s",
hc->hc_peer_ipstr, ticket_id, hc->hc_url);
}
/**
@ -467,7 +491,7 @@ http_access_verify_channel(http_connection_t *hc, int mask,
if (access_verify2(hc->hc_access, mask))
return -1;
if (channel_access(ch, hc->hc_access, hc->hc_username))
if (channel_access(ch, hc->hc_access, 0))
res = 0;
return res;
}
@ -498,7 +522,40 @@ http_exec(http_connection_t *hc, http_path_t *hp, char *remain)
return 0;
}
/*
* Dump request
*/
#if ENABLE_TRACE
static void
dump_request(http_connection_t *hc)
{
char buf[2048] = "";
http_arg_t *ra;
int first, ptr = 0;
first = 1;
TAILQ_FOREACH(ra, &hc->hc_req_args, link) {
tvh_strlcatf(buf, sizeof(buf), ptr, first ? "?%s=%s" : "&%s=%s", ra->key, ra->val);
first = 0;
}
first = 1;
TAILQ_FOREACH(ra, &hc->hc_args, link) {
tvh_strlcatf(buf, sizeof(buf), ptr, first ? "{{%s=%s" : ",%s=%s", ra->key, ra->val);
first = 0;
}
if (!first)
tvh_strlcatf(buf, sizeof(buf), ptr, "}}");
tvhtrace("http", "%s %s %s%s", http_ver2str(hc->hc_version),
http_cmd2str(hc->hc_cmd), hc->hc_url, buf);
}
#else
static inline void
dump_request(http_connection_t *hc)
{
}
#endif
/**
* HTTP GET
@ -510,6 +567,8 @@ http_cmd_get(http_connection_t *hc)
char *remain;
char *args;
dump_request(hc);
hp = http_resolve(hc, &remain, &args);
if(hp == NULL) {
http_error(hc, HTTP_STATUS_NOT_FOUND);
@ -560,7 +619,7 @@ http_cmd_post(http_connection_t *hc, htsbuf_queue_t *spill)
if(tcp_read_data(hc->hc_fd, hc->hc_post_data, hc->hc_post_len, spill) < 0)
return -1;
/* Parse content-type */
/* Parse content-type */
v = http_arg_get(&hc->hc_args, "Content-Type");
if(v != NULL) {
char *argv[2];
@ -574,6 +633,8 @@ http_cmd_post(http_connection_t *hc, htsbuf_queue_t *spill)
http_parse_get_args(hc, hc->hc_post_data);
}
dump_request(hc);
hp = http_resolve(hc, &remain, &args);
if(hp == NULL) {
http_error(hc, HTTP_STATUS_NOT_FOUND);
@ -603,39 +664,6 @@ http_process_request(http_connection_t *hc, htsbuf_queue_t *spill)
}
}
/*
*
*/
#if ENABLE_TRACE
static void
dump_request(http_connection_t *hc)
{
char buf[2048] = "";
http_arg_t *ra;
int first;
first = 1;
TAILQ_FOREACH(ra, &hc->hc_req_args, link) {
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), first ? "?%s=%s" : "&%s=%s", ra->key, ra->val);
first = 0;
}
first = 1;
TAILQ_FOREACH(ra, &hc->hc_args, link) {
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), first ? "{{%s=%s" : ",%s=%s", ra->key, ra->val);
first = 0;
}
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "}}");
tvhtrace("http", "%s%s", hc->hc_url, buf);
}
#else
static inline void
dump_request(http_connection_t *hc)
{
}
#endif
/**
* Process a request, extract info from headers, dispatch command and
* clean up
@ -645,18 +673,42 @@ process_request(http_connection_t *hc, htsbuf_queue_t *spill)
{
char *v, *argv[2];
int n, rval = -1;
uint8_t authbuf[150];
char authbuf[150];
dump_request(hc);
hc->hc_url_orig = tvh_strdupa(hc->hc_url);
v = http_arg_get(&hc->hc_args, "x-forwarded-for");
if (v)
tcp_get_sockaddr((struct sockaddr*)hc->hc_peer, v);
tcp_get_ip_str((struct sockaddr*)hc->hc_peer, authbuf, sizeof(authbuf));
hc->hc_peer_ipstr = tvh_strdupa(authbuf);
hc->hc_representative = hc->hc_peer_ipstr;
hc->hc_username = NULL;
hc->hc_password = NULL;
hc->hc_session = NULL;
/* Set keep-alive status */
v = http_arg_get(&hc->hc_args, "connection");
switch(hc->hc_version) {
case RTSP_VERSION_1_0:
hc->hc_keep_alive = 1;
/* Extract CSeq */
if((v = http_arg_get(&hc->hc_args, "CSeq")) != NULL)
hc->hc_cseq = strtoll(v, NULL, 10);
else
hc->hc_cseq = 0;
free(hc->hc_session);
if ((v = http_arg_get(&hc->hc_args, "Session")) != NULL)
hc->hc_session = tvh_strdupa(v);
else
hc->hc_session = NULL;
if(hc->hc_cseq == 0) {
http_error(hc, HTTP_STATUS_BAD_REQUEST);
return -1;
}
break;
case HTTP_VERSION_1_0:
@ -673,36 +725,38 @@ process_request(http_connection_t *hc, htsbuf_queue_t *spill)
/* Extract authorization */
if((v = http_arg_get(&hc->hc_args, "Authorization")) != NULL) {
if((n = http_tokenize(v, argv, 2, -1)) == 2) {
n = base64_decode(authbuf, argv[1], sizeof(authbuf) - 1);
n = base64_decode((uint8_t *)authbuf, argv[1], sizeof(authbuf) - 1);
if (n < 0)
n = 0;
authbuf[n] = 0;
if((n = http_tokenize((char *)authbuf, argv, 2, ':')) == 2) {
hc->hc_username = strdup(argv[0]);
hc->hc_password = strdup(argv[1]);
if((n = http_tokenize(authbuf, argv, 2, ':')) == 2) {
hc->hc_username = tvh_strdupa(argv[0]);
hc->hc_password = tvh_strdupa(argv[1]);
// No way to actually track this
}
}
}
if(hc->hc_username != NULL) {
hc->hc_representative = strdup(hc->hc_username);
} else {
hc->hc_representative = malloc(50);
/* Not threadsafe ? */
tcp_get_ip_str((struct sockaddr*)hc->hc_peer, hc->hc_representative, 50);
}
if (hc->hc_username)
hc->hc_representative = hc->hc_username;
switch(hc->hc_version) {
case RTSP_VERSION_1_0:
dump_request(hc);
if (hc->hc_cseq)
rval = hc->hc_process(hc, spill);
else
http_error(hc, HTTP_STATUS_HTTP_VERSION);
break;
case HTTP_VERSION_1_0:
case HTTP_VERSION_1_1:
rval = http_process_request(hc, spill);
if (!hc->hc_cseq)
rval = hc->hc_process(hc, spill);
else
http_error(hc, HTTP_STATUS_HTTP_VERSION);
break;
}
free(hc->hc_representative);
return rval;
}
@ -737,6 +791,28 @@ http_arg_get(struct http_arg_list *list, const char *name)
return NULL;
}
/**
* Find an argument associated with a connection and remove it
*/
char *
http_arg_get_remove(struct http_arg_list *list, const char *name)
{
static char __thread buf[128];
http_arg_t *ra;
TAILQ_FOREACH(ra, list, link)
if(!strcasecmp(ra->key, name)) {
TAILQ_REMOVE(list, ra, link);
strncpy(buf, ra->val, sizeof(buf)-1);
buf[sizeof(buf)-1] = '\0';
free(ra->key);
free(ra->val);
free(ra);
return buf;
}
buf[0] = '\0';
return buf;
}
/**
* Set an argument associated with a connection
@ -871,11 +947,13 @@ http_deescape(char *s)
/**
* Parse arguments of a HTTP GET url, not perfect, but works for us
*/
static void
void
http_parse_get_args(http_connection_t *hc, char *args)
{
char *k, *v;
if (args && *args == '&')
args++;
while(args) {
k = args;
if((args = strchr(args, '=')) == NULL)
@ -897,12 +975,16 @@ http_parse_get_args(http_connection_t *hc, char *args)
/**
*
*/
static void
http_serve_requests(http_connection_t *hc, htsbuf_queue_t *spill)
void
http_serve_requests(http_connection_t *hc)
{
htsbuf_queue_t spill;
char *argv[3], *c, *cmdline = NULL, *hdrline = NULL;
int n;
int n, r;
http_arg_init(&hc->hc_args);
http_arg_init(&hc->hc_req_args);
htsbuf_queue_init(&spill, 0);
htsbuf_queue_init(&hc->hc_reply, 0);
do {
@ -910,7 +992,7 @@ http_serve_requests(http_connection_t *hc, htsbuf_queue_t *spill)
if (cmdline) free(cmdline);
if ((cmdline = tcp_read_line(hc->hc_fd, spill)) == NULL)
if ((cmdline = tcp_read_line(hc->hc_fd, &spill)) == NULL)
goto error;
if((n = http_tokenize(cmdline, argv, 3, -1)) != 3)
@ -927,24 +1009,28 @@ http_serve_requests(http_connection_t *hc, htsbuf_queue_t *spill)
while(1) {
if (hdrline) free(hdrline);
if ((hdrline = tcp_read_line(hc->hc_fd, spill)) == NULL)
if ((hdrline = tcp_read_line(hc->hc_fd, &spill)) == NULL)
goto error;
if(!*hdrline)
break; /* header complete */
break; /* header complete */
if((n = http_tokenize(hdrline, argv, 2, -1)) < 2)
continue;
if((c = strrchr(argv[0], ':')) == NULL)
goto error;
if((n = http_tokenize(hdrline, argv, 2, -1)) < 2) {
if ((c = strchr(hdrline, ':')) != NULL) {
*c = '\0';
argv[0] = hdrline;
argv[1] = c + 1;
} else {
continue;
}
} else if((c = strrchr(argv[0], ':')) == NULL)
goto error;
*c = 0;
http_arg_set(&hc->hc_args, argv[0], argv[1]);
}
if(process_request(hc, spill))
break;
r = process_request(hc, &spill);
free(hc->hc_post_data);
hc->hc_post_data = NULL;
@ -954,11 +1040,8 @@ http_serve_requests(http_connection_t *hc, htsbuf_queue_t *spill)
htsbuf_queue_flush(&hc->hc_reply);
free(hc->hc_username);
hc->hc_username = NULL;
free(hc->hc_password);
hc->hc_password = NULL;
if (r)
break;
hc->hc_logout_cookie = 0;
@ -977,41 +1060,29 @@ static void
http_serve(int fd, void **opaque, struct sockaddr_storage *peer,
struct sockaddr_storage *self)
{
htsbuf_queue_t spill;
http_connection_t hc;
// Note: global_lock held on entry */
/* Note: global_lock held on entry */
pthread_mutex_unlock(&global_lock);
memset(&hc, 0, sizeof(http_connection_t));
*opaque = &hc;
http_arg_init(&hc.hc_args);
http_arg_init(&hc.hc_req_args);
hc.hc_fd = fd;
hc.hc_peer = peer;
hc.hc_self = self;
hc.hc_paths = &http_paths;
hc.hc_process = http_process_request;
hc.hc_fd = fd;
hc.hc_peer = peer;
hc.hc_self = self;
http_serve_requests(&hc);
htsbuf_queue_init(&spill, 0);
http_serve_requests(&hc, &spill);
http_arg_flush(&hc.hc_args);
http_arg_flush(&hc.hc_req_args);
htsbuf_queue_flush(&hc.hc_reply);
htsbuf_queue_flush(&spill);
close(fd);
// Note: leave global_lock held for parent
pthread_mutex_lock(&global_lock);
free(hc.hc_post_data);
free(hc.hc_username);
free(hc.hc_password);
*opaque = NULL;
}
static void
void
http_cancel( void *opaque )
{
http_connection_t *hc = opaque;

View file

@ -22,9 +22,12 @@
#include "htsbuf.h"
#include "url.h"
#include "tvhpoll.h"
#include "access.h"
#include "access.h"
struct channel;
struct http_path;
typedef LIST_HEAD(, http_path) http_path_list_t;
typedef TAILQ_HEAD(http_arg_list, http_arg) http_arg_list_t;
@ -71,12 +74,17 @@ typedef struct http_arg {
#define HTTP_STATUS_UNSUPPORTED 415
#define HTTP_STATUS_BAD_RANGE 417
#define HTTP_STATUS_EXPECTATION 418
#define HTTP_STATUS_BANDWIDTH 453
#define HTTP_STATUS_BAD_SESSION 454
#define HTTP_STATUS_METHOD_INVALID 455
#define HTTP_STATUS_BAD_TRANSFER 456
#define HTTP_STATUS_INTERNAL 500
#define HTTP_STATUS_NOT_IMPLEMENTED 501
#define HTTP_STATUS_BAD_GATEWAY 502
#define HTTP_STATUS_SERVICE 503
#define HTTP_STATUS_GATEWAY_TIMEOUT 504
#define HTTP_STATUS_HTTP_VERSION 505
#define HTTP_STATUS_OP_NOT_SUPPRT 551
typedef enum http_state {
HTTP_CON_WAIT_REQUEST,
@ -92,6 +100,7 @@ typedef enum http_state {
} http_state_t;
typedef enum http_cmd {
HTTP_CMD_NONE,
HTTP_CMD_GET,
HTTP_CMD_HEAD,
HTTP_CMD_POST,
@ -112,9 +121,13 @@ typedef enum http_ver {
typedef struct http_connection {
int hc_fd;
struct sockaddr_storage *hc_peer;
char *hc_peer_ipstr;
struct sockaddr_storage *hc_self;
char *hc_representative;
http_path_list_t *hc_paths;
int (*hc_process)(struct http_connection *hc, htsbuf_queue_t *spill);
char *hc_url;
char *hc_url_orig;
int hc_keep_alive;
@ -138,6 +151,8 @@ typedef struct http_connection {
int hc_no_output;
int hc_logout_cookie;
int hc_shutdown;
uint64_t hc_cseq;
char *hc_session;
/* Support for HTTP POST */
@ -146,6 +161,7 @@ typedef struct http_connection {
} http_connection_t;
extern void *http_server;
const char *http_cmd2str(int val);
int http_str2cmd(const char *str);
@ -160,6 +176,7 @@ static inline void http_arg_init(struct http_arg_list *list)
void http_arg_flush(struct http_arg_list *list);
char *http_arg_get(struct http_arg_list *list, const char *name);
char *http_arg_get_remove(struct http_arg_list *list, const char *name);
void http_arg_set(struct http_arg_list *list, const char *key, const char *val);
@ -177,7 +194,11 @@ void http_redirect(http_connection_t *hc, const char *location,
void http_send_header(http_connection_t *hc, int rc, const char *content,
int64_t contentlen, const char *encoding,
const char *location, int maxage, const char *range,
const char *disposition);
const char *disposition, http_arg_list_t *args);
void http_serve_requests(http_connection_t *hc);
void http_cancel(void *opaque);
typedef int (http_callback_t)(http_connection_t *hc,
const char *remain, void *opaque);
@ -213,6 +234,8 @@ int http_access_verify_channel(http_connection_t *hc, int mask,
void http_deescape(char *s);
void http_parse_get_args(http_connection_t *hc, char *args);
/*
* HTTP/RTSP Client
*/
@ -235,6 +258,7 @@ struct http_client {
TAILQ_ENTRY(http_client) hc_link;
int hc_id;
int hc_fd;
char *hc_scheme;
char *hc_host;

View file

@ -89,11 +89,20 @@ static pthread_cond_t http_cond;
static th_pipe_t http_pipe;
static char *http_user_agent;
/*
*
*/
static inline int
shortid( http_client_t *hc )
{
return hc->hc_id;
}
/*
*
*/
static int
http_port( const char *scheme, int port )
http_port( http_client_t *hc, const char *scheme, int port )
{
if (port <= 0 || port > 65535) {
if (scheme && strcmp(scheme, "http") == 0)
@ -103,7 +112,7 @@ http_port( const char *scheme, int port )
else if (scheme && strcmp(scheme, "rtsp") == 0)
port = 554;
else {
tvhlog(LOG_ERR, "httpc", "Unknown scheme '%s'", scheme ? scheme : "");
tvhlog(LOG_ERR, "httpc", "%04X: Unknown scheme '%s'", shortid(hc), scheme ? scheme : "");
return -EINVAL;
}
}
@ -196,6 +205,7 @@ static int
http_client_flush( http_client_t *hc, int result )
{
hc->hc_result = result;
tvhtrace("httpc", "%04X: client flush %i", shortid(hc), result);
if (result < 0)
http_client_shutdown(hc, 0, 0);
hc->hc_in_data = 0;
@ -389,8 +399,8 @@ http_client_ssl_send( http_client_t *hc, const void *buf, size_t len )
if (hc->hc_verify_peer > 0) {
if (SSL_get_peer_certificate(ssl->ssl) == NULL ||
SSL_get_verify_result(ssl->ssl) != X509_V_OK) {
tvhlog(LOG_ERR, "httpc", "SSL peer verification failed (%s:%i)%s %li",
hc->hc_host, hc->hc_port,
tvhlog(LOG_ERR, "httpc", "%04X: SSL peer verification failed (%s:%i)%s %li",
shortid(hc), hc->hc_host, hc->hc_port,
SSL_get_peer_certificate(ssl->ssl) ? " X509" : "",
SSL_get_verify_result(ssl->ssl));
errno = EPERM;
@ -605,7 +615,7 @@ error:
htsbuf_read(&q, body, body_size);
#if ENABLE_TRACE
tvhtrace("httpc", "sending %s cmd", http_ver2str(hc->hc_version));
tvhtrace("httpc", "%04X: sending %s cmd", shortid(hc), http_ver2str(hc->hc_version));
tvhlog_hexdump("httpc", body, body_size);
#endif
@ -630,7 +640,7 @@ http_client_finish( http_client_t *hc )
#if ENABLE_TRACE
if (hc->hc_data) {
tvhtrace("httpc", "received %s data", http_ver2str(hc->hc_version));
tvhtrace("httpc", "%04X: received %s data", shortid(hc), http_ver2str(hc->hc_version));
tvhlog_hexdump("httpc", hc->hc_data, hc->hc_csize);
}
#endif
@ -740,7 +750,7 @@ http_client_data_chunked( http_client_t *hc, char *buf, size_t len, int *end )
}
l = 0;
if (hc->hc_chunk_csize) {
s = d = hc->hc_chunk;
s = hc->hc_chunk;
if (buf[0] == '\n' && s[hc->hc_chunk_csize-1] == '\r')
l = 1;
else if (len > 1 && buf[0] == '\r' && buf[1] == '\n')
@ -766,7 +776,10 @@ http_client_data_chunked( http_client_t *hc, char *buf, size_t len, int *end )
return res;
continue;
}
if (s[0] == '0' && s[1] == '\0')
d = s + 1;
while (*d == '0' && *d)
d++;
if (s[0] == '0' && *d == '\0')
hc->hc_chunk_trails = 1;
else {
hc->hc_chunk_size = strtoll(s, NULL, 16);
@ -850,10 +863,9 @@ int
http_client_run( http_client_t *hc )
{
char *buf, *saveptr, *argv[3], *d, *p;
int ver;
int ver, res, delimsize = 4;
ssize_t r;
size_t len;
int res;
if (hc == NULL)
return 0;
@ -882,15 +894,21 @@ http_client_run( http_client_t *hc )
buf = alloca(hc->hc_io_size);
if (!hc->hc_in_data && hc->hc_rpos > 3 &&
(d = strstr(hc->hc_rbuf, "\r\n\r\n")) != NULL)
goto header;
if (!hc->hc_in_data && hc->hc_rpos > 3) {
if ((d = strstr(hc->hc_rbuf, "\r\n\r\n")) != NULL)
goto header;
if ((d = strstr(hc->hc_rbuf, "\n\n")) != NULL) {
delimsize = 2;
goto header;
}
}
retry:
if (hc->hc_ssl)
r = http_client_ssl_recv(hc, buf, hc->hc_io_size);
else
r = recv(hc->hc_fd, buf, hc->hc_io_size, MSG_DONTWAIT);
tvhtrace("httpc", "%04X: recv %zi", shortid(hc), r);
if (r == 0) {
if (hc->hc_in_data && !hc->hc_keepalive)
return http_client_finish(hc);
@ -905,7 +923,7 @@ retry:
}
#if ENABLE_TRACE
if (r > 0) {
tvhtrace("httpc", "received %s answer", http_ver2str(hc->hc_version));
tvhtrace("httpc", "%04X: received %s answer", shortid(hc), http_ver2str(hc->hc_version));
tvhlog_hexdump("httpc", buf, r);
}
#endif
@ -934,8 +952,11 @@ retry:
next_header:
if (hc->hc_rpos < 3)
return HTTP_CON_RECEIVING;
if ((d = strstr(hc->hc_rbuf, "\r\n\r\n")) == NULL)
return HTTP_CON_RECEIVING;
if ((d = strstr(hc->hc_rbuf, "\r\n\r\n")) == NULL) {
delimsize = 2;
if ((d = strstr(hc->hc_rbuf, "\n\n")) == NULL)
return HTTP_CON_RECEIVING;
}
header:
*d = '\0';
@ -943,11 +964,11 @@ header:
hc->hc_reconnected = 0;
http_client_clear_state(hc);
hc->hc_rpos = len;
hc->hc_hsize = d - hc->hc_rbuf + 4;
hc->hc_hsize = d - hc->hc_rbuf + delimsize;
p = strtok_r(hc->hc_rbuf, "\r\n", &saveptr);
if (p == NULL)
return http_client_flush(hc, -EINVAL);
tvhtrace("httpc", "%s answer '%s'", http_ver2str(hc->hc_version), p);
tvhtrace("httpc", "%04X: %s answer '%s'", shortid(hc), http_ver2str(hc->hc_version), p);
if (http_tokenize(p, argv, 3, -1) != 3)
return http_client_flush(hc, -EINVAL);
if ((ver = http_str2ver(argv[0])) < 0)
@ -1019,7 +1040,7 @@ header:
* Redirected
*/
static void
http_client_basic_args ( http_arg_list_t *h, const url_t *url, int keepalive )
http_client_basic_args ( http_client_t *hc, http_arg_list_t *h, const url_t *url, int keepalive )
{
char buf[256];
@ -1028,7 +1049,7 @@ http_client_basic_args ( http_arg_list_t *h, const url_t *url, int keepalive )
http_arg_set(h, "Host", url->host);
} else {
snprintf(buf, sizeof(buf), "%s:%u", url->host,
http_port(url->scheme, url->port));
http_port(hc, url->scheme, url->port));
http_arg_set(h, "Host", buf);
}
if (http_user_agent) {
@ -1082,8 +1103,8 @@ http_client_redirected ( http_client_t *hc )
memset(&u, 0, sizeof(u));
if (urlparse(location2 ? location2 : location, &u)) {
tvherror("httpc", "redirection - cannot parse url '%s'",
location2 ? location2 : location);
tvherror("httpc", "%04X: redirection - cannot parse url '%s'",
shortid(hc), location2 ? location2 : location);
free(location);
return -EIO;
}
@ -1091,7 +1112,7 @@ http_client_redirected ( http_client_t *hc )
if (strcmp(u.scheme, hc->hc_scheme) ||
strcmp(u.host, hc->hc_host) ||
http_port(u.scheme, u.port) != hc->hc_port ||
http_port(hc, u.scheme, u.port) != hc->hc_port ||
!hc->hc_keepalive) {
efd = hc->hc_efd;
http_client_shutdown(hc, 1, 1);
@ -1109,7 +1130,7 @@ http_client_redirected ( http_client_t *hc )
http_client_flush(hc, 0);
http_client_basic_args(&h, &u, hc->hc_keepalive);
http_client_basic_args(hc, &h, &u, hc->hc_keepalive);
hc->hc_reconnected = 1;
hc->hc_shutdown = 0;
hc->hc_pevents = 0;
@ -1130,7 +1151,7 @@ http_client_simple( http_client_t *hc, const url_t *url )
{
http_arg_list_t h;
http_client_basic_args(&h, url, 0);
http_client_basic_args(hc, &h, url, 0);
return http_client_send(hc, HTTP_CMD_GET, url->path, url->query,
&h, NULL, 0);
}
@ -1144,14 +1165,14 @@ http_client_ssl_peer_verify( http_client_t *hc, int verify )
hc->hc_verify_peer = verify ? 1 : 0;
if ((ssl = hc->hc_ssl) != NULL) {
if (!SSL_CTX_set_default_verify_paths(ssl->ctx))
tvherror("httpc", "SSL - unable to load CA certificates for verification");
tvherror("httpc", "%04X: SSL - unable to load CA certificates for verification", shortid(hc));
SSL_CTX_set_verify_depth(ssl->ctx, 1);
SSL_CTX_set_verify(ssl->ctx,
hc->hc_verify_peer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE,
NULL);
}
} else {
tvherror("httpc", "SSL peer verification method must be set only once");
tvherror("httpc", "%04X: SSL peer verification method must be set only once", shortid(hc));
}
}
@ -1240,7 +1261,7 @@ http_client_reconnect
if (scheme == NULL || host == NULL)
return -EINVAL;
port = http_port(scheme, port);
port = http_port(hc, scheme, port);
hc->hc_pevents = 0;
hc->hc_version = ver;
hc->hc_scheme = strdup(scheme);
@ -1248,37 +1269,37 @@ http_client_reconnect
hc->hc_port = port;
hc->hc_fd = tcp_connect(host, port, hc->hc_bindaddr, errbuf, sizeof(errbuf), -1);
if (hc->hc_fd < 0) {
tvhlog(LOG_ERR, "httpc", "Unable to connect to %s:%i - %s", host, port, errbuf);
tvhlog(LOG_ERR, "httpc", "%04X: Unable to connect to %s:%i - %s", shortid(hc), host, port, errbuf);
return -EINVAL;
}
hc->hc_einprogress = 1;
tvhtrace("httpc", "Connected to %s:%i", host, port);
tvhtrace("httpc", "%04X: Connected to %s:%i", shortid(hc), host, port);
http_client_ssl_free(hc);
if (strcasecmp(scheme, "https") == 0 || strcasecmp(scheme, "rtsps") == 0) {
ssl = calloc(1, sizeof(*ssl));
hc->hc_ssl = ssl;
ssl->ctx = SSL_CTX_new(SSLv23_client_method());
if (ssl->ctx == NULL) {
tvhlog(LOG_ERR, "httpc", "Unable to get SSL_CTX");
tvhlog(LOG_ERR, "httpc", "%04X: Unable to get SSL_CTX", shortid(hc));
goto err1;
}
/* do not use SSLv2 */
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION);
/* adjust cipher list */
if (SSL_CTX_set_cipher_list(ssl->ctx, "HIGH:MEDIUM") != 1) {
tvhlog(LOG_ERR, "httpc", "Unable to adjust SSL cipher list");
tvhlog(LOG_ERR, "httpc", "%04X: Unable to adjust SSL cipher list", shortid(hc));
goto err2;
}
ssl->rbio = BIO_new(BIO_s_mem());
ssl->wbio = BIO_new(BIO_s_mem());
ssl->ssl = SSL_new(ssl->ctx);
if (ssl->ssl == NULL || ssl->rbio == NULL || ssl->wbio == NULL) {
tvhlog(LOG_ERR, "httpc", "Unable to get SSL handle");
tvhlog(LOG_ERR, "httpc", "%04X: Unable to get SSL handle", shortid(hc));
goto err3;
}
SSL_set_bio(ssl->ssl, ssl->rbio, ssl->wbio);
if (!SSL_set_tlsext_host_name(ssl->ssl, host)) {
tvhlog(LOG_ERR, "httpc", "Unable to set SSL hostname");
tvhlog(LOG_ERR, "httpc", "%04X: Unable to set SSL hostname", shortid(hc));
goto err4;
}
}
@ -1304,8 +1325,10 @@ http_client_connect
const char *host, int port, const char *bindaddr )
{
http_client_t *hc;
static int tally;
hc = calloc(1, sizeof(http_client_t));
hc->hc_id = ++tally;
hc->hc_aux = aux;
hc->hc_io_size = 1024;
hc->hc_rtsp_stream_id = -1;
@ -1364,6 +1387,7 @@ http_client_close ( http_client_t *hc )
}
http_client_shutdown(hc, 1, 0);
http_client_flush(hc, 0);
tvhtrace("httpc", "%04X: Closed", shortid(hc));
while ((wcmd = TAILQ_FIRST(&hc->hc_wqueue)) != NULL)
http_client_cmd_destroy(hc, wcmd);
http_client_ssl_free(hc);
@ -1642,7 +1666,7 @@ http_client_testsuite_run( void )
path = getenv("TVHEADEND_HTTPC_TEST");
if (path == NULL)
path = TVHEADEND_DATADIR "/support/httpc-test.txt";
fp = fopen(path, "r");
fp = tvh_fopen(path, "r");
if (fp == NULL) {
tvhlog(LOG_NOTICE, "httpc", "Test: unable to open '%s': %s", path, strerror(errno));
return;

View file

@ -342,10 +342,28 @@ idnode_get_display
if (p) {
if (p->rend)
return p->rend(self);
if (p->islist) {
else if (p->islist) {
htsmsg_t *l = (htsmsg_t*)p->get(self);
if (l)
return htsmsg_list_2_csv(l);
} else if (p->list) {
htsmsg_t *l = p->list(self), *m;
htsmsg_field_t *f;
uint32_t k, v;
char *r = NULL;
const char *s;
if (l && !idnode_get_u32(self, p->id, &v))
HTSMSG_FOREACH(f, l) {
m = htsmsg_field_get_map(f);
if (!htsmsg_get_u32(m, "key", &k) &&
(s = htsmsg_get_str(m, "val")) != NULL &&
v == k) {
r = strdup(s);
break;
}
}
htsmsg_destroy(l);
return r;
}
}
return NULL;
@ -993,7 +1011,7 @@ idnode_set_find_index
return -1;
}
void
int
idnode_set_remove
( idnode_set_t *is, idnode_t *in )
{
@ -1002,7 +1020,9 @@ idnode_set_remove
memmove(&is->is_array[i], &is->is_array[i+1],
(is->is_count - i - 1) * sizeof(idnode_t *));
is->is_count--;
return 1;
}
return 0;
}
void
@ -1422,11 +1442,13 @@ idnode_thread ( void *p )
HTSMSG_FOREACH(f, q) {
node = idnode_find(f->hmf_name, NULL, NULL);
event = htsmsg_field_get_str(f);
m = htsmsg_create_map();
htsmsg_add_str(m, "uuid", f->hmf_name);
if (!node)
htsmsg_add_u32(m, "removed", 1);
notify_by_msg(event, m);
if (event) {
m = htsmsg_create_map();
htsmsg_add_str(m, "uuid", f->hmf_name);
if (!node)
htsmsg_add_u32(m, "removed", 1);
notify_by_msg(event, m);
}
}
/* Finished */

View file

@ -203,7 +203,7 @@ static inline idnode_set_t * idnode_set_create(int sorted)
is->is_sorted = sorted; return is; }
void idnode_set_add
( idnode_set_t *is, idnode_t *in, idnode_filter_t *filt );
void idnode_set_remove ( idnode_set_t *is, idnode_t *in );
int idnode_set_remove ( idnode_set_t *is, idnode_t *in );
ssize_t idnode_set_find_index( idnode_set_t *is, idnode_t *in );
static inline int idnode_set_exists ( idnode_set_t *is, idnode_t *in )
{ return idnode_set_find_index(is, in) >= 0; }

View file

@ -144,6 +144,8 @@ imagecache_image_fetch ( imagecache_image_t *img )
tvhpoll_t *efd = NULL;
http_client_t *hc;
lock_assert(&global_lock);
if (img->url == NULL || img->url[0] == '\0')
return res;
@ -156,7 +158,7 @@ imagecache_image_fetch ( imagecache_image_t *img )
if (hts_settings_makedirs(path))
goto error;
snprintf(tmp, sizeof(tmp), "%s.tmp", path);
if (!(fp = fopen(tmp, "wb")))
if (!(fp = tvh_fopen(tmp, "wb")))
goto error;
/* Fetch (release lock, incase of delays) */
@ -575,9 +577,7 @@ imagecache_open ( uint32_t id )
} else if (i->state == QUEUED) {
i->state = FETCHING;
TAILQ_REMOVE(&imagecache_queue, i, q_link);
pthread_mutex_unlock(&global_lock);
e = imagecache_image_fetch(i);
pthread_mutex_lock(&global_lock);
if (e)
return -1;
}

View file

@ -18,10 +18,18 @@
#include "input.h"
#include "notify.h"
#include "access.h"
tvh_input_list_t tvh_inputs;
tvh_hardware_list_t tvh_hardware;
const idclass_t tvh_input_instance_class =
{
.ic_class = "tvh_input_instance",
.ic_caption = "Input Instance",
.ic_perm_def = ACCESS_ADMIN
};
/*
* Create entry
*/
@ -59,6 +67,23 @@ tvh_hardware_delete ( tvh_hardware_t *th )
idnode_unlink(&th->th_id);
}
/*
*
*/
void
tvh_input_instance_clear_stats ( tvh_input_instance_t *tii )
{
tvh_input_stream_stats_t *s = &tii->tii_stats;
atomic_exchange(&s->ber, 0);
atomic_exchange(&s->unc, 0);
atomic_exchange(&s->cc, 0);
atomic_exchange(&s->te, 0);
atomic_exchange(&s->ec_block, 0);
atomic_exchange(&s->tc_block, 0);
}
/*
* Input status handling
*/
@ -75,10 +100,10 @@ tvh_input_stream_create_msg
htsmsg_add_str(m, "stream", st->stream_name);
htsmsg_add_u32(m, "subs", st->subs_count);
htsmsg_add_u32(m, "weight", st->max_weight);
htsmsg_add_u32(m, "signal", st->stats.signal);
htsmsg_add_s32(m, "signal", st->stats.signal);
htsmsg_add_u32(m, "signal_scale", st->stats.signal_scale);
htsmsg_add_u32(m, "ber", st->stats.ber);
htsmsg_add_u32(m, "snr", st->stats.snr);
htsmsg_add_s32(m, "snr", st->stats.snr);
htsmsg_add_u32(m, "snr_scale", st->stats.snr_scale);
htsmsg_add_u32(m, "unc", st->stats.unc);
htsmsg_add_u32(m, "bps", st->stats.bps);

View file

@ -27,6 +27,7 @@
*/
typedef struct tvh_hardware tvh_hardware_t;
typedef struct tvh_input tvh_input_t;
typedef struct tvh_input_instance tvh_input_instance_t;
typedef struct tvh_input_stream tvh_input_stream_t;
typedef struct tvh_input_stream_stats tvh_input_stream_stats_t;
@ -87,6 +88,20 @@ struct tvh_input {
void (*ti_get_streams) (struct tvh_input *, tvh_input_stream_list_t*);
};
/*
* Generic input instance super-class
*/
struct tvh_input_instance {
idnode_t tii_id;
LIST_ENTRY(tvh_input_instance) tii_input_link;
tvh_input_stream_stats_t tii_stats;
void (*tii_delete) (tvh_input_instance_t *tii);
void (*tii_clear_stats) (tvh_input_instance_t *tii);
};
/*
* Generic hardware super-class
*/
@ -103,6 +118,7 @@ void tvh_hardware_delete ( tvh_hardware_t *th );
* Class and Global list defs
*/
extern const idclass_t tvh_input_class;
extern const idclass_t tvh_input_instance_class;
tvh_input_list_t tvh_inputs;
tvh_hardware_list_t tvh_hardware;
@ -120,14 +136,16 @@ htsmsg_t * tvh_input_stream_create_msg ( tvh_input_stream_t *st );
void tvh_input_stream_destroy ( tvh_input_stream_t *st );
static inline tvh_input_instance_t *
tvh_input_instance_find_by_uuid(const char *uuid)
{ return (tvh_input_instance_t*)idnode_find(uuid, &tvh_input_instance_class, NULL); }
void tvh_input_instance_clear_stats ( tvh_input_instance_t *tii );
/*
* Input subsystem includes
*/
#if ENABLE_MPEGPS
#include "input/mpegps.h"
#endif
#if ENABLE_MPEGTS
#include "input/mpegts.h"
#include "input/mpegts/mpegts_mux_sched.h"

View file

@ -1 +0,0 @@
v4l/v4l.h

View file

@ -1,789 +0,0 @@
/*
* TV Input - Linux analogue (v4lv2) interface
* Copyright (C) 2007 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <pthread.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#include <inttypes.h>
#include "settings.h"
#include "tvheadend.h"
#include "service.h"
#include "v4l.h"
#include "parsers.h"
#include "notify.h"
#include "psi.h"
#include "channels.h"
struct v4l_adapter_queue v4l_adapters;
static void v4l_adapter_notify(v4l_adapter_t *va);
const idclass_t v4l_class = {
.ic_super = &service_class,
.ic_class = "v4l",
};
/**
*
*/
static void
v4l_input(v4l_adapter_t *va)
{
service_t *t = va->va_current_service;
elementary_stream_t *st;
uint8_t buf[4000];
uint8_t *ptr, *pkt;
int len, l, r;
len = read(va->va_fd, buf, 4000);
if(len < 1)
return;
ptr = buf;
pthread_mutex_lock(&t->s_stream_mutex);
service_set_streaming_status_flags(t,
TSS_INPUT_HARDWARE | TSS_INPUT_SERVICE);
while(len > 0) {
switch(va->va_startcode) {
default:
va->va_startcode = va->va_startcode << 8 | *ptr;
va->va_lenlock = 0;
ptr++; len--;
continue;
case 0x000001e0:
st = t->s_video;
break;
case 0x000001c0:
st = t->s_audio;
break;
}
if(va->va_lenlock == 2) {
l = st->es_buf_ps.sb_size;
st->es_buf_ps.sb_data = pkt = realloc(st->es_buf_ps.sb_data, l);
r = l - st->es_buf_ps.sb_ptr;
if(r > len)
r = len;
memcpy(pkt + st->es_buf_ps.sb_ptr, ptr, r);
ptr += r;
len -= r;
st->es_buf_ps.sb_ptr += r;
if(st->es_buf_ps.sb_ptr == l) {
service_set_streaming_status_flags(t, TSS_MUX_PACKETS);
parse_mpeg_ps(t, st, pkt + 6, l - 6);
st->es_buf_ps.sb_size = 0;
va->va_startcode = 0;
} else {
assert(st->es_buf_ps.sb_ptr < l);
}
} else {
st->es_buf_ps.sb_size = st->es_buf_ps.sb_size << 8 | *ptr;
va->va_lenlock++;
if(va->va_lenlock == 2) {
st->es_buf_ps.sb_size += 6;
st->es_buf_ps.sb_ptr = 6;
}
ptr++; len--;
}
}
pthread_mutex_unlock(&t->s_stream_mutex);
}
/**
*
*/
static void *
v4l_thread(void *aux)
{
v4l_adapter_t *va = aux;
struct pollfd pfd[2];
int r;
pfd[0].fd = va->va_pipe[0];
pfd[0].events = POLLIN;
pfd[1].fd = va->va_fd;
pfd[1].events = POLLIN;
while(1) {
r = poll(pfd, 2, -1);
if(r < 0) {
tvhlog(LOG_ALERT, "v4l", "%s: poll() error %s, sleeping one second",
va->va_path, strerror(errno));
sleep(1);
continue;
}
if(pfd[0].revents & POLLIN) {
// Message on control pipe, used to exit thread, do so
break;
}
if(pfd[1].revents & POLLIN) {
v4l_input(va);
}
}
close(va->va_pipe[0]);
return NULL;
}
/**
*
*/
static int
v4l_service_start(service_t *t, int instance)
{
v4l_adapter_t *va = t->s_v4l_adapter;
int frequency = t->s_v4l_frequency;
struct v4l2_frequency vf;
int result;
v4l2_std_id std = 0xff;
int fd;
if(va->va_current_service != NULL)
return 1; // Adapter busy
fd = tvh_open(va->va_path, O_RDWR | O_NONBLOCK, 0);
if(fd == -1) {
tvhlog(LOG_ERR, "v4l",
"%s: Unable to open device: %s\n", va->va_path,
strerror(errno));
return -1;
}
if(!va->va_file) {
result = ioctl(fd, VIDIOC_S_STD, &std);
if(result < 0) {
tvhlog(LOG_ERR, "v4l",
"%s: Unable to set PAL -- %s", va->va_path, strerror(errno));
close(fd);
return -1;
}
memset(&vf, 0, sizeof(vf));
vf.tuner = 0;
vf.type = V4L2_TUNER_ANALOG_TV;
vf.frequency = (frequency * 16) / 1000000;
result = ioctl(fd, VIDIOC_S_FREQUENCY, &vf);
if(result < 0) {
tvhlog(LOG_ERR, "v4l",
"%s: Unable to tune to %dHz", va->va_path, frequency);
close(fd);
return -1;
}
tvhlog(LOG_INFO, "v4l",
"%s: Tuned to %dHz", va->va_path, frequency);
}
if(pipe(va->va_pipe)) {
tvhlog(LOG_ERR, "v4l",
"%s: Unable to create control pipe [%s]", va->va_path, strerror(errno));
close(fd);
return -1;
}
va->va_fd = fd;
va->va_current_service = t;
tvhthread_create(&va->va_thread, NULL, v4l_thread, va);
v4l_adapter_notify(va);
return 0;
}
/**
*
*/
static void
v4l_service_refresh(service_t *t)
{
}
/**
*
*/
static void
v4l_service_stop(service_t *t)
{
char c = 'q';
v4l_adapter_t *va = t->s_v4l_adapter;
assert(va->va_current_service != NULL);
if(tvh_write(va->va_pipe[1], &c, 1))
tvhlog(LOG_ERR, "v4l", "Unable to close video thread -- %s",
strerror(errno));
pthread_join(va->va_thread, NULL);
close(va->va_pipe[1]);
close(va->va_fd);
va->va_current_service = NULL;
v4l_adapter_notify(va);
}
/**
*
*/
static void
v4l_service_save(service_t *t)
{
v4l_adapter_t *va = t->s_v4l_adapter;
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_u32(m, "frequency", t->s_v4l_frequency);
if(t->s_ch != NULL) {
htsmsg_add_str(m, "channelname", t->s_ch->ch_name);
htsmsg_add_u32(m, "mapped", 1);
}
pthread_mutex_lock(&t->s_stream_mutex);
psi_save_service_settings(m, t);
pthread_mutex_unlock(&t->s_stream_mutex);
hts_settings_save(m, "v4lservices/%s/%s",
va->va_identifier, idnode_uuid_as_str(&t->s_id));
htsmsg_destroy(m);
}
/**
*
*/
static int
v4l_service_is_enabled(service_t *t)
{
return t->s_enabled;
}
/**
*
*/
static int
v4l_grace_period(service_t *t)
{
return 2;
}
/**
* Generate a descriptive name for the source
*/
static void
v4l_service_setsourceinfo(service_t *t, struct source_info *si)
{
char buf[64];
memset(si, 0, sizeof(struct source_info));
si->si_type = S_MPEG_PS;
si->si_adapter = strdup(t->s_v4l_adapter->va_displayname);
snprintf(buf, sizeof(buf), "%d Hz", t->s_v4l_frequency);
si->si_mux = strdup(buf);
}
/**
*
*/
service_t *
v4l_service_find(v4l_adapter_t *va, const char *id, int create)
{
service_t *t;
char buf[200];
int vaidlen = strlen(va->va_identifier);
if(id != NULL) {
if(strncmp(id, va->va_identifier, vaidlen))
return NULL;
LIST_FOREACH(t, &va->va_services, s_group_link)
if(!strcmp(idnode_uuid_as_str(&t->s_id), id))
return t;
}
if(create == 0)
return NULL;
if(id == NULL) {
va->va_tally++;
snprintf(buf, sizeof(buf), "%s_%d", va->va_identifier, va->va_tally);
id = buf;
} else {
va->va_tally = MAX(atoi(id + vaidlen + 1), va->va_tally);
}
t = service_create(id, 0, &v4l_class);
t->s_start_feed = v4l_service_start;
t->s_refresh_feed = v4l_service_refresh;
t->s_stop_feed = v4l_service_stop;
t->s_config_save = v4l_service_save;
t->s_setsourceinfo = v4l_service_setsourceinfo;
t->s_is_enabled = v4l_service_is_enabled;
t->s_grace_period = v4l_grace_period;
t->s_iptv_fd = -1;
t->s_v4l_adapter = va;
pthread_mutex_lock(&t->s_stream_mutex);
service_make_nicename(t);
t->s_video = service_stream_create(t, -1, SCT_MPEG2VIDEO);
t->s_audio = service_stream_create(t, -1, SCT_MPEG2AUDIO);
pthread_mutex_unlock(&t->s_stream_mutex);
LIST_INSERT_HEAD(&va->va_services, t, s_group_link);
return t;
}
/**
*
*/
static void
v4l_adapter_add(const char *path, const char *displayname,
const char *devicename, int file)
{
v4l_adapter_t *va;
int i, r;
va = calloc(1, sizeof(v4l_adapter_t));
va->va_identifier = strdup(path);
r = strlen(va->va_identifier);
for(i = 0; i < r; i++)
if(!isalnum((int)va->va_identifier[i]))
va->va_identifier[i] = '_';
va->va_displayname = strdup(displayname);
va->va_path = path ? strdup(path) : NULL;
va->va_devicename = devicename ? strdup(devicename) : NULL;
va->va_file = file;
TAILQ_INSERT_TAIL(&v4l_adapters, va, va_global_link);
}
/**
*
*/
static void
v4l_adapter_check(const char *path, int fd)
{
int r, i;
char devicename[100];
struct v4l2_capability caps;
r = ioctl(fd, VIDIOC_QUERYCAP, &caps);
if(r) {
tvhlog(LOG_WARNING, "v4l",
"%s: Can not query capabilities, device skipped", path);
return;
}
tvhlog(LOG_INFO, "v4l", "%s: %s %s %s capabilities: 0x%08x",
path, caps.driver, caps.card, caps.bus_info, caps.capabilities);
if(!(caps.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
tvhlog(LOG_WARNING, "v4l",
"%s: Device is not a video capture device, device skipped", path);
return;
}
/* Enum video standards */
for(i = 0;; i++) {
struct v4l2_standard standard;
memset(&standard, 0, sizeof(standard));
standard.index = i;
if(ioctl(fd, VIDIOC_ENUMSTD, &standard))
break;
tvhlog(LOG_INFO, "v4l",
"%s: Standard #%d: %016llx %s, frameperiod: %d/%d, %d lines",
path,
standard.index,
standard.id,
standard.name,
standard.frameperiod.numerator,
standard.frameperiod.denominator,
standard.framelines);
}
/* Enum video inputs */
for(i = 0;; i++) {
struct v4l2_input input;
memset(&input, 0, sizeof(input));
input.index = i;
if(ioctl(fd, VIDIOC_ENUMINPUT, &input))
break;
const char *type;
switch(input.type) {
case V4L2_INPUT_TYPE_TUNER:
type = "Tuner";
break;
case V4L2_INPUT_TYPE_CAMERA:
type = "Camera";
break;
default:
type = "Unknown";
break;
}
int f = input.status;
tvhlog(LOG_INFO, "v4l",
"%s: Input #%d: %s (%s), audio:0x%x, tuner:%d, standard:%016llx, "
"%s%s%s",
path,
input.index,
input.name,
type,
input.audioset,
input.tuner,
input.std,
f & V4L2_IN_ST_NO_POWER ? "[No power] " : "",
f & V4L2_IN_ST_NO_SIGNAL ? "[No signal] " : "",
f & V4L2_IN_ST_NO_COLOR ? "[No color] " : "");
}
int can_mpeg = 0;
/* Enum formats */
for(i = 0;; i++) {
struct v4l2_fmtdesc fmtdesc;
memset(&fmtdesc, 0, sizeof(fmtdesc));
fmtdesc.index = i;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc))
break;
tvhlog(LOG_INFO, "v4l",
"%s: Format #%d: %s [%.4s] %s",
path,
fmtdesc.index,
fmtdesc.description,
(char*)&fmtdesc.pixelformat,
fmtdesc.flags & V4L2_FMT_FLAG_COMPRESSED ? "(compressed)" : "");
if(fmtdesc.pixelformat == V4L2_PIX_FMT_MPEG)
can_mpeg = 1;
}
if(!(caps.capabilities & V4L2_CAP_TUNER)) {
tvhlog(LOG_WARNING, "v4l",
"%s: Device does not have a tuner, device skipped", path);
return;
}
if(!can_mpeg) {
tvhlog(LOG_WARNING, "v4l",
"%s: Device lacks MPEG encoder, device skipped", path);
return;
}
snprintf(devicename, sizeof(devicename), "%s %s %s",
caps.card, caps.driver, caps.bus_info);
tvhlog(LOG_INFO, "v4l",
"%s: Using adapter", devicename);
v4l_adapter_add(path, devicename, devicename, 0);
}
/**
*
*/
static void
v4l_adapter_probe(const char *path)
{
int fd;
fd = tvh_open(path, O_RDWR | O_NONBLOCK, 0);
if(fd == -1) {
if(errno != ENOENT)
tvhlog(LOG_ALERT, "v4l",
"Unable to open %s -- %s", path, strerror(errno));
return;
}
v4l_adapter_check(path, fd);
close(fd);
}
/**
* Save config for the given adapter
*/
static void
v4l_adapter_save(v4l_adapter_t *va)
{
htsmsg_t *m = htsmsg_create_map();
lock_assert(&global_lock);
htsmsg_add_str(m, "displayname", va->va_displayname);
htsmsg_add_u32(m, "logging", va->va_logging);
hts_settings_save(m, "v4ladapters/%s", va->va_identifier);
htsmsg_destroy(m);
}
/**
*
*/
htsmsg_t *
v4l_adapter_build_msg(v4l_adapter_t *va)
{
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_str(m, "identifier", va->va_identifier);
htsmsg_add_str(m, "name", va->va_displayname);
htsmsg_add_str(m, "type", "v4l");
if(va->va_path)
htsmsg_add_str(m, "path", va->va_path);
if(va->va_devicename)
htsmsg_add_str(m, "devicename", va->va_devicename);
if(va->va_current_service != NULL) {
char buf[100];
snprintf(buf, sizeof(buf), "%d Hz",
va->va_current_service->s_v4l_frequency);
htsmsg_add_str(m, "currentMux", buf);
} else {
htsmsg_add_str(m, "currentMux", "- inactive -");
}
return m;
}
/**
*
*/
static void
v4l_adapter_notify(v4l_adapter_t *va)
{
notify_by_msg("tvAdapter", v4l_adapter_build_msg(va));
}
/**
*
*/
v4l_adapter_t *
v4l_adapter_find_by_identifier(const char *identifier)
{
v4l_adapter_t *va;
TAILQ_FOREACH(va, &v4l_adapters, va_global_link)
if(!strcmp(identifier, va->va_identifier))
return va;
return NULL;
}
/**
*
*/
void
v4l_adapter_set_displayname(v4l_adapter_t *va, const char *name)
{
lock_assert(&global_lock);
if(!strcmp(name, va->va_displayname))
return;
tvhlog(LOG_NOTICE, "v4l", "Adapter \"%s\" renamed to \"%s\"",
va->va_displayname, name);
tvh_str_set(&va->va_displayname, name);
v4l_adapter_save(va);
v4l_adapter_notify(va);
}
/**
*
*/
void
v4l_adapter_set_logging(v4l_adapter_t *va, int on)
{
if(va->va_logging == on)
return;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "v4l", "Adapter \"%s\" detailed logging set to: %s",
va->va_displayname, on ? "On" : "Off");
va->va_logging = on;
v4l_adapter_save(va);
v4l_adapter_notify(va);
}
/**
*
*/
static void
v4l_service_create_by_msg(v4l_adapter_t *va, htsmsg_t *c, const char *name)
{
const char *s;
unsigned int u32;
service_t *t = v4l_service_find(va, name, 1);
if(t == NULL)
return;
s = htsmsg_get_str(c, "channelname");
if(htsmsg_get_u32(c, "mapped", &u32))
u32 = 0;
if(!htsmsg_get_u32(c, "frequency", &u32))
t->s_v4l_frequency = u32;
if(s && u32)
service_map_channel(t, channel_find_by_name(s, 1, 0), 0);
}
/**
*
*/
static void
v4l_service_load(v4l_adapter_t *va)
{
htsmsg_t *l, *c;
htsmsg_field_t *f;
if((l = hts_settings_load("v4lservices/%s", va->va_identifier)) == NULL)
return;
HTSMSG_FOREACH(f, l) {
if((c = htsmsg_get_map_by_field(f)) == NULL)
continue;
v4l_service_create_by_msg(va, c, f->hmf_name);
}
htsmsg_destroy(l);
}
/**
*
*/
void
v4l_init(void)
{
htsmsg_t *l, *c;
htsmsg_field_t *f;
char buf[256];
int i;
v4l_adapter_t *va;
TAILQ_INIT(&v4l_adapters);
for(i = 0; i < 8; i++) {
snprintf(buf, sizeof(buf), "/dev/video%d", i);
v4l_adapter_probe(buf);
}
l = hts_settings_load("v4ladapters");
if(l != NULL) {
HTSMSG_FOREACH(f, l) {
if((c = htsmsg_get_map_by_field(f)) == NULL)
continue;
if((va = v4l_adapter_find_by_identifier(f->hmf_name)) == NULL) {
/* Not discovered by hardware, create it */
va = calloc(1, sizeof(v4l_adapter_t));
va->va_identifier = strdup(f->hmf_name);
va->va_path = NULL;
va->va_devicename = NULL;
}
tvh_str_update(&va->va_displayname, htsmsg_get_str(c, "displayname"));
htsmsg_get_u32(c, "logging", &va->va_logging);
}
htsmsg_destroy(l);
}
TAILQ_FOREACH(va, &v4l_adapters, va_global_link)
v4l_service_load(va);
}

View file

@ -1,82 +0,0 @@
/*
* TV Input - Linux analogue (v4lv2) interface
* Copyright (C) 2007 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef V4L_H_
#define V4L_H_
#define __user
#include <linux/videodev2.h>
LIST_HEAD(v4l_adapter_list, v4l_adapter);
TAILQ_HEAD(v4l_adapter_queue, v4l_adapter);
extern struct v4l_adapter_queue v4l_adapters;
typedef struct v4l_adapter {
TAILQ_ENTRY(v4l_adapter) va_global_link;
char *va_path;
char *va_identifier;
char *va_displayname;
char *va_devicename;
int va_file;
uint32_t va_logging;
// struct v4l2_capability va_caps;
struct service *va_current_service;
struct service_list va_services;
int va_tally;
/** Receiver thread stuff */
int va_fd;
pthread_t va_thread;
int va_pipe[2];
/** Mpeg stream parsing */
uint32_t va_startcode;
int va_lenlock;
} v4l_adapter_t;
v4l_adapter_t *v4l_adapter_find_by_identifier(const char *identifier);
void v4l_adapter_set_displayname(v4l_adapter_t *va, const char *name);
void v4l_adapter_set_logging(v4l_adapter_t *va, int on);
htsmsg_t *v4l_adapter_build_msg(v4l_adapter_t *va);
service_t *v4l_service_find(v4l_adapter_t *va, const char *id,
int create);
void v4l_init(void);
#endif /* V4L_H */

View file

@ -32,13 +32,14 @@
#define MPEGTS_ONID_NONE 0xFFFF
#define MPEGTS_TSID_NONE 0xFFFF
#define MPEGTS_PSI_SECTION_SIZE 5000
#define MPEGTS_FULLMUX_PID 0x2000
#define MPEGTS_TABLES_PID 0x2001
#define MPEGTS_PID_NONE 0xFFFF
/* Types */
typedef struct mpegts_apid mpegts_apid_t;
typedef struct mpegts_apids mpegts_apids_t;
typedef struct mpegts_table mpegts_table_t;
typedef struct mpegts_psi_section mpegts_psi_section_t;
typedef struct mpegts_network mpegts_network_t;
typedef struct mpegts_mux mpegts_mux_t;
typedef struct mpegts_service mpegts_service_t;
@ -73,6 +74,44 @@ void mpegts_init ( int linuxdvb_mask, str_list_t *satip_client,
str_list_t *tsfiles, int tstuners );
void mpegts_done ( void );
/* **************************************************************************
* PIDs
* *************************************************************************/
struct mpegts_apid {
uint16_t pid;
uint16_t weight;
};
struct mpegts_apids {
mpegts_apid_t *pids;
int alloc;
int count;
int all;
int sorted;
};
int mpegts_pid_init ( mpegts_apids_t *pids );
void mpegts_pid_done ( mpegts_apids_t *pids );
mpegts_apids_t *mpegts_pid_alloc ( void );
void mpegts_pid_destroy ( mpegts_apids_t **pids );
void mpegts_pid_reset ( mpegts_apids_t *pids );
int mpegts_pid_add ( mpegts_apids_t *pids, uint16_t pid, uint16_t weight );
int mpegts_pid_add_group ( mpegts_apids_t *pids, mpegts_apids_t *vals );
int mpegts_pid_del ( mpegts_apids_t *pids, uint16_t pid, uint16_t weight );
int mpegts_pid_del_group ( mpegts_apids_t *pids, mpegts_apids_t *vals );
int mpegts_pid_find_windex ( mpegts_apids_t *pids, uint16_t pid, uint16_t weight );
int mpegts_pid_find_rindex ( mpegts_apids_t *pids, uint16_t pid );
static inline int mpegts_pid_wexists ( mpegts_apids_t *pids, uint16_t pid, uint16_t weight )
{ return pids->all || mpegts_pid_find_windex(pids, pid, weight) >= 0; }
static inline int mpegts_pid_rexists ( mpegts_apids_t *pids, uint16_t pid )
{ return pids->all || mpegts_pid_find_rindex(pids, pid) >= 0; }
int mpegts_pid_copy ( mpegts_apids_t *dst, mpegts_apids_t *src );
int mpegts_pid_compare ( mpegts_apids_t *dst, mpegts_apids_t *src,
mpegts_apids_t *add, mpegts_apids_t *del );
int mpegts_pid_weighted( mpegts_apids_t *dst, mpegts_apids_t *src, int limit );
int mpegts_pid_dump ( mpegts_apids_t *pids, char *buf, int len, int wflag, int raw );
/* **************************************************************************
* Data / SI processing
* *************************************************************************/
@ -88,56 +127,63 @@ struct mpegts_packet
typedef int (*mpegts_table_callback_t)
( mpegts_table_t*, const uint8_t *buf, int len, int tableid );
typedef void (*mpegts_psi_section_callback_t)
( const uint8_t *tsb, size_t len, void *opaque );
struct mpegts_table_mux_cb
{
int tag;
int (*cb) ( mpegts_table_t*, mpegts_mux_t *mm,
int (*cb) ( mpegts_table_t*, mpegts_mux_t *mm, uint16_t nbid,
const uint8_t dtag, const uint8_t *dptr, int dlen );
};
struct mpegts_psi_section
{
int ps_offset;
int ps_lock;
uint8_t ps_data[MPEGTS_PSI_SECTION_SIZE];
};
typedef struct mpegts_table_state
{
int tableid;
uint64_t extraid;
int version;
int complete;
int working;
uint32_t sections[8];
RB_ENTRY(mpegts_table_state) link;
} mpegts_table_state_t;
typedef struct mpegts_pid_sub
{
RB_ENTRY(mpegts_pid_sub) mps_link;
#define MPS_NONE 0x0
#define MPS_STREAM 0x1
#define MPS_TABLE 0x2
#define MPS_FTABLE 0x4
int mps_type;
void *mps_owner;
LIST_ENTRY(mpegts_pid_sub) mps_svcraw_link;
#define MPS_NONE 0x00
#define MPS_ALL 0x01
#define MPS_RAW 0x02
#define MPS_STREAM 0x04
#define MPS_SERVICE 0x08
#define MPS_TABLE 0x10
#define MPS_FTABLE 0x20
#define MPS_TABLES 0x40
int mps_type;
#define MPS_WEIGHT_PAT 1000
#define MPS_WEIGHT_CAT 999
#define MPS_WEIGHT_SDT 999
#define MPS_WEIGHT_NIT 999
#define MPS_WEIGHT_BAT 999
#define MPS_WEIGHT_VCT 999
#define MPS_WEIGHT_EIT 999
#define MPS_WEIGHT_PMT 998
#define MPS_WEIGHT_PCR 997
#define MPS_WEIGHT_CA 996
#define MPS_WEIGHT_VIDEO 900
#define MPS_WEIGHT_AUDIO 800
#define MPS_WEIGHT_SUBTITLE 700
#define MPS_WEIGHT_ESOTHER 500
#define MPS_WEIGHT_RAW 400
#define MPS_WEIGHT_NIT2 300
#define MPS_WEIGHT_SDT2 300
#define MPS_WEIGHT_TDT 101
#define MPS_WEIGHT_PMT_SCAN 100
int mps_weight;
void *mps_owner;
} mpegts_pid_sub_t;
typedef struct mpegts_pid
{
int mp_pid;
int mp_fd; // linuxdvb demux fd
int mp_type; // mask for all subscribers
int8_t mp_cc;
RB_HEAD(,mpegts_pid_sub) mp_subs; // subscribers to pid
LIST_HEAD(,mpegts_pid_sub) mp_svc_subs;
RB_ENTRY(mpegts_pid) mp_link;
} mpegts_pid_t;
struct mpegts_table
{
mpegts_psi_table_t;
/**
* Flags, must never be changed after creation.
* We inspect it without holding global_lock
@ -148,12 +194,18 @@ struct mpegts_table
#define MT_FULL 0x0002
#define MT_QUICKREQ 0x0004
#define MT_FASTSWITCH 0x0008
#define MT_RECORD 0x0010
#define MT_SKIPSUBS 0x0020
#define MT_SCANSUBS 0x0040
#define MT_FAST 0x0080
#define MT_SLOW 0x0100
#define MT_DEFER 0x0200
#define MT_ONESHOT 0x0010
#define MT_RECORD 0x0020
#define MT_SKIPSUBS 0x0040
#define MT_SCANSUBS 0x0080
#define MT_FAST 0x0100
#define MT_SLOW 0x0200
#define MT_DEFER 0x0400
/**
* PID subscription weight
*/
int mt_weight;
/**
* Cycle queue
@ -167,20 +219,12 @@ struct mpegts_table
* File descriptor for filter
*/
LIST_ENTRY(mpegts_table) mt_link;
TAILQ_ENTRY(mpegts_table) mt_defer_link;
mpegts_mux_t *mt_mux;
char *mt_name;
void *mt_opaque;
void *mt_bat;
mpegts_table_callback_t mt_callback;
RB_HEAD(,mpegts_table_state) mt_state;
int mt_complete;
int mt_incomplete;
uint8_t mt_finished;
uint8_t mt_subscribed;
uint8_t mt_defer_cmd;
@ -191,22 +235,11 @@ struct mpegts_table
int mt_count;
int mt_pid;
int mt_id;
int mt_table; // SI table id (base)
int mt_mask; // mask
int mt_destroyed; // Refcounting
int mt_arefcount;
int8_t mt_cc;
tvhlog_limit_t mt_err_log;
mpegts_psi_section_t mt_sect;
struct mpegts_table_mux_cb *mt_mux_cb;
mpegts_service_t *mt_service;
@ -224,17 +257,11 @@ struct mpegts_table
struct mpegts_table_feed {
TAILQ_ENTRY(mpegts_table_feed) mtf_link;
uint8_t mtf_tsb[188];
int mtf_len;
mpegts_mux_t *mtf_mux;
uint8_t mtf_tsb[0];
};
/*
* Assemble SI section
*/
void mpegts_psi_section_reassemble
( mpegts_psi_section_t *ps, const uint8_t *tsb, int crc, int ccerr,
mpegts_psi_section_callback_t cb, void *opaque );
/* **************************************************************************
* Logical network
* *************************************************************************/
@ -283,7 +310,8 @@ struct mpegts_network
void (*mn_display_name) (mpegts_network_t*, char *buf, size_t len);
void (*mn_config_save) (mpegts_network_t*);
mpegts_mux_t* (*mn_create_mux)
(mpegts_mux_t*, uint16_t onid, uint16_t tsid, void *conf);
(mpegts_network_t*, void *origin, uint16_t onid, uint16_t tsid,
void *conf, int force);
mpegts_service_t* (*mn_create_service)
(mpegts_mux_t*, uint16_t sid, uint16_t pmt_pid);
const idclass_t* (*mn_mux_class) (mpegts_network_t*);
@ -293,13 +321,15 @@ struct mpegts_network
* Configuration
*/
uint16_t mn_nid;
uint16_t mn_satip_source;
int mn_autodiscovery;
int mn_skipinitscan;
char *mn_charset;
int mn_idlescan;
int mn_ignore_chnum;
int mn_autoclean_svc;
int mn_autoclean_mux;
int mn_sid_chnum;
int mn_localtime;
int mn_satpos;
};
typedef enum mpegts_mux_scan_state
@ -331,6 +361,19 @@ enum mpegts_mux_epg_flag
};
#define MM_EPG_LAST MM_EPG_ONLY_OPENTV_SKY_AUSAT
enum mpegts_mux_ac3_flag
{
MM_AC3_STANDARD,
MM_AC3_PMT_06,
MM_AC3_PMT_N05,
};
typedef struct tsdebug_packet {
TAILQ_ENTRY(tsdebug_packet) link;
uint8_t pkt[188];
off_t pos;
} tsdebug_packet_t;
/* Multiplex */
struct mpegts_mux
{
@ -345,12 +388,6 @@ struct mpegts_mux
uint16_t mm_onid;
uint16_t mm_tsid;
/*
* Versioning
*/
int64_t mm_created;
int64_t mm_updated;
/*
* Services
*/
@ -376,21 +413,31 @@ struct mpegts_mux
MM_ORIG_AUTO ///< From NIT
} mm_dmc_origin2;
#endif
mpegts_mux_t *mm_dmc_origin;
void *mm_dmc_origin;
time_t mm_dmc_origin_expire;
char *mm_fastscan_muxes;
/*
* Physical instances
*/
LIST_HEAD(, mpegts_mux_instance) mm_instances;
mpegts_mux_instance_t *mm_active;
mpegts_mux_instance_t *mm_active;
LIST_HEAD(,service) mm_transports;
/*
* Raw subscriptions
*/
LIST_HEAD(, th_subscription) mm_raw_subs;
/*
* Data processing
*/
RB_HEAD(, mpegts_pid) mm_pids;
LIST_HEAD(, mpegts_pid_sub) mm_all_subs;
int mm_last_pid;
mpegts_pid_t *mm_last_mp;
@ -414,9 +461,9 @@ struct mpegts_mux
void (*mm_config_save) (mpegts_mux_t *mm);
void (*mm_display_name) (mpegts_mux_t*, char *buf, size_t len);
int (*mm_is_enabled) (mpegts_mux_t *mm);
int (*mm_start) (mpegts_mux_t *mm, const char *r, int w, int flags);
void (*mm_stop) (mpegts_mux_t *mm, int force);
void (*mm_stop) (mpegts_mux_t *mm, int force, int reason);
void (*mm_open_table) (mpegts_mux_t*,mpegts_table_t*,int subscribe);
void (*mm_unsubscribe_table)(mpegts_mux_t*,mpegts_table_t*);
void (*mm_close_table) (mpegts_mux_t*,mpegts_table_t*);
void (*mm_create_instances) (mpegts_mux_t*);
int (*mm_is_epg) (mpegts_mux_t*);
@ -428,20 +475,48 @@ struct mpegts_mux
int mm_enabled;
int mm_epg;
char *mm_charset;
int mm_pmt_06_ac3;
int mm_pmt_ac3;
/*
* TSDEBUG
*/
#if ENABLE_TSDEBUG
int mm_tsdebug_fd;
int mm_tsdebug_fd2;
off_t mm_tsdebug_pos;
TAILQ_HEAD(, tsdebug_packet) mm_tsdebug_packets;
#endif
};
#define PREFCAPID_OFF 0
#define PREFCAPID_ON 1
#define PREFCAPID_FORCE 2
/* Service */
struct mpegts_service
{
service_t; // Parent
int (*s_update_pids)(mpegts_service_t *t, struct mpegts_apids *pids);
int (*s_link)(mpegts_service_t *master, mpegts_service_t *slave);
int (*s_unlink)(mpegts_service_t *master, mpegts_service_t *slave);
int s_dvb_subscription_flags;
mpegts_apids_t *s_pids;
LIST_HEAD(, mpegts_service) s_masters;
LIST_ENTRY(mpegts_service) s_masters_link;
LIST_HEAD(, mpegts_service) s_slaves;
LIST_ENTRY(mpegts_service) s_slaves_link;
mpegts_apids_t *s_slaves_pids;
/*
* Fields defined by DVB standard EN 300 468
*/
uint32_t s_dvb_channel_num;
uint16_t s_dvb_channel_minor;
uint8_t s_dvb_channel_dtag;
uint16_t s_dvb_service_id;
char *s_dvb_svcname;
char *s_dvb_provider;
@ -452,12 +527,9 @@ struct mpegts_service
uint16_t s_dvb_prefcapid;
int s_dvb_prefcapid_lock;
uint16_t s_dvb_forcecaid;
/*
* History
*/
int64_t s_dvb_created;
int64_t s_dvb_updated;
time_t s_dvb_created;
time_t s_dvb_last_seen;
time_t s_dvb_check_seen;
/*
* EIT/EPG control
@ -465,6 +537,7 @@ struct mpegts_service
int s_dvb_eit_enable;
uint64_t s_dvb_opentv_chnum;
uint16_t s_dvb_opentv_id;
/*
* Link to carrying multiplex and active adapter
@ -485,6 +558,7 @@ struct mpegts_service
* in order to recude load.
*/
sbuf_t s_tsbuf;
time_t s_tsbuf_last;
/**
* Average continuity errors
@ -511,10 +585,9 @@ struct mpegts_service
/* Physical mux instance */
struct mpegts_mux_instance
{
idnode_t mmi_id;
tvh_input_instance_t;
LIST_ENTRY(mpegts_mux_instance) mmi_mux_link;
LIST_ENTRY(mpegts_mux_instance) mmi_input_link;
LIST_ENTRY(mpegts_mux_instance) mmi_active_link;
streaming_pad_t mmi_streaming_pad;
@ -522,13 +595,7 @@ struct mpegts_mux_instance
mpegts_mux_t *mmi_mux;
mpegts_input_t *mmi_input;
LIST_HEAD(,th_subscription) mmi_subs;
tvh_input_stream_stats_t mmi_stats;
int mmi_tune_failed;
void (*mmi_delete) (mpegts_mux_instance_t *mmi);
};
struct mpegts_mux_sub
@ -557,11 +624,13 @@ struct mpegts_input
int mi_initscan;
int mi_idlescan;
char *mi_linked;
LIST_ENTRY(mpegts_input) mi_global_link;
mpegts_network_link_list_t mi_networks;
LIST_HEAD(,mpegts_mux_instance) mi_mux_instances;
LIST_HEAD(,tvh_input_instance) mi_mux_instances;
/*
@ -574,7 +643,6 @@ struct mpegts_input
*/
uint8_t mi_running; /* threads running */
uint8_t mi_live; /* stream is live */
time_t mi_last_dispatch;
/* Data input */
@ -591,7 +659,6 @@ struct mpegts_input
/* Active sources */
LIST_HEAD(,mpegts_mux_instance) mi_mux_active;
LIST_HEAD(,service) mi_transports;
/* Table processing */
pthread_t mi_table_tid;
@ -609,22 +676,22 @@ struct mpegts_input
int (*mi_is_enabled) (mpegts_input_t*, mpegts_mux_t *mm, int flags);
void (*mi_enabled_updated)(mpegts_input_t*);
void (*mi_display_name) (mpegts_input_t*, char *buf, size_t len);
int (*mi_is_free) (mpegts_input_t*);
int (*mi_get_weight) (mpegts_input_t*, int flags);
int (*mi_get_weight) (mpegts_input_t*, mpegts_mux_t *mm, int flags);
int (*mi_get_priority) (mpegts_input_t*, mpegts_mux_t *mm, int flags);
int (*mi_get_grace) (mpegts_input_t*, mpegts_mux_t *mm);
int (*mi_warm_mux) (mpegts_input_t*,mpegts_mux_instance_t*);
int (*mi_start_mux) (mpegts_input_t*,mpegts_mux_instance_t*);
void (*mi_stop_mux) (mpegts_input_t*,mpegts_mux_instance_t*);
void (*mi_open_service) (mpegts_input_t*,mpegts_service_t*,int first);
void (*mi_open_service) (mpegts_input_t*,mpegts_service_t*,int flags, int first);
void (*mi_close_service) (mpegts_input_t*,mpegts_service_t*);
mpegts_pid_t *(*mi_open_pid)(mpegts_input_t*,mpegts_mux_t*,int,int,void*);
void (*mi_close_pid) (mpegts_input_t*,mpegts_mux_t*,int,int,void*);
mpegts_pid_t *(*mi_open_pid)(mpegts_input_t*,mpegts_mux_t*,int,int,int,void*);
int (*mi_close_pid) (mpegts_input_t*,mpegts_mux_t*,int,int,int,void*);
void (*mi_create_mux_instance) (mpegts_input_t*,mpegts_mux_t*);
void (*mi_started_mux) (mpegts_input_t*,mpegts_mux_instance_t*);
void (*mi_stopping_mux) (mpegts_input_t*,mpegts_mux_instance_t*);
void (*mi_stopped_mux) (mpegts_input_t*,mpegts_mux_instance_t*);
int (*mi_has_subscription) (mpegts_input_t*, mpegts_mux_t *mm);
void (*mi_tuning_error) (mpegts_input_t*,mpegts_mux_t *);
idnode_set_t *(*mi_network_list) (mpegts_input_t*);
};
@ -672,7 +739,7 @@ int mpegts_input_set_networks ( mpegts_input_t *mi, htsmsg_t *msg );
int mpegts_input_add_network ( mpegts_input_t *mi, mpegts_network_t *mn );
void mpegts_input_open_service ( mpegts_input_t *mi, mpegts_service_t *s, int init );
void mpegts_input_open_service ( mpegts_input_t *mi, mpegts_service_t *s, int flags, int init );
void mpegts_input_close_service ( mpegts_input_t *mi, mpegts_service_t *s );
void mpegts_input_status_timer ( void *p );
@ -687,6 +754,8 @@ int mpegts_input_class_network_set ( void *o, const void *p );
htsmsg_t *mpegts_input_class_network_enum ( void *o );
char *mpegts_input_class_network_rend ( void *o );
int mpegts_mps_cmp( mpegts_pid_sub_t *a, mpegts_pid_sub_t *b );
void mpegts_network_register_builder
( const idclass_t *idc,
mpegts_network_t *(*build)(const idclass_t *idc, htsmsg_t *conf) );
@ -718,6 +787,7 @@ void mpegts_network_delete ( mpegts_network_t *mn, int delconf );
int mpegts_network_set_nid ( mpegts_network_t *mn, uint16_t nid );
int mpegts_network_set_network_name ( mpegts_network_t *mn, const char *name );
void mpegts_network_scan ( mpegts_network_t *mn );
mpegts_mux_t *mpegts_mux_create0
( mpegts_mux_t *mm, const idclass_t *class, const char *uuid,
@ -742,6 +812,8 @@ void mpegts_mux_delete ( mpegts_mux_t *mm, int delconf );
void mpegts_mux_save ( mpegts_mux_t *mm, htsmsg_t *c );
void mpegts_mux_tuning_error( const char *mux_uuid, mpegts_mux_instance_t *mmi_match );
mpegts_mux_instance_t *mpegts_mux_instance_create0
( mpegts_mux_instance_t *mmi, const idclass_t *class, const char *uuid,
mpegts_input_t *mi, mpegts_mux_t *mm );
@ -752,6 +824,9 @@ mpegts_service_t *mpegts_mux_find_service(mpegts_mux_t *ms, uint16_t sid);
(struct type*)mpegts_mux_instance_create0(calloc(1, sizeof(struct type)),\
&type##_class, uuid,\
mi, mm);
void mpegts_mux_instance_delete ( tvh_input_instance_t *tii );
int mpegts_mux_instance_start
( mpegts_mux_instance_t **mmiptr );
@ -762,12 +837,16 @@ int mpegts_mux_set_onid ( mpegts_mux_t *mm, uint16_t onid );
int mpegts_mux_set_crid_authority ( mpegts_mux_t *mm, const char *defauth );
void mpegts_mux_open_table ( mpegts_mux_t *mm, mpegts_table_t *mt, int subscribe );
void mpegts_mux_unsubscribe_table ( mpegts_mux_t *mm, mpegts_table_t *mt );
void mpegts_mux_close_table ( mpegts_mux_t *mm, mpegts_table_t *mt );
void mpegts_mux_remove_subscriber(mpegts_mux_t *mm, th_subscription_t *s, int reason);
int mpegts_mux_subscribe(mpegts_mux_t *mm, const char *name, int weight, int flags);
int mpegts_mux_subscribe(mpegts_mux_t *mm, mpegts_input_t *mi,
const char *name, int weight, int flags);
void mpegts_mux_unsubscribe_by_name(mpegts_mux_t *mm, const char *name);
void mpegts_mux_unsubscribe_linked(mpegts_input_t *mi);
void mpegts_mux_scan_done ( mpegts_mux_t *mm, const char *buf, int res );
void mpegts_mux_bouquet_rescan ( const char *src, const char *extra );
@ -794,9 +873,7 @@ void mpegts_input_recv_packets
(mpegts_input_t *mi, mpegts_mux_instance_t *mmi, sbuf_t *sb,
int64_t *pcr, uint16_t *pcr_pid);
int mpegts_input_is_free ( mpegts_input_t *mi );
int mpegts_input_get_weight ( mpegts_input_t *mi, int flags );
int mpegts_input_get_weight ( mpegts_input_t *mi, mpegts_mux_t *mm, int flags );
int mpegts_input_get_priority ( mpegts_input_t *mi, mpegts_mux_t *mm, int flags );
int mpegts_input_get_grace ( mpegts_input_t *mi, mpegts_mux_t *mm );
@ -805,10 +882,35 @@ void mpegts_input_save ( mpegts_input_t *mi, htsmsg_t *c );
void mpegts_input_flush_mux ( mpegts_input_t *mi, mpegts_mux_t *mm );
mpegts_pid_t * mpegts_input_open_pid
( mpegts_input_t *mi, mpegts_mux_t *mm, int pid, int type, void *owner );
( mpegts_input_t *mi, mpegts_mux_t *mm, int pid, int type, int weight, void *owner );
void mpegts_input_close_pid
( mpegts_input_t *mi, mpegts_mux_t *mm, int pid, int type, void *owner );
int mpegts_input_close_pid
( mpegts_input_t *mi, mpegts_mux_t *mm, int pid, int type, int weight, void *owner );
void mpegts_input_close_pids
( mpegts_input_t *mi, mpegts_mux_t *mm, void *owner, int all );
static inline void
tsdebug_write(mpegts_mux_t *mm, uint8_t *buf, size_t len)
{
#if ENABLE_TSDEBUG
if (mm->mm_tsdebug_fd2 >= 0)
if (write(mm->mm_tsdebug_fd2, buf, len) != len)
tvherror("tsdebug", "unable to write input data (%i)", errno);
#endif
}
static inline ssize_t
sbuf_tsdebug_read(mpegts_mux_t *mm, sbuf_t *sb, int fd)
{
#if ENABLE_TSDEBUG
ssize_t r = sbuf_read(sb, fd);
tsdebug_write(mm, sb->sb_data + sb->sb_ptr - r, r);
return r;
#else
return sbuf_read(sb, fd);
#endif
}
void mpegts_table_dispatch
(const uint8_t *sec, size_t r, void *mt);
@ -820,7 +922,7 @@ static inline void mpegts_table_grab
}
void mpegts_table_release_
(mpegts_table_t *mt);
static inline void mpegts_table_release
static inline int mpegts_table_release
(mpegts_table_t *mt)
{
int v = atomic_dec(&mt->mt_arefcount, 1);
@ -828,20 +930,51 @@ static inline void mpegts_table_release
if (v == 1) {
assert(mt->mt_destroyed == 1);
mpegts_table_release_(mt);
return 1;
}
return 0;
}
int mpegts_table_type
( mpegts_table_t *mt );
mpegts_table_t *mpegts_table_add
(mpegts_mux_t *mm, int tableid, int mask,
mpegts_table_callback_t callback, void *opaque,
const char *name, int flags, int pid);
const char *name, int flags, int pid, int weight);
void mpegts_table_flush_all
(mpegts_mux_t *mm);
void mpegts_table_destroy ( mpegts_table_t *mt );
void mpegts_table_consistency_check( mpegts_mux_t *mm );
void dvb_bat_destroy
(struct mpegts_table *mt);
int dvb_pat_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
int dvb_cat_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
int dvb_pmt_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tabelid);
int dvb_nit_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
int dvb_bat_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
int dvb_fs_sdt_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
int dvb_sdt_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
int dvb_tdt_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
int dvb_tot_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
int atsc_vct_callback
(struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
void psi_tables_default ( struct mpegts_mux *mm );
void psi_tables_dvb ( struct mpegts_mux *mm );
void psi_tables_atsc_t ( struct mpegts_mux *mm );
void psi_tables_atsc_c ( struct mpegts_mux *mm );
mpegts_service_t *mpegts_service_create0
( mpegts_service_t *ms, const idclass_t *class, const char *uuid,
mpegts_mux_t *mm, uint16_t sid, uint16_t pmt_pid, htsmsg_t *conf );
@ -854,14 +987,20 @@ mpegts_service_t *mpegts_service_create0
mpegts_service_create0(calloc(1, sizeof(mpegts_service_t)),\
&mpegts_service_class, u, m, s, p, c)
mpegts_service_t *mpegts_service_create_raw(mpegts_mux_t *mm);
mpegts_service_t *mpegts_service_find
( mpegts_mux_t *mm, uint16_t sid, uint16_t pmt_pid, int create, int *save );
mpegts_service_t *
mpegts_service_find_by_pid ( mpegts_mux_t *mm, int pid );
static inline mpegts_service_t *mpegts_service_find_by_uuid(const char *uuid)
{ return idnode_find(uuid, &mpegts_service_class, NULL); }
void mpegts_service_delete ( service_t *s, int delconf );
/*
* MPEG-TS event handler
*/
@ -871,7 +1010,7 @@ typedef struct mpegts_listener
LIST_ENTRY(mpegts_listener) ml_link;
void *ml_opaque;
void (*ml_mux_start) (mpegts_mux_t *mm, void *p);
void (*ml_mux_stop) (mpegts_mux_t *mm, void *p);
void (*ml_mux_stop) (mpegts_mux_t *mm, void *p, int reason);
void (*ml_mux_create) (mpegts_mux_t *mm, void *p);
void (*ml_mux_delete) (mpegts_mux_t *mm, void *p);
} mpegts_listener_t;
@ -891,6 +1030,13 @@ LIST_HEAD(,mpegts_listener) mpegts_listeners;
if (ml->op) ml->op(t, ml->ml_opaque);\
} (void)0
#define mpegts_fire_event1(t, op, arg1)\
{\
mpegts_listener_t *ml;\
LIST_FOREACH(ml, &mpegts_listeners, ml_link)\
if (ml->op) ml->op(t, ml->ml_opaque, arg1);\
} (void)0
#endif /* __TVH_MPEGTS_H__ */
/******************************************************************************

Some files were not shown because too many files have changed in this diff Show more