Compare commits
692 commits
feature/se
...
master
Author | SHA1 | Date | |
---|---|---|---|
b82ae7239a | |||
![]() |
b6095136b3 | ||
![]() |
fff2c36063 | ||
![]() |
130f413ed8 | ||
![]() |
eb8aa7f00d | ||
![]() |
70bd3350b1 | ||
![]() |
92736802f6 | ||
![]() |
8d8374b430 | ||
![]() |
41bb5cdd99 | ||
![]() |
8dbc38df4a | ||
![]() |
fd16a1e3ef | ||
![]() |
a00a3da84d | ||
![]() |
5971f7b84b | ||
![]() |
e0630fa458 | ||
![]() |
1c10cd6fe9 | ||
![]() |
4d7d61e39a | ||
![]() |
02948826e9 | ||
![]() |
86b3650e42 | ||
![]() |
01406e0e5f | ||
![]() |
cb6e8aed90 | ||
![]() |
2bcf09f2ab | ||
![]() |
09265f0205 | ||
![]() |
ed3a02ec14 | ||
![]() |
8ae5b7fce4 | ||
![]() |
aaaa7a1cfd | ||
![]() |
bd219954c1 | ||
![]() |
97229aa881 | ||
![]() |
5ba809b6bb | ||
![]() |
c79a41dbb2 | ||
![]() |
cb98d6a94d | ||
![]() |
08c4c75364 | ||
![]() |
2623cc52d9 | ||
![]() |
4fce256468 | ||
![]() |
db3101400d | ||
![]() |
3049486768 | ||
![]() |
d1e6a61a29 | ||
![]() |
2f2d7cef42 | ||
![]() |
3f4c630ecc | ||
![]() |
0e4a41168f | ||
![]() |
dc311fd3f8 | ||
![]() |
209d02282b | ||
![]() |
03f044a72b | ||
![]() |
4112e4b30b | ||
![]() |
79e5ff8d4b | ||
![]() |
99920b83e3 | ||
![]() |
d4f1c108fe | ||
![]() |
536736daaa | ||
![]() |
2b00b88784 | ||
![]() |
c4d8b7a3fe | ||
![]() |
c649ef2bdf | ||
![]() |
84ee61634d | ||
![]() |
3e56c175e3 | ||
![]() |
706beee703 | ||
![]() |
8cb63d919b | ||
![]() |
98464be592 | ||
![]() |
926e04eacc | ||
![]() |
9d042c2418 | ||
![]() |
9270518314 | ||
![]() |
d4160380a2 | ||
![]() |
6a110d871b | ||
![]() |
89bb07fe82 | ||
![]() |
f29a293e37 | ||
![]() |
a1844b88af | ||
![]() |
91ad9832ff | ||
![]() |
ef7946f5ee | ||
![]() |
bfec4e7f9c | ||
![]() |
7749976fed | ||
![]() |
60338204e1 | ||
![]() |
1b2e2c8482 | ||
![]() |
a7cb06cd6b | ||
![]() |
0af90088e3 | ||
![]() |
1ca7ed5e89 | ||
![]() |
6b472cd999 | ||
![]() |
caa6f8ed26 | ||
![]() |
bda5d8ce25 | ||
![]() |
abb14095cb | ||
![]() |
f3582868d9 | ||
![]() |
b1b9b42685 | ||
![]() |
4e1e909946 | ||
![]() |
ce3a9a1347 | ||
![]() |
e78b3f4e27 | ||
![]() |
01a095ceb5 | ||
![]() |
f2af011618 | ||
![]() |
e3debcd435 | ||
![]() |
310a77ce93 | ||
![]() |
203b48ed94 | ||
![]() |
903ca33ed2 | ||
![]() |
2221286368 | ||
![]() |
04e5a9d26f | ||
![]() |
c8ae8a9cb0 | ||
![]() |
daaebf916a | ||
![]() |
6751ade06e | ||
![]() |
14f03b8efb | ||
![]() |
02427fe00c | ||
![]() |
098213f545 | ||
![]() |
d000264e28 | ||
![]() |
fdbcbcf015 | ||
![]() |
ecdf96828d | ||
![]() |
ad5d5690d1 | ||
![]() |
79ec2df4dd | ||
![]() |
2a792c4e4c | ||
![]() |
e138492f5e | ||
![]() |
995aaf895b | ||
![]() |
d58afc0415 | ||
![]() |
c7a87457cf | ||
![]() |
2518d4f821 | ||
![]() |
e4f034aed3 | ||
![]() |
1c885f100d | ||
![]() |
d63c36a36e | ||
![]() |
ffe92dfed1 | ||
![]() |
b988813057 | ||
![]() |
08c4af7e6a | ||
![]() |
fabd01191d | ||
![]() |
ad4dd4fa50 | ||
![]() |
9a8242a082 | ||
![]() |
4eeecb2a0f | ||
![]() |
3b9253679b | ||
![]() |
f6d9813ff5 | ||
![]() |
3e4c8e7314 | ||
![]() |
e4cdd3ca0f | ||
![]() |
722c9250ac | ||
![]() |
169b71bb72 | ||
![]() |
1f79d9dd5f | ||
![]() |
b427d7ecba | ||
![]() |
6bc1a6fe74 | ||
![]() |
0d4f44385c | ||
![]() |
1ccdcd33a2 | ||
![]() |
266b28cfd1 | ||
![]() |
baab7707a2 | ||
![]() |
3fab60a1b6 | ||
![]() |
04f0ef966d | ||
![]() |
41431f71d8 | ||
![]() |
9ee27b671b | ||
![]() |
d5c5f67bfe | ||
![]() |
75cad931c4 | ||
![]() |
cc58797fb6 | ||
![]() |
4f2b94f281 | ||
![]() |
a1fd6adaaf | ||
![]() |
b941aedf93 | ||
![]() |
27382c275f | ||
![]() |
3332f8614b | ||
![]() |
8974fac450 | ||
![]() |
55281a87ac | ||
![]() |
6cd5f1b379 | ||
![]() |
dddc4249ef | ||
![]() |
3f4002d984 | ||
![]() |
5518397784 | ||
![]() |
fc68ad0a74 | ||
![]() |
b520cbeb34 | ||
![]() |
6547bbe2e0 | ||
![]() |
30f5c44d2c | ||
![]() |
1063fbf472 | ||
![]() |
a139c2545c | ||
![]() |
47f249fe09 | ||
![]() |
02a9b02e1c | ||
![]() |
c54f9b0ca7 | ||
![]() |
0c6cce080a | ||
![]() |
db5cf2adff | ||
![]() |
c308e1705e | ||
![]() |
d8e7bafc3b | ||
![]() |
4db85f0396 | ||
![]() |
34ed9da0e6 | ||
![]() |
3ed12146c9 | ||
![]() |
e5dc86b3dd | ||
![]() |
0b548808a7 | ||
![]() |
023563f602 | ||
![]() |
a7445b1f2d | ||
![]() |
e247500a08 | ||
![]() |
93f3f6fbdd | ||
![]() |
c61be4ed90 | ||
![]() |
5e7efab324 | ||
![]() |
ac3599bc79 | ||
![]() |
7414ebafe4 | ||
![]() |
72835054ce | ||
![]() |
bb5fe1145d | ||
![]() |
db52de8151 | ||
![]() |
fbd41d806b | ||
![]() |
35029f1e51 | ||
![]() |
9c8dde32f5 | ||
![]() |
f72c67b629 | ||
![]() |
953dfa0f3f | ||
![]() |
196462292e | ||
![]() |
819fc6766b | ||
![]() |
fb7ffa7a2c | ||
![]() |
a08311dff9 | ||
![]() |
fe8e0b0c16 | ||
![]() |
8480557f76 | ||
![]() |
5c5950b698 | ||
![]() |
3dfc1eba78 | ||
![]() |
28e8db1dc2 | ||
![]() |
de0d8d379f | ||
![]() |
ecf91c3866 | ||
![]() |
e1a14dff0e | ||
![]() |
e424493113 | ||
![]() |
2665e13507 | ||
![]() |
fd24aa9339 | ||
![]() |
e16a5ca3d1 | ||
![]() |
74adbe8f7d | ||
![]() |
e1b2da8e74 | ||
![]() |
6601ca39f9 | ||
![]() |
a698e088e8 | ||
![]() |
c4adde611f | ||
![]() |
ce01edeb8c | ||
![]() |
0138a37aee | ||
![]() |
385c4167c0 | ||
![]() |
2d703fbe42 | ||
![]() |
19e1ae1451 | ||
![]() |
0eb8587510 | ||
![]() |
c66213c745 | ||
![]() |
be3749f45c | ||
![]() |
7cafa5ef70 | ||
![]() |
8b7c67abec | ||
![]() |
cd99597a5c | ||
![]() |
ffa0639149 | ||
![]() |
9dbaa441f3 | ||
![]() |
3c889e15a9 | ||
![]() |
38b541e420 | ||
![]() |
20d521975e | ||
![]() |
6753203c65 | ||
![]() |
a4c2cc2a0f | ||
![]() |
514adb06dd | ||
![]() |
af7e0a385d | ||
![]() |
2dfc2710b7 | ||
![]() |
2b6dd0f73d | ||
![]() |
1ef7dae510 | ||
![]() |
29308193bd | ||
![]() |
787ba1f1d6 | ||
![]() |
9a6d0961e4 | ||
![]() |
c522d71ed0 | ||
![]() |
afede2267f | ||
![]() |
fd505efe5a | ||
![]() |
7bb903a397 | ||
![]() |
ca0a703779 | ||
![]() |
d90bc3ebd3 | ||
![]() |
cfbe927b43 | ||
![]() |
cbf1b15d00 | ||
![]() |
1c9b527011 | ||
![]() |
a87aae0aaf | ||
![]() |
da6eb2c4b3 | ||
![]() |
78e4ddf86e | ||
![]() |
83f94f6f0c | ||
![]() |
56ac461d06 | ||
![]() |
be1cc5c97c | ||
![]() |
d563dc9127 | ||
![]() |
fd1bc1c5f7 | ||
![]() |
1c3f608194 | ||
![]() |
b306c7c9d5 | ||
![]() |
3c95a84d81 | ||
![]() |
ea60066a3f | ||
![]() |
3e33da55c9 | ||
![]() |
53984a38a4 | ||
![]() |
af698f0c19 | ||
![]() |
2904a96ea1 | ||
![]() |
97f52f6d20 | ||
![]() |
c19217e6e3 | ||
![]() |
434fc5de6a | ||
![]() |
96898a66e9 | ||
![]() |
bd06d3c79a | ||
![]() |
ef799f9c24 | ||
![]() |
2b22bc75a1 | ||
![]() |
ce78f14570 | ||
![]() |
cd4da6aa36 | ||
![]() |
81ae6bf0cd | ||
![]() |
a69ee153d0 | ||
![]() |
e797e46129 | ||
![]() |
6acd4b60a3 | ||
![]() |
e91074c812 | ||
![]() |
50016a15f2 | ||
![]() |
1d8b5411df | ||
![]() |
19422a4d29 | ||
![]() |
ab258685f4 | ||
![]() |
a2491ffcd7 | ||
![]() |
a50f74c9aa | ||
![]() |
ba7ec12f91 | ||
![]() |
2ba84e8a82 | ||
![]() |
1bd1564a8c | ||
![]() |
051412d7e0 | ||
![]() |
2f830922f5 | ||
![]() |
6327ca671e | ||
![]() |
f2cc2bc866 | ||
![]() |
b826a7785e | ||
![]() |
496e4b1f00 | ||
![]() |
51e9aec9a0 | ||
![]() |
379672a6fb | ||
![]() |
54533b30fc | ||
![]() |
d386ed020a | ||
![]() |
fafec1e053 | ||
![]() |
538e7acb56 | ||
![]() |
f6537ab39f | ||
![]() |
89a5ca4914 | ||
![]() |
cc1fbb0202 | ||
![]() |
cd1f081909 | ||
![]() |
c475b2531b | ||
![]() |
4cfbb2e024 | ||
![]() |
77d947fe57 | ||
![]() |
4a9dfec70f | ||
![]() |
f578c96949 | ||
![]() |
a6ac407dbf | ||
![]() |
3396fe7a4b | ||
![]() |
2ebbd5f42a | ||
![]() |
9fc758f946 | ||
![]() |
9ce2fa7885 | ||
![]() |
6bb5e0f7f4 | ||
![]() |
ac34185e67 | ||
![]() |
36283c6598 | ||
![]() |
53f8b5068e | ||
![]() |
31cd397f3c | ||
![]() |
7ab46e3d5d | ||
![]() |
d09e0c9fa8 | ||
![]() |
2726332724 | ||
![]() |
399aa99e89 | ||
![]() |
aac2d0774f | ||
![]() |
2796c69750 | ||
![]() |
4050f98ef1 | ||
![]() |
83d05d49d4 | ||
![]() |
1fca590172 | ||
![]() |
7a2560e642 | ||
![]() |
78b5fb6dce | ||
![]() |
9a265d503d | ||
![]() |
18c9d1dcc2 | ||
![]() |
e92996132b | ||
![]() |
412df6d098 | ||
![]() |
0bc1987d1e | ||
![]() |
d2acb64025 | ||
![]() |
facee6f9e5 | ||
![]() |
4b05735b87 | ||
![]() |
a650b2ea79 | ||
![]() |
3a92f1d2f8 | ||
![]() |
4835c6c53e | ||
![]() |
a39bb434f4 | ||
![]() |
ce2fc637b6 | ||
![]() |
362fb68002 | ||
![]() |
bdfc4d779f | ||
![]() |
e2f9483dbe | ||
![]() |
94880c5b3e | ||
![]() |
30d6fa5300 | ||
![]() |
adcd748fc0 | ||
![]() |
5eca4d7be8 | ||
![]() |
e6d124d110 | ||
![]() |
927ebf2f0f | ||
![]() |
ebdd85f5ba | ||
![]() |
e95e23ab2f | ||
![]() |
2f13317fa0 | ||
![]() |
5a063fe71d | ||
![]() |
98783a5c9e | ||
![]() |
c664af6f60 | ||
![]() |
3c0a279825 | ||
![]() |
b98e688f57 | ||
![]() |
0515caa92f | ||
![]() |
62e2971020 | ||
![]() |
ee66809d18 | ||
![]() |
879b842ea1 | ||
![]() |
f39ddd1f05 | ||
![]() |
22554c723d | ||
![]() |
cde875ecb2 | ||
![]() |
c3ec4b114d | ||
![]() |
2b8ad616a4 | ||
![]() |
1a9319571b | ||
![]() |
a7d26442b1 | ||
![]() |
078dff5b98 | ||
![]() |
d83aefe0fe | ||
![]() |
44a1df1fac | ||
![]() |
28337dcbbe | ||
![]() |
6a27c4ccab | ||
![]() |
3feccf34b5 | ||
![]() |
9508a68aa1 | ||
![]() |
b1883ed51e | ||
![]() |
bca6214866 | ||
![]() |
dcdef424fc | ||
![]() |
f88f2f33ff | ||
![]() |
519ec9677e | ||
![]() |
3077ffa4d3 | ||
![]() |
1377fad8a0 | ||
![]() |
8174eb953d | ||
![]() |
abea248bab | ||
![]() |
8018b54d6b | ||
![]() |
b28f37bf8a | ||
![]() |
e565e7cbf0 | ||
![]() |
3ddf768edf | ||
![]() |
fc7d3d8484 | ||
![]() |
a8a175136d | ||
![]() |
aca5c6b976 | ||
![]() |
bc9874cc26 | ||
![]() |
ca0021e3b6 | ||
![]() |
b3db352ecf | ||
![]() |
9801ef1a1a | ||
![]() |
a2c01ed520 | ||
![]() |
ba7d8fa23a | ||
![]() |
c92ccca440 | ||
![]() |
941dbcbd74 | ||
![]() |
f8b3bf40ec | ||
![]() |
ac92591ba1 | ||
![]() |
7006b9fd88 | ||
![]() |
60f959ba59 | ||
![]() |
3dba7bf3ac | ||
![]() |
dafea1a693 | ||
![]() |
f157e1818d | ||
![]() |
4df20c2b6a | ||
![]() |
93aac31870 | ||
![]() |
273835ff46 | ||
![]() |
37eb9d2cfd | ||
![]() |
ed513c0d6f | ||
![]() |
a65f6179e5 | ||
![]() |
fc57f52aeb | ||
![]() |
2820a7cf41 | ||
![]() |
65fd96395a | ||
![]() |
f48e390437 | ||
![]() |
d9f4230881 | ||
![]() |
30cc0a73bd | ||
![]() |
eb5cfaa6e9 | ||
![]() |
04fcdce37d | ||
![]() |
6f6f20eac0 | ||
![]() |
4949382c87 | ||
![]() |
0d5194c17c | ||
![]() |
b497168ab4 | ||
![]() |
b2132d72c2 | ||
![]() |
baec61b674 | ||
![]() |
7b01d9c1f0 | ||
![]() |
ad8b61ba5a | ||
![]() |
619654f1f4 | ||
![]() |
ef9556efff | ||
![]() |
d9b781771d | ||
![]() |
0818adf576 | ||
![]() |
1723e733f5 | ||
![]() |
e9eadac057 | ||
![]() |
26b18fe141 | ||
![]() |
33f72a3483 | ||
![]() |
940ab268e5 | ||
![]() |
45eec90f11 | ||
![]() |
0a7ce2caf5 | ||
![]() |
98b5304be6 | ||
![]() |
0196790734 | ||
![]() |
028826b7df | ||
![]() |
76558d286a | ||
![]() |
bfa463df8b | ||
![]() |
eed4d8cdc5 | ||
![]() |
f5c8e4ce86 | ||
![]() |
396700797b | ||
![]() |
f1631752c4 | ||
![]() |
022e8d45a5 | ||
![]() |
f8de301910 | ||
![]() |
6f18074477 | ||
![]() |
c51b16224f | ||
![]() |
09bd4c4d0e | ||
![]() |
ba36e4584d | ||
![]() |
0872d71c12 | ||
![]() |
900b20e280 | ||
![]() |
1afee1663a | ||
![]() |
1027f294d0 | ||
![]() |
6f8b1c5ad6 | ||
![]() |
b85ec59d46 | ||
![]() |
93e533ffad | ||
![]() |
60d41123b5 | ||
![]() |
6605d5ec02 | ||
![]() |
c43c607d5a | ||
![]() |
9697eab66c | ||
![]() |
a7704655f8 | ||
![]() |
3af9d86ef8 | ||
![]() |
24d24a8107 | ||
![]() |
306f2520a6 | ||
![]() |
6c8b7d6e48 | ||
![]() |
75ffd0cc5a | ||
![]() |
dbd3a1237e | ||
![]() |
b305d98315 | ||
![]() |
294afe51b1 | ||
![]() |
c051d0bda5 | ||
![]() |
86f10581d5 | ||
![]() |
a890c6bf4b | ||
![]() |
c4089cc7a3 | ||
![]() |
fdbc68abf2 | ||
![]() |
4dbc7d94ac | ||
![]() |
cb5aed875d | ||
![]() |
4dcdbef28e | ||
![]() |
2635a3d1bf | ||
![]() |
1f62c46d72 | ||
![]() |
9aa0600b81 | ||
![]() |
2dcf53f71e | ||
![]() |
c1ed929c31 | ||
![]() |
cde79eb75f | ||
![]() |
75fb7a929d | ||
![]() |
e2bd233a4f | ||
![]() |
56d8c8fc2a | ||
![]() |
0324cf8c8b | ||
![]() |
03b88cefc0 | ||
![]() |
e07ee906cc | ||
![]() |
159efe7411 | ||
![]() |
74090c626a | ||
![]() |
b47a14bd8b | ||
![]() |
74c563ffdf | ||
![]() |
8df348fbc3 | ||
![]() |
5dade4fd37 | ||
![]() |
31b47ffe00 | ||
![]() |
0748af88cf | ||
![]() |
d5ce076150 | ||
![]() |
9a5b4db242 | ||
![]() |
c040ab0542 | ||
![]() |
c6793b3bab | ||
![]() |
3c5d40916d | ||
![]() |
eae66e1424 | ||
![]() |
ba4e998cf4 | ||
![]() |
5a9b31e7da | ||
![]() |
a67c6bbc1b | ||
![]() |
75084b79e6 | ||
![]() |
7a16e29293 | ||
![]() |
c8885ce5cb | ||
![]() |
8db7c13281 | ||
![]() |
066b273387 | ||
![]() |
932ade39e8 | ||
![]() |
abea3ddee0 | ||
![]() |
34be15898f | ||
![]() |
cfa7cd0577 | ||
![]() |
f39f7b87ab | ||
![]() |
7a707122e9 | ||
![]() |
32ad45b47c | ||
![]() |
d02f23302b | ||
![]() |
3184c8b01d | ||
![]() |
c7be037d0c | ||
![]() |
774323d002 | ||
![]() |
5d7039c6da | ||
![]() |
6de939111d | ||
![]() |
a7754e8009 | ||
![]() |
1827ef1347 | ||
![]() |
cfc78aba5f | ||
![]() |
080dfb4ef9 | ||
![]() |
948de597c3 | ||
![]() |
85a5d7c4d2 | ||
![]() |
c08adfdaef | ||
![]() |
82cb4f7f42 | ||
![]() |
70c65a3fba | ||
![]() |
1145484e9f | ||
![]() |
f1fda287d8 | ||
![]() |
d9bb16eea6 | ||
![]() |
920cb3582c | ||
![]() |
40a2e93c8a | ||
![]() |
49e22d400a | ||
![]() |
32540dd18b | ||
![]() |
d4fed8839b | ||
![]() |
4fadb70767 | ||
![]() |
811eeb4376 | ||
![]() |
70a17dfb42 | ||
![]() |
450c2be234 | ||
![]() |
5ba47ff711 | ||
![]() |
c4ce18c47d | ||
![]() |
3a6088cfd9 | ||
![]() |
388f57ecc3 | ||
![]() |
27e3b34d8c | ||
![]() |
98ed29fb67 | ||
![]() |
ad16b3dbe7 | ||
![]() |
b0b48bdf85 | ||
![]() |
39d985be1c | ||
![]() |
19dbe37cc0 | ||
![]() |
9ce0c5542d | ||
![]() |
129bb84bf6 | ||
![]() |
9469ef0ddf | ||
![]() |
c6d2a0912f | ||
![]() |
a609cd2edb | ||
![]() |
5ec120c378 | ||
![]() |
d6b59b9ee3 | ||
![]() |
07a8f5ff06 | ||
![]() |
d760c4cb7c | ||
![]() |
0b416b3e29 | ||
![]() |
d18fc1a422 | ||
![]() |
f98f580ee0 | ||
![]() |
b0e030b326 | ||
![]() |
67938bc9d9 | ||
![]() |
3535d7d22e | ||
![]() |
0e8647a534 | ||
![]() |
8668734eff | ||
![]() |
2b2cb3236e | ||
![]() |
b87eb898c8 | ||
![]() |
b659bce22d | ||
![]() |
75e4e787ed | ||
![]() |
d254f520a6 | ||
![]() |
5ba389bd86 | ||
![]() |
da2ae270f5 | ||
![]() |
2d7cda7eef | ||
![]() |
f20bab4aed | ||
![]() |
a9d3e4a7d9 | ||
![]() |
5339e1a956 | ||
![]() |
c65dfa1884 | ||
![]() |
6391e7b6c5 | ||
![]() |
89baf880d6 | ||
![]() |
95ef1c4c0b | ||
![]() |
4fcc326b98 | ||
![]() |
64eec14399 | ||
![]() |
360dd56661 | ||
![]() |
5caf1202fa | ||
![]() |
a95ef0b237 | ||
![]() |
4e42e041c5 | ||
![]() |
7db9f44a30 | ||
![]() |
638b870a19 | ||
![]() |
1b193a3f7d | ||
![]() |
6967a9a281 | ||
![]() |
3e505d77d9 | ||
![]() |
70b3ef658b | ||
![]() |
bbed9a309e | ||
![]() |
f63d16f605 | ||
![]() |
6b152b942d | ||
![]() |
bb31907761 | ||
![]() |
bd20a5393c | ||
![]() |
8c743ba44b | ||
![]() |
abfae4c9f8 | ||
![]() |
01e7cc1478 | ||
![]() |
1d9a3483a5 | ||
![]() |
8265dbd902 | ||
![]() |
9778bca323 | ||
![]() |
452ca85ff6 | ||
![]() |
1f1b36a9e2 | ||
![]() |
2ae32e61af | ||
![]() |
cf827862dc | ||
![]() |
ea7c5b5056 | ||
![]() |
36a06a66e0 | ||
![]() |
c2c5b81330 | ||
![]() |
60574bf3e4 | ||
![]() |
39a4db4994 | ||
![]() |
3e4c0a39d2 | ||
![]() |
3579d04f48 | ||
![]() |
fbe1f1f302 | ||
![]() |
448b0a4acd | ||
![]() |
4d6c1bba0e | ||
![]() |
bb07ead99b | ||
![]() |
d37a0c6df4 | ||
![]() |
f4d0859337 | ||
![]() |
fe24184ded | ||
![]() |
d6bfd1ffe6 | ||
![]() |
25e48734c9 | ||
![]() |
3539dbf13d | ||
![]() |
fe995bda63 | ||
![]() |
d1bdce093d | ||
![]() |
664d24308d | ||
![]() |
085fd89465 | ||
![]() |
3ba0ae6e62 | ||
![]() |
44633b8f74 | ||
![]() |
e874526824 | ||
![]() |
3081776240 | ||
![]() |
1a622c1b67 | ||
![]() |
ea23369c5d | ||
![]() |
da3d9ecb48 | ||
![]() |
00f3cc0e8a | ||
![]() |
701e6b373d | ||
![]() |
ba4bb99489 | ||
![]() |
daae05eb75 | ||
![]() |
3ba5bb9d0b | ||
![]() |
6bcecff056 | ||
![]() |
a670ec1eee | ||
![]() |
a6420f9713 | ||
![]() |
d0a6684f40 | ||
![]() |
898f88a098 | ||
![]() |
98854eaacd | ||
![]() |
68b07236d0 | ||
![]() |
f7d356cff2 | ||
![]() |
1ddd261627 | ||
![]() |
dbd31acc3c | ||
![]() |
c7d08fb316 | ||
![]() |
bf1c902fbf | ||
![]() |
b70fa32a71 | ||
![]() |
207fcbfb10 | ||
![]() |
a0a26513ff | ||
![]() |
f34f872cb9 | ||
![]() |
b0e31dd880 | ||
![]() |
6ddbd16704 | ||
![]() |
f15b8a6978 | ||
![]() |
e59a8b41e7 | ||
![]() |
a2dfb056ea | ||
![]() |
8aee57930d | ||
![]() |
33792bf601 | ||
![]() |
7743670045 | ||
![]() |
5df9cdf97d | ||
![]() |
4f68d03875 | ||
![]() |
fa22f18dfc | ||
![]() |
a0e69f8c7e | ||
![]() |
d49e965738 | ||
![]() |
46f7e41e62 | ||
![]() |
ccf64a0c57 | ||
![]() |
c119b442dd | ||
![]() |
a2d7e1797c | ||
![]() |
4290f79748 | ||
![]() |
fc7e0f4a36 | ||
![]() |
29ccb30ff6 | ||
![]() |
2cee0bfeb6 | ||
![]() |
cee7147eef | ||
![]() |
9cabe1ad55 | ||
![]() |
7c1ff4740b | ||
![]() |
c4f30d1e51 | ||
![]() |
7548fc5c3e | ||
![]() |
a563d786e9 | ||
![]() |
3f7ac5b5e0 | ||
![]() |
0d10db2514 | ||
![]() |
003c4a2699 | ||
![]() |
b2290d30a8 | ||
![]() |
0bb2356a5f | ||
![]() |
114298ad15 |
240 changed files with 22108 additions and 6472 deletions
|
@ -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}"
|
||||
]
|
||||
}
|
||||
|
|
35
Makefile
35
Makefile
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
14
README.md
14
README.md
|
@ -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
84
configure
vendored
|
@ -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
|
||||
|
|
2050
data/conf/epggrab/opentv/dict/skynz
Normal file
2050
data/conf/epggrab/opentv/dict/skynz
Normal file
File diff suppressed because it is too large
Load diff
257
data/conf/epggrab/opentv/genre/skynz
Normal file
257
data/conf/epggrab/opentv/genre/skynz
Normal 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
|
||||
]
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
"nid": 4096,
|
||||
"tsid": 17,
|
||||
"sid": 17008,
|
||||
"bouquetid": 0,
|
||||
"channel" : [
|
||||
17
|
||||
],
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
"nid": 64511,
|
||||
"tsid": 5800,
|
||||
"sid": 3635,
|
||||
"bouquetid": 0,
|
||||
"channel" : [
|
||||
17
|
||||
],
|
||||
|
|
24
data/conf/epggrab/opentv/prov/skynz
Normal file
24
data/conf/epggrab/opentv/prov/skynz
Normal 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]+)\\)"
|
||||
]
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
"nid": 2,
|
||||
"tsid": 2004,
|
||||
"sid": 4152,
|
||||
"bouquetid": 4101,
|
||||
"channel" : [
|
||||
17
|
||||
],
|
||||
|
|
|
@ -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
638
data/conf/satellites
Normal 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 & DirecTV 10/12",
|
||||
"pos": -1030
|
||||
},
|
||||
{
|
||||
"name": "DirecTV 4S/8/SES 1",
|
||||
"pos": -1010
|
||||
},
|
||||
{
|
||||
"name": "DirecTV 14/Galaxy 16/Spaceway 2 & 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
|
||||
}
|
||||
]
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>IP Server</b>
|
||||
<hr>
|
||||
|
||||
<dd>SAT>IP Server is something like DVB network tuner. TVHeadend can
|
||||
forward mpegts input streams including on-the-fly descramling to SAT>IP
|
||||
clients.</dd>
|
||||
|
||||
<dd>Only networks with the "SAT>IP Source" field set are exported
|
||||
through the SAT>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>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>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>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>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>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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>IP Source Number</b>
|
||||
<dd>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>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>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).
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
41
docs/html/config_streamprofile.html
Normal file
41
docs/html/config_streamprofile.html
Normal 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>
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
~
|
||||
|
|
119
src/access.c
119
src/access.c
|
@ -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);
|
||||
|
|
31
src/access.h
31
src/access.h
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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 },
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -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); }
|
||||
|
|
164
src/channels.c
164
src/channels.c
|
@ -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",
|
||||
|
|
|
@ -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 );
|
||||
|
|
200
src/config.c
200
src/config.c
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
5
src/descrambler.h
Executable file → Normal 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 );
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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
157
src/descrambler/cwc.c
Executable file → Normal 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
211
src/descrambler/descrambler.c
Executable file → Normal 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
215
src/descrambler/dvbcam.c
Normal 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
37
src/descrambler/dvbcam.h
Normal 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
0
src/descrambler/libaesdec/libaesdec.c
Executable file → Normal file
0
src/descrambler/libaesdec/libaesdec.h
Executable file → Normal file
0
src/descrambler/libaesdec/libaesdec.h
Executable file → Normal file
169
src/descrambler/tsdebugcw.c
Normal file
169
src/descrambler/tsdebugcw.c
Normal 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);
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -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__ */
|
||||
|
|
114
src/dvr/dvr.h
114
src/dvr/dvr.h
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
{}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
450
src/dvr/dvr_db.c
450
src/dvr/dvr_db.c
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
69
src/epg.c
69
src/epg.c
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
/* ************************************************************************
|
||||
|
|
88
src/epgdb.c
88
src/epgdb.c
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, §, &last, &ver);
|
||||
r = dvb_table_begin((mpegts_psi_table_t *)mt, ptr, len,
|
||||
tableid, extraid, 11, &st, §, &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;
|
||||
|
|
|
@ -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, §, &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);
|
||||
|
|
|
@ -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
26
src/epggrab/module/xmltv.c
Executable file → Normal 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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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,
|
||||
|
|
73
src/file.c
73
src/file.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
|
|
327
src/http.c
327
src/http.c
|
@ -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;
|
||||
|
|
28
src/http.h
28
src/http.h
|
@ -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;
|
||||
|
|
94
src/httpc.c
94
src/httpc.c
|
@ -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;
|
||||
|
|
36
src/idnode.c
36
src/idnode.c
|
@ -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 */
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
29
src/input.c
29
src/input.c
|
@ -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);
|
||||
|
|
26
src/input.h
26
src/input.h
|
@ -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"
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
v4l/v4l.h
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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,11 +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_sid_chnum;
|
||||
int mn_localtime;
|
||||
int mn_satpos;
|
||||
};
|
||||
|
||||
typedef enum mpegts_mux_scan_state
|
||||
|
@ -329,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
|
||||
{
|
||||
|
@ -368,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;
|
||||
|
||||
|
@ -406,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*);
|
||||
|
@ -420,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;
|
||||
|
@ -444,6 +527,9 @@ struct mpegts_service
|
|||
uint16_t s_dvb_prefcapid;
|
||||
int s_dvb_prefcapid_lock;
|
||||
uint16_t s_dvb_forcecaid;
|
||||
time_t s_dvb_created;
|
||||
time_t s_dvb_last_seen;
|
||||
time_t s_dvb_check_seen;
|
||||
|
||||
/*
|
||||
* EIT/EPG control
|
||||
|
@ -451,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
|
||||
|
@ -471,6 +558,7 @@ struct mpegts_service
|
|||
* in order to recude load.
|
||||
*/
|
||||
sbuf_t s_tsbuf;
|
||||
time_t s_tsbuf_last;
|
||||
|
||||
/**
|
||||
* Average continuity errors
|
||||
|
@ -497,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;
|
||||
|
@ -508,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
|
||||
|
@ -543,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;
|
||||
|
||||
|
||||
/*
|
||||
|
@ -560,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 */
|
||||
|
@ -577,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;
|
||||
|
@ -595,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*);
|
||||
};
|
||||
|
||||
|
@ -658,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 );
|
||||
|
@ -673,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) );
|
||||
|
@ -704,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,
|
||||
|
@ -728,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 );
|
||||
|
@ -738,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 );
|
||||
|
||||
|
@ -748,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 );
|
||||
|
@ -780,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 );
|
||||
|
||||
|
@ -791,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);
|
||||
|
@ -806,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);
|
||||
|
@ -814,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 );
|
||||
|
@ -840,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
|
||||
*/
|
||||
|
@ -857,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;
|
||||
|
@ -877,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
Loading…
Add table
Reference in a new issue